changeset 12:bc29606a740c

Added dwt-addons in original directory structure of eclipse.org
author Frank Benoit <benoit@tionex.de>
date Sat, 14 Mar 2009 18:23:29 +0100
parents 43904fec5dca
children a6998d2a84b3
files java/src/java/beans/PropertyChangeEvent.d java/src/java/beans/PropertyChangeListener.d java/src/java/beans/PropertyChangeSupport.d java/src/java/io/BufferedReader.d java/src/java/io/PushbackReader.d java/src/java/io/StringReader.d java/src/java/io/StringWriter.d java/src/java/lang/JThread.d java/src/java/lang/Math.d java/src/java/net/MalformedURLException.d java/src/java/net/URL.d java/src/java/text/CharacterIterator.d java/src/java/text/MessageFormat.d java/src/java/util/Date.d java/src/java/util/Random.d java/src/java/util/StringTokenizer.d java/src/java/util/Timer.d java/src/java/util/TimerTask.d java/src/java/util/regex/Matcher.d java/src/java/util/regex/Pattern.d java/src/java/util/regex/PatternException.d org.eclipse.core.commands/src/org/eclipse/core/commands/AbstractHandler.d org.eclipse.core.commands/src/org/eclipse/core/commands/AbstractHandlerWithState.d org.eclipse.core.commands/src/org/eclipse/core/commands/AbstractParameterValueConverter.d org.eclipse.core.commands/src/org/eclipse/core/commands/Category.d org.eclipse.core.commands/src/org/eclipse/core/commands/CategoryEvent.d org.eclipse.core.commands/src/org/eclipse/core/commands/Command.d org.eclipse.core.commands/src/org/eclipse/core/commands/CommandEvent.d org.eclipse.core.commands/src/org/eclipse/core/commands/CommandManager.d org.eclipse.core.commands/src/org/eclipse/core/commands/CommandManagerEvent.d org.eclipse.core.commands/src/org/eclipse/core/commands/ExecutionEvent.d org.eclipse.core.commands/src/org/eclipse/core/commands/ExecutionException.d org.eclipse.core.commands/src/org/eclipse/core/commands/HandlerEvent.d org.eclipse.core.commands/src/org/eclipse/core/commands/ICategoryListener.d org.eclipse.core.commands/src/org/eclipse/core/commands/ICommandListener.d org.eclipse.core.commands/src/org/eclipse/core/commands/ICommandManagerListener.d org.eclipse.core.commands/src/org/eclipse/core/commands/IExecutionListener.d org.eclipse.core.commands/src/org/eclipse/core/commands/IExecutionListenerWithChecks.d org.eclipse.core.commands/src/org/eclipse/core/commands/IHandler.d org.eclipse.core.commands/src/org/eclipse/core/commands/IHandler2.d org.eclipse.core.commands/src/org/eclipse/core/commands/IHandlerAttributes.d org.eclipse.core.commands/src/org/eclipse/core/commands/IHandlerListener.d org.eclipse.core.commands/src/org/eclipse/core/commands/INamedHandleStateIds.d org.eclipse.core.commands/src/org/eclipse/core/commands/IObjectWithState.d org.eclipse.core.commands/src/org/eclipse/core/commands/IParameter.d org.eclipse.core.commands/src/org/eclipse/core/commands/IParameterTypeListener.d org.eclipse.core.commands/src/org/eclipse/core/commands/IParameterValues.d org.eclipse.core.commands/src/org/eclipse/core/commands/IStateListener.d org.eclipse.core.commands/src/org/eclipse/core/commands/ITypedParameter.d org.eclipse.core.commands/src/org/eclipse/core/commands/NamedHandleObjectWithState.d org.eclipse.core.commands/src/org/eclipse/core/commands/NotEnabledException.d org.eclipse.core.commands/src/org/eclipse/core/commands/NotHandledException.d org.eclipse.core.commands/src/org/eclipse/core/commands/ParameterType.d org.eclipse.core.commands/src/org/eclipse/core/commands/ParameterTypeEvent.d org.eclipse.core.commands/src/org/eclipse/core/commands/ParameterValueConversionException.d org.eclipse.core.commands/src/org/eclipse/core/commands/ParameterValuesException.d org.eclipse.core.commands/src/org/eclipse/core/commands/Parameterization.d org.eclipse.core.commands/src/org/eclipse/core/commands/ParameterizedCommand.d org.eclipse.core.commands/src/org/eclipse/core/commands/SerializationException.d org.eclipse.core.commands/src/org/eclipse/core/commands/State.d org.eclipse.core.commands/src/org/eclipse/core/commands/common/AbstractBitSetEvent.d org.eclipse.core.commands/src/org/eclipse/core/commands/common/AbstractHandleObjectEvent.d org.eclipse.core.commands/src/org/eclipse/core/commands/common/AbstractNamedHandleEvent.d org.eclipse.core.commands/src/org/eclipse/core/commands/common/CommandException.d org.eclipse.core.commands/src/org/eclipse/core/commands/common/EventManager.d org.eclipse.core.commands/src/org/eclipse/core/commands/common/HandleObject.d org.eclipse.core.commands/src/org/eclipse/core/commands/common/HandleObjectManager.d org.eclipse.core.commands/src/org/eclipse/core/commands/common/IIdentifiable.d org.eclipse.core.commands/src/org/eclipse/core/commands/common/NamedHandleObject.d org.eclipse.core.commands/src/org/eclipse/core/commands/common/NamedHandleObjectComparator.d org.eclipse.core.commands/src/org/eclipse/core/commands/common/NotDefinedException.d org.eclipse.core.commands/src/org/eclipse/core/commands/contexts/Context.d org.eclipse.core.commands/src/org/eclipse/core/commands/contexts/ContextEvent.d org.eclipse.core.commands/src/org/eclipse/core/commands/contexts/ContextManager.d org.eclipse.core.commands/src/org/eclipse/core/commands/contexts/ContextManagerEvent.d org.eclipse.core.commands/src/org/eclipse/core/commands/contexts/IContextListener.d org.eclipse.core.commands/src/org/eclipse/core/commands/contexts/IContextManagerListener.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/AbstractOperation.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/DefaultOperationHistory.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IAdvancedUndoableOperation.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IAdvancedUndoableOperation2.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/ICompositeOperation.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IContextReplacingOperation.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IOperationApprover.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IOperationApprover2.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IOperationHistory.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IOperationHistoryListener.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IUndoContext.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IUndoableOperation.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/LinearUndoEnforcer.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/LinearUndoViolationDetector.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/ObjectUndoContext.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/OperationHistoryEvent.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/OperationHistoryFactory.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/OperationStatus.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/TriggeredOperations.d org.eclipse.core.commands/src/org/eclipse/core/commands/operations/UndoContext.d org.eclipse.core.commands/src/org/eclipse/core/commands/util/Tracing.d org.eclipse.core.commands/src/org/eclipse/core/internal/commands/operations/GlobalUndoContext.d org.eclipse.core.commands/src/org/eclipse/core/internal/commands/util/Util.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Deadlock.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/DeadlockDetector.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/ImplicitJobs.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/InternalJob.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobActivator.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobChangeEvent.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobListeners.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobManager.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobMessages.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobOSGiUtils.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobQueue.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobStatus.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/LockManager.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/ObjectMap.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/OrderedLock.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Queue.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Semaphore.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/StringPool.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/ThreadJob.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Worker.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/WorkerPool.d org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/IJobChangeEvent.d org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/IJobChangeListener.d org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/IJobManager.d org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/IJobStatus.d org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/ILock.d org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/ISchedulingRule.d org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/Job.d org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/JobChangeAdapter.d org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/LockListener.d org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/MultiRule.d org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/ProgressProvider.d org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/messages.properties org.eclipse.core.runtime/src/org/eclipse/core/runtime/ILog.d org.eclipse.core.runtime/src/org/eclipse/core/runtime/Platform.d org.eclipse.draw2d/src/org/eclipse/draw2d/AbsoluteBendpoint.d org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractBackground.d org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractConnectionAnchor.d org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractHintLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractLabeledBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractLocator.d org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractRouter.d org.eclipse.draw2d/src/org/eclipse/draw2d/AccessibleBase.d org.eclipse.draw2d/src/org/eclipse/draw2d/ActionEvent.d org.eclipse.draw2d/src/org/eclipse/draw2d/ActionListener.d org.eclipse.draw2d/src/org/eclipse/draw2d/AncestorHelper.d org.eclipse.draw2d/src/org/eclipse/draw2d/AncestorListener.d org.eclipse.draw2d/src/org/eclipse/draw2d/AnchorListener.d org.eclipse.draw2d/src/org/eclipse/draw2d/Animation.d org.eclipse.draw2d/src/org/eclipse/draw2d/Animator.d org.eclipse.draw2d/src/org/eclipse/draw2d/ArrowButton.d org.eclipse.draw2d/src/org/eclipse/draw2d/ArrowLocator.d org.eclipse.draw2d/src/org/eclipse/draw2d/AutomaticRouter.d org.eclipse.draw2d/src/org/eclipse/draw2d/Bendpoint.d org.eclipse.draw2d/src/org/eclipse/draw2d/BendpointConnectionRouter.d org.eclipse.draw2d/src/org/eclipse/draw2d/BendpointLocator.d org.eclipse.draw2d/src/org/eclipse/draw2d/Border.d org.eclipse.draw2d/src/org/eclipse/draw2d/BorderLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/BufferedGraphicsSource.d org.eclipse.draw2d/src/org/eclipse/draw2d/Button.d org.eclipse.draw2d/src/org/eclipse/draw2d/ButtonBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/ButtonGroup.d org.eclipse.draw2d/src/org/eclipse/draw2d/ButtonModel.d org.eclipse.draw2d/src/org/eclipse/draw2d/ButtonStateTransitionListener.d org.eclipse.draw2d/src/org/eclipse/draw2d/ChangeEvent.d org.eclipse.draw2d/src/org/eclipse/draw2d/ChangeListener.d org.eclipse.draw2d/src/org/eclipse/draw2d/CheckBox.d org.eclipse.draw2d/src/org/eclipse/draw2d/ChopboxAnchor.d org.eclipse.draw2d/src/org/eclipse/draw2d/Clickable.d org.eclipse.draw2d/src/org/eclipse/draw2d/ClickableEventHandler.d org.eclipse.draw2d/src/org/eclipse/draw2d/ColorConstants.d org.eclipse.draw2d/src/org/eclipse/draw2d/CompoundBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/Connection.d org.eclipse.draw2d/src/org/eclipse/draw2d/ConnectionAnchor.d org.eclipse.draw2d/src/org/eclipse/draw2d/ConnectionAnchorBase.d org.eclipse.draw2d/src/org/eclipse/draw2d/ConnectionEndpointLocator.d org.eclipse.draw2d/src/org/eclipse/draw2d/ConnectionLayer.d org.eclipse.draw2d/src/org/eclipse/draw2d/ConnectionLocator.d org.eclipse.draw2d/src/org/eclipse/draw2d/ConnectionRouter.d org.eclipse.draw2d/src/org/eclipse/draw2d/CoordinateListener.d org.eclipse.draw2d/src/org/eclipse/draw2d/Cursors.d org.eclipse.draw2d/src/org/eclipse/draw2d/DefaultRangeModel.d org.eclipse.draw2d/src/org/eclipse/draw2d/DeferredUpdateManager.d org.eclipse.draw2d/src/org/eclipse/draw2d/DelegatingLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/Ellipse.d org.eclipse.draw2d/src/org/eclipse/draw2d/EllipseAnchor.d org.eclipse.draw2d/src/org/eclipse/draw2d/EventDispatcher.d org.eclipse.draw2d/src/org/eclipse/draw2d/EventListenerList.d org.eclipse.draw2d/src/org/eclipse/draw2d/ExclusionSearch.d org.eclipse.draw2d/src/org/eclipse/draw2d/FanRouter.d org.eclipse.draw2d/src/org/eclipse/draw2d/Figure.d org.eclipse.draw2d/src/org/eclipse/draw2d/FigureCanvas.d org.eclipse.draw2d/src/org/eclipse/draw2d/FigureListener.d org.eclipse.draw2d/src/org/eclipse/draw2d/FigureUtilities.d org.eclipse.draw2d/src/org/eclipse/draw2d/FlowLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/FocusBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/FocusEvent.d org.eclipse.draw2d/src/org/eclipse/draw2d/FocusListener.d org.eclipse.draw2d/src/org/eclipse/draw2d/FocusTraverseManager.d org.eclipse.draw2d/src/org/eclipse/draw2d/FrameBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/FreeformFigure.d org.eclipse.draw2d/src/org/eclipse/draw2d/FreeformHelper.d org.eclipse.draw2d/src/org/eclipse/draw2d/FreeformLayer.d org.eclipse.draw2d/src/org/eclipse/draw2d/FreeformLayeredPane.d org.eclipse.draw2d/src/org/eclipse/draw2d/FreeformLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/FreeformListener.d org.eclipse.draw2d/src/org/eclipse/draw2d/FreeformViewport.d org.eclipse.draw2d/src/org/eclipse/draw2d/Graphics.d org.eclipse.draw2d/src/org/eclipse/draw2d/GraphicsSource.d org.eclipse.draw2d/src/org/eclipse/draw2d/GridData.d org.eclipse.draw2d/src/org/eclipse/draw2d/GridLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/GroupBoxBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/IFigure.d org.eclipse.draw2d/src/org/eclipse/draw2d/ImageFigure.d org.eclipse.draw2d/src/org/eclipse/draw2d/ImageUtilities.d org.eclipse.draw2d/src/org/eclipse/draw2d/InputEvent.d org.eclipse.draw2d/src/org/eclipse/draw2d/KeyEvent.d org.eclipse.draw2d/src/org/eclipse/draw2d/KeyListener.d org.eclipse.draw2d/src/org/eclipse/draw2d/Label.d org.eclipse.draw2d/src/org/eclipse/draw2d/LabelAnchor.d org.eclipse.draw2d/src/org/eclipse/draw2d/LabeledBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/LabeledContainer.d org.eclipse.draw2d/src/org/eclipse/draw2d/Layer.d org.eclipse.draw2d/src/org/eclipse/draw2d/LayeredPane.d org.eclipse.draw2d/src/org/eclipse/draw2d/LayoutAnimator.d org.eclipse.draw2d/src/org/eclipse/draw2d/LayoutListener.d org.eclipse.draw2d/src/org/eclipse/draw2d/LayoutManager.d org.eclipse.draw2d/src/org/eclipse/draw2d/LightweightSystem.d org.eclipse.draw2d/src/org/eclipse/draw2d/LineBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/Locator.d org.eclipse.draw2d/src/org/eclipse/draw2d/ManhattanConnectionRouter.d org.eclipse.draw2d/src/org/eclipse/draw2d/MarginBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/MidpointLocator.d org.eclipse.draw2d/src/org/eclipse/draw2d/MouseEvent.d org.eclipse.draw2d/src/org/eclipse/draw2d/MouseListener.d org.eclipse.draw2d/src/org/eclipse/draw2d/MouseMotionListener.d org.eclipse.draw2d/src/org/eclipse/draw2d/NativeGraphicsSource.d org.eclipse.draw2d/src/org/eclipse/draw2d/Orientable.d org.eclipse.draw2d/src/org/eclipse/draw2d/Panel.d org.eclipse.draw2d/src/org/eclipse/draw2d/Polygon.d org.eclipse.draw2d/src/org/eclipse/draw2d/PolygonDecoration.d org.eclipse.draw2d/src/org/eclipse/draw2d/Polyline.d org.eclipse.draw2d/src/org/eclipse/draw2d/PolylineConnection.d org.eclipse.draw2d/src/org/eclipse/draw2d/PolylineDecoration.d org.eclipse.draw2d/src/org/eclipse/draw2d/PopUpHelper.d org.eclipse.draw2d/src/org/eclipse/draw2d/PositionConstants.d org.eclipse.draw2d/src/org/eclipse/draw2d/PrintFigureOperation.d org.eclipse.draw2d/src/org/eclipse/draw2d/PrintOperation.d org.eclipse.draw2d/src/org/eclipse/draw2d/PrinterGraphics.d org.eclipse.draw2d/src/org/eclipse/draw2d/RangeModel.d org.eclipse.draw2d/src/org/eclipse/draw2d/RectangleFigure.d org.eclipse.draw2d/src/org/eclipse/draw2d/RelativeBendpoint.d org.eclipse.draw2d/src/org/eclipse/draw2d/RelativeLocator.d org.eclipse.draw2d/src/org/eclipse/draw2d/RotatableDecoration.d org.eclipse.draw2d/src/org/eclipse/draw2d/RoundedRectangle.d org.eclipse.draw2d/src/org/eclipse/draw2d/RoutingAnimator.d org.eclipse.draw2d/src/org/eclipse/draw2d/RoutingListener.d org.eclipse.draw2d/src/org/eclipse/draw2d/SWTEventDispatcher.d org.eclipse.draw2d/src/org/eclipse/draw2d/SWTGraphics.d org.eclipse.draw2d/src/org/eclipse/draw2d/ScalableFigure.d org.eclipse.draw2d/src/org/eclipse/draw2d/ScalableFreeformLayeredPane.d org.eclipse.draw2d/src/org/eclipse/draw2d/ScalableLayeredPane.d org.eclipse.draw2d/src/org/eclipse/draw2d/ScaledGraphics.d org.eclipse.draw2d/src/org/eclipse/draw2d/SchemeBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/ScrollBar.d org.eclipse.draw2d/src/org/eclipse/draw2d/ScrollBarLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/ScrollPane.d org.eclipse.draw2d/src/org/eclipse/draw2d/ScrollPaneLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/ScrollPaneSolver.d org.eclipse.draw2d/src/org/eclipse/draw2d/Shape.d org.eclipse.draw2d/src/org/eclipse/draw2d/ShortestPathConnectionRouter.d org.eclipse.draw2d/src/org/eclipse/draw2d/SimpleEtchedBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/SimpleLoweredBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/SimpleRaisedBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/StackLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/SubordinateUpdateManager.d org.eclipse.draw2d/src/org/eclipse/draw2d/TextUtilities.d org.eclipse.draw2d/src/org/eclipse/draw2d/TitleBarBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/Toggle.d org.eclipse.draw2d/src/org/eclipse/draw2d/ToggleButton.d org.eclipse.draw2d/src/org/eclipse/draw2d/ToggleModel.d org.eclipse.draw2d/src/org/eclipse/draw2d/ToolTipHelper.d org.eclipse.draw2d/src/org/eclipse/draw2d/ToolbarLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/TreeSearch.d org.eclipse.draw2d/src/org/eclipse/draw2d/Triangle.d org.eclipse.draw2d/src/org/eclipse/draw2d/UpdateListener.d org.eclipse.draw2d/src/org/eclipse/draw2d/UpdateManager.d org.eclipse.draw2d/src/org/eclipse/draw2d/Viewport.d org.eclipse.draw2d/src/org/eclipse/draw2d/ViewportLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/XYAnchor.d org.eclipse.draw2d/src/org/eclipse/draw2d/XYLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Dimension.d org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Geometry.d org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Insets.d org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Point.d org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/PointList.d org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/PrecisionDimension.d org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/PrecisionPoint.d org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/PrecisionRectangle.d org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Ray.d org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Rectangle.d org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Transform.d org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Translatable.d org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Transposer.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/BreakCycles.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Cell.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CollapsedEdges.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CompoundBreakCycles.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CompoundDirectedGraph.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CompoundDirectedGraphLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CompoundHorizontalPlacement.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CompoundPopulateRanks.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CompoundRankSorter.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CompoundVerticalPlacement.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/ConvertCompoundGraph.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/DirectedGraph.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/DirectedGraphLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Edge.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/EdgeList.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/GraphUtilities.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/GraphVisitor.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/HorizontalPlacement.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/InitialRankSolver.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/InvertEdges.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/LocalOptimizer.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/MinCross.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/NestingTree.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Node.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/NodeCluster.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/NodeList.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/NodePair.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Obstacle.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Path.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/PopulateRanks.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Rank.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/RankAssignmentSolver.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/RankList.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/RankSorter.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/RevertableChange.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/RouteEdges.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Segment.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/ShortestPathRouter.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/SortSubgraphs.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/SpanningTreeVisitor.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Subgraph.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/SubgraphBoundary.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/TightSpanningTreeSolver.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/TransposeMetrics.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Vertex.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/VerticalPlacement.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/VirtualNode.d org.eclipse.draw2d/src/org/eclipse/draw2d/graph/VirtualNodeCreation.d org.eclipse.draw2d/src/org/eclipse/draw2d/internal/MultiValueMap.d org.eclipse.draw2d/src/org/eclipse/draw2d/parts/ScrollableThumbnail.d org.eclipse.draw2d/src/org/eclipse/draw2d/parts/Thumbnail.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/AbstractFlowBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/BidiChars.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/BidiInfo.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/BidiProcessor.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/BlockBox.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/BlockFlow.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/BlockFlowLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/CaretInfo.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/CompositeBox.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/ContentBox.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowAdapter.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowBox.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowContainerLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowContext.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowFigure.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowFigureLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowPage.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowUtilities.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/InlineFlow.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/InlineFlowLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/LineBox.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/LineRoot.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/NestedLine.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/PageFlowLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/ParagraphTextLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/SimpleTextLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/TextFlow.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/TextFragmentBox.d org.eclipse.draw2d/src/org/eclipse/draw2d/text/TextLayout.d org.eclipse.draw2d/src/org/eclipse/draw2d/widgets/ImageBorder.d org.eclipse.draw2d/src/org/eclipse/draw2d/widgets/MultiLineLabel.d org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/IRuntimeConstants.d org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/LocalizationUtils.d org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/PrintStackUtil.d org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/RuntimeLog.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/Assert.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/AssertionFailedException.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/CoreException.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/IAdaptable.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/IAdapterFactory.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/IAdapterManager.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/ILogListener.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/IPath.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/IProgressMonitor.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/IProgressMonitorWithBlocking.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/ISafeRunnable.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/IStatus.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/ListenerList.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/MultiStatus.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/NullProgressMonitor.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/OperationCanceledException.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/Path.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/PlatformObject.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/ProgressMonitorWrapper.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/QualifiedName.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/SafeRunner.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/Status.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/SubMonitor.d org.eclipse.equinox.common/src/org/eclipse/core/runtime/SubProgressMonitor.d org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/AnnotationBag.d org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/IProjectionListener.d org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/IProjectionPosition.d org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionAnnotation.d org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionAnnotationHover.d org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionAnnotationModel.d org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionRulerColumn.d org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionSummary.d org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionSupport.d org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionViewer.d org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/SourceViewerInformationControl.d org.eclipse.jface.text/src/org/eclipse/jface/contentassist/IContentAssistSubjectControl.d org.eclipse.jface.text/src/org/eclipse/jface/contentassist/ISubjectControlContentAssistProcessor.d org.eclipse.jface.text/src/org/eclipse/jface/contentassist/ISubjectControlContentAssistant.d org.eclipse.jface.text/src/org/eclipse/jface/contentassist/ISubjectControlContextInformationPresenter.d org.eclipse.jface.text/src/org/eclipse/jface/contentassist/ISubjectControlContextInformationValidator.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/DelayedInputChangeListener.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/InformationControlReplacer.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/InternalAccessor.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/NonDeletingPositionUpdater.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/StickyHoverManager.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/TableOwnerDrawSupport.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/BrowserInformationControl.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/BrowserInformationControlInput.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/BrowserInput.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTML2TextReader.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLMessages.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLPrinter.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLTextPresenter.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/SingleCharReader.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/SubstitutionTextReader.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/AdditionalInfoController2.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/CompletionProposalPopup2.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/ContentAssistMessages.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/ContentAssistant2.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/ContextInformationPopup2.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/Helper2.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/IContentAssistListener2.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/IProposalListener.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/LineBreakingReader.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/PopupCloser2.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/ChangeRegion.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/Colors.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/Hunk.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/HunkComputer.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/LineIndexOutOfBoundsException.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/Range.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/RevisionPainter.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/RevisionSelectionProvider.d org.eclipse.jface.text/src/org/eclipse/jface/internal/text/source/DiffPainter.d org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractHoverInformationControlManager.d org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractInformationControl.d org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractInformationControlManager.d org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractReusableInformationControlCreator.d org.eclipse.jface.text/src/org/eclipse/jface/text/CursorLinePainter.d org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultAutoIndentStrategy.d org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultDocumentAdapter.d org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultIndentLineAutoEditStrategy.d org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultInformationControl.d org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultTextDoubleClickStrategy.d org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultTextHover.d org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultUndoManager.d org.eclipse.jface.text/src/org/eclipse/jface/text/DocumentClone.d org.eclipse.jface.text/src/org/eclipse/jface/text/DocumentCommand.d org.eclipse.jface.text/src/org/eclipse/jface/text/FindReplaceDocumentAdapterContentProposalProvider.d org.eclipse.jface.text/src/org/eclipse/jface/text/IAutoEditStrategy.d org.eclipse.jface.text/src/org/eclipse/jface/text/IAutoIndentStrategy.d org.eclipse.jface.text/src/org/eclipse/jface/text/IDelayedInputChangeProvider.d org.eclipse.jface.text/src/org/eclipse/jface/text/IDocumentAdapter.d org.eclipse.jface.text/src/org/eclipse/jface/text/IDocumentAdapterExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/IEditingSupport.d org.eclipse.jface.text/src/org/eclipse/jface/text/IEditingSupportRegistry.d org.eclipse.jface.text/src/org/eclipse/jface/text/IEventConsumer.d org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTarget.d org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTargetExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTargetExtension3.d org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControl.d org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlCreator.d org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlCreatorExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlExtension2.d org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlExtension3.d org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlExtension4.d org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlExtension5.d org.eclipse.jface.text/src/org/eclipse/jface/text/IInputChangedListener.d org.eclipse.jface.text/src/org/eclipse/jface/text/IMarkRegionTarget.d org.eclipse.jface.text/src/org/eclipse/jface/text/IMarkSelection.d org.eclipse.jface.text/src/org/eclipse/jface/text/IPaintPositionManager.d org.eclipse.jface.text/src/org/eclipse/jface/text/IPainter.d org.eclipse.jface.text/src/org/eclipse/jface/text/IRewriteTarget.d org.eclipse.jface.text/src/org/eclipse/jface/text/ISelectionValidator.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextDoubleClickStrategy.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextHover.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextHoverExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextHoverExtension2.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextInputListener.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextListener.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextOperationTarget.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextOperationTargetExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextPresentationListener.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextSelection.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewer.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension2.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension3.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension4.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension5.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension6.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension7.d org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension8.d org.eclipse.jface.text/src/org/eclipse/jface/text/IUndoManager.d org.eclipse.jface.text/src/org/eclipse/jface/text/IUndoManagerExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/IViewportListener.d org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenKeeper.d org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenKeeperExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenOwner.d org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenOwnerExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/JFaceTextMessages.d org.eclipse.jface.text/src/org/eclipse/jface/text/JFaceTextUtil.d org.eclipse.jface.text/src/org/eclipse/jface/text/MarginPainter.d org.eclipse.jface.text/src/org/eclipse/jface/text/MarkSelection.d org.eclipse.jface.text/src/org/eclipse/jface/text/PaintManager.d org.eclipse.jface.text/src/org/eclipse/jface/text/PropagatingFontFieldEditor.d org.eclipse.jface.text/src/org/eclipse/jface/text/RegExMessages.d org.eclipse.jface.text/src/org/eclipse/jface/text/TabsToSpacesConverter.d org.eclipse.jface.text/src/org/eclipse/jface/text/TextAttribute.d org.eclipse.jface.text/src/org/eclipse/jface/text/TextEvent.d org.eclipse.jface.text/src/org/eclipse/jface/text/TextPresentation.d org.eclipse.jface.text/src/org/eclipse/jface/text/TextSelection.d org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewer.d org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewerHoverManager.d org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewerUndoManager.d org.eclipse.jface.text/src/org/eclipse/jface/text/WhitespaceCharacterPainter.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AdditionalInfoController.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposal.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistEvent.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistSubjectControlAdapter.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformation.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformationPopup.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformationValidator.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/Helper.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionListener.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionListenerExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposal.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension2.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension3.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension4.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension5.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension6.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistListener.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistProcessor.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistant.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistantExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistantExtension2.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistantExtension3.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistantExtension4.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformation.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationPresenter.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationValidator.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/JFaceTextMessages.d org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/PopupCloser.d org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/ContentFormatter.d org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/ContextBasedFormattingStrategy.d org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/FormattingContext.d org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/FormattingContextProperties.d org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IContentFormatter.d org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IContentFormatterExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IFormattingContext.d org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IFormattingStrategy.d org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IFormattingStrategyExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/MultiPassContentFormatter.d org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/AbstractHyperlinkDetector.d org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/DefaultHyperlinkPresenter.d org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/HyperlinkManager.d org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/HyperlinkMessages.d org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlink.d org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlinkDetector.d org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlinkDetectorExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlinkDetectorExtension2.d org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlinkPresenter.d org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlinkPresenterExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/MultipleHyperlinkPresenter.d org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/URLHyperlink.d org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/URLHyperlinkDetector.d org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationPresenter.d org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationPresenterExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationProvider.d org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationProviderExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationProviderExtension2.d org.eclipse.jface.text/src/org/eclipse/jface/text/information/InformationPresenter.d org.eclipse.jface.text/src/org/eclipse/jface/text/link/LinkedModeUI.d org.eclipse.jface.text/src/org/eclipse/jface/text/link/LinkedPositionAnnotations.d org.eclipse.jface.text/src/org/eclipse/jface/text/link/ProposalPosition.d org.eclipse.jface.text/src/org/eclipse/jface/text/link/TabStopIterator.d org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationDamager.d org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationReconciler.d org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationReconcilerExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationRepairer.d org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/PresentationReconciler.d org.eclipse.jface.text/src/org/eclipse/jface/text/quickassist/IQuickAssistAssistant.d org.eclipse.jface.text/src/org/eclipse/jface/text/quickassist/IQuickAssistAssistantExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/quickassist/IQuickAssistInvocationContext.d org.eclipse.jface.text/src/org/eclipse/jface/text/quickassist/IQuickAssistProcessor.d org.eclipse.jface.text/src/org/eclipse/jface/text/quickassist/IQuickFixableAnnotation.d org.eclipse.jface.text/src/org/eclipse/jface/text/quickassist/QuickAssistAssistant.d org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/AbstractReconcileStep.d org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/AbstractReconciler.d org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/DirtyRegion.d org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/DirtyRegionQueue.d org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilableModel.d org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcileResult.d org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcileStep.d org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconciler.d org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilerExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilingStrategy.d org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilingStrategyExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/MonoReconciler.d org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/Reconciler.d org.eclipse.jface.text/src/org/eclipse/jface/text/revisions/IRevisionListener.d org.eclipse.jface.text/src/org/eclipse/jface/text/revisions/IRevisionRulerColumn.d org.eclipse.jface.text/src/org/eclipse/jface/text/revisions/IRevisionRulerColumnExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/revisions/Revision.d org.eclipse.jface.text/src/org/eclipse/jface/text/revisions/RevisionEvent.d org.eclipse.jface.text/src/org/eclipse/jface/text/revisions/RevisionInformation.d org.eclipse.jface.text/src/org/eclipse/jface/text/revisions/RevisionRange.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/BufferedRuleBasedScanner.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/DefaultDamagerRepairer.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/DefaultPartitioner.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/EndOfLineRule.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/FastPartitioner.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/ICharacterScanner.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IPartitionTokenScanner.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IPredicateRule.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IRule.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IToken.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/ITokenScanner.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IWhitespaceDetector.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IWordDetector.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/MultiLineRule.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/NumberRule.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/PatternRule.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/RuleBasedDamagerRepairer.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/RuleBasedPartitionScanner.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/RuleBasedPartitioner.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/RuleBasedScanner.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/SingleLineRule.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/Token.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/WhitespaceRule.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/WordPatternRule.d org.eclipse.jface.text/src/org/eclipse/jface/text/rules/WordRule.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/AbstractRulerColumn.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationBarHoverManager.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationColumn.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationPainter.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationRulerColumn.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/ChangeRulerColumn.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/CompositeRuler.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/ContentAssistantFacade.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/DefaultAnnotationHover.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/DefaultCharacterPairMatcher.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationAccess.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationAccessExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationAccessExtension2.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationHover.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationHoverExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationHoverExtension2.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationPresentation.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/IChangeRulerColumn.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/ICharacterPairMatcher.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/ILineDiffInfo.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/ILineDiffer.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/ILineDifferExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/ILineDifferExtension2.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/ILineRange.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/IOverviewRuler.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/ISharedTextColors.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/ISourceViewer.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/ISourceViewerExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/ISourceViewerExtension2.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/ISourceViewerExtension3.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/ISourceViewerExtension4.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRuler.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRulerColumn.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRulerExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRulerInfo.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRulerInfoExtension.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRulerListener.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/ImageUtilities.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/JFaceTextMessages.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/LineChangeHover.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/LineNumberChangeRulerColumn.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/LineNumberRulerColumn.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/LineRange.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/MatchingCharacterPainter.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/OverviewRuler.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/OverviewRulerHoverManager.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewer.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewerConfiguration.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/TextInvocationContext.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/VerticalRuler.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/VerticalRulerEvent.d org.eclipse.jface.text/src/org/eclipse/jface/text/source/VisualAnnotationModel.d org.eclipse.jface.text/src/org/eclipse/jface/text/templates/ContextTypeRegistry.d org.eclipse.jface.text/src/org/eclipse/jface/text/templates/InclusivePositionUpdater.d org.eclipse.jface.text/src/org/eclipse/jface/text/templates/JFaceTextTemplateMessages.d org.eclipse.jface.text/src/org/eclipse/jface/text/templates/PositionBasedCompletionProposal.d org.eclipse.jface.text/src/org/eclipse/jface/text/templates/TemplateCompletionProcessor.d org.eclipse.jface.text/src/org/eclipse/jface/text/templates/TemplateProposal.d org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplatePersistenceData.d org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplatePersistenceMessages.d org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplateReaderWriter.d org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplateStore.d org.eclipse.jface/src/org/eclipse/jface/action/AbstractAction.d org.eclipse.jface/src/org/eclipse/jface/action/AbstractGroupMarker.d org.eclipse.jface/src/org/eclipse/jface/action/Action.d org.eclipse.jface/src/org/eclipse/jface/action/ActionContributionItem.d org.eclipse.jface/src/org/eclipse/jface/action/ContributionItem.d org.eclipse.jface/src/org/eclipse/jface/action/ContributionManager.d org.eclipse.jface/src/org/eclipse/jface/action/ControlContribution.d org.eclipse.jface/src/org/eclipse/jface/action/CoolBarManager.d org.eclipse.jface/src/org/eclipse/jface/action/ExternalActionManager.d org.eclipse.jface/src/org/eclipse/jface/action/ExternalActionManager.properties org.eclipse.jface/src/org/eclipse/jface/action/GroupMarker.d org.eclipse.jface/src/org/eclipse/jface/action/IAction.d org.eclipse.jface/src/org/eclipse/jface/action/IContributionItem.d org.eclipse.jface/src/org/eclipse/jface/action/IContributionManager.d org.eclipse.jface/src/org/eclipse/jface/action/IContributionManagerOverrides.d org.eclipse.jface/src/org/eclipse/jface/action/ICoolBarManager.d org.eclipse.jface/src/org/eclipse/jface/action/IMenuCreator.d org.eclipse.jface/src/org/eclipse/jface/action/IMenuListener.d org.eclipse.jface/src/org/eclipse/jface/action/IMenuListener2.d org.eclipse.jface/src/org/eclipse/jface/action/IMenuManager.d org.eclipse.jface/src/org/eclipse/jface/action/IStatusLineManager.d org.eclipse.jface/src/org/eclipse/jface/action/IToolBarManager.d org.eclipse.jface/src/org/eclipse/jface/action/LegacyActionTools.d org.eclipse.jface/src/org/eclipse/jface/action/MenuManager.d org.eclipse.jface/src/org/eclipse/jface/action/Separator.d org.eclipse.jface/src/org/eclipse/jface/action/StatusLine.d org.eclipse.jface/src/org/eclipse/jface/action/StatusLineContributionItem.d org.eclipse.jface/src/org/eclipse/jface/action/StatusLineLayoutData.d org.eclipse.jface/src/org/eclipse/jface/action/StatusLineManager.d org.eclipse.jface/src/org/eclipse/jface/action/SubContributionItem.d org.eclipse.jface/src/org/eclipse/jface/action/SubContributionManager.d org.eclipse.jface/src/org/eclipse/jface/action/SubCoolBarManager.d org.eclipse.jface/src/org/eclipse/jface/action/SubMenuManager.d org.eclipse.jface/src/org/eclipse/jface/action/SubStatusLineManager.d org.eclipse.jface/src/org/eclipse/jface/action/SubToolBarManager.d org.eclipse.jface/src/org/eclipse/jface/action/ToolBarContributionItem.d org.eclipse.jface/src/org/eclipse/jface/action/ToolBarManager.d org.eclipse.jface/src/org/eclipse/jface/action/images/stop.gif org.eclipse.jface/src/org/eclipse/jface/bindings/Binding.d org.eclipse.jface/src/org/eclipse/jface/bindings/BindingManager.d org.eclipse.jface/src/org/eclipse/jface/bindings/BindingManagerEvent.d org.eclipse.jface/src/org/eclipse/jface/bindings/CachedBindingSet.d org.eclipse.jface/src/org/eclipse/jface/bindings/IBindingManagerListener.d org.eclipse.jface/src/org/eclipse/jface/bindings/ISchemeListener.d org.eclipse.jface/src/org/eclipse/jface/bindings/Scheme.d org.eclipse.jface/src/org/eclipse/jface/bindings/SchemeEvent.d org.eclipse.jface/src/org/eclipse/jface/bindings/Trigger.d org.eclipse.jface/src/org/eclipse/jface/bindings/TriggerSequence.d org.eclipse.jface/src/org/eclipse/jface/bindings/keys/IKeyLookup.d org.eclipse.jface/src/org/eclipse/jface/bindings/keys/KeyBinding.d org.eclipse.jface/src/org/eclipse/jface/bindings/keys/KeyLookupFactory.d org.eclipse.jface/src/org/eclipse/jface/bindings/keys/KeySequence.d org.eclipse.jface/src/org/eclipse/jface/bindings/keys/KeySequenceText.d org.eclipse.jface/src/org/eclipse/jface/bindings/keys/KeyStroke.d org.eclipse.jface/src/org/eclipse/jface/bindings/keys/ParseException.d org.eclipse.jface/src/org/eclipse/jface/bindings/keys/SWTKeyLookup.d org.eclipse.jface/src/org/eclipse/jface/bindings/keys/SWTKeySupport.d org.eclipse.jface/src/org/eclipse/jface/bindings/keys/formatting/AbstractKeyFormatter.d org.eclipse.jface/src/org/eclipse/jface/bindings/keys/formatting/EmacsKeyFormatter.d org.eclipse.jface/src/org/eclipse/jface/bindings/keys/formatting/FormalKeyFormatter.d org.eclipse.jface/src/org/eclipse/jface/bindings/keys/formatting/IKeyFormatter.d org.eclipse.jface/src/org/eclipse/jface/bindings/keys/formatting/KeyFormatterFactory.d org.eclipse.jface/src/org/eclipse/jface/bindings/keys/formatting/NativeKeyFormatter.d org.eclipse.jface/src/org/eclipse/jface/commands/ActionHandler.d org.eclipse.jface/src/org/eclipse/jface/commands/PersistentState.d org.eclipse.jface/src/org/eclipse/jface/commands/RadioState.d org.eclipse.jface/src/org/eclipse/jface/commands/ToggleState.d org.eclipse.jface/src/org/eclipse/jface/contexts/IContextIds.d org.eclipse.jface/src/org/eclipse/jface/dialogs/AnimatorFactory.d org.eclipse.jface/src/org/eclipse/jface/dialogs/ControlAnimator.d org.eclipse.jface/src/org/eclipse/jface/dialogs/ControlEnableState.d org.eclipse.jface/src/org/eclipse/jface/dialogs/Dialog.d org.eclipse.jface/src/org/eclipse/jface/dialogs/DialogMessageArea.d org.eclipse.jface/src/org/eclipse/jface/dialogs/DialogPage.d org.eclipse.jface/src/org/eclipse/jface/dialogs/DialogSettings.d org.eclipse.jface/src/org/eclipse/jface/dialogs/DialogTray.d org.eclipse.jface/src/org/eclipse/jface/dialogs/ErrorDialog.d org.eclipse.jface/src/org/eclipse/jface/dialogs/ErrorSupportProvider.d org.eclipse.jface/src/org/eclipse/jface/dialogs/IDialogBlockedHandler.d org.eclipse.jface/src/org/eclipse/jface/dialogs/IDialogConstants.d org.eclipse.jface/src/org/eclipse/jface/dialogs/IDialogPage.d org.eclipse.jface/src/org/eclipse/jface/dialogs/IDialogSettings.d org.eclipse.jface/src/org/eclipse/jface/dialogs/IInputValidator.d org.eclipse.jface/src/org/eclipse/jface/dialogs/IMessageProvider.d org.eclipse.jface/src/org/eclipse/jface/dialogs/IPageChangeProvider.d org.eclipse.jface/src/org/eclipse/jface/dialogs/IPageChangedListener.d org.eclipse.jface/src/org/eclipse/jface/dialogs/IPageChangingListener.d org.eclipse.jface/src/org/eclipse/jface/dialogs/IconAndMessageDialog.d org.eclipse.jface/src/org/eclipse/jface/dialogs/ImageAndMessageArea.d org.eclipse.jface/src/org/eclipse/jface/dialogs/InputDialog.d org.eclipse.jface/src/org/eclipse/jface/dialogs/MessageDialog.d org.eclipse.jface/src/org/eclipse/jface/dialogs/MessageDialogWithToggle.d org.eclipse.jface/src/org/eclipse/jface/dialogs/PageChangedEvent.d org.eclipse.jface/src/org/eclipse/jface/dialogs/PageChangingEvent.d org.eclipse.jface/src/org/eclipse/jface/dialogs/PopupDialog.d org.eclipse.jface/src/org/eclipse/jface/dialogs/ProgressIndicator.d org.eclipse.jface/src/org/eclipse/jface/dialogs/ProgressMonitorDialog.d org.eclipse.jface/src/org/eclipse/jface/dialogs/StatusDialog.d org.eclipse.jface/src/org/eclipse/jface/dialogs/TitleAreaDialog.d org.eclipse.jface/src/org/eclipse/jface/dialogs/TrayDialog.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/AutoCompleteField.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/ComboContentAdapter.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/ContentProposalAdapter.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/ControlDecoration.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/DecoratedField.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/FieldAssistColors.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/FieldDecoration.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/FieldDecorationRegistry.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/IContentProposal.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/IContentProposalListener.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/IContentProposalListener2.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/IContentProposalProvider.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/IControlContentAdapter.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/IControlContentAdapter2.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/IControlCreator.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/SimpleContentProposalProvider.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/TextContentAdapter.d org.eclipse.jface/src/org/eclipse/jface/fieldassist/TextControlCreator.d org.eclipse.jface/src/org/eclipse/jface/internal/ConfigureColumnsDialog.d org.eclipse.jface/src/org/eclipse/jface/internal/InternalPolicy.d org.eclipse.jface/src/org/eclipse/jface/internal/provisional/action/CoolBarManager2.d org.eclipse.jface/src/org/eclipse/jface/internal/provisional/action/ICoolBarManager2.d org.eclipse.jface/src/org/eclipse/jface/internal/provisional/action/IToolBarContributionItem.d org.eclipse.jface/src/org/eclipse/jface/internal/provisional/action/IToolBarManager2.d org.eclipse.jface/src/org/eclipse/jface/internal/provisional/action/ToolBarContributionItem2.d org.eclipse.jface/src/org/eclipse/jface/internal/provisional/action/ToolBarManager2.d org.eclipse.jface/src/org/eclipse/jface/layout/AbstractColumnLayout.d org.eclipse.jface/src/org/eclipse/jface/layout/GridDataFactory.d org.eclipse.jface/src/org/eclipse/jface/layout/GridLayoutFactory.d org.eclipse.jface/src/org/eclipse/jface/layout/LayoutConstants.d org.eclipse.jface/src/org/eclipse/jface/layout/LayoutGenerator.d org.eclipse.jface/src/org/eclipse/jface/layout/TableColumnLayout.d org.eclipse.jface/src/org/eclipse/jface/layout/TreeColumnLayout.d org.eclipse.jface/src/org/eclipse/jface/menus/AbstractTrimWidget.d org.eclipse.jface/src/org/eclipse/jface/menus/IMenuStateIds.d org.eclipse.jface/src/org/eclipse/jface/menus/IWidget.d org.eclipse.jface/src/org/eclipse/jface/menus/TextState.d org.eclipse.jface/src/org/eclipse/jface/operation/AccumulatingProgressMonitor.d org.eclipse.jface/src/org/eclipse/jface/operation/IRunnableContext.d org.eclipse.jface/src/org/eclipse/jface/operation/IRunnableWithProgress.d org.eclipse.jface/src/org/eclipse/jface/operation/IThreadListener.d org.eclipse.jface/src/org/eclipse/jface/operation/ModalContext.d org.eclipse.jface/src/org/eclipse/jface/preference/BooleanFieldEditor.d org.eclipse.jface/src/org/eclipse/jface/preference/BooleanPropertyAction.d org.eclipse.jface/src/org/eclipse/jface/preference/ColorFieldEditor.d org.eclipse.jface/src/org/eclipse/jface/preference/ColorSelector.d org.eclipse.jface/src/org/eclipse/jface/preference/ComboFieldEditor.d org.eclipse.jface/src/org/eclipse/jface/preference/DirectoryFieldEditor.d org.eclipse.jface/src/org/eclipse/jface/preference/FieldEditor.d org.eclipse.jface/src/org/eclipse/jface/preference/FieldEditorPreferencePage.d org.eclipse.jface/src/org/eclipse/jface/preference/FileFieldEditor.d org.eclipse.jface/src/org/eclipse/jface/preference/FontFieldEditor.d org.eclipse.jface/src/org/eclipse/jface/preference/IPersistentPreferenceStore.d org.eclipse.jface/src/org/eclipse/jface/preference/IPreferenceNode.d org.eclipse.jface/src/org/eclipse/jface/preference/IPreferencePage.d org.eclipse.jface/src/org/eclipse/jface/preference/IPreferencePageContainer.d org.eclipse.jface/src/org/eclipse/jface/preference/IPreferenceStore.d org.eclipse.jface/src/org/eclipse/jface/preference/IntegerFieldEditor.d org.eclipse.jface/src/org/eclipse/jface/preference/JFacePreferences.d org.eclipse.jface/src/org/eclipse/jface/preference/ListEditor.d org.eclipse.jface/src/org/eclipse/jface/preference/PathEditor.d org.eclipse.jface/src/org/eclipse/jface/preference/PreferenceContentProvider.d org.eclipse.jface/src/org/eclipse/jface/preference/PreferenceConverter.d org.eclipse.jface/src/org/eclipse/jface/preference/PreferenceDialog.d org.eclipse.jface/src/org/eclipse/jface/preference/PreferenceLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/preference/PreferenceManager.d org.eclipse.jface/src/org/eclipse/jface/preference/PreferenceNode.d org.eclipse.jface/src/org/eclipse/jface/preference/PreferencePage.d org.eclipse.jface/src/org/eclipse/jface/preference/PreferenceStore.d org.eclipse.jface/src/org/eclipse/jface/preference/RadioGroupFieldEditor.d org.eclipse.jface/src/org/eclipse/jface/preference/ScaleFieldEditor.d org.eclipse.jface/src/org/eclipse/jface/preference/StringButtonFieldEditor.d org.eclipse.jface/src/org/eclipse/jface/preference/StringFieldEditor.d org.eclipse.jface/src/org/eclipse/jface/resource/AbstractResourceManager.d org.eclipse.jface/src/org/eclipse/jface/resource/ArrayFontDescriptor.d org.eclipse.jface/src/org/eclipse/jface/resource/ColorDescriptor.d org.eclipse.jface/src/org/eclipse/jface/resource/ColorRegistry.d org.eclipse.jface/src/org/eclipse/jface/resource/CompositeImageDescriptor.d org.eclipse.jface/src/org/eclipse/jface/resource/DataFormatException.d org.eclipse.jface/src/org/eclipse/jface/resource/DerivedImageDescriptor.d org.eclipse.jface/src/org/eclipse/jface/resource/DeviceResourceDescriptor.d org.eclipse.jface/src/org/eclipse/jface/resource/DeviceResourceException.d org.eclipse.jface/src/org/eclipse/jface/resource/DeviceResourceManager.d org.eclipse.jface/src/org/eclipse/jface/resource/FileImageDescriptor.d org.eclipse.jface/src/org/eclipse/jface/resource/FontDescriptor.d org.eclipse.jface/src/org/eclipse/jface/resource/FontRegistry.d org.eclipse.jface/src/org/eclipse/jface/resource/ImageDataImageDescriptor.d org.eclipse.jface/src/org/eclipse/jface/resource/ImageDescriptor.d org.eclipse.jface/src/org/eclipse/jface/resource/ImageRegistry.d org.eclipse.jface/src/org/eclipse/jface/resource/JFaceColors.d org.eclipse.jface/src/org/eclipse/jface/resource/JFaceResources.d org.eclipse.jface/src/org/eclipse/jface/resource/LocalResourceManager.d org.eclipse.jface/src/org/eclipse/jface/resource/MissingImageDescriptor.d org.eclipse.jface/src/org/eclipse/jface/resource/RGBColorDescriptor.d org.eclipse.jface/src/org/eclipse/jface/resource/ResourceManager.d org.eclipse.jface/src/org/eclipse/jface/resource/ResourceRegistry.d org.eclipse.jface/src/org/eclipse/jface/resource/StringConverter.d org.eclipse.jface/src/org/eclipse/jface/resource/URLImageDescriptor.d org.eclipse.jface/src/org/eclipse/jface/util/Assert.d org.eclipse.jface/src/org/eclipse/jface/util/DelegatingDragAdapter.d org.eclipse.jface/src/org/eclipse/jface/util/DelegatingDropAdapter.d org.eclipse.jface/src/org/eclipse/jface/util/Geometry.d org.eclipse.jface/src/org/eclipse/jface/util/ILogger.d org.eclipse.jface/src/org/eclipse/jface/util/IOpenEventListener.d org.eclipse.jface/src/org/eclipse/jface/util/IPropertyChangeListener.d org.eclipse.jface/src/org/eclipse/jface/util/ISafeRunnableRunner.d org.eclipse.jface/src/org/eclipse/jface/util/ListenerList.d org.eclipse.jface/src/org/eclipse/jface/util/LocalSelectionTransfer.d org.eclipse.jface/src/org/eclipse/jface/util/OpenStrategy.d org.eclipse.jface/src/org/eclipse/jface/util/Policy.d org.eclipse.jface/src/org/eclipse/jface/util/PropertyChangeEvent.d org.eclipse.jface/src/org/eclipse/jface/util/SafeRunnable.d org.eclipse.jface/src/org/eclipse/jface/util/SafeRunnableDialog.d org.eclipse.jface/src/org/eclipse/jface/util/StatusHandler.d org.eclipse.jface/src/org/eclipse/jface/util/TransferDragSourceListener.d org.eclipse.jface/src/org/eclipse/jface/util/TransferDropTargetListener.d org.eclipse.jface/src/org/eclipse/jface/util/Util.d org.eclipse.jface/src/org/eclipse/jface/viewers/AbstractComboBoxCellEditor.d org.eclipse.jface/src/org/eclipse/jface/viewers/AbstractListViewer.d org.eclipse.jface/src/org/eclipse/jface/viewers/AbstractTableViewer.d org.eclipse.jface/src/org/eclipse/jface/viewers/AbstractTreeViewer.d org.eclipse.jface/src/org/eclipse/jface/viewers/AcceptAllFilter.d org.eclipse.jface/src/org/eclipse/jface/viewers/ArrayContentProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/BaseLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/CellEditor.d org.eclipse.jface/src/org/eclipse/jface/viewers/CellLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/CellNavigationStrategy.d org.eclipse.jface/src/org/eclipse/jface/viewers/CheckStateChangedEvent.d org.eclipse.jface/src/org/eclipse/jface/viewers/CheckboxCellEditor.d org.eclipse.jface/src/org/eclipse/jface/viewers/CheckboxTableViewer.d org.eclipse.jface/src/org/eclipse/jface/viewers/CheckboxTreeViewer.d org.eclipse.jface/src/org/eclipse/jface/viewers/ColorCellEditor.d org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnLayoutData.d org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnPixelData.d org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewer.d org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewerEditor.d org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewerEditorActivationEvent.d org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewerEditorActivationListener.d org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewerEditorActivationStrategy.d org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewerEditorDeactivationEvent.d org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewerToolTipSupport.d org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnWeightData.d org.eclipse.jface/src/org/eclipse/jface/viewers/ComboBoxCellEditor.d org.eclipse.jface/src/org/eclipse/jface/viewers/ComboBoxViewerCellEditor.d org.eclipse.jface/src/org/eclipse/jface/viewers/ComboViewer.d org.eclipse.jface/src/org/eclipse/jface/viewers/ContentViewer.d org.eclipse.jface/src/org/eclipse/jface/viewers/CustomHashtable.d org.eclipse.jface/src/org/eclipse/jface/viewers/DecoratingLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/DecoratingStyledCellLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/DecorationContext.d org.eclipse.jface/src/org/eclipse/jface/viewers/DecorationOverlayIcon.d org.eclipse.jface/src/org/eclipse/jface/viewers/DelegatingStyledCellLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/DialogCellEditor.d org.eclipse.jface/src/org/eclipse/jface/viewers/DoubleClickEvent.d org.eclipse.jface/src/org/eclipse/jface/viewers/EditingSupport.d org.eclipse.jface/src/org/eclipse/jface/viewers/FocusCellHighlighter.d org.eclipse.jface/src/org/eclipse/jface/viewers/FocusCellOwnerDrawHighlighter.d org.eclipse.jface/src/org/eclipse/jface/viewers/IBaseLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/IBasicPropertyConstants.d org.eclipse.jface/src/org/eclipse/jface/viewers/ICellEditorListener.d org.eclipse.jface/src/org/eclipse/jface/viewers/ICellEditorValidator.d org.eclipse.jface/src/org/eclipse/jface/viewers/ICellModifier.d org.eclipse.jface/src/org/eclipse/jface/viewers/ICheckStateListener.d org.eclipse.jface/src/org/eclipse/jface/viewers/ICheckable.d org.eclipse.jface/src/org/eclipse/jface/viewers/IColorDecorator.d org.eclipse.jface/src/org/eclipse/jface/viewers/IColorProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/IContentProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/IDecoration.d org.eclipse.jface/src/org/eclipse/jface/viewers/IDecorationContext.d org.eclipse.jface/src/org/eclipse/jface/viewers/IDelayedLabelDecorator.d org.eclipse.jface/src/org/eclipse/jface/viewers/IDoubleClickListener.d org.eclipse.jface/src/org/eclipse/jface/viewers/IElementComparer.d org.eclipse.jface/src/org/eclipse/jface/viewers/IFilter.d org.eclipse.jface/src/org/eclipse/jface/viewers/IFontDecorator.d org.eclipse.jface/src/org/eclipse/jface/viewers/IFontProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/IInputProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/IInputSelectionProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/ILabelDecorator.d org.eclipse.jface/src/org/eclipse/jface/viewers/ILabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/ILabelProviderListener.d org.eclipse.jface/src/org/eclipse/jface/viewers/ILazyContentProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/ILazyTreeContentProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/ILazyTreePathContentProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/ILightweightLabelDecorator.d org.eclipse.jface/src/org/eclipse/jface/viewers/IOpenListener.d org.eclipse.jface/src/org/eclipse/jface/viewers/IPostSelectionProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/ISelection.d org.eclipse.jface/src/org/eclipse/jface/viewers/ISelectionChangedListener.d org.eclipse.jface/src/org/eclipse/jface/viewers/ISelectionProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/IStructuredContentProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/IStructuredSelection.d org.eclipse.jface/src/org/eclipse/jface/viewers/ITableColorProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/ITableFontProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/ITableLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/ITreeContentProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/ITreePathContentProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/ITreePathLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/ITreeSelection.d org.eclipse.jface/src/org/eclipse/jface/viewers/ITreeViewerListener.d org.eclipse.jface/src/org/eclipse/jface/viewers/IViewerLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/LabelDecorator.d org.eclipse.jface/src/org/eclipse/jface/viewers/LabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/LabelProviderChangedEvent.d org.eclipse.jface/src/org/eclipse/jface/viewers/ListViewer.d org.eclipse.jface/src/org/eclipse/jface/viewers/NamedHandleObjectLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/OpenEvent.d org.eclipse.jface/src/org/eclipse/jface/viewers/OwnerDrawLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/SWTFocusCellManager.d org.eclipse.jface/src/org/eclipse/jface/viewers/SelectionChangedEvent.d org.eclipse.jface/src/org/eclipse/jface/viewers/StructuredSelection.d org.eclipse.jface/src/org/eclipse/jface/viewers/StructuredViewer.d org.eclipse.jface/src/org/eclipse/jface/viewers/StyledCellLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/StyledString.d org.eclipse.jface/src/org/eclipse/jface/viewers/TableColumnViewerLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/TableLayout.d org.eclipse.jface/src/org/eclipse/jface/viewers/TableTreeViewer.d org.eclipse.jface/src/org/eclipse/jface/viewers/TableViewer.d org.eclipse.jface/src/org/eclipse/jface/viewers/TableViewerColumn.d org.eclipse.jface/src/org/eclipse/jface/viewers/TableViewerEditor.d org.eclipse.jface/src/org/eclipse/jface/viewers/TableViewerFocusCellManager.d org.eclipse.jface/src/org/eclipse/jface/viewers/TableViewerRow.d org.eclipse.jface/src/org/eclipse/jface/viewers/TextCellEditor.d org.eclipse.jface/src/org/eclipse/jface/viewers/TreeColumnViewerLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/TreeExpansionEvent.d org.eclipse.jface/src/org/eclipse/jface/viewers/TreeNode.d org.eclipse.jface/src/org/eclipse/jface/viewers/TreeNodeContentProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/TreePath.d org.eclipse.jface/src/org/eclipse/jface/viewers/TreePathViewerSorter.d org.eclipse.jface/src/org/eclipse/jface/viewers/TreeSelection.d org.eclipse.jface/src/org/eclipse/jface/viewers/TreeViewer.d org.eclipse.jface/src/org/eclipse/jface/viewers/TreeViewerColumn.d org.eclipse.jface/src/org/eclipse/jface/viewers/TreeViewerEditor.d org.eclipse.jface/src/org/eclipse/jface/viewers/TreeViewerFocusCellManager.d org.eclipse.jface/src/org/eclipse/jface/viewers/TreeViewerRow.d org.eclipse.jface/src/org/eclipse/jface/viewers/Viewer.d org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerCell.d org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerColumn.d org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerComparator.d org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerDropAdapter.d org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerFilter.d org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerLabel.d org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerRow.d org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerSorter.d org.eclipse.jface/src/org/eclipse/jface/viewers/WrappedViewerLabelProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/AbstractConcurrentModel.d org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/AbstractVirtualTable.d org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/BackgroundContentProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/ChangeQueue.d org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/ConcurrentTableUpdator.d org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/DeferredContentProvider.d org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/FastProgressReporter.d org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/IConcurrentModel.d org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/IConcurrentModelListener.d org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/IntHashMap.d org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/LazySortedCollection.d org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/SetModel.d org.eclipse.jface/src/org/eclipse/jface/window/ApplicationWindow.d org.eclipse.jface/src/org/eclipse/jface/window/DefaultToolTip.d org.eclipse.jface/src/org/eclipse/jface/window/IShellProvider.d org.eclipse.jface/src/org/eclipse/jface/window/SameShellProvider.d org.eclipse.jface/src/org/eclipse/jface/window/ToolTip.d org.eclipse.jface/src/org/eclipse/jface/window/Window.d org.eclipse.jface/src/org/eclipse/jface/window/WindowManager.d org.eclipse.jface/src/org/eclipse/jface/wizard/IWizard.d org.eclipse.jface/src/org/eclipse/jface/wizard/IWizardContainer.d org.eclipse.jface/src/org/eclipse/jface/wizard/IWizardContainer2.d org.eclipse.jface/src/org/eclipse/jface/wizard/IWizardNode.d org.eclipse.jface/src/org/eclipse/jface/wizard/IWizardPage.d org.eclipse.jface/src/org/eclipse/jface/wizard/ProgressMonitorPart.d org.eclipse.jface/src/org/eclipse/jface/wizard/Wizard.d org.eclipse.jface/src/org/eclipse/jface/wizard/WizardDialog.d org.eclipse.jface/src/org/eclipse/jface/wizard/WizardPage.d org.eclipse.jface/src/org/eclipse/jface/wizard/WizardSelectionPage.d org.eclipse.osgi/src/org/osgi/framework/Bundle.d org.eclipse.osgi/supplement/src/org/osgi/util/NLS.d org.eclipse.text/src/org/eclipse/jface/text/AbstractDocument.d org.eclipse.text/src/org/eclipse/jface/text/AbstractLineTracker.d org.eclipse.text/src/org/eclipse/jface/text/Assert.d org.eclipse.text/src/org/eclipse/jface/text/BadLocationException.d org.eclipse.text/src/org/eclipse/jface/text/BadPartitioningException.d org.eclipse.text/src/org/eclipse/jface/text/BadPositionCategoryException.d org.eclipse.text/src/org/eclipse/jface/text/ConfigurableLineTracker.d org.eclipse.text/src/org/eclipse/jface/text/CopyOnWriteTextStore.d org.eclipse.text/src/org/eclipse/jface/text/DefaultLineTracker.d org.eclipse.text/src/org/eclipse/jface/text/DefaultPositionUpdater.d org.eclipse.text/src/org/eclipse/jface/text/Document.d org.eclipse.text/src/org/eclipse/jface/text/DocumentEvent.d org.eclipse.text/src/org/eclipse/jface/text/DocumentPartitioningChangedEvent.d org.eclipse.text/src/org/eclipse/jface/text/DocumentRewriteSession.d org.eclipse.text/src/org/eclipse/jface/text/DocumentRewriteSessionEvent.d org.eclipse.text/src/org/eclipse/jface/text/DocumentRewriteSessionType.d org.eclipse.text/src/org/eclipse/jface/text/FindReplaceDocumentAdapter.d org.eclipse.text/src/org/eclipse/jface/text/GapTextStore.d org.eclipse.text/src/org/eclipse/jface/text/IDocument.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentExtension.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentExtension2.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentExtension3.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentExtension4.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentInformationMapping.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentInformationMappingExtension.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentInformationMappingExtension2.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentListener.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioner.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitionerExtension.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitionerExtension2.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitionerExtension3.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListener.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListenerExtension.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListenerExtension2.d org.eclipse.text/src/org/eclipse/jface/text/IDocumentRewriteSessionListener.d org.eclipse.text/src/org/eclipse/jface/text/ILineTracker.d org.eclipse.text/src/org/eclipse/jface/text/ILineTrackerExtension.d org.eclipse.text/src/org/eclipse/jface/text/IPositionUpdater.d org.eclipse.text/src/org/eclipse/jface/text/IRegion.d org.eclipse.text/src/org/eclipse/jface/text/IRepairableDocument.d org.eclipse.text/src/org/eclipse/jface/text/IRepairableDocumentExtension.d org.eclipse.text/src/org/eclipse/jface/text/ISlaveDocumentManager.d org.eclipse.text/src/org/eclipse/jface/text/ISlaveDocumentManagerExtension.d org.eclipse.text/src/org/eclipse/jface/text/ISynchronizable.d org.eclipse.text/src/org/eclipse/jface/text/ITextStore.d org.eclipse.text/src/org/eclipse/jface/text/ITypedRegion.d org.eclipse.text/src/org/eclipse/jface/text/Line.d org.eclipse.text/src/org/eclipse/jface/text/ListLineTracker.d org.eclipse.text/src/org/eclipse/jface/text/Position.d org.eclipse.text/src/org/eclipse/jface/text/Region.d org.eclipse.text/src/org/eclipse/jface/text/RewriteSessionEditProcessor.d org.eclipse.text/src/org/eclipse/jface/text/SequentialRewriteTextStore.d org.eclipse.text/src/org/eclipse/jface/text/SlaveDocumentEvent.d org.eclipse.text/src/org/eclipse/jface/text/TextMessages.d org.eclipse.text/src/org/eclipse/jface/text/TextUtilities.d org.eclipse.text/src/org/eclipse/jface/text/TreeLineTracker.d org.eclipse.text/src/org/eclipse/jface/text/TypedPosition.d org.eclipse.text/src/org/eclipse/jface/text/TypedRegion.d org.eclipse.text/src/org/eclipse/jface/text/link/ILinkedModeListener.d org.eclipse.text/src/org/eclipse/jface/text/link/InclusivePositionUpdater.d org.eclipse.text/src/org/eclipse/jface/text/link/LinkedModeManager.d org.eclipse.text/src/org/eclipse/jface/text/link/LinkedModeModel.d org.eclipse.text/src/org/eclipse/jface/text/link/LinkedPosition.d org.eclipse.text/src/org/eclipse/jface/text/link/LinkedPositionGroup.d org.eclipse.text/src/org/eclipse/jface/text/projection/ChildDocument.d org.eclipse.text/src/org/eclipse/jface/text/projection/ChildDocumentManager.d org.eclipse.text/src/org/eclipse/jface/text/projection/Fragment.d org.eclipse.text/src/org/eclipse/jface/text/projection/FragmentUpdater.d org.eclipse.text/src/org/eclipse/jface/text/projection/IMinimalMapping.d org.eclipse.text/src/org/eclipse/jface/text/projection/ProjectionDocument.d org.eclipse.text/src/org/eclipse/jface/text/projection/ProjectionDocumentEvent.d org.eclipse.text/src/org/eclipse/jface/text/projection/ProjectionDocumentManager.d org.eclipse.text/src/org/eclipse/jface/text/projection/ProjectionMapping.d org.eclipse.text/src/org/eclipse/jface/text/projection/ProjectionTextStore.d org.eclipse.text/src/org/eclipse/jface/text/projection/Segment.d org.eclipse.text/src/org/eclipse/jface/text/projection/SegmentUpdater.d org.eclipse.text/src/org/eclipse/jface/text/source/Annotation.d org.eclipse.text/src/org/eclipse/jface/text/source/AnnotationMap.d org.eclipse.text/src/org/eclipse/jface/text/source/AnnotationModel.d org.eclipse.text/src/org/eclipse/jface/text/source/AnnotationModelEvent.d org.eclipse.text/src/org/eclipse/jface/text/source/IAnnotationMap.d org.eclipse.text/src/org/eclipse/jface/text/source/IAnnotationModel.d org.eclipse.text/src/org/eclipse/jface/text/source/IAnnotationModelExtension.d org.eclipse.text/src/org/eclipse/jface/text/source/IAnnotationModelExtension2.d org.eclipse.text/src/org/eclipse/jface/text/source/IAnnotationModelListener.d org.eclipse.text/src/org/eclipse/jface/text/source/IAnnotationModelListenerExtension.d org.eclipse.text/src/org/eclipse/jface/text/templates/DocumentTemplateContext.d org.eclipse.text/src/org/eclipse/jface/text/templates/GlobalTemplateVariables.d org.eclipse.text/src/org/eclipse/jface/text/templates/SimpleTemplateVariableResolver.d org.eclipse.text/src/org/eclipse/jface/text/templates/Template.d org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateBuffer.d org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateContext.d org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateContextType.d org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateException.d org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateTranslator.d org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateVariable.d org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateVariableResolver.d org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateVariableType.d org.eclipse.text/src/org/eclipse/jface/text/templates/TextTemplateMessages.d org.eclipse.text/src/org/eclipse/text/edits/CopySourceEdit.d org.eclipse.text/src/org/eclipse/text/edits/CopyTargetEdit.d org.eclipse.text/src/org/eclipse/text/edits/CopyingRangeMarker.d org.eclipse.text/src/org/eclipse/text/edits/DeleteEdit.d org.eclipse.text/src/org/eclipse/text/edits/EditDocument.d org.eclipse.text/src/org/eclipse/text/edits/ISourceModifier.d org.eclipse.text/src/org/eclipse/text/edits/InsertEdit.d org.eclipse.text/src/org/eclipse/text/edits/MalformedTreeException.d org.eclipse.text/src/org/eclipse/text/edits/Messages.properties org.eclipse.text/src/org/eclipse/text/edits/MoveSourceEdit.d org.eclipse.text/src/org/eclipse/text/edits/MoveTargetEdit.d org.eclipse.text/src/org/eclipse/text/edits/MultiTextEdit.d org.eclipse.text/src/org/eclipse/text/edits/RangeMarker.d org.eclipse.text/src/org/eclipse/text/edits/ReplaceEdit.d org.eclipse.text/src/org/eclipse/text/edits/TextEdit.d org.eclipse.text/src/org/eclipse/text/edits/TextEditCopier.d org.eclipse.text/src/org/eclipse/text/edits/TextEditGroup.d org.eclipse.text/src/org/eclipse/text/edits/TextEditMessages.d org.eclipse.text/src/org/eclipse/text/edits/TextEditProcessor.d org.eclipse.text/src/org/eclipse/text/edits/TextEditVisitor.d org.eclipse.text/src/org/eclipse/text/edits/TreeIterationInfo.d org.eclipse.text/src/org/eclipse/text/edits/UndoCollector.d org.eclipse.text/src/org/eclipse/text/edits/UndoEdit.d org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoEvent.d org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoManager.d org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoManagerRegistry.d org.eclipse.text/src/org/eclipse/text/undo/IDocumentUndoListener.d org.eclipse.text/src/org/eclipse/text/undo/IDocumentUndoManager.d org.eclipse.text/src/org/eclipse/text/undo/UndoMessages.d org.eclipse.text/src/org/eclipse/text/undo/UndoMessages.properties org.eclipse.tools/Sleak/org/eclipse/swt/tools/internal/Sleak.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/AbstractFormPart.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/DetailsPart.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/FormColors.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/FormDialog.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/HyperlinkGroup.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/HyperlinkSettings.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/IDetailsPage.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/IDetailsPageProvider.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/IFormColors.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/IFormPart.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/IManagedForm.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/IMessage.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/IMessageManager.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/IMessagePrefixProvider.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/IPartSelectionListener.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/ManagedForm.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/MasterDetailsBlock.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/SectionPart.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/events/ExpansionAdapter.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/events/ExpansionEvent.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/events/HyperlinkAdapter.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/events/HyperlinkEvent.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/events/IExpansionListener.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/events/IHyperlinkListener.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/AbstractHyperlink.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ColumnLayout.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ColumnLayoutData.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ExpandableComposite.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/Form.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/FormText.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/FormToolkit.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/Hyperlink.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ILayoutExtension.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ImageHyperlink.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/LayoutCache.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/LayoutComposite.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ScrolledForm.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ScrolledFormText.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ScrolledPageBook.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/Section.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/SharedScrolledComposite.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/SizeCache.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/TableWrapData.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/TableWrapLayout.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ToggleHyperlink.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/TreeNode.d org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/Twistie.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/IMessageToolTipManager.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/MessageManager.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/Messages.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/AggregateHyperlinkSegment.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/BreakSegment.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/BulletParagraph.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/BusyIndicator.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/ControlSegment.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormFonts.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormHeading.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormImages.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormTextModel.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormUtil.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormsResources.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/IFocusSelectable.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/IHyperlinkSegment.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/ImageHyperlinkSegment.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/ImageSegment.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/Locator.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/ObjectSegment.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/Paragraph.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/ParagraphSegment.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/PixelConverter.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/SWTUtil.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/SelectionData.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/TextHyperlinkSegment.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/TextSegment.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/TitleRegion.d org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/WrappedPageBook.d packageimport.rb rakefile res/eclipse-red-128.png res/eclipse-red-16.png res/eclipse-red-24.png res/eclipse-red-256.png res/eclipse-red-32.png res/eclipse-red-48.png res/eclipse-red-64.png res/icons.full.help.gif res/icons.full.message_error.gif res/icons.full.message_info.gif res/icons.full.message_warning.gif res/icons.full.page.gif res/icons.full.pref_dialog_title.gif res/icons.full.title_banner.png res/org.eclipse.core.internal.jobs.messages.properties res/org.eclipse.draw2d.checkboxenabledoff.gif res/org.eclipse.draw2d.checkboxenabledon.gif res/org.eclipse.jface.action.ExternalActionManager.properties res/org.eclipse.jface.action.images.stop.gif res/org.eclipse.jface.bindings.keys.formatting.AbstractKeyFormatter.properties res/org.eclipse.jface.bindings.keys.formatting.EmacsKeyFormatter.properties res/org.eclipse.jface.bindings.keys.formatting.NativeKeyFormatter.properties res/org.eclipse.jface.dialogs.images.help.gif res/org.eclipse.jface.dialogs.images.message_error.gif res/org.eclipse.jface.dialogs.images.message_info.gif res/org.eclipse.jface.dialogs.images.message_warning.gif res/org.eclipse.jface.dialogs.images.popup_menu.gif res/org.eclipse.jface.dialogs.images.popup_menu_disabled.gif res/org.eclipse.jface.dialogs.images.title_banner.gif res/org.eclipse.jface.fieldassist.images.contassist_ovr.gif res/org.eclipse.jface.fieldassist.images.error_ovr.gif res/org.eclipse.jface.fieldassist.images.errorqf_ovr.gif res/org.eclipse.jface.fieldassist.images.info_ovr.gif res/org.eclipse.jface.fieldassist.images.required_field_cue.gif res/org.eclipse.jface.fieldassist.images.warn_ovr.gif res/org.eclipse.jface.images.dots_button.gif res/org.eclipse.jface.internal.text.html.HTMLMessages.properties res/org.eclipse.jface.internal.text.link.contentassist.ContentAssistMessages.properties res/org.eclipse.jface.messages.properties res/org.eclipse.jface.preference.images.pref_dialog_title.gif res/org.eclipse.jface.resource.jfacefonts.properties res/org.eclipse.jface.resource.jfacefonts_aix.properties res/org.eclipse.jface.resource.jfacefonts_hp-ux.properties res/org.eclipse.jface.resource.jfacefonts_linux.properties res/org.eclipse.jface.resource.jfacefonts_linux_gtk.properties res/org.eclipse.jface.resource.jfacefonts_macosx.properties res/org.eclipse.jface.resource.jfacefonts_qnx.properties res/org.eclipse.jface.resource.jfacefonts_sunos.properties res/org.eclipse.jface.resource.jfacefonts_windows2000.properties res/org.eclipse.jface.resource.jfacefonts_windows98.properties res/org.eclipse.jface.resource.jfacefonts_windowsnt.properties res/org.eclipse.jface.resource.jfacefonts_windowsxp.properties res/org.eclipse.jface.text.JFaceTextMessages.properties res/org.eclipse.jface.text.RegExMessages.properties res/org.eclipse.jface.text.TextMessages.properties res/org.eclipse.jface.text.contentassist.JFaceTextMessages.properties res/org.eclipse.jface.text.hyperlink.HyperlinkMessages.properties res/org.eclipse.jface.text.source.projection.collapsed.gif res/org.eclipse.jface.text.source.projection.expanded.gif res/org.eclipse.jface.text.templates.JFaceTextTemplateMessages.properties res/org.eclipse.jface.text.templates.TextTemplateMessages.properties res/org.eclipse.jface.text.templates.persistence.TemplatePersistenceMessages.properties res/org.eclipse.jface.wizard.images.page.gif res/org.eclipse.text.edits.TextEditMessages.properties res/org.eclipse.text.undo.UndoMessages.properties res/org.eclipse.ui.internal.forms.Messages.properties
diffstat 1382 files changed, 330456 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/beans/PropertyChangeEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,38 @@
+module java.beans.PropertyChangeEvent;
+
+import java.lang.all;
+import java.util.EventObject;
+static import tango.core.Array;
+
+class PropertyChangeEvent : EventObject {
+    private String propertyName;
+    private Object oldValue;
+    private Object newValue;
+    private Object propagationId;
+
+    this( Object source, String propertyName, Object oldValue, Object newValue) {
+        super( source );
+        this.propertyName = propertyName;
+        this.oldValue = oldValue;
+        this.newValue = newValue;
+    }
+    Object getNewValue(){
+        return newValue;
+    }
+    Object getOldValue(){
+        return oldValue;
+    }
+    Object getPropagationId(){
+        return propagationId;
+    }
+    String getPropertyName(){
+        return propertyName;
+    }
+    void setPropagationId(Object propagationId){
+        this.propagationId = propagationId;
+    }
+    public override String toString() {
+        return this.classinfo.name ~ "[source=" ~ source.toString() ~ "]";
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/beans/PropertyChangeListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,9 @@
+module java.beans.PropertyChangeListener;
+
+import java.lang.all;
+import java.beans.PropertyChangeEvent;
+
+interface PropertyChangeListener {
+    void   propertyChange(PropertyChangeEvent evt);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/beans/PropertyChangeSupport.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,58 @@
+module java.beans.PropertyChangeSupport;
+
+import java.lang.all;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeEvent;
+
+class PropertyChangeSupport {
+    PropertyChangeListener[][ String ] listeners;
+    Object obj;
+    this(Object obj){
+        this.obj = obj;
+    }
+    void addPropertyChangeListener(PropertyChangeListener listener){
+        addPropertyChangeListener( "", listener );
+    }
+    void addPropertyChangeListener(String propertyName, PropertyChangeListener listener){
+        PropertyChangeListener[] list;
+        if( auto l = propertyName in listeners ){
+            list = *l;
+        }
+        list ~= listener;
+        listeners[ propertyName.dup ] = list;
+    }
+    void firePropertyChange(String propertyName, bool oldValue, bool newValue){
+        firePropertyChange( propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue) );
+    }
+    void firePropertyChange(String propertyName, int oldValue, int newValue){
+        firePropertyChange( propertyName, new Integer(oldValue), new Integer(newValue) );
+    }
+    void firePropertyChange(String propertyName, Object oldValue, Object newValue){
+        PropertyChangeListener[] list;
+        if( auto l = propertyName in listeners ){
+            list = *l;
+        }
+        auto evt = new PropertyChangeEvent( obj, propertyName, oldValue, newValue );
+        foreach( listener; list ){
+            if( listener ){
+                listener.propertyChange( evt );
+            }
+        }
+    }
+    void removePropertyChangeListener(PropertyChangeListener listener){
+        removePropertyChangeListener( "", listener );
+    }
+    void removePropertyChangeListener(String propertyName, PropertyChangeListener listener){
+        if( auto list = propertyName in listeners ){
+            list.length = tango.core.Array.remove( *list, listener );
+            if( list.length > 0 ){
+                listeners[ propertyName.dup ] = *list;
+            }
+            else{
+                listeners.remove( propertyName );
+            }
+        }
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/io/BufferedReader.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,24 @@
+module java.io.BufferedReader;
+
+import java.lang.all;
+import java.io.Reader;
+
+class BufferedReader : Reader {
+    this(Reader reader){
+        implMissing(__FILE__,__LINE__);
+    }
+    public override int read(char[] cbuf, int off, int len){
+        implMissing(__FILE__,__LINE__);
+        return 0;
+    }
+    public override void  close(){
+        implMissing(__FILE__,__LINE__);
+    }
+    public String  readLine() {
+        implMissing(__FILE__,__LINE__);
+        return null;
+    }
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/io/PushbackReader.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,23 @@
+module java.io.PushbackReader;
+
+import java.lang.all;
+import java.io.Reader;
+
+class PushbackReader : Reader {
+
+    this( Reader reader ){
+        implMissing(__FILE__,__LINE__);
+    }
+    void unread( char c ){
+        implMissing(__FILE__,__LINE__);
+    }
+    int read(char[] cbuf, int off, int len){
+        implMissing(__FILE__,__LINE__);
+        return 0;
+    }
+    void  close(){
+        implMissing(__FILE__,__LINE__);
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/io/StringReader.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,20 @@
+module java.io.StringReader;
+
+import java.lang.all;
+import java.io.Reader;
+
+class StringReader : Reader {
+    String str;
+    this( String str ){
+        implMissing(__FILE__,__LINE__);
+        this.str = str;
+    }
+    public override int read(char[] cbuf, int off, int len){
+        cbuf[ off .. off+len ] = str[ 0 .. len ];
+        str = str[ len .. $ ];
+        return len;
+    }
+    public override void  close(){
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/io/StringWriter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,9 @@
+module java.io.StringWriter;
+
+import java.lang.all;
+import java.io.Writer;
+
+class StringWriter : Writer {
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/lang/JThread.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,137 @@
+module java.lang.JThread;
+
+import tango.core.Thread;
+import java.lang.util;
+import java.lang.Runnable;
+import tango.util.log.Trace;
+
+class JThread {
+
+    private Thread thread;
+    private Runnable runnable;
+
+    private alias ThreadLocal!(JThread) TTLS;
+    private static TTLS tls;
+
+    public static const int MAX_PRIORITY  = 10;
+    public static const int MIN_PRIORITY  =  1;
+    public static const int NORM_PRIORITY =  5;
+
+    private static TTLS getTls(){
+        if( tls is null ){
+            synchronized( JThread.classinfo ){
+                if( tls is null ){
+                    tls = new TTLS();
+                }
+            }
+        }
+        return tls;
+    }
+
+    public this(){
+        thread = new Thread(&internalRun);
+    }
+    public this( void delegate() dg ){
+        thread = new Thread(&internalRun);
+        runnable = dgRunnable( dg );
+    }
+    public this(Runnable runnable){
+        thread = new Thread(&internalRun);
+        this.runnable = runnable;
+    }
+    public this(Runnable runnable, String name){
+        thread = new Thread(&internalRun);
+        this.runnable = runnable;
+        thread.name = name;
+    }
+    public this(String name){
+        thread = new Thread(&internalRun);
+        thread.name = name;
+    }
+
+    public void start(){
+        thread.start();
+    }
+
+    public static JThread currentThread(){
+        auto res = getTls().val();
+        if( res is null ){
+            // no synchronized needed
+            res = new JThread();
+            res.thread = Thread.getThis();
+            getTls().val( res );
+        }
+        assert( res );
+        return res;
+    }
+    public int getPriority() {
+        return (thread.priority-Thread.PRIORITY_MIN) * (MAX_PRIORITY-MIN_PRIORITY) / (Thread.PRIORITY_MAX-Thread.PRIORITY_MIN) + MIN_PRIORITY;
+    }
+    public void setPriority( int newPriority ) {
+//         assert( MIN_PRIORITY < MAX_PRIORITY );
+//         assert( Thread.PRIORITY_MIN < Thread.PRIORITY_MAX );
+        auto scaledPrio = (newPriority-MIN_PRIORITY) * (Thread.PRIORITY_MAX-Thread.PRIORITY_MIN) / (MAX_PRIORITY-MIN_PRIORITY) +Thread.PRIORITY_MIN;
+        Trace.formatln( "JThread.setPriority: scale ({} {} {}) -> ({} {} {})", MIN_PRIORITY, newPriority, MAX_PRIORITY, Thread.PRIORITY_MIN, scaledPrio, Thread.PRIORITY_MAX);
+//         thread.priority( scaledPrio );
+    }
+
+    private void internalRun(){
+        getTls().val( this );
+        if( runnable !is null ){
+            runnable.run();
+        }
+        else {
+            run();
+        }
+    }
+
+    public bool isAlive(){
+        return thread.isRunning();
+    }
+
+    public bool isDaemon() {
+        return thread.isDaemon();
+    }
+
+    public void join(){
+        thread.join();
+    }
+
+    public void setDaemon(bool on) {
+        thread.isDaemon(on);
+    }
+
+    public void setName(String name){
+        thread.name = name;
+    }
+    public String getName(){
+        return thread.name;
+    }
+
+    void interrupt() {
+        implMissing(__FILE__,__LINE__);
+    }
+
+    static bool interrupted() {
+        implMissing(__FILE__,__LINE__);
+        return false;
+    }
+
+    public void run(){
+        // default impl, do nothing
+    }
+    public static void sleep( int time ){
+        Thread.sleep(time/1000.0);
+    }
+    public Thread nativeThread(){
+        assert(thread);
+        return thread;
+    }
+    public override char[] toString(){
+        return "JThread "~thread.name;
+    }
+    public static void yield(){
+        Thread.yield();
+    }
+}
+
--- a/java/src/java/lang/Math.d	Fri Mar 13 17:44:24 2009 +0100
+++ b/java/src/java/lang/Math.d	Sat Mar 14 18:23:29 2009 +0100
@@ -17,7 +17,9 @@
     static int    min(uint   a, int    b){ return a < b ? a : b; }
     static int    min(int    a, uint   b){ return a < b ? a : b; }
     static int    min(uint   a, uint   b){ return a < b ? a : b; }
+    static int    min(int    a, long   b){ return a < b ? a : b; }
     static long   min(long   a, long   b){ return a < b ? a : b; }
+    static long   min(long   a, int    b){ return a < b ? a : b; }
 
     static double max(double a, double b){ return a > b ? a : b; }
     static float  max(float  a, float  b){ return a > b ? a : b; }
@@ -25,7 +27,9 @@
     static int    max(uint   a, int    b){ return a > b ? a : b; }
     static int    max(int    a, uint   b){ return a > b ? a : b; }
     static int    max(uint   a, uint   b){ return a > b ? a : b; }
+    static int    max(int    a, long   b){ return a > b ? a : b; }
     static long   max(long   a, long   b){ return a > b ? a : b; }
+    static long   max(long   a, int    b){ return a > b ? a : b; }
 
 
     static double sin(double a)  { return tango.math.Math.sin(a); }
@@ -36,6 +40,7 @@
     static double rint(double a) { return tango.math.Math.rndint(a); }
     static double ceil(double a) { return tango.math.Math.ceil(a); }
     static double floor(double a) { return tango.math.Math.floor(a); }
+    static double sqrt(double a) { return tango.math.Math.sqrt(a); }
 }
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/net/MalformedURLException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,6 @@
+module java.net.MalformedURLException;
+
+import java.lang.all;
+
+class MalformedURLException {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/net/URL.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,10 @@
+module java.net.URL;
+
+import java.lang.all;
+
+class URL {
+    this(String){
+        implMissing(__FILE__,__LINE__);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/text/CharacterIterator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,19 @@
+module java.text.CharacterIterator;
+
+import java.lang.all;
+
+interface CharacterIterator {
+    static const char DONE = '\u00FF';
+    Object clone();
+    char   current();
+    char   first();
+    int    getBeginIndex();
+    int    getEndIndex();
+    int    getIndex();
+    char   last();
+    char   next();
+    char   previous();
+    char   setIndex(int position);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/text/MessageFormat.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,20 @@
+module java.text.MessageFormat;
+
+import java.lang.all;
+import tango.text.convert.Format;
+
+class MessageFormat {
+    public static String format( String frmt, Object[] args... ){
+        switch( args.length ){
+        case 0: return Format(frmt);
+        case 1: return Format(frmt, args[0]);
+        case 2: return Format(frmt, args[0], args[1]);
+        case 3: return Format(frmt, args[0], args[1], args[2]);
+        case 4: return Format(frmt, args[0], args[1], args[2], args[3]);
+        case 5: return Format(frmt, args[0], args[1], args[2], args[3], args[4]);
+        default:
+            implMissing(__FILE__, __LINE__ );
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/util/Date.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,12 @@
+module java.util.Date;
+
+import java.lang.all;
+
+class Date {
+    long getTime(){
+        implMissing(__FILE__,__LINE__);
+        return 0;
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/util/Random.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,17 @@
+module java.util.Random;
+
+import java.lang.all;
+static import tango.math.random.Kiss;
+
+class Random {
+    tango.math.random.Kiss.Kiss kiss;
+
+    public this(int seed ){
+        kiss.seed(seed);
+    }
+    public bool  nextBoolean(){
+        return kiss.toInt(2) is 0;
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/util/StringTokenizer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,48 @@
+module java.util.StringTokenizer;
+
+import java.lang.all;
+
+class StringTokenizer {
+
+    this(String){
+        implMissing(__FILE__,__LINE__);
+    }
+
+    this(String,String){
+        implMissing(__FILE__,__LINE__);
+    }
+
+    this(String,String,bool){
+        implMissing(__FILE__,__LINE__);
+    }
+
+    bool hasMoreTokens(){
+        implMissing(__FILE__,__LINE__);
+        return false;
+    }
+
+    String nextToken(){
+        implMissing(__FILE__,__LINE__);
+        return null;
+    }
+
+    String nextToken(String delim){
+        implMissing(__FILE__,__LINE__);
+        return null;
+    }
+
+    bool hasMoreElements(){
+        implMissing(__FILE__,__LINE__);
+        return false;
+    }
+
+    Object nextElement(){
+        implMissing(__FILE__,__LINE__);
+        return null;
+    }
+
+    int countTokens(){
+        implMissing(__FILE__,__LINE__);
+        return 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/util/Timer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,419 @@
+module java.util.Timer;
+
+import java.util.TimerTask;
+// import tango.util.container.CircularList;
+import java.lang.JThread;
+import tango.core.sync.Mutex;
+import tango.core.sync.Condition;
+// import tango.time.Time;
+// import tango.time.Clock;
+import tango.text.convert.Format;
+import java.lang.all;
+
+class Timer {
+    private static final class TaskQueue {
+        private Mutex mutex;
+        private Condition cond;
+
+
+        private static const int DEFAULT_SIZE = 32;
+        private bool nullOnEmpty;
+        private TimerTask heap[];
+        private int elements;
+        public this() {
+            mutex = new Mutex();
+            cond = new Condition( mutex );
+            heap = new TimerTask[DEFAULT_SIZE];
+            elements = 0;
+            nullOnEmpty = false;
+        }
+
+        private void add(TimerTask task) {
+            elements++;
+            if (elements is heap.length) {
+                TimerTask new_heap[] = new TimerTask[heap.length * 2];
+                System.arraycopy(heap, 0, new_heap, 0, heap.length);
+                heap = new_heap;
+            }
+            heap[elements] = task;
+        }
+
+        private void remove() {
+            // clear the entry first
+            heap[elements] = null;
+            elements--;
+            if (elements + DEFAULT_SIZE / 2 <= (heap.length / 4)) {
+                TimerTask new_heap[] = new TimerTask[heap.length / 2];
+                System.arraycopy(heap, 0, new_heap, 0, elements + 1);
+                heap = new_heap;
+            }
+        }
+
+        public void enqueue(TimerTask task) {
+            synchronized( mutex ){
+                if (heap is null) {
+                    throw new IllegalStateException("cannot enqueue when stop() has been called on queue");
+                }
+
+                heap[0] = task;
+                add(task);
+                int child = elements;
+                int parent = child / 2;
+                while (heap[parent].scheduled > task.scheduled) {
+                    heap[child] = heap[parent];
+                    child = parent;
+                    parent = child / 2;
+                }
+                heap[child] = task;
+                heap[0] = null;
+                cond.notify();
+            }
+        }
+
+        private TimerTask top() {
+            if (elements is 0) {
+                return null;
+            }
+            else {
+                return heap[1];
+            }
+        }
+
+        public TimerTask serve() {
+            synchronized( mutex ){
+                TimerTask task = null;
+                while (task is null) {
+                    task = top();
+
+                    if ((heap is null) || (task is null && nullOnEmpty)) {
+                        return null;
+                    }
+
+                    if (task !is null) {
+                        // The time to wait until the task should be served
+                        long time = task.scheduled - System.currentTimeMillis();
+                        if (time > 0) {
+                            // This task should not yet be served
+                            // So wait until this task is ready
+                            // or something else happens to the queue
+                            task = null;  // set to null to make sure we call top()
+                            try {
+                                cond.wait(time);
+                            }
+                            catch (InterruptedException _) {
+                            }
+                        }
+                    }
+                    else {
+                        // wait until a task is added
+                        // or something else happens to the queue
+                        try {
+                            cond.wait();
+                        }
+                        catch (InterruptedException _) {
+                        }
+                    }
+                }
+
+                TimerTask lastTask = heap[elements];
+                remove();
+
+                int parent = 1;
+                int child = 2;
+                heap[1] = lastTask;
+                while (child <= elements) {
+                    if (child < elements) {
+                        if (heap[child].scheduled > heap[child + 1].scheduled) {
+                            child++;
+                        }
+                    }
+
+                    if (lastTask.scheduled <= heap[child].scheduled)
+                        break;
+
+                    heap[parent] = heap[child];
+                    parent = child;
+                    child = parent * 2;
+                }
+
+                heap[parent] = lastTask;
+                return task;
+            }
+        }
+
+        public void setNullOnEmpty(bool nullOnEmpty) {
+            synchronized( mutex ){
+                this.nullOnEmpty = nullOnEmpty;
+                cond.notify();
+            }
+        }
+
+        public void stop() {
+            synchronized( mutex ){
+                this.heap = null;
+                this.elements = 0;
+                cond.notify();
+            }
+        }
+
+    }
+
+    private static final class Scheduler : Runnable {
+        private TaskQueue queue;
+
+        public this(TaskQueue queue) {
+            this.queue = queue;
+        }
+
+        public void run() {
+            TimerTask task;
+            while ((task = queue.serve()) !is null) {
+                if (task.scheduled >= 0) {
+                    task.lastExecutionTime = task.scheduled;
+                    if (task.period < 0) {
+                        task.scheduled = -1;
+                    }
+                    try {
+                        task.run();
+                    }
+//                     catch (ThreadDeath death) {
+//                         // If an exception escapes, the Timer becomes invalid.
+//                         queue.stop();
+//                         throw death;
+//                     }
+                    catch (Exception t) {
+                        queue.stop();
+                    }
+                }
+                if (task.scheduled >= 0) {
+                    if (task.fixed) {
+                        task.scheduled += task.period;
+                    }
+                    else {
+                        task.scheduled = task.period + System.currentTimeMillis();
+                    }
+
+                    try {
+                        queue.enqueue(task);
+                    }
+                    catch (IllegalStateException ise) {
+                        // Ignore. Apparently the Timer queue has been stopped.
+                    }
+                }
+            }
+        }
+    }
+
+    private static int nr;
+    private TaskQueue queue;
+    private Scheduler scheduler;
+    private JThread thread;
+    private bool canceled;
+
+    public this() {
+        this(false);
+    }
+
+    public this(bool daemon) {
+        this(daemon, JThread.NORM_PRIORITY);
+    }
+
+    private this(bool daemon, int priority) {
+        this(daemon, priority, Format( "Timer-{}", ++nr));
+    }
+
+    private this(bool daemon, int priority, String name) {
+        canceled = false;
+        queue = new TaskQueue();
+        scheduler = new Scheduler(queue);
+        thread = new JThread(scheduler, name);
+        thread.setDaemon(daemon);
+        thread.setPriority(priority);
+        thread.start();
+    }
+
+    public void cancel() {
+        canceled = true;
+        queue.stop();
+    }
+
+    private void schedule(TimerTask task, long time, long period, bool fixed) {
+        if (time < 0)
+            throw new IllegalArgumentException("negative time");
+
+        if (task.scheduled is 0 && task.lastExecutionTime is -1) {
+            task.scheduled = time;
+            task.period = period;
+            task.fixed = fixed;
+        }
+        else {
+            throw new IllegalStateException("task was already scheduled or canceled");
+        }
+
+        if (!this.canceled && this.thread !is null) {
+            queue.enqueue(task);
+        }
+        else {
+            throw new IllegalStateException("timer was canceled or scheduler thread has died");
+        }
+    }
+
+    private static void positiveDelay(long delay) {
+        if (delay < 0) {
+            throw new IllegalArgumentException("delay is negative");
+        }
+    }
+
+    private static void positivePeriod(long period) {
+        if (period < 0) {
+            throw new IllegalArgumentException("period is negative");
+        }
+    }
+
+//     public void schedule(TimerTask task, Date date) {
+//         long time = date.getTime();
+//         schedule(task, time, -1, false);
+//     }
+
+//     public void schedule(TimerTask task, Date date, long period) {
+//         positivePeriod(period);
+//         long time = date.getTime();
+//         schedule(task, time, period, false);
+//     }
+
+    public void schedule(TimerTask task, long delay) {
+        positiveDelay(delay);
+        long time = System.currentTimeMillis() + delay;
+        schedule(task, time, -1, false);
+    }
+
+    public void schedule(TimerTask task, long delay, long period)  {
+        positiveDelay(delay);
+        positivePeriod(period);
+        long time = System.currentTimeMillis() + delay;
+        schedule(task, time, period, false);
+    }
+
+//     public void scheduleAtFixedRate(TimerTask task, Date date, long period)  {
+//         positivePeriod(period);
+//         long time = date.getTime();
+//         schedule(task, time, period, true);
+//     }
+
+    public void scheduleAtFixedRate(TimerTask task, long delay, long period)  {
+        positiveDelay(delay);
+        positivePeriod(period);
+        long time = System.currentTimeMillis() + delay;
+        schedule(task, time, period, true);
+    }
+
+    protected void finalize() {
+        queue.setNullOnEmpty(true);
+    }
+
+
+    ///////////////////////////////////////////////////
+    /+    alias CircularList!( TimerTask ) ListType;
+
+    private JThread thread;
+    private ListType schedules;
+    private Mutex mutex;
+    private Condition cond;
+    private bool isCanceled = false;
+
+    this(){
+        this(false);
+    }
+    this(bool isDaemon){
+        mutex = new Mutex();
+        cond = new Condition( mutex );
+
+        schedules = new ListType();
+        thread = new JThread( &run );
+        thread.setDaemon( isDaemon );
+        thread.start();
+    }
+    private void run(){
+
+        while( !isCanceled ){
+            TimerTask timerTask = null;
+            synchronized(mutex){
+                bool isReady = false;
+                do{
+                    if( isCanceled ){
+                        return;
+                    }
+
+                    if( schedules.size() is 0 ){
+                        cond.wait();
+                    }
+                    else{
+                        timerTask = schedules.head();
+                        TimeSpan toWait = timerTask.executionTime - Clock.now();
+                        if( toWait.interval() > 0 ){
+                            cond.wait( toWait.interval() );
+                        }
+                        else{
+                            schedules.removeHead();
+                            isReady = true;
+                        }
+                    }
+                }while( !isReady );
+            }
+            if( timerTask ){
+                timerTask.run();
+                if( timerTask.period.millis > 0 ){
+                    timerTask.executionTime += timerTask.period;
+                    synchronized(mutex){
+                        int index = 0;
+                        foreach( tt; schedules ){
+                            if( tt.executionTime > timerTask.executionTime ){
+                                break;
+                            }
+                            index++;
+                        }
+                        schedules.addAt( index, timerTask );
+                    }
+                }
+            }
+        }
+    }
+    void   cancel(){
+        synchronized(mutex){
+            isCanceled = true;
+            cond.notifyAll();
+        }
+    }
+    void   schedule(TimerTask task, long delay){
+        scheduleAtFixedRate( task, delay, 0 );
+    }
+    void   scheduleAtFixedRate(TimerTask task, long delay, long period){
+        assert( task );
+        version(TANGOSVN){
+            task.executionTime = Clock.now + TimeSpan.fromMillis(delay);
+        } else {
+            task.executionTime = Clock.now + TimeSpan.millis(delay);
+        }
+        task.timer = this;
+        synchronized(mutex){
+            int index = 0;
+            if( schedules.size() > 0 )
+                foreach( tt; schedules ){
+                    if( tt.executionTime > task.executionTime ){
+                        break;
+                    }
+                    index++;
+                }
+            schedules.addAt( index, task );
+            cond.notifyAll();
+        }
+    }
+
+    //     void   schedule(TimerTask task, Date time){}
+    //     void   schedule(TimerTask task, Date firstTime, long period){}
+    //     void   schedule(TimerTask task, long delay, long period){}
+    //     void   scheduleAtFixedRate(TimerTask task, Date firstTime, long period){}
+    +/
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/util/TimerTask.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,32 @@
+module java.util.TimerTask;
+
+import tango.time.Time;
+import java.lang.all;
+import java.util.Timer;
+
+class TimerTask : Runnable {
+
+    package long scheduled;
+    package long lastExecutionTime;
+    package long period;
+    package bool fixed;
+
+    this(){
+        this.scheduled = 0;
+        this.lastExecutionTime = -1;
+    }
+
+    bool cancel(){
+        bool prevented_execution = (this.scheduled >= 0);
+        this.scheduled = -1;
+        return prevented_execution;
+    }
+
+    abstract void run();
+
+    long scheduledExcecutionTime(){
+        return lastExecutionTime;
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/util/regex/Matcher.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,41 @@
+module java.util.regex.Matcher;
+
+import java.lang.all;
+import java.util.regex.Pattern;
+
+class Matcher {
+    public Pattern pattern(){
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+    public String    group(){
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+    public String    group(int n){
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+    public  String replaceFirst(String replacement) {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+    public int start(){
+        implMissing( __FILE__, __LINE__ );
+        return 0;
+    }
+    public int end(){
+        implMissing( __FILE__, __LINE__ );
+        return 0;
+    }
+    public bool find(){
+        implMissing( __FILE__, __LINE__ );
+        return false;
+    }
+    public bool find(int start){
+        implMissing( __FILE__, __LINE__ );
+        return false;
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/util/regex/Pattern.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,36 @@
+module java.util.regex.Pattern;
+
+import java.lang.all;
+import java.util.regex.Matcher;
+
+class Pattern {
+    public static const int MULTILINE;
+    public static const int CASE_INSENSITIVE ;
+    public static const int UNICODE_CASE ;
+
+    public String pattern(){
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+    public int flags(){
+        implMissing( __FILE__, __LINE__ );
+        return 0;
+    }
+    public static Pattern compile(String regex){
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+    public static Pattern compile(String regex, int flags){
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+    public Matcher matcher(CharSequence input){
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+    public Matcher matcher(String input){
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/java/util/regex/PatternException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,11 @@
+module java.util.regex.PatternSyntaxException;
+
+import java.lang.all;
+
+
+class PatternSyntaxException : IllegalArgumentException {
+    this(String desc, String regex, int index) {
+        super(desc);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/AbstractHandler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.AbstractHandler;
+
+import org.eclipse.core.commands.common.EventManager;
+import org.eclipse.core.commands.IHandler2;
+import org.eclipse.core.commands.IHandlerListener;
+import org.eclipse.core.commands.HandlerEvent;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * This class is a partial implementation of <code>IHandler</code>. This
+ * abstract implementation provides support for handler listeners. You should
+ * subclass from this method unless you want to implement your own listener
+ * support. Subclasses should call
+ * {@link AbstractHandler#fireHandlerChanged(HandlerEvent)}when the handler
+ * changes. Subclasses can also override {@link AbstractHandler#isEnabled()} and
+ * {@link AbstractHandler#isHandled()}.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class AbstractHandler : EventManager, IHandler2 {
+
+    /**
+     * Track this base class enabled state.
+     * 
+     * @since 3.4
+     */
+    private bool baseEnabled = true;
+
+    /**
+     * @see IHandler#addHandlerListener(IHandlerListener)
+     */
+    public void addHandlerListener(IHandlerListener handlerListener) {
+        addListenerObject(cast(Object)handlerListener);
+    }
+
+    /**
+     * The default implementation does nothing. Subclasses who attach listeners
+     * to other objects are encouraged to detach them in this method.
+     *
+     * @see org.eclipse.core.commands.IHandler#dispose()
+     */
+    public void dispose() {
+        // Do nothing.
+    }
+
+    /**
+     * Fires an event to all registered listeners describing changes to this
+     * instance.
+     * <p>
+     * Subclasses may extend the definition of this method (i.e., if a different
+     * type of listener can be attached to a subclass). This is used primarily
+     * for support of <code>AbstractHandler</code> in
+     * <code>org.eclipse.ui.workbench</code>, and clients should be wary of
+     * overriding this behaviour. If this method is overridden, then the first
+     * line of the method should be "<code>super.fireHandlerChanged(handlerEvent);</code>".
+     * </p>
+     *
+     * @param handlerEvent
+     *            the event describing changes to this instance. Must not be
+     *            <code>null</code>.
+     */
+    protected void fireHandlerChanged(HandlerEvent handlerEvent) {
+        if (handlerEvent is null) {
+            throw new NullPointerException();
+        }
+
+        Object[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            IHandlerListener listener = cast(IHandlerListener) listeners[i];
+            listener.handlerChanged(handlerEvent);
+        }
+    }
+
+    /**
+     * Whether this handler is capable of executing at this time. Subclasses may
+     * override this method. If clients override this method they should also
+     * consider overriding {@link #setEnabled(Object)} so they can be notified
+     * about framework execution contexts.
+     *
+     * @return <code>true</code>
+     * @see #setEnabled(Object)
+     * @see #setBaseEnabled(bool)
+     */
+    public bool isEnabled() {
+        return baseEnabled;
+    }
+
+    /**
+     * Allow the default {@link #isEnabled()} to answer our enabled state. It
+     * will fire a HandlerEvent if necessary. If clients use this method they
+     * should also consider overriding {@link #setEnabled(Object)} so they can
+     * be notified about framework execution contexts.
+     * 
+     * @param state
+     *            the enabled state
+     * @since 3.4
+     */
+    protected void setBaseEnabled(bool state) {
+        if (baseEnabled is state) {
+            return;
+        }
+        baseEnabled = state;
+        fireHandlerChanged(new HandlerEvent(this, true, false));
+    }
+
+    /**
+     * Called by the framework to allow the handler to update its enabled state
+     * by extracting the same information available at execution time. Clients
+     * may override if they need to extract information from the application
+     * context.
+     * 
+     * @param evaluationContext
+     *            the application context. May be <code>null</code>
+     * @since 3.4
+     * @see #setBaseEnabled(bool)
+     */
+    public void setEnabled(Object evaluationContext) {
+    }
+
+    /**
+     * Whether this handler is capable of handling delegated responsibilities at
+     * this time. Subclasses may override this method.
+     *
+     * @return <code>true</code>
+     */
+    public bool isHandled() {
+        return true;
+    }
+
+    /**
+     * <p>
+     * Returns true iff there is one or more IHandlerListeners attached to this
+     * AbstractHandler.
+     * </p>
+     * <p>
+     * Subclasses may extend the definition of this method (i.e., if a different
+     * type of listener can be attached to a subclass). This is used primarily
+     * for support of <code>AbstractHandler</code> in
+     * <code>org.eclipse.ui.workbench</code>, and clients should be wary of
+     * overriding this behaviour. If this method is overridden, then the return
+     * value should include "<code>super.hasListeners() ||</code>".
+     * </p>
+     *
+     * @return true iff there is one or more IHandlerListeners attached to this
+     *         AbstractHandler
+     */
+    protected bool hasListeners() {
+        return isListenerAttached();
+    }
+
+    /**
+     * @see IHandler#removeHandlerListener(IHandlerListener)
+     */
+    public void removeHandlerListener(IHandlerListener handlerListener) {
+        removeListenerObject(cast(Object)handlerListener);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/AbstractHandlerWithState.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.AbstractHandlerWithState;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.IObjectWithState;
+import org.eclipse.core.commands.IStateListener;
+import org.eclipse.core.commands.State;
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+/**
+ * <p>
+ * An abstract implementation of {@link IObjectWithState}. This provides basic
+ * handling for adding and remove state. When state is added, the handler
+ * attaches itself as a listener and fire a handleStateChange event to notify
+ * this handler. When state is removed, the handler removes itself as a
+ * listener.
+ * </p>
+ * <p>
+ * Clients may extend this class.
+ * </p>
+ *
+ * @since 3.2
+ */
+public abstract class AbstractHandlerWithState : AbstractHandler,
+        IObjectWithState, IStateListener {
+
+    /**
+     * The map of states currently held by this handler. If this handler has no
+     * state (generally, when inactive), then this will be <code>null</code>.
+     */
+    private Map states = null;
+
+    /**
+     * <p>
+     * Adds a state to this handler. This will add this handler as a listener to
+     * the state, and then fire a handleStateChange so that the handler can
+     * respond to the incoming state.
+     * </p>
+     * <p>
+     * Clients may extend this method, but they should call this super method
+     * first before doing anything else.
+     * </p>
+     *
+     * @param stateId
+     *            The identifier indicating the type of state being added; must
+     *            not be <code>null</code>.
+     * @param state
+     *            The state to add; must not be <code>null</code>.
+     */
+    public void addState(String stateId, State state) {
+        if (state is null) {
+            throw new NullPointerException("Cannot add a null state"); //$NON-NLS-1$
+        }
+
+        if (states is null) {
+            states = new HashMap(3);
+        }
+        states.put(stateId, state);
+        state.addListener(this);
+        handleStateChange(state, null);
+    }
+
+    public final State getState(String stateId) {
+        if ((states is null) || (states.isEmpty())) {
+            return null;
+        }
+
+        return cast(State) states.get(stateId);
+    }
+
+    public final String[] getStateIds() {
+        if ((states is null) || (states.isEmpty())) {
+            return null;
+        }
+
+        Set stateIds = states.keySet();
+        return stringcast( stateIds.toArray());
+    }
+
+    /**
+     * <p>
+     * Removes a state from this handler. This will remove this handler as a
+     * listener to the state. No event is fired to notify the handler of this
+     * change.
+     * </p>
+     * <p>
+     * Clients may extend this method, but they should call this super method
+     * first before doing anything else.
+     * </p>
+     *
+     * @param stateId
+     *            The identifier of the state to remove; must not be
+     *            <code>null</code>.
+     */
+    public void removeState(String stateId) {
+        if (stateId is null) {
+            throw new NullPointerException("Cannot remove a null state"); //$NON-NLS-1$
+        }
+
+        State state = cast(State) states.get(stateId);
+        if (state !is null) {
+            state.removeListener(this);
+            if (states !is null) {
+                states.remove(state);
+                if (states.isEmpty()) {
+                    states = null;
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/AbstractParameterValueConverter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.AbstractParameterValueConverter;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * Supports conversion between objects and strings for command parameter values.
+ * Extenders must produce strings that identify objects (of a specific command
+ * parameter type) as well as consume the strings to locate and return the
+ * objects they identify.
+ * </p>
+ * <p>
+ * This class offers multiple handlers of a command a consistent way of
+ * converting string parameter values into the objects that the handlers would
+ * prefer to deal with. This class also gives clients a way to serialize
+ * object parameters as strings so that entire parameterized commands can be
+ * serialized, stored and later deserialized and executed.
+ * </p>
+ * <p>
+ * This class will typically be extended so the subclass can be referenced from
+ * the <code>converter</code> attribute of the
+ * <code>commandParameterType</code> elemement of the
+ * <code>org.eclipse.ui.commands</code> extension-point. Objects implementing
+ * this interface may also be passed directly to
+ * {@link ParameterType#define(String, AbstractParameterValueConverter)} by
+ * clients.
+ * </p>
+ *
+ * @see ParameterType#define(String, AbstractParameterValueConverter)
+ * @see ParameterizedCommand#serialize()
+ * @since 3.2
+ */
+public abstract class AbstractParameterValueConverter {
+
+    /**
+     * Converts a string encoded command parameter value into the parameter
+     * value object.
+     *
+     * @param parameterValue
+     *            a command parameter value string describing an object; may be
+     *            <code>null</code>
+     * @return the object described by the command parameter value string; may
+     *         be <code>null</code>
+     * @throws ParameterValueConversionException
+     *             if an object cannot be produced from the
+     *             <code>parameterValue</code> string
+     */
+    public abstract Object convertToObject(String parameterValue);
+
+    /**
+     * Converts a command parameter value object into a string that encodes a
+     * reference to the object or serialization of the object.
+     *
+     * @param parameterValue
+     *            an object to convert into an identifying string; may be
+     *            <code>null</code>
+     * @return a string describing the provided object; may be <code>null</code>
+     * @throws ParameterValueConversionException
+     *             if a string reference or serialization cannot be provided for
+     *             the <code>parameterValue</code>
+     */
+    public abstract String convertToString(Object parameterValue);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/Category.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.Category;
+
+import org.eclipse.core.commands.common.NamedHandleObject;
+import org.eclipse.core.internal.commands.util.Util;
+import org.eclipse.core.commands.ICategoryListener;
+import org.eclipse.core.commands.CategoryEvent;
+
+import java.lang.all;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.util.Iterator;
+import tango.text.convert.Format;
+
+/**
+ * <p>
+ * A logical group for a set of commands. A command belongs to exactly one
+ * category. The category has no functional effect, but may be used in graphical
+ * tools that want to group the set of commands somehow.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class Category : NamedHandleObject {
+
+    /**
+     * A collection of objects listening to changes to this category. This
+     * collection is <code>null</code> if there are no listeners.
+     */
+    private Collection categoryListeners;
+
+    /**
+     * Constructs a new instance of <code>Category</code> based on the given
+     * identifier. When a category is first constructed, it is undefined.
+     * Category should only be constructed by the <code>CommandManager</code>
+     * to ensure that identifier remain unique.
+     *
+     * @param id
+     *            The identifier for the category. This value must not be
+     *            <code>null</code>, and must be unique amongst all
+     *            categories.
+     */
+    this(String id) {
+        super(id);
+    }
+
+    /**
+     * Adds a listener to this category that will be notified when this
+     * category's state changes.
+     *
+     * @param categoryListener
+     *            The listener to be added; must not be <code>null</code>.
+     */
+    public final void addCategoryListener(
+            ICategoryListener categoryListener) {
+        if (categoryListener is null) {
+            throw new NullPointerException();
+        }
+        if (categoryListeners is null) {
+            categoryListeners = new ArrayList();
+        }
+        if (!categoryListeners.contains(cast(Object)categoryListener)) {
+            categoryListeners.add(cast(Object)categoryListener);
+        }
+    }
+
+    /**
+     * <p>
+     * Defines this category by giving it a name, and possibly a description as
+     * well. The defined property automatically becomes <code>true</code>.
+     * </p>
+     * <p>
+     * Notification is sent to all listeners that something has changed.
+     * </p>
+     *
+     * @param name
+     *            The name of this command; must not be <code>null</code>.
+     * @param description
+     *            The description for this command; may be <code>null</code>.
+     */
+    public final void define(String name, String description) {
+        if (name is null) {
+            throw new NullPointerException(
+                    "The name of a command cannot be null"); //$NON-NLS-1$
+        }
+
+        bool definedChanged = !this.defined;
+        this.defined = true;
+
+        bool nameChanged = !Util.equals(this.name, name);
+        this.name = name;
+
+        bool descriptionChanged = !Util.equals(this.description,
+                description);
+        this.description = description;
+
+        fireCategoryChanged(new CategoryEvent(this, definedChanged,
+                descriptionChanged, nameChanged));
+    }
+
+    /**
+     * Notifies the listeners for this category that it has changed in some way.
+     *
+     * @param categoryEvent
+     *            The event to send to all of the listener; must not be
+     *            <code>null</code>.
+     */
+    private final void fireCategoryChanged(CategoryEvent categoryEvent) {
+        if (categoryEvent is null) {
+            throw new NullPointerException();
+        }
+        if (categoryListeners !is null) {
+            Iterator listenerItr = categoryListeners.iterator();
+            while (listenerItr.hasNext()) {
+                ICategoryListener listener = cast(ICategoryListener) listenerItr
+                        .next();
+                listener.categoryChanged(categoryEvent);
+            }
+        }
+    }
+
+    /**
+     * Removes a listener from this category.
+     *
+     * @param categoryListener
+     *            The listener to be removed; must not be <code>null</code>.
+     *
+     */
+    public final void removeCategoryListener(
+            ICategoryListener categoryListener) {
+        if (categoryListener is null) {
+            throw new NullPointerException();
+        }
+
+        if (categoryListeners !is null) {
+            categoryListeners.remove(cast(Object)categoryListener);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.common.HandleObject#toString()
+     */
+    public override String toString() {
+        if (string is null) {
+            string = Format( "Category({},{},{},{})", id, name, description, defined );
+        }
+        return string;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.common.HandleObject#undefine()
+     */
+    public override void undefine() {
+        string = null;
+
+        final bool definedChanged = defined;
+        defined = false;
+
+        final bool nameChanged = name !is null;
+        name = null;
+
+        final bool descriptionChanged = description !is null;
+        description = null;
+
+        fireCategoryChanged(new CategoryEvent(this, definedChanged,
+                descriptionChanged, nameChanged));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/CategoryEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.commands.CategoryEvent;
+
+import org.eclipse.core.commands.common.AbstractNamedHandleEvent;
+import org.eclipse.core.commands.Category;
+
+import java.lang.all;
+
+/**
+ * An instance of this class describes changes to an instance of
+ * <code>Category</code>.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see ICategoryListener#categoryChanged(CategoryEvent)
+ */
+public final class CategoryEvent : AbstractNamedHandleEvent {
+
+    /**
+     * The category that has changed; this value is never <code>null</code>.
+     */
+    private final Category category;
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param category
+     *            the instance of the interface that changed.
+     * @param definedChanged
+     *            true, iff the defined property changed.
+     * @param descriptionChanged
+     *            true, iff the description property changed.
+     * @param nameChanged
+     *            true, iff the name property changed.
+     */
+    public this(Category category, bool definedChanged,
+            bool descriptionChanged, bool nameChanged) {
+        super(definedChanged, descriptionChanged, nameChanged);
+
+        if (category is null) {
+            throw new NullPointerException();
+        }
+        this.category = category;
+    }
+
+    /**
+     * Returns the instance of the interface that changed.
+     *
+     * @return the instance of the interface that changed. Guaranteed not to be
+     *         <code>null</code>.
+     */
+    public final Category getCategory() {
+        return category;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/Command.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1131 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.Command;
+
+// import java.io.BufferedWriter;
+// import java.io.IOException;
+// import java.io.StringWriter;
+
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.commands.util.Tracing;
+import org.eclipse.core.internal.commands.util.Util;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.SafeRunner;
+
+import org.eclipse.core.commands.IParameter;
+import org.eclipse.core.commands.IHandlerListener;
+import org.eclipse.core.commands.NotEnabledException;
+import org.eclipse.core.commands.NotHandledException;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.CommandEvent;
+import org.eclipse.core.commands.State;
+import org.eclipse.core.commands.Category;
+import org.eclipse.core.commands.NamedHandleObjectWithState;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ParameterType;
+import org.eclipse.core.commands.IExecutionListener;
+import org.eclipse.core.commands.ICommandListener;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.commands.IHandler2;
+import org.eclipse.core.commands.IObjectWithState;
+import org.eclipse.core.commands.IExecutionListenerWithChecks;
+import org.eclipse.core.commands.ITypedParameter;
+import org.eclipse.core.commands.HandlerEvent;
+
+import java.lang.all;
+import tango.io.Stdout;
+
+/**
+ * <p>
+ * A command is an abstract representation for some semantic behaviour. It is
+ * not the actual implementation of this behaviour, nor is it the visual
+ * appearance of this behaviour in the user interface. Instead, it is a bridge
+ * between the two.
+ * </p>
+ * <p>
+ * The concept of a command is based on the command design pattern. The notable
+ * difference is how the command delegates responsibility for execution. Rather
+ * than allowing concrete subclasses, it uses a handler mechanism (see the
+ * <code>handlers</code> extension point). This provides another level of
+ * indirection.
+ * </p>
+ * <p>
+ * A command will exist in two states: defined and undefined. A command is
+ * defined if it is declared in the XML of a resolved plug-in. If the plug-in is
+ * unloaded or the command is simply not declared, then it is undefined. Trying
+ * to reference an undefined command will succeed, but trying to access any of
+ * its functionality will fail with a <code>NotDefinedException</code>. If
+ * you need to know when a command changes from defined to undefined (or vice
+ * versa), then attach a command listener.
+ * </p>
+ * <p>
+ * Commands are mutable and will change as their definition changes.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class Command : NamedHandleObjectWithState,
+        Comparable {
+
+    /**
+     * This flag can be set to <code>true</code> if commands should print
+     * information to <code>System.out</code> when executing.
+     */
+    public static bool DEBUG_COMMAND_EXECUTION = false;
+
+    /**
+     * This flag can be set to <code>true</code> if commands should print
+     * information to <code>System.out</code> when changing handlers.
+     */
+    public static bool DEBUG_HANDLERS = false;
+
+    /**
+     * This flag can be set to a particular command identifier if only that
+     * command should print information to <code>System.out</code> when
+     * changing handlers.
+     */
+    public static String DEBUG_HANDLERS_COMMAND_ID = null;
+
+    /**
+     * The category to which this command belongs. This value should not be
+     * <code>null</code> unless the command is undefined.
+     */
+    private Category category = null;
+
+    /**
+     * A collection of objects listening to the execution of this command. This
+     * collection is <code>null</code> if there are no listeners.
+     */
+    private /+transient+/ ListenerList executionListeners = null;
+
+    /**
+     * The handler currently associated with this command. This value may be
+     * <code>null</code> if there is no handler currently.
+     */
+    private /+transient+/ IHandler handler = null;
+
+    /**
+     * The help context identifier for this command. This can be
+     * <code>null</code> if there is no help currently associated with the
+     * command.
+     *
+     * @since 3.2
+     */
+    private String helpContextId;
+
+    /**
+     * The ordered array of parameters understood by this command. This value
+     * may be <code>null</code> if there are no parameters, or if the command
+     * is undefined. It may also be empty.
+     */
+    private IParameter[] parameters = null;
+
+    /**
+     * The type of the return value of this command. This value may be
+     * <code>null</code> if the command does not declare a return type.
+     *
+     * @since 3.2
+     */
+    private ParameterType returnType = null;
+
+    /**
+     * Our command will listen to the active handler for enablement changes so
+     * that they can be fired from the command itself.
+     *
+     * @since 3.3
+     */
+    private IHandlerListener handlerListener;
+
+    /**
+     * Constructs a new instance of <code>Command</code> based on the given
+     * identifier. When a command is first constructed, it is undefined.
+     * Commands should only be constructed by the <code>CommandManager</code>
+     * to ensure that the identifier remains unique.
+     *
+     * @param id
+     *            The identifier for the command. This value must not be
+     *            <code>null</code>, and must be unique amongst all commands.
+     */
+    this(String id) {
+        super(id);
+    }
+
+    /**
+     * Adds a listener to this command that will be notified when this command's
+     * state changes.
+     *
+     * @param commandListener
+     *            The listener to be added; must not be <code>null</code>.
+     */
+    public final void addCommandListener(ICommandListener commandListener) {
+        if (commandListener is null) {
+            throw new NullPointerException("Cannot add a null command listener"); //$NON-NLS-1$
+        }
+        addListenerObject(cast(Object)commandListener);
+    }
+
+    /**
+     * Adds a listener to this command that will be notified when this command
+     * is about to execute.
+     *
+     * @param executionListener
+     *            The listener to be added; must not be <code>null</code>.
+     */
+    public final void addExecutionListener(
+            IExecutionListener executionListener) {
+        if (executionListener is null) {
+            throw new NullPointerException(
+                    "Cannot add a null execution listener"); //$NON-NLS-1$
+        }
+
+        if (executionListeners is null) {
+            executionListeners = new ListenerList(ListenerList.IDENTITY);
+        }
+
+        executionListeners.add(cast(Object)executionListener);
+    }
+
+    /**
+     * <p>
+     * Adds a state to this command. This will add this state to the active
+     * handler, if the active handler is an instance of {@link IObjectWithState}.
+     * </p>
+     * <p>
+     * A single instance of {@link State} cannot be registered with multiple
+     * commands. Each command requires its own unique instance.
+     * </p>
+     *
+     * @param id
+     *            The identifier of the state to add; must not be
+     *            <code>null</code>.
+     * @param state
+     *            The state to add; must not be <code>null</code>.
+     * @since 3.2
+     */
+    public override void addState(String id, State state) {
+        super.addState(id, state);
+        state.setId(id);
+        if ( auto h = cast(IObjectWithState)handler) {
+            h.addState(id, state);
+        }
+    }
+
+    /**
+     * Compares this command with another command by comparing each of its
+     * non-transient attributes.
+     *
+     * @param object
+     *            The object with which to compare; must be an instance of
+     *            <code>Command</code>.
+     * @return A negative integer, zero or a postivie integer, if the object is
+     *         greater than, equal to or less than this command.
+     */
+    public final int compareTo(Object object) {
+        Command castedObject = cast(Command) object;
+        int compareTo = Util.compare(category, castedObject.category);
+        if (compareTo is 0) {
+            compareTo = Util.compare(defined, castedObject.defined);
+            if (compareTo is 0) {
+                compareTo = Util.compare(description, castedObject.description);
+                if (compareTo is 0) {
+                    compareTo = Util.compare(cast(Object)handler, cast(Object)castedObject.handler);
+                    if (compareTo is 0) {
+                        compareTo = Util.compare(id, castedObject.id);
+                        if (compareTo is 0) {
+                            compareTo = Util.compare(name, castedObject.name);
+                            if (compareTo is 0) {
+                                Object[] left, right;
+                                foreach( p; parameters ){
+                                    left ~= cast(Object)p;
+                                }
+                                foreach( p; castedObject.parameters ){
+                                    right ~= cast(Object)p;
+                                }
+                                compareTo = Util.compare(left,
+                                        right);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return compareTo;
+    }
+
+    /**
+     * <p>
+     * Defines this command by giving it a name, and possibly a description as
+     * well. The defined property automatically becomes <code>true</code>.
+     * </p>
+     * <p>
+     * Notification is sent to all listeners that something has changed.
+     * </p>
+     *
+     * @param name
+     *            The name of this command; must not be <code>null</code>.
+     * @param description
+     *            The description for this command; may be <code>null</code>.
+     * @param category
+     *            The category for this command; must not be <code>null</code>.
+     * @since 3.2
+     */
+    public final void define(String name, String description,
+            Category category) {
+        define(name, description, category, null);
+    }
+
+    /**
+     * <p>
+     * Defines this command by giving it a name, and possibly a description as
+     * well. The defined property automatically becomes <code>true</code>.
+     * </p>
+     * <p>
+     * Notification is sent to all listeners that something has changed.
+     * </p>
+     *
+     * @param name
+     *            The name of this command; must not be <code>null</code>.
+     * @param description
+     *            The description for this command; may be <code>null</code>.
+     * @param category
+     *            The category for this command; must not be <code>null</code>.
+     * @param parameters
+     *            The parameters understood by this command. This value may be
+     *            either <code>null</code> or empty if the command does not
+     *            accept parameters.
+     */
+    public final void define(String name, String description,
+            Category category, IParameter[] parameters) {
+        define(name, description, category, parameters, null);
+    }
+
+    /**
+     * <p>
+     * Defines this command by giving it a name, and possibly a description as
+     * well. The defined property automatically becomes <code>true</code>.
+     * </p>
+     * <p>
+     * Notification is sent to all listeners that something has changed.
+     * </p>
+     *
+     * @param name
+     *            The name of this command; must not be <code>null</code>.
+     * @param description
+     *            The description for this command; may be <code>null</code>.
+     * @param category
+     *            The category for this command; must not be <code>null</code>.
+     * @param parameters
+     *            The parameters understood by this command. This value may be
+     *            either <code>null</code> or empty if the command does not
+     *            accept parameters.
+     * @param returnType
+     *            The type of value returned by this command. This value may be
+     *            <code>null</code> if the command does not declare a return
+     *            type.
+     * @since 3.2
+     */
+    public final void define(String name, String description,
+            Category category, IParameter[] parameters,
+            ParameterType returnType) {
+        define(name, description, category, parameters, returnType, null);
+    }
+
+    /**
+     * <p>
+     * Defines this command by giving it a name, and possibly a description as
+     * well. The defined property automatically becomes <code>true</code>.
+     * </p>
+     * <p>
+     * Notification is sent to all listeners that something has changed.
+     * </p>
+     *
+     * @param name
+     *            The name of this command; must not be <code>null</code>.
+     * @param description
+     *            The description for this command; may be <code>null</code>.
+     * @param category
+     *            The category for this command; must not be <code>null</code>.
+     * @param parameters
+     *            The parameters understood by this command. This value may be
+     *            either <code>null</code> or empty if the command does not
+     *            accept parameters.
+     * @param returnType
+     *            The type of value returned by this command. This value may be
+     *            <code>null</code> if the command does not declare a return
+     *            type.
+     * @param helpContextId
+     *            The identifier of the help context to associate with this
+     *            command; may be <code>null</code> if this command does not
+     *            have any help associated with it.
+     * @since 3.2
+     */
+    public final void define(String name, String description,
+            Category category, IParameter[] parameters,
+            ParameterType returnType, String helpContextId) {
+        if (name is null) {
+            throw new NullPointerException(
+                    "The name of a command cannot be null"); //$NON-NLS-1$
+        }
+
+        if (category is null) {
+            throw new NullPointerException(
+                    "The category of a command cannot be null"); //$NON-NLS-1$
+        }
+
+        bool definedChanged = !this.defined;
+        this.defined = true;
+
+        bool nameChanged = !Util.equals(this.name, name);
+        this.name = name;
+
+        bool descriptionChanged = !Util.equals(this.description,
+                description);
+        this.description = description;
+
+        bool categoryChanged = !Util.equals(this.category, category);
+        this.category = category;
+
+        Object[] pLeft, pRight;
+        foreach( p; this.parameters ){
+            pLeft ~= cast(Object)p;
+        }
+        foreach( p; parameters ){
+            pRight ~= cast(Object)p;
+        }
+        bool parametersChanged = !Util.equals(pLeft,
+                pRight);
+        this.parameters = parameters;
+
+        bool returnTypeChanged = !Util.equals(this.returnType,
+                returnType);
+        this.returnType = returnType;
+
+        bool helpContextIdChanged = !Util.equals(this.helpContextId,
+                helpContextId);
+        this.helpContextId = helpContextId;
+
+        fireCommandChanged(new CommandEvent(this, categoryChanged,
+                definedChanged, descriptionChanged, false, nameChanged,
+                parametersChanged, returnTypeChanged, helpContextIdChanged));
+    }
+
+    /**
+     * Executes this command by delegating to the current handler, if any. If
+     * the debugging flag is set, then this method prints information about
+     * which handler is selected for performing this command. This method will
+     * succeed regardless of whether the command is enabled or defined. It is
+     * generally preferred to call {@link #executeWithChecks(ExecutionEvent)}.
+     *
+     * @param event
+     *            An event containing all the information about the current
+     *            state of the application; must not be <code>null</code>.
+     * @return The result of the execution; may be <code>null</code>. This
+     *         result will be available to the client executing the command, and
+     *         execution listeners.
+     * @throws ExecutionException
+     *             If the handler has problems executing this command.
+     * @throws NotHandledException
+     *             If there is no handler.
+     * @deprecated Please use {@link #executeWithChecks(ExecutionEvent)}
+     *             instead.
+     */
+    public final Object execute(ExecutionEvent event) {
+        firePreExecute(event);
+        IHandler handler = this.handler;
+
+        // Perform the execution, if there is a handler.
+        if ((handler !is null) && (handler.isHandled())) {
+            try {
+                Object returnValue = handler.execute(event);
+                firePostExecuteSuccess(returnValue);
+                return returnValue;
+            } catch (ExecutionException e) {
+                firePostExecuteFailure(e);
+                throw e;
+            }
+        }
+
+        NotHandledException e = new NotHandledException(
+                "There is no handler to execute. " ~ getId()); //$NON-NLS-1$
+        fireNotHandled(e);
+        throw e;
+    }
+
+    /**
+     * Executes this command by delegating to the current handler, if any. If
+     * the debugging flag is set, then this method prints information about
+     * which handler is selected for performing this command. This does checks
+     * to see if the command is enabled and defined. If it is not both enabled
+     * and defined, then the execution listeners will be notified and an
+     * exception thrown.
+     *
+     * @param event
+     *            An event containing all the information about the current
+     *            state of the application; must not be <code>null</code>.
+     * @return The result of the execution; may be <code>null</code>. This
+     *         result will be available to the client executing the command, and
+     *         execution listeners.
+     * @throws ExecutionException
+     *             If the handler has problems executing this command.
+     * @throws NotDefinedException
+     *             If the command you are trying to execute is not defined.
+     * @throws NotEnabledException
+     *             If the command you are trying to execute is not enabled.
+     * @throws NotHandledException
+     *             If there is no handler.
+     * @since 3.2
+     */
+    public final Object executeWithChecks(ExecutionEvent event) {
+        firePreExecute(event);
+        IHandler handler = this.handler;
+
+        if (!isDefined()) {
+            NotDefinedException exception = new NotDefinedException(
+                    "Trying to execute a command that is not defined. " //$NON-NLS-1$
+                            ~ getId());
+            fireNotDefined(exception);
+            throw exception;
+        }
+
+        // Perform the execution, if there is a handler.
+        if ((handler !is null) && (handler.isHandled())) {
+            setEnabled(event.getApplicationContext());
+            if (!isEnabled()) {
+                NotEnabledException exception = new NotEnabledException(
+                        "Trying to execute the disabled command " ~ getId()); //$NON-NLS-1$
+                fireNotEnabled(exception);
+                throw exception;
+            }
+
+            try {
+                Object returnValue = handler.execute(event);
+                firePostExecuteSuccess(returnValue);
+                return returnValue;
+            } catch (ExecutionException e) {
+                firePostExecuteFailure(e);
+                throw e;
+            }
+        }
+
+        NotHandledException e = new NotHandledException(
+                "There is no handler to execute for command " ~ getId()); //$NON-NLS-1$
+        fireNotHandled(e);
+        throw e;
+    }
+
+    /**
+     * Notifies the listeners for this command that it has changed in some way.
+     *
+     * @param commandEvent
+     *            The event to send to all of the listener; must not be
+     *            <code>null</code>.
+     */
+    private final void fireCommandChanged(CommandEvent commandEvent) {
+        if (commandEvent is null) {
+            throw new NullPointerException("Cannot fire a null event"); //$NON-NLS-1$
+        }
+
+        Object[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            ICommandListener listener = cast(ICommandListener) listeners[i];
+            SafeRunner.run(new class(listener) ISafeRunnable {
+                ICommandListener listener_;
+                this(ICommandListener a){ this.listener_ = a; }
+                public void handleException(Exception exception) {
+                }
+
+                public void run() {
+                    listener_.commandChanged(commandEvent);
+                }
+            });
+        }
+    }
+
+    /**
+     * Notifies the execution listeners for this command that an attempt to
+     * execute has failed because the command is not defined.
+     *
+     * @param e
+     *            The exception that is about to be thrown; never
+     *            <code>null</code>.
+     * @since 3.2
+     */
+    private final void fireNotDefined(NotDefinedException e) {
+        // Debugging output
+        if (DEBUG_COMMAND_EXECUTION) {
+            Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ "not defined: id=" ~ getId() ~ "; exception=" ~ e.toString ); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        if (executionListeners !is null) {
+            Object[] listeners = executionListeners.getListeners();
+            for (int i = 0; i < listeners.length; i++) {
+                Object object = listeners[i];
+                if ( auto listener = cast(IExecutionListenerWithChecks)object ) {
+                    listener.notDefined(getId(), e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Notifies the execution listeners for this command that an attempt to
+     * execute has failed because there is no handler.
+     *
+     * @param e
+     *            The exception that is about to be thrown; never
+     *            <code>null</code>.
+     * @since 3.2
+     */
+    private final void fireNotEnabled(NotEnabledException e) {
+        // Debugging output
+        if (DEBUG_COMMAND_EXECUTION) {
+            Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ "not enabled: id=" ~ getId() ~ "; exception=" ~ e.toString ); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        if (executionListeners !is null) {
+            Object[] listeners = executionListeners.getListeners();
+            for (int i = 0; i < listeners.length; i++) {
+                Object object = listeners[i];
+                if ( auto listener = cast(IExecutionListenerWithChecks)object ) {
+                    listener.notEnabled(getId(), e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Notifies the execution listeners for this command that an attempt to
+     * execute has failed because there is no handler.
+     *
+     * @param e
+     *            The exception that is about to be thrown; never
+     *            <code>null</code>.
+     */
+    private final void fireNotHandled(NotHandledException e) {
+        // Debugging output
+        if (DEBUG_COMMAND_EXECUTION) {
+            Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ "not handled: id=" ~ getId() ~ "; exception=" ~ e.toString ); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        if (executionListeners !is null) {
+            Object[] listeners = executionListeners.getListeners();
+            for (int i = 0; i < listeners.length; i++) {
+                IExecutionListener listener = cast(IExecutionListener) listeners[i];
+                listener.notHandled(getId(), e);
+            }
+        }
+    }
+
+    /**
+     * Notifies the execution listeners for this command that an attempt to
+     * execute has failed during the execution.
+     *
+     * @param e
+     *            The exception that has been thrown; never <code>null</code>.
+     *            After this method completes, the exception will be thrown
+     *            again.
+     */
+    private final void firePostExecuteFailure(ExecutionException e) {
+        // Debugging output
+        if (DEBUG_COMMAND_EXECUTION) {
+            Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ "failure: id=" ~ getId() ~ "; exception=" ~ e.toString ); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        if (executionListeners !is null) {
+            Object[] listeners = executionListeners.getListeners();
+            for (int i = 0; i < listeners.length; i++) {
+                IExecutionListener listener = cast(IExecutionListener) listeners[i];
+                listener.postExecuteFailure(getId(), e);
+            }
+        }
+    }
+
+    /**
+     * Notifies the execution listeners for this command that an execution has
+     * completed successfully.
+     *
+     * @param returnValue
+     *            The return value from the command; may be <code>null</code>.
+     */
+    private final void firePostExecuteSuccess(Object returnValue) {
+        // Debugging output
+        if (DEBUG_COMMAND_EXECUTION) {
+            Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ "success: id=" ~ getId() ~ "; returnValue=" //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ returnValue.toString );
+        }
+
+        if (executionListeners !is null) {
+            Object[] listeners = executionListeners.getListeners();
+            for (int i = 0; i < listeners.length; i++) {
+                IExecutionListener listener = cast(IExecutionListener) listeners[i];
+                listener.postExecuteSuccess(getId(), returnValue);
+            }
+        }
+    }
+
+    /**
+     * Notifies the execution listeners for this command that an attempt to
+     * execute is about to start.
+     *
+     * @param event
+     *            The execution event that will be used; never <code>null</code>.
+     */
+    private final void firePreExecute(ExecutionEvent event) {
+        // Debugging output
+        if (DEBUG_COMMAND_EXECUTION) {
+            Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ "starting: id=" ~ getId() ~ "; event=" ~ event.toString ); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        if (executionListeners !is null) {
+            Object[] listeners = executionListeners.getListeners();
+            for (int i = 0; i < listeners.length; i++) {
+                IExecutionListener listener = cast(IExecutionListener) listeners[i];
+                listener.preExecute(getId(), event);
+            }
+        }
+    }
+
+    /**
+     * Returns the category for this command.
+     *
+     * @return The category for this command; never <code>null</code>.
+     * @throws NotDefinedException
+     *             If the handle is not currently defined.
+     */
+    public final Category getCategory() {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot get the category from an undefined command. " //$NON-NLS-1$
+                            ~ id);
+        }
+
+        return category;
+    }
+
+    /**
+     * Returns the current handler for this command. This is used by the command
+     * manager for determining the appropriate help context identifiers and by
+     * the command service to allow handlers to update elements.
+     * <p>
+     * This value can change at any time and should never be cached.
+     * </p>
+     *
+     * @return The current handler for this command; may be <code>null</code>.
+     * @since 3.3
+     */
+    public final IHandler getHandler() {
+        return handler;
+    }
+
+    /**
+     * Returns the help context identifier associated with this command. This
+     * method should not be called by clients. Clients should use
+     * {@link CommandManager#getHelpContextId(Command)} instead.
+     *
+     * @return The help context identifier for this command; may be
+     *         <code>null</code> if there is none.
+     * @since 3.2
+     */
+    final String getHelpContextId() {
+        return helpContextId;
+    }
+
+    /**
+     * Returns the parameter with the provided id or <code>null</code> if this
+     * command does not have a parameter with the id.
+     *
+     * @param parameterId
+     *            The id of the parameter to retrieve.
+     * @return The parameter with the provided id or <code>null</code> if this
+     *         command does not have a parameter with the id.
+     * @throws NotDefinedException
+     *             If the handle is not currently defined.
+     * @since 3.2
+     */
+    public final IParameter getParameter(String parameterId) {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot get a parameter from an undefined command. " //$NON-NLS-1$
+                            ~ id);
+        }
+
+        if (parameters is null) {
+            return null;
+        }
+
+        for (int i = 0; i < parameters.length; i++) {
+            IParameter parameter = parameters[i];
+            if (parameter.getId().equals(parameterId)) {
+                return parameter;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the parameters for this command. This call triggers provides a
+     * copy of the array, so excessive calls to this method should be avoided.
+     *
+     * @return The parameters for this command. This value might be
+     *         <code>null</code>, if the command has no parameters.
+     * @throws NotDefinedException
+     *             If the handle is not currently defined.
+     */
+    public final IParameter[] getParameters() {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot get the parameters from an undefined command. " //$NON-NLS-1$
+                            ~ id);
+        }
+
+        if ((parameters is null) || (parameters.length is 0)) {
+            return null;
+        }
+
+        IParameter[] returnValue = new IParameter[parameters.length];
+        SimpleType!(IParameter).arraycopy(parameters, 0, returnValue, 0, parameters.length);
+        return returnValue;
+    }
+
+    /**
+     * Returns the {@link ParameterType} for the parameter with the provided id
+     * or <code>null</code> if this command does not have a parameter type
+     * with the id.
+     *
+     * @param parameterId
+     *            The id of the parameter to retrieve the {@link ParameterType}
+     *            of.
+     * @return The {@link ParameterType} for the parameter with the provided id
+     *         or <code>null</code> if this command does not have a parameter
+     *         type with the provided id.
+     * @throws NotDefinedException
+     *             If the handle is not currently defined.
+     * @since 3.2
+     */
+    public final ParameterType getParameterType(String parameterId) {
+        IParameter parameter = getParameter(parameterId);
+        if ( auto parameterWithType = cast(ITypedParameter)parameter ) {
+            return parameterWithType.getParameterType();
+        }
+        return null;
+    }
+
+    /**
+     * Returns the {@link ParameterType} for the return value of this command or
+     * <code>null</code> if this command does not declare a return value
+     * parameter type.
+     *
+     * @return The {@link ParameterType} for the return value of this command or
+     *         <code>null</code> if this command does not declare a return
+     *         value parameter type.
+     * @throws NotDefinedException
+     *             If the handle is not currently defined.
+     * @since 3.2
+     */
+    public final ParameterType getReturnType() {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot get the return type of an undefined command. " //$NON-NLS-1$
+                            ~ id);
+        }
+
+        return returnType;
+    }
+
+    /**
+     * Returns whether this command has a handler, and whether this handler is
+     * also handled and enabled.
+     *
+     * @return <code>true</code> if the command is handled; <code>false</code>
+     *         otherwise.
+     */
+    public final bool isEnabled() {
+        if (handler is null) {
+            return false;
+        }
+
+        try {
+            return handler.isEnabled();
+        } catch (Exception e) {
+            if (DEBUG_HANDLERS) {
+                // since this has the ability to generate megs of logs, only
+                // provide information if tracing
+                Tracing.printTrace("HANDLERS", "Handler " ~ (cast(Object)handler).toString()  ~ " for "  //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+                        ~ id ~ " threw unexpected exception"); //$NON-NLS-1$
+                ExceptionPrintStackTrace( e, Stdout );
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Called be the framework to allow the handler to update its enabled state.
+     *
+     * @param evaluationContext
+     *            the state to evaluate against. May be <code>null</code>
+     *            which indicates that the handler can query whatever model that
+     *            is necessary.  This context must not be cached.
+     * @since 3.4
+     */
+    public void setEnabled(Object evaluationContext) {
+        if (null !is cast(IHandler2)handler ) {
+            (cast(IHandler2) handler).setEnabled(evaluationContext);
+        }
+    }
+
+    /**
+     * Returns whether this command has a handler, and whether this handler is
+     * also handled.
+     *
+     * @return <code>true</code> if the command is handled; <code>false</code>
+     *         otherwise.
+     */
+    public final bool isHandled() {
+        if (handler is null) {
+            return false;
+        }
+
+        return handler.isHandled();
+    }
+
+    /**
+     * Removes a listener from this command.
+     *
+     * @param commandListener
+     *            The listener to be removed; must not be <code>null</code>.
+     *
+     */
+    public final void removeCommandListener(
+            ICommandListener commandListener) {
+        if (commandListener is null) {
+            throw new NullPointerException(
+                    "Cannot remove a null command listener"); //$NON-NLS-1$
+        }
+
+        removeListenerObject(cast(Object)commandListener);
+    }
+
+    /**
+     * Removes a listener from this command.
+     *
+     * @param executionListener
+     *            The listener to be removed; must not be <code>null</code>.
+     *
+     */
+    public final void removeExecutionListener(
+            IExecutionListener executionListener) {
+        if (executionListener is null) {
+            throw new NullPointerException(
+                    "Cannot remove a null execution listener"); //$NON-NLS-1$
+        }
+
+        if (executionListeners !is null) {
+            executionListeners.remove(cast(Object)executionListener);
+            if (executionListeners.isEmpty()) {
+                executionListeners = null;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Removes a state from this command. This will remove the state from the
+     * active handler, if the active handler is an instance of
+     * {@link IObjectWithState}.
+     * </p>
+     *
+     * @param stateId
+     *            The identifier of the state to remove; must not be
+     *            <code>null</code>.
+     * @since 3.2
+     */
+    public override void removeState(String stateId) {
+        if ( auto h = cast(IObjectWithState)handler ) {
+            h.removeState(stateId);
+        }
+        super.removeState(stateId);
+    }
+
+    /**
+     * Changes the handler for this command. This will remove all the state from
+     * the currently active handler (if any), and add it to <code>handler</code>.
+     * If debugging is turned on, then this will also print information about
+     * the change to <code>System.out</code>.
+     *
+     * @param handler
+     *            The new handler; may be <code>null</code> if none.
+     * @return <code>true</code> if the handler changed; <code>false</code>
+     *         otherwise.
+     */
+    public final bool setHandler(IHandler handler) {
+        if (Util.equals(cast(Object)handler, cast(Object)this.handler)) {
+            return false;
+        }
+
+        // Swap the state around.
+        String[] stateIds = getStateIds();
+        if (stateIds !is null) {
+            for (int i = 0; i < stateIds.length; i++) {
+                String stateId = stateIds[i];
+                if ( auto h = cast(IObjectWithState)this.handler ) {
+                    h.removeState(stateId);
+                }
+                if ( auto h = cast(IObjectWithState)handler ) {
+                    State stateToAdd = getState(stateId);
+                    h.addState(stateId, stateToAdd);
+                }
+            }
+        }
+
+        bool enabled = isEnabled();
+        if (this.handler !is null) {
+            this.handler.removeHandlerListener(getHandlerListener());
+        }
+
+        // Update the handler, and flush the string representation.
+        this.handler = handler;
+        if (this.handler !is null) {
+            this.handler.addHandlerListener(getHandlerListener());
+        }
+        string = null;
+
+        // Debugging output
+        if ((DEBUG_HANDLERS)
+                && ((DEBUG_HANDLERS_COMMAND_ID is null) || (DEBUG_HANDLERS_COMMAND_ID
+                        .equals(id)))) {
+            StringBuffer buffer = new StringBuffer("Command('"); //$NON-NLS-1$
+            buffer.append(id);
+            buffer.append("') has changed to "); //$NON-NLS-1$
+            if (handler is null) {
+                buffer.append("no handler"); //$NON-NLS-1$
+            } else {
+                buffer.append('\'');
+                buffer.append(( cast(Object)handler).toString);
+                buffer.append("' as its handler"); //$NON-NLS-1$
+            }
+            Tracing.printTrace("HANDLERS", buffer.toString()); //$NON-NLS-1$
+        }
+
+        // Send notification
+        fireCommandChanged(new CommandEvent(this, false, false, false, true,
+                false, false, false, false, enabled !is isEnabled()));
+
+        return true;
+    }
+
+    /**
+     * @return the handler listener
+     */
+    private IHandlerListener getHandlerListener() {
+        if (handlerListener is null) {
+            handlerListener = new class IHandlerListener {
+                public void handlerChanged(HandlerEvent handlerEvent) {
+                    bool enabledChanged = handlerEvent.isEnabledChanged();
+                    bool handledChanged = handlerEvent.isHandledChanged();
+                    fireCommandChanged(new CommandEvent(this.outer, false,
+                            false, false, handledChanged, false, false, false,
+                            false, enabledChanged));
+                }
+            };
+        }
+        return handlerListener;
+    }
+
+    /**
+     * The string representation of this command -- for debugging purposes only.
+     * This string should not be shown to an end user.
+     *
+     * @return The string representation; never <code>null</code>.
+     */
+    public override final String toString() {
+        if (string is null) {
+            String parms;
+            foreach( p; parameters ){
+                parms ~= "{"~(cast(Object)p).toString~"}";
+            }
+            string = Format("Command({},{},\n\t\t{},\n\t\t{},\n\t\t{},\n\t\t{},{},{})",
+                id,
+                name is null ? "":name,
+                description is null?"":description,
+                category is null?"":category.toString(),
+                handler is null?"": (cast(Object)handler).toString(),
+                parms,
+                returnType is null?"":returnType.toString(),
+                defined
+            );
+        }
+        return string;
+    }
+
+    /**
+     * Makes this command become undefined. This has the side effect of changing
+     * the name and description to <code>null</code>. This also removes all
+     * state and disposes of it. Notification is sent to all listeners.
+     */
+    public override final void undefine() {
+        bool enabledChanged = isEnabled();
+
+        string = null;
+
+        bool definedChanged = defined;
+        defined = false;
+
+        bool nameChanged = name !is null;
+        name = null;
+
+        bool descriptionChanged = description !is null;
+        description = null;
+
+        bool categoryChanged = category !is null;
+        category = null;
+
+        bool parametersChanged = parameters !is null;
+        parameters = null;
+
+        bool returnTypeChanged = returnType !is null;
+        returnType = null;
+
+        String[] stateIds = getStateIds();
+        if (stateIds !is null) {
+            if ( auto handlerWithState = cast(IObjectWithState)handler ) {
+                for (int i = 0; i < stateIds.length; i++) {
+                    String stateId = stateIds[i];
+                    handlerWithState.removeState(stateId);
+
+                    State state = getState(stateId);
+                    removeState(stateId);
+                    state.dispose();
+                }
+            } else {
+                for (int i = 0; i < stateIds.length; i++) {
+                    String stateId = stateIds[i];
+                    State state = getState(stateId);
+                    removeState(stateId);
+                    state.dispose();
+                }
+            }
+        }
+
+        fireCommandChanged(new CommandEvent(this, categoryChanged,
+                definedChanged, descriptionChanged, false, nameChanged,
+                parametersChanged, returnTypeChanged, false, enabledChanged));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/CommandEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,299 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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 org.eclipse.core.commands.CommandEvent;
+
+import org.eclipse.core.commands.common.AbstractNamedHandleEvent;
+import org.eclipse.core.commands.Command;
+
+import java.lang.all;
+
+/**
+ * An instance of this class describes changes to an instance of
+ * <code>Command</code>.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see ICommandListener#commandChanged(CommandEvent)
+ */
+public final class CommandEvent : AbstractNamedHandleEvent {
+
+    /**
+     * The bit used to represent whether the command has changed its category.
+     */
+    private static const int CHANGED_CATEGORY = LAST_USED_BIT << 1;
+
+    /**
+     * The bit used to represent whether the command has changed its handler.
+     */
+    private static const int CHANGED_HANDLED = LAST_USED_BIT << 2;
+
+    /**
+     * The bit used to represent whether the command has changed its parameters.
+     */
+    private static const int CHANGED_PARAMETERS = LAST_USED_BIT << 3;
+
+    /**
+     * The bit used to represent whether the command has changed its return
+     * type.
+     *
+     * @since 3.2
+     */
+    private static const int CHANGED_RETURN_TYPE = LAST_USED_BIT << 4;
+
+    /**
+     * The bit used to represent whether the command has changed its help
+     * context identifier.
+     *
+     * @since 3.2
+     */
+    private static const int CHANGED_HELP_CONTEXT_ID = LAST_USED_BIT << 5;
+
+    /**
+     * The bit used to represent whether this commands active handler has
+     * changed its enablement state.
+     *
+     * @since 3.3
+     */
+    private static const int CHANGED_ENABLED = LAST_USED_BIT << 6;
+
+    /**
+     * The command that has changed; this value is never <code>null</code>.
+     */
+    private const Command command;
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param command
+     *            the instance of the interface that changed.
+     * @param categoryChanged
+     *            <code>true</code>, iff the category property changed.
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     * @param descriptionChanged
+     *            <code>true</code>, iff the description property changed.
+     * @param handledChanged
+     *            <code>true</code>, iff the handled property changed.
+     * @param nameChanged
+     *            <code>true</code>, iff the name property changed.
+     * @param parametersChanged
+     *            <code>true</code> if the parameters have changed;
+     *            <code>false</code> otherwise.
+     */
+    public this(Command command, bool categoryChanged,
+            bool definedChanged, bool descriptionChanged,
+            bool handledChanged, bool nameChanged,
+            bool parametersChanged) {
+        this(command, categoryChanged, definedChanged, descriptionChanged,
+                handledChanged, nameChanged, parametersChanged, false);
+    }
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param command
+     *            the instance of the interface that changed.
+     * @param categoryChanged
+     *            <code>true</code>, iff the category property changed.
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     * @param descriptionChanged
+     *            <code>true</code>, iff the description property changed.
+     * @param handledChanged
+     *            <code>true</code>, iff the handled property changed.
+     * @param nameChanged
+     *            <code>true</code>, iff the name property changed.
+     * @param parametersChanged
+     *            <code>true</code> if the parameters have changed;
+     *            <code>false</code> otherwise.
+     * @param returnTypeChanged
+     *            <code>true</code> iff the return type property changed;
+     *            <code>false</code> otherwise.
+     * @since 3.2
+     */
+    public this(Command command, bool categoryChanged,
+            bool definedChanged, bool descriptionChanged,
+            bool handledChanged, bool nameChanged,
+            bool parametersChanged, bool returnTypeChanged) {
+        this(command, categoryChanged, definedChanged, descriptionChanged,
+                handledChanged, nameChanged, parametersChanged,
+                returnTypeChanged, false);
+    }
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param command
+     *            the instance of the interface that changed.
+     * @param categoryChanged
+     *            <code>true</code>, iff the category property changed.
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     * @param descriptionChanged
+     *            <code>true</code>, iff the description property changed.
+     * @param handledChanged
+     *            <code>true</code>, iff the handled property changed.
+     * @param nameChanged
+     *            <code>true</code>, iff the name property changed.
+     * @param parametersChanged
+     *            <code>true</code> if the parameters have changed;
+     *            <code>false</code> otherwise.
+     * @param returnTypeChanged
+     *            <code>true</code> iff the return type property changed;
+     *            <code>false</code> otherwise.
+     * @param helpContextIdChanged
+     *            <code>true</code> iff the help context identifier changed;
+     *            <code>false</code> otherwise.
+     * @since 3.2
+     */
+    public this(Command command, bool categoryChanged,
+            bool definedChanged, bool descriptionChanged,
+            bool handledChanged, bool nameChanged,
+            bool parametersChanged, bool returnTypeChanged,
+            bool helpContextIdChanged) {
+        this(command, categoryChanged, definedChanged, descriptionChanged,
+                handledChanged, nameChanged, parametersChanged,
+                returnTypeChanged, helpContextIdChanged, false);
+    }
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param command
+     *            the instance of the interface that changed.
+     * @param categoryChanged
+     *            <code>true</code>, iff the category property changed.
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     * @param descriptionChanged
+     *            <code>true</code>, iff the description property changed.
+     * @param handledChanged
+     *            <code>true</code>, iff the handled property changed.
+     * @param nameChanged
+     *            <code>true</code>, iff the name property changed.
+     * @param parametersChanged
+     *            <code>true</code> if the parameters have changed;
+     *            <code>false</code> otherwise.
+     * @param returnTypeChanged
+     *            <code>true</code> iff the return type property changed;
+     *            <code>false</code> otherwise.
+     * @param helpContextIdChanged
+     *            <code>true</code> iff the help context identifier changed;
+     *            <code>false</code> otherwise.
+     * @param enabledChanged
+     *            <code>true</code> iff the comand enablement changed;
+     *            <code>false</code> otherwise.
+     * @since 3.3
+     */
+    public this(Command command, bool categoryChanged,
+            bool definedChanged, bool descriptionChanged,
+            bool handledChanged, bool nameChanged,
+            bool parametersChanged, bool returnTypeChanged,
+            bool helpContextIdChanged, bool enabledChanged) {
+        super(definedChanged, descriptionChanged, nameChanged);
+
+        if (command is null) {
+            throw new NullPointerException();
+        }
+        this.command = command;
+
+        if (categoryChanged) {
+            changedValues |= CHANGED_CATEGORY;
+        }
+        if (handledChanged) {
+            changedValues |= CHANGED_HANDLED;
+        }
+        if (parametersChanged) {
+            changedValues |= CHANGED_PARAMETERS;
+        }
+        if (returnTypeChanged) {
+            changedValues |= CHANGED_RETURN_TYPE;
+        }
+        if (helpContextIdChanged) {
+            changedValues |= CHANGED_HELP_CONTEXT_ID;
+        }
+        if (enabledChanged) {
+            changedValues |= CHANGED_ENABLED;
+        }
+    }
+
+    /**
+     * Returns the instance of the interface that changed.
+     *
+     * @return the instance of the interface that changed. Guaranteed not to be
+     *         <code>null</code>.
+     */
+    public final Command getCommand() {
+        return command;
+    }
+
+    /**
+     * Returns whether or not the category property changed.
+     *
+     * @return <code>true</code>, iff the category property changed.
+     */
+    public final bool isCategoryChanged() {
+        return ((changedValues & CHANGED_CATEGORY) !is 0);
+    }
+
+    /**
+     * Returns whether or not the handled property changed.
+     *
+     * @return <code>true</code>, iff the handled property changed.
+     */
+    public final bool isHandledChanged() {
+        return ((changedValues & CHANGED_HANDLED) !is 0);
+    }
+
+    /**
+     * Returns whether or not the help context identifier changed.
+     *
+     * @return <code>true</code>, iff the help context identifier changed.
+     * @since 3.2
+     */
+    public final bool isHelpContextIdChanged() {
+        return ((changedValues & CHANGED_HELP_CONTEXT_ID) !is 0);
+    }
+
+    /**
+     * Returns whether or not the parameters have changed.
+     *
+     * @return <code>true</code>, iff the parameters property changed.
+     */
+    public final bool isParametersChanged() {
+        return ((changedValues & CHANGED_PARAMETERS) !is 0);
+    }
+
+    /**
+     * Returns whether or not the return type property changed.
+     *
+     * @return <code>true</code>, iff the return type property changed.
+     * @since 3.2
+     */
+    public final bool isReturnTypeChanged() {
+        return ((changedValues & CHANGED_RETURN_TYPE) !is 0);
+    }
+
+    /**
+     * Return whether the enable property changed.
+     *
+     * @return <code>true</code> iff the comand enablement changed
+     * @since 3.3
+     */
+    public final bool isEnabledChanged() {
+        return ((changedValues & CHANGED_ENABLED) !is 0);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/CommandManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1074 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.CommandManager;
+
+import org.eclipse.core.commands.common.HandleObjectManager;
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.runtime.ListenerList;
+
+import org.eclipse.core.commands.ICategoryListener;
+import org.eclipse.core.commands.ICommandListener;
+import org.eclipse.core.commands.IExecutionListener;
+import org.eclipse.core.commands.IExecutionListenerWithChecks;
+import org.eclipse.core.commands.IParameterTypeListener;
+import org.eclipse.core.commands.ICommandManagerListener;
+import org.eclipse.core.commands.CommandEvent;
+import org.eclipse.core.commands.CommandManagerEvent;
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.Category;
+import org.eclipse.core.commands.CategoryEvent;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.commands.ParameterType;
+import org.eclipse.core.commands.IParameter;
+import org.eclipse.core.commands.Parameterization;
+import org.eclipse.core.commands.ParameterizedCommand;
+import org.eclipse.core.commands.ParameterTypeEvent;
+import org.eclipse.core.commands.SerializationException;
+
+import org.eclipse.core.commands.NotEnabledException;
+import org.eclipse.core.commands.NotHandledException;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.ExecutionEvent;
+
+import java.lang.all;
+
+import java.util.WeakHashMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Iterator;
+import java.util.Collections;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * <p>
+ * A central repository for commands -- both in the defined and undefined
+ * states. Commands can be created and retrieved using this manager. It is
+ * possible to listen to changes in the collection of commands by attaching a
+ * listener to the manager.
+ * </p>
+ *
+ * @see CommandManager#getCommand(String)
+ * @since 3.1
+ */
+public final class CommandManager : HandleObjectManager,
+        ICategoryListener, ICommandListener, IParameterTypeListener {
+
+    /**
+     * A listener that forwards incoming execution events to execution listeners
+     * on this manager. The execution events will come from any command on this
+     * manager.
+     *
+     * @since 3.1
+     */
+    private final class ExecutionListener :
+            IExecutionListenerWithChecks {
+
+        public void notDefined(String commandId, NotDefinedException exception) {
+            if (executionListeners !is null) {
+                Object[] listeners = executionListeners.getListeners();
+                for (int i = 0; i < listeners.length; i++) {
+                    Object object = listeners[i];
+                    if ( auto listener = cast(IExecutionListenerWithChecks)object ) {
+                        listener.notDefined(commandId, exception);
+                    }
+                }
+            }
+        }
+
+        public void notEnabled(String commandId, NotEnabledException exception) {
+            if (executionListeners !is null) {
+                Object[] listeners = executionListeners.getListeners();
+                for (int i = 0; i < listeners.length; i++) {
+                    Object object = listeners[i];
+                    if ( auto listener = cast(IExecutionListenerWithChecks)object ) {
+                        listener.notEnabled(commandId, exception);
+                    }
+                }
+            }
+        }
+
+        public final void notHandled(String commandId,
+                NotHandledException exception) {
+            if (executionListeners !is null) {
+                Object[] listeners = executionListeners.getListeners();
+                for (int i = 0; i < listeners.length; i++) {
+                    Object object = listeners[i];
+                    if ( auto listener = cast(IExecutionListener)object ) {
+                        listener.notHandled(commandId, exception);
+                    }
+                }
+            }
+        }
+
+        public final void postExecuteFailure(String commandId,
+                ExecutionException exception) {
+            if (executionListeners !is null) {
+                Object[] listeners = executionListeners.getListeners();
+                for (int i = 0; i < listeners.length; i++) {
+                    Object object = listeners[i];
+                    if ( auto listener = cast(IExecutionListener)object ) {
+                        listener.postExecuteFailure(commandId, exception);
+                    }
+                }
+            }
+        }
+
+        public final void postExecuteSuccess(String commandId,
+                Object returnValue) {
+            if (executionListeners !is null) {
+                Object[] listeners = executionListeners.getListeners();
+                for (int i = 0; i < listeners.length; i++) {
+                    Object object = listeners[i];
+                    if ( auto listener = cast(IExecutionListener)object ) {
+                        listener.postExecuteSuccess(commandId, returnValue);
+                    }
+                }
+            }
+        }
+
+        public final void preExecute(String commandId,
+                ExecutionEvent event) {
+            if (executionListeners !is null) {
+                Object[] listeners = executionListeners.getListeners();
+                for (int i = 0; i < listeners.length; i++) {
+                    Object object = listeners[i];
+                    if ( auto listener = cast(IExecutionListener)object ) {
+                        listener.preExecute(commandId, event);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * The identifier of the category in which all auto-generated commands will
+     * appear. This value must never be <code>null</code>.
+     *
+     * @since 3.2
+     */
+    public static const String AUTOGENERATED_CATEGORY_ID = "org.eclipse.core.commands.categories.autogenerated"; //$NON-NLS-1$
+
+    /**
+     * The escape character to use for serialization and deserialization of
+     * parameterized commands.
+     */
+    static const char ESCAPE_CHAR = '%';
+
+    /**
+     * The character that separates a parameter id from its value.
+     */
+    static const char ID_VALUE_CHAR = '=';
+
+    /**
+     * The character that indicates the end of a list of parameters.
+     */
+    static const char PARAMETER_END_CHAR = ')';
+
+    /**
+     * The character that separators parameters from each other.
+     */
+    static const char PARAMETER_SEPARATOR_CHAR = ',';
+
+    /**
+     * The character that indicates the start of a list of parameters.
+     */
+    static const char PARAMETER_START_CHAR = '(';
+
+    this(){
+        categoriesById = new HashMap();
+        definedCategoryIds = new HashSet();
+        definedParameterTypeIds = new HashSet();
+        helpContextIdsByHandler = new WeakHashMap();
+        parameterTypesById = new HashMap();
+    }
+
+    /**
+     * Unescapes special characters in the command id, parameter ids and
+     * parameter values for {@link #deserialize(String)}. The special characters
+     * {@link #PARAMETER_START_CHAR}, {@link #PARAMETER_END_CHAR},
+     * {@link #ID_VALUE_CHAR}, {@link #PARAMETER_SEPARATOR_CHAR} and
+     * {@link #ESCAPE_CHAR} are escaped by prepending an {@link #ESCAPE_CHAR}
+     * character.
+     * <p>
+     * See also ParameterizedCommand.escape(String)
+     * </p>
+     *
+     * @param escapedText
+     *            a <code>String</code> that may contain escaped special
+     *            characters for command serialization.
+     * @return a <code>String</code> representing <code>escapedText</code>
+     *         with any escaped characters replaced by their literal values
+     * @throws SerializationException
+     *             if <code>escapedText</code> contains an invalid escape
+     *             sequence
+     * @since 3.2
+     */
+    private static final String unescape(String escapedText) {
+
+        // defer initialization of a StringBuffer until we know we need one
+        StringBuffer buffer;
+
+        for (int i = 0; i < escapedText.length; i++) {
+
+            char c = escapedText.charAt(i);
+            if (c !is ESCAPE_CHAR) {
+                // normal unescaped character
+                if (buffer !is null) {
+                    buffer.append(c);
+                }
+            } else {
+                if (buffer is null) {
+                    buffer = new StringBuffer(escapedText.substring(0, i));
+                }
+
+                if (++i < escapedText.length) {
+                    c = escapedText.charAt(i);
+                    switch (c) {
+                    case PARAMETER_START_CHAR:
+                    case PARAMETER_END_CHAR:
+                    case ID_VALUE_CHAR:
+                    case PARAMETER_SEPARATOR_CHAR:
+                    case ESCAPE_CHAR:
+                        buffer.append(c);
+                        break;
+                    default:
+                        throw new SerializationException(
+                                "Invalid character '" ~ c ~ "' in escape sequence"); //$NON-NLS-1$ //$NON-NLS-2$
+                    }
+                } else {
+                    throw new SerializationException(
+                            "Unexpected termination of escape sequence"); //$NON-NLS-1$
+                }
+            }
+
+        }
+
+        if (buffer is null) {
+            return escapedText;
+        }
+
+        return buffer.toString();
+    }
+
+    /**
+     * The map of category identifiers (<code>String</code>) to categories (
+     * <code>Category</code>). This collection may be empty, but it is never
+     * <code>null</code>.
+     */
+    private const Map categoriesById;
+
+    /**
+     * The set of identifiers for those categories that are defined. This value
+     * may be empty, but it is never <code>null</code>.
+     */
+    private const Set definedCategoryIds;
+
+    /**
+     * The set of identifiers for those command parameter types that are
+     * defined. This value may be empty, but it is never <code>null</code>.
+     *
+     * @since 3.2
+     */
+    private const Set definedParameterTypeIds;
+
+    /**
+     * The execution listener for this command manager. This just forwards
+     * events from commands controlled by this manager to listeners on this
+     * manager.
+     */
+    private IExecutionListenerWithChecks executionListener = null;
+
+    /**
+     * The collection of execution listeners. This collection is
+     * <code>null</code> if there are no listeners.
+     */
+    private ListenerList executionListeners = null;
+
+    /**
+     * The help context identifiers ({@link String}) for a handler ({@link IHandler}).
+     * This map may be empty, but it is never <code>null</code>. Entries are
+     * removed if all strong references to the handler are removed.
+     *
+     * @since 3.2
+     */
+    private const WeakHashMap helpContextIdsByHandler;
+
+    /**
+     * The map of parameter type identifiers (<code>String</code>) to
+     * parameter types ( <code>ParameterType</code>). This collection may be
+     * empty, but it is never <code>null</code>.
+     *
+     * @since 3.2
+     */
+    private const Map parameterTypesById;
+
+    /**
+     * Adds a listener to this command manager. The listener will be notified
+     * when the set of defined commands changes. This can be used to track the
+     * global appearance and disappearance of commands.
+     *
+     * @param listener
+     *            The listener to attach; must not be <code>null</code>.
+     */
+    public final void addCommandManagerListener(
+            ICommandManagerListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Adds an execution listener to this manager. This listener will be
+     * notified if any of the commands controlled by this manager execute. This
+     * can be used to support macros and instrumentation of commands.
+     *
+     * @param listener
+     *            The listener to attach; must not be <code>null</code>.
+     */
+    public final void addExecutionListener(IExecutionListener listener) {
+        if (listener is null) {
+            throw new NullPointerException(
+                    "Cannot add a null execution listener"); //$NON-NLS-1$
+        }
+
+        if (executionListeners is null) {
+            executionListeners = new ListenerList(ListenerList.IDENTITY);
+
+            // Add an execution listener to every command.
+            executionListener = new ExecutionListener();
+            Iterator commandItr = handleObjectsById.values().iterator();
+            while (commandItr.hasNext()) {
+                Command command = cast(Command) commandItr.next();
+                command.addExecutionListener(executionListener);
+            }
+
+        }
+
+        executionListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.ICategoryListener#categoryChanged(org.eclipse.core.commands.CategoryEvent)
+     */
+    public final void categoryChanged(CategoryEvent categoryEvent) {
+        if (categoryEvent.isDefinedChanged()) {
+            Category category = categoryEvent.getCategory();
+            String categoryId = category.getId();
+            bool categoryIdAdded = category.isDefined();
+            if (categoryIdAdded) {
+                definedCategoryIds.add(categoryId);
+            } else {
+                definedCategoryIds.remove(categoryId);
+            }
+            if (isListenerAttached()) {
+                fireCommandManagerChanged(new CommandManagerEvent(this, null,
+                        false, false, categoryId, categoryIdAdded, true));
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.commands.ICommandListener#commandChanged(org.eclipse.commands.CommandEvent)
+     */
+    public final void commandChanged(CommandEvent commandEvent) {
+        if (commandEvent.isDefinedChanged()) {
+            Command command = commandEvent.getCommand();
+            String commandId = command.getId();
+            bool commandIdAdded = command.isDefined();
+            if (commandIdAdded) {
+                definedHandleObjects.add(command);
+            } else {
+                definedHandleObjects.remove(command);
+            }
+            if (isListenerAttached()) {
+                fireCommandManagerChanged(new CommandManagerEvent(this,
+                        commandId, commandIdAdded, true, null, false, false));
+            }
+        }
+    }
+
+    /**
+     * Sets the name and description of the category for uncategorized commands.
+     * This is the category that will be returned if
+     * {@link #getCategory(String)} is called with <code>null</code>.
+     *
+     * @param name
+     *            The name of the category for uncategorized commands; must not
+     *            be <code>null</code>.
+     * @param description
+     *            The description of the category for uncategorized commands;
+     *            may be <code>null</code>.
+     * @since 3.2
+     */
+    public final void defineUncategorizedCategory(String name,
+            String description) {
+        Category category = getCategory(AUTOGENERATED_CATEGORY_ID);
+        category.define(name, description);
+    }
+
+    /**
+     * <p>
+     * Returns a {@link ParameterizedCommand} with a command and
+     * parameterizations as specified in the provided
+     * <code>serializedParameterizedCommand</code> string. The
+     * <code>serializedParameterizedCommand</code> must use the format
+     * returned by {@link ParameterizedCommand#serialize()} and described in the
+     * Javadoc for that method.
+     * </p>
+     * <p>
+     * If a parameter id encoded in the
+     * <code>serializedParameterizedCommand</code> does not exist in the
+     * encoded command, that parameter id and value are ignored. A given
+     * parameter id should not be used more than once in
+     * <code>serializedParameterizedCommand</code>. This will not result in
+     * an exception, but in this case the value of the parameter when the
+     * command is executed is unspecified.
+     * </p>
+     * <p>
+     * This method will never return <code>null</code>, however it may throw
+     * an exception if there is a problem processing the serialization string or
+     * the encoded command is undefined.
+     * </p>
+     *
+     * @param serializedParameterizedCommand
+     *            a string representing a command id and parameter ids and
+     *            values; must not be <code>null</code>
+     * @return a {@link ParameterizedCommand} with the command and
+     *         parameterizations encoded in the
+     *         <code>serializedParameterizedCommand</code>; never
+     *         <code>null</code>.
+     * @throws NotDefinedException
+     *             if the command indicated in
+     *             <code>serializedParameterizedCommand</code> is not defined
+     * @throws SerializationException
+     *             if there is an error deserializing
+     *             <code>serializedParameterizedCommand</code>
+     * @see ParameterizedCommand#serialize()
+     * @since 3.2
+     */
+    public final ParameterizedCommand deserialize(
+            String serializedParameterizedCommand) {
+
+        int lparenPosition = unescapedIndexOf(
+                serializedParameterizedCommand, PARAMETER_START_CHAR);
+
+        String commandIdEscaped;
+        String serializedParameters;
+        if (lparenPosition is -1) {
+            commandIdEscaped = serializedParameterizedCommand;
+            serializedParameters = null;
+        } else {
+            commandIdEscaped = serializedParameterizedCommand.substring(0,
+                    lparenPosition);
+
+            if (serializedParameterizedCommand
+                    .charAt(serializedParameterizedCommand.length - 1) !is PARAMETER_END_CHAR) {
+                throw new SerializationException(
+                        "Parentheses must be balanced in serialized ParameterizedCommand"); //$NON-NLS-1$
+            }
+
+            serializedParameters = serializedParameterizedCommand.substring(
+                    lparenPosition + 1, // skip PARAMETER_START_CHAR
+                    serializedParameterizedCommand.length - 1); // skip
+            // PARAMETER_END_CHAR
+        }
+
+        String commandId = unescape(commandIdEscaped);
+        Command command = getCommand(commandId);
+        IParameter[] parameters = command.getParameters();
+        Parameterization[] parameterizations = getParameterizations(
+                serializedParameters, parameters);
+
+        return new ParameterizedCommand(command, parameterizations);
+    }
+
+    /**
+     * Notifies all of the listeners to this manager that the set of defined
+     * command identifiers has changed.
+     *
+     * @param event
+     *            The event to send to all of the listeners; must not be
+     *            <code>null</code>.
+     */
+    private final void fireCommandManagerChanged(CommandManagerEvent event) {
+        if (event is null) {
+            throw new NullPointerException();
+        }
+
+        Object[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            ICommandManagerListener listener = cast(ICommandManagerListener) listeners[i];
+            listener.commandManagerChanged(event);
+        }
+    }
+
+    /**
+     * Returns all of the commands known by this manager -- defined and
+     * undefined.
+     *
+     * @return All of the commands; may be empty, but never <code>null</code>.
+     * @since 3.2
+     */
+    public final Command[] getAllCommands() {
+        return arraycast!(Command)( handleObjectsById.values().toArray());
+    }
+
+    /**
+     * Gets the category with the given identifier. If no such category
+     * currently exists, then the category will be created (but be undefined).
+     *
+     * @param categoryId
+     *            The identifier to find; must not be <code>null</code>. If
+     *            the category is <code>null</code>, then a category suitable
+     *            for uncategorized items is defined and returned.
+     * @return The category with the given identifier; this value will never be
+     *         <code>null</code>, but it might be undefined.
+     * @see Category
+     */
+    public final Category getCategory(String categoryId) {
+        if (categoryId is null) {
+            return getCategory(AUTOGENERATED_CATEGORY_ID);
+        }
+
+        checkId(categoryId);
+
+        Category category = cast(Category) categoriesById.get(categoryId);
+        if (category is null) {
+            category = new Category(categoryId);
+            categoriesById.put(categoryId, category);
+            category.addCategoryListener(this);
+        }
+
+        return category;
+    }
+
+    /**
+     * Gets the command with the given identifier. If no such command currently
+     * exists, then the command will be created (but will be undefined).
+     *
+     * @param commandId
+     *            The identifier to find; must not be <code>null</code> and
+     *            must not be zero-length.
+     * @return The command with the given identifier; this value will never be
+     *         <code>null</code>, but it might be undefined.
+     * @see Command
+     */
+    public final Command getCommand(String commandId) {
+        checkId(commandId);
+
+        Command command = cast(Command) handleObjectsById.get(commandId);
+        if (command is null) {
+            command = new Command(commandId);
+            handleObjectsById.put(commandId, command);
+            command.addCommandListener(this);
+
+            if (executionListener !is null) {
+                command.addExecutionListener(executionListener);
+            }
+        }
+
+        return command;
+    }
+
+    /**
+     * Returns the categories that are defined.
+     *
+     * @return The defined categories; this value may be empty, but it is never
+     *         <code>null</code>.
+     * @since 3.2
+     */
+    public final Category[] getDefinedCategories() {
+        Category[] categories = new Category[definedCategoryIds.size()];
+        Iterator categoryIdItr = definedCategoryIds.iterator();
+        int i = 0;
+        while (categoryIdItr.hasNext()) {
+            String categoryId = stringcast( categoryIdItr.next());
+            categories[i++] = getCategory(categoryId);
+        }
+        return categories;
+    }
+
+    /**
+     * Returns the set of identifiers for those category that are defined.
+     *
+     * @return The set of defined category identifiers; this value may be empty,
+     *         but it is never <code>null</code>.
+     */
+    public final Set getDefinedCategoryIds() {
+        return Collections.unmodifiableSet(definedCategoryIds);
+    }
+
+    /**
+     * Returns the set of identifiers for those commands that are defined.
+     *
+     * @return The set of defined command identifiers; this value may be empty,
+     *         but it is never <code>null</code>.
+     */
+    public final Set getDefinedCommandIds() {
+        return getDefinedHandleObjectIds();
+    }
+
+    /**
+     * Returns the commands that are defined.
+     *
+     * @return The defined commands; this value may be empty, but it is never
+     *         <code>null</code>.
+     * @since 3.2
+     */
+    public final Command[] getDefinedCommands() {
+        return arraycast!(Command)( definedHandleObjects
+                .toArray());
+    }
+
+    /**
+     * Returns the set of identifiers for those parameter types that are
+     * defined.
+     *
+     * @return The set of defined command parameter type identifiers; this value
+     *         may be empty, but it is never <code>null</code>.
+     * @since 3.2
+     */
+    public final Set getDefinedParameterTypeIds() {
+        return Collections.unmodifiableSet(definedParameterTypeIds);
+    }
+
+    /**
+     * Returns the command parameter types that are defined.
+     *
+     * @return The defined command parameter types; this value may be empty, but
+     *         it is never <code>null</code>.
+     * @since 3.2
+     */
+    public final ParameterType[] getDefinedParameterTypes() {
+        ParameterType[] parameterTypes = new ParameterType[definedParameterTypeIds
+                .size()];
+        Iterator iterator = definedParameterTypeIds.iterator();
+        int i = 0;
+        while (iterator.hasNext()) {
+            String parameterTypeId = stringcast( iterator.next());
+            parameterTypes[i++] = getParameterType(parameterTypeId);
+        }
+        return parameterTypes;
+    }
+
+    /**
+     * Gets the help context identifier for a particular command. The command's
+     * handler is first checked for a help context identifier. If the handler
+     * does not have a help context identifier, then the help context identifier
+     * for the command is returned. If neither has a help context identifier,
+     * then <code>null</code> is returned.
+     *
+     * @param command
+     *            The command for which the help context should be retrieved;
+     *            must not be <code>null</code>.
+     * @return The help context identifier to use for the given command; may be
+     *         <code>null</code>.
+     * @throws NotDefinedException
+     *             If the given command is not defined.
+     * @since 3.2
+     */
+    public final String getHelpContextId(Command command) {
+        // Check if the command is defined.
+        if (!command.isDefined()) {
+            throw new NotDefinedException("The command is not defined. " //$NON-NLS-1$
+                    ~ command.getId());
+        }
+
+        // Check the handler.
+        IHandler handler = command.getHandler();
+        if (handler !is null) {
+            String helpContextId = stringcast( helpContextIdsByHandler.get( cast(Object) handler) );
+            if (helpContextId !is null) {
+                return helpContextId;
+            }
+        }
+
+        // Simply return whatever the command has as a help context identifier.
+        return command.getHelpContextId();
+    }
+
+    /**
+     * Returns an array of parameterizations for the provided command by
+     * deriving the parameter ids and values from the provided
+     * <code>serializedParameters</code> string.
+     *
+     * @param serializedParameters
+     *            a String encoding parameter ids and values; must not be
+     *            <code>null</code>.
+     * @param parameters
+     *            array of parameters of the command being deserialized; may be
+     *            <code>null</code>.
+     * @return an array of parameterizations; may be <code>null</code>.
+     * @throws SerializationException
+     *             if there is an error deserializing the parameters
+     * @since 3.2
+     */
+    private final Parameterization[] getParameterizations(
+            String serializedParameters, IParameter[] parameters) {
+
+        if (serializedParameters is null
+                || (serializedParameters.length is 0)) {
+            return null;
+        }
+
+        if ((parameters is null) || (parameters.length is 0)) {
+            return null;
+        }
+
+        ArrayList paramList = new ArrayList();
+
+        int commaPosition; // split off each param by looking for ','
+        do {
+            commaPosition = unescapedIndexOf(serializedParameters, ',');
+
+            String idEqualsValue;
+            if (commaPosition is -1) {
+                // no more parameters after this
+                idEqualsValue = serializedParameters;
+            } else {
+                // take the first parameter...
+                idEqualsValue = serializedParameters
+                        .substring(0, commaPosition);
+
+                // ... and put the rest back into serializedParameters
+                serializedParameters = serializedParameters
+                        .substring(commaPosition + 1);
+            }
+
+            int equalsPosition = unescapedIndexOf(idEqualsValue, '=');
+
+            String parameterId;
+            String parameterValue;
+            if (equalsPosition is -1) {
+                // missing values are null
+                parameterId = unescape(idEqualsValue);
+                parameterValue = null;
+            } else {
+                parameterId = unescape(idEqualsValue.substring(0,
+                        equalsPosition));
+                parameterValue = unescape(idEqualsValue
+                        .substring(equalsPosition + 1));
+            }
+
+            for (int i = 0; i < parameters.length; i++) {
+                IParameter parameter = parameters[i];
+                if (parameter.getId().equals(parameterId)) {
+                    paramList.add(new Parameterization(parameter,
+                            parameterValue));
+                    break;
+                }
+            }
+
+        } while (commaPosition !is -1);
+
+        return arraycast!(Parameterization)( paramList
+                .toArray());
+    }
+
+    /**
+     * Gets the command {@link ParameterType} with the given identifier. If no
+     * such command parameter type currently exists, then the command parameter
+     * type will be created (but will be undefined).
+     *
+     * @param parameterTypeId
+     *            The identifier to find; must not be <code>null</code> and
+     *            must not be zero-length.
+     * @return The {@link ParameterType} with the given identifier; this value
+     *         will never be <code>null</code>, but it might be undefined.
+     * @since 3.2
+     */
+    public final ParameterType getParameterType(String parameterTypeId) {
+        checkId(parameterTypeId);
+
+        ParameterType parameterType = cast(ParameterType) parameterTypesById
+                .get(parameterTypeId);
+        if (parameterType is null) {
+            parameterType = new ParameterType(parameterTypeId);
+            parameterTypesById.put(parameterTypeId, parameterType);
+            parameterType.addListener(this);
+        }
+
+        return parameterType;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 3.2
+     */
+    public final void parameterTypeChanged(
+            ParameterTypeEvent parameterTypeEvent) {
+        if (parameterTypeEvent.isDefinedChanged()) {
+            ParameterType parameterType = parameterTypeEvent
+                    .getParameterType();
+            String parameterTypeId = parameterType.getId();
+            bool parameterTypeIdAdded = parameterType.isDefined();
+            if (parameterTypeIdAdded) {
+                definedParameterTypeIds.add(parameterTypeId);
+            } else {
+                definedParameterTypeIds.remove(parameterTypeId);
+            }
+
+            fireCommandManagerChanged(new CommandManagerEvent(this,
+                    parameterTypeId, parameterTypeIdAdded, true));
+        }
+    }
+
+    /**
+     * Removes a listener from this command manager.
+     *
+     * @param listener
+     *            The listener to be removed; must not be <code>null</code>.
+     */
+    public final void removeCommandManagerListener(
+            ICommandManagerListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Removes an execution listener from this command manager.
+     *
+     * @param listener
+     *            The listener to be removed; must not be <code>null</code>.
+     */
+    public final void removeExecutionListener(IExecutionListener listener) {
+        if (listener is null) {
+            throw new NullPointerException("Cannot remove a null listener"); //$NON-NLS-1$
+        }
+
+        if (executionListeners is null) {
+            return;
+        }
+
+        executionListeners.remove(cast(Object)listener);
+
+        if (executionListeners.isEmpty()) {
+            executionListeners = null;
+
+            // Remove the execution listener to every command.
+            Iterator commandItr = handleObjectsById.values().iterator();
+            while (commandItr.hasNext()) {
+                Command command = cast(Command) commandItr.next();
+                command.removeExecutionListener(executionListener);
+            }
+            executionListener = null;
+
+        }
+    }
+
+    /**
+     * Block updates all of the handlers for all of the commands. If the handler
+     * is <code>null</code> or the command id does not exist in the map, then
+     * the command becomes unhandled. Otherwise, the handler is set to the
+     * corresponding value in the map.
+     *
+     * @param handlersByCommandId
+     *            A map of command identifiers (<code>String</code>) to
+     *            handlers (<code>IHandler</code>). This map may be
+     *            <code>null</code> if all handlers should be cleared.
+     *            Similarly, if the map is empty, then all commands will become
+     *            unhandled.
+     */
+    public final void setHandlersByCommandId(Map handlersByCommandId) {
+        // Make that all the reference commands are created.
+        Iterator commandIdItr = handlersByCommandId.keySet().iterator();
+        while (commandIdItr.hasNext()) {
+            getCommand(stringcast(commandIdItr.next()));
+        }
+
+        // Now, set-up the handlers on all of the existing commands.
+        Iterator commandItr = handleObjectsById.values().iterator();
+        while (commandItr.hasNext()) {
+            Command command = cast(Command) commandItr.next();
+            String commandId = command.getId();
+            Object value = handlersByCommandId.get(commandId);
+            if ( cast(IHandler)value ) {
+                command.setHandler(cast(IHandler) value);
+            } else {
+                command.setHandler(null);
+            }
+        }
+    }
+
+    /**
+     * Sets the help context identifier to associate with a particular handler.
+     *
+     * @param handler
+     *            The handler with which to register a help context identifier;
+     *            must not be <code>null</code>.
+     * @param helpContextId
+     *            The help context identifier to register; may be
+     *            <code>null</code> if the help context identifier should be
+     *            removed.
+     * @since 3.2
+     */
+    public final void setHelpContextId(IHandler handler,
+            String helpContextId) {
+        if (handler is null) {
+            throw new NullPointerException("The handler cannot be null"); //$NON-NLS-1$
+        }
+        if (helpContextId is null) {
+            helpContextIdsByHandler.remove(cast(Object) handler);
+        } else {
+            helpContextIdsByHandler.put(cast(Object) handler, stringcast(helpContextId));
+        }
+    }
+
+    /**
+     * Searches for the index of a <code>char</code> in a <code>String</code>
+     * but disregards characters prefixed with the {@link #ESCAPE_CHAR} escape
+     * character. This is used by {@link #deserialize(String)} and
+     * {@link #getParameterizations(String, IParameter[])} to parse the
+     * serialized parameterized command string.
+     *
+     * @param escapedText
+     *            the string to search for the index of <code>ch</code> in
+     * @param ch
+     *            a character to search for in <code>escapedText</code>
+     * @return the index of the first unescaped occurrence of the character in
+     *         <code>escapedText</code>, or <code>-1</code> if the
+     *         character does not occur unescaped.
+     * @see String#indexOf(int)
+     */
+    private final int unescapedIndexOf(String escapedText, char ch) {
+
+        int pos = escapedText.indexOf(ch);
+
+        // first char can't be escaped
+        if (pos is 0) {
+            return pos;
+        }
+
+        while (pos !is -1) {
+            // look back for the escape character
+            if (escapedText.charAt(pos - 1) !is ESCAPE_CHAR) {
+                return pos;
+            }
+
+            // scan for the next instance of ch
+            pos = escapedText.indexOf(ch, pos + 1);
+        }
+
+        return pos;
+
+    }
+    /**
+     * Fires the <code>notEnabled</code> event for
+     * <code>executionListeners</code>.
+     * <p>
+     * <b>Note:</b> This supports bridging actions to the command framework,
+     * and should not be used outside the framework.
+     * </p>
+     *
+     * @param commandId
+     *            The command id of the command about to execute, never
+     *            <code>null</code>.
+     * @param exception
+     *            The exception, never <code>null</code>.
+     * @since 3.4
+     */
+    public void fireNotEnabled(String commandId, NotEnabledException exception) {
+        if (executionListener !is null) {
+            executionListener.notEnabled(commandId, exception);
+        }
+    }
+
+    /**
+     * Fires the <code>notDefined</code> event for
+     * <code>executionListeners</code>.
+     * <p>
+     * <b>Note:</b> This supports bridging actions to the command framework,
+     * and should not be used outside the framework.
+     * </p>
+     *
+     * @param commandId
+     *            The command id of the command about to execute, never
+     *            <code>null</code>.
+     * @param exception
+     *            The exception, never <code>null</code>.
+     * @since 3.4
+     */
+    public void fireNotDefined(String commandId, NotDefinedException exception) {
+        if (executionListener !is null) {
+            executionListener.notDefined(commandId, exception);
+        }
+    }
+
+    /**
+     * Fires the <code>preExecute</code> event for
+     * <code>executionListeners</code>.
+     * <p>
+     * <b>Note:</b> This supports bridging actions to the command framework,
+     * and should not be used outside the framework.
+     * </p>
+     *
+     * @param commandId
+     *            The command id of the command about to execute, never
+     *            <code>null</code>.
+     * @param event
+     *            The event that triggered the command, may be <code>null</code>.
+     * @since 3.4
+     */
+    public void firePreExecute(String commandId, ExecutionEvent event) {
+        if (executionListener !is null) {
+            executionListener.preExecute(commandId, event);
+        }
+    }
+
+    /**
+     * Fires the <code>postExecuteSuccess</code> event for
+     * <code>executionListeners</code>.
+     * <p>
+     * <b>Note:</b> This supports bridging actions to the command framework,
+     * and should not be used outside the framework.
+     * </p>
+     *
+     * @param commandId
+     *            The command id of the command executed, never
+     *            <code>null</code>.
+     * @param returnValue
+     *            The value returned from the command, may be <code>null</code>.
+     * @since 3.4
+     */
+    public void firePostExecuteSuccess(String commandId, Object returnValue) {
+        if (executionListener !is null) {
+            executionListener.postExecuteSuccess(commandId, returnValue);
+        }
+    }
+
+    /**
+     * Fires the <code>postExecuteFailure</code> event for
+     * <code>executionListeners</code>.
+     * <p>
+     * <b>Note:</b> This supports bridging actions to the command framework,
+     * and should not be used outside the framework.
+     * </p>
+     *
+     * @param commandId
+     *            The command id of the command executed, never
+     *            <code>null</code>.
+     * @param exception
+     *            The exception, never <code>null</code>.
+     * @since 3.4
+     */
+    public void firePostExecuteFailure(String commandId,
+            ExecutionException exception) {
+        if (executionListener !is null) {
+            executionListener.postExecuteFailure(commandId, exception);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/CommandManagerEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,310 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.commands.CommandManagerEvent;
+
+import org.eclipse.core.commands.CommandManager;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * An event indicating that the set of defined command identifiers has changed.
+ * </p>
+ *
+ * @since 3.1
+ * @see ICommandManagerListener#commandManagerChanged(CommandManagerEvent)
+ */
+public final class CommandManagerEvent {
+
+    /**
+     * The bit used to represent whether the given category has become defined.
+     * If this bit is not set and there is no category id, then no category has
+     * become defined nor undefined. If this bit is not set and there is a
+     * category id, then the category has become undefined.
+     */
+    private static const int CHANGED_CATEGORY_DEFINED = 1;
+
+    /**
+     * The bit used to represent whether the given command has become defined.
+     * If this bit is not set and there is no command id, then no command has
+     * become defined nor undefined. If this bit is not set and there is a
+     * command id, then the command has become undefined.
+     */
+    private static const int CHANGED_COMMAND_DEFINED = 1 << 1;
+
+    /**
+     * The bit used to represent whether the given command parameter type has
+     * become defined. If this bit is not set and there is no parameter type id,
+     * then no parameter type has become defined nor undefined. If this bit is
+     * not set and there is a parameter type id, then the parameter type has
+     * become undefined.
+     *
+     * @since 3.2
+     */
+    private static const int CHANGED_PARAMETER_TYPE_DEFINED = 1 << 2;
+
+    /**
+     * The category identifier that was added or removed from the list of
+     * defined category identifiers. This value is <code>null</code> if the
+     * list of defined category identifiers did not change.
+     */
+    private const String categoryId;
+
+    /**
+     * A collection of bits representing whether certain values have changed. A
+     * bit is set (i.e., <code>1</code>) if the corresponding property has
+     * changed.
+     */
+    private const int changedValues;
+
+    /**
+     * The command identifier that was added or removed from the list of defined
+     * command identifiers. This value is <code>null</code> if the list of
+     * defined command identifiers did not change.
+     */
+    private const String commandId;
+
+    /**
+     * The command parameter type identifier that was added or removed from the
+     * list of defined parameter type identifiers. This value is
+     * <code>null</code> if the list of defined parameter type identifiers did
+     * not change.
+     *
+     * @since 3.2
+     */
+    private const String parameterTypeId;
+
+    /**
+     * The command manager that has changed.
+     */
+    private const CommandManager commandManager;
+
+    /**
+     * Creates a new <code>CommandManagerEvent</code> instance to describe
+     * changes to commands and/or categories.
+     *
+     * @param commandManager
+     *            the instance of the interface that changed; must not be
+     *            <code>null</code>.
+     * @param commandId
+     *            The command identifier that was added or removed; must not be
+     *            <code>null</code> if commandIdChanged is <code>true</code>.
+     * @param commandIdAdded
+     *            Whether the command identifier became defined (otherwise, it
+     *            became undefined).
+     * @param commandIdChanged
+     *            Whether the list of defined command identifiers has changed.
+     * @param categoryId
+     *            The category identifier that was added or removed; must not be
+     *            <code>null</code> if categoryIdChanged is <code>true</code>.
+     * @param categoryIdAdded
+     *            Whether the category identifier became defined (otherwise, it
+     *            became undefined).
+     * @param categoryIdChanged
+     *            Whether the list of defined category identifiers has changed.
+     */
+    public this(CommandManager commandManager,
+            String commandId, bool commandIdAdded,
+            bool commandIdChanged, String categoryId,
+            bool categoryIdAdded, bool categoryIdChanged) {
+        if (commandManager is null) {
+            throw new NullPointerException(
+                    "An event must refer to its command manager"); //$NON-NLS-1$
+        }
+
+        if (commandIdChanged && (commandId is null)) {
+            throw new NullPointerException(
+                    "If the list of defined commands changed, then the added/removed command must be mentioned"); //$NON-NLS-1$
+        }
+
+        if (categoryIdChanged && (categoryId is null)) {
+            throw new NullPointerException(
+                    "If the list of defined categories changed, then the added/removed category must be mentioned"); //$NON-NLS-1$
+        }
+
+        this.commandManager = commandManager;
+        this.commandId = commandId;
+        this.categoryId = categoryId;
+
+        // this constructor only works for changes to commands and categories
+        this.parameterTypeId = null;
+
+        int changedValues = 0;
+        if (categoryIdChanged && categoryIdAdded) {
+            changedValues |= CHANGED_CATEGORY_DEFINED;
+        }
+        if (commandIdChanged && commandIdAdded) {
+            changedValues |= CHANGED_COMMAND_DEFINED;
+        }
+        this.changedValues = changedValues;
+    }
+
+    /**
+     * Creates a new <code>CommandManagerEvent</code> instance to describe
+     * changes to command parameter types.
+     *
+     * @param commandManager
+     *            the instance of the interface that changed; must not be
+     *            <code>null</code>.
+     * @param parameterTypeId
+     *            The command parameter type identifier that was added or
+     *            removed; must not be <code>null</code> if
+     *            parameterTypeIdChanged is <code>true</code>.
+     * @param parameterTypeIdAdded
+     *            Whether the parameter type identifier became defined
+     *            (otherwise, it became undefined).
+     * @param parameterTypeIdChanged
+     *            Whether the list of defined parameter type identifiers has
+     *            changed.
+     *
+     * @since 3.2
+     */
+    public this(CommandManager commandManager,
+            String parameterTypeId, bool parameterTypeIdAdded,
+            bool parameterTypeIdChanged) {
+
+        if (commandManager is null) {
+            throw new NullPointerException(
+                    "An event must refer to its command manager"); //$NON-NLS-1$
+        }
+
+        if (parameterTypeIdChanged && (parameterTypeId is null)) {
+            throw new NullPointerException(
+                    "If the list of defined command parameter types changed, then the added/removed parameter type must be mentioned"); //$NON-NLS-1$
+        }
+
+        this.commandManager = commandManager;
+        this.commandId = null;
+        this.categoryId = null;
+
+        this.parameterTypeId = parameterTypeId;
+
+        int changedValues = 0;
+        if (parameterTypeIdChanged && parameterTypeIdAdded) {
+            changedValues |= CHANGED_PARAMETER_TYPE_DEFINED;
+        }
+
+        this.changedValues = changedValues;
+    }
+
+    /**
+     * Returns the category identifier that was added or removed.
+     *
+     * @return The category identifier that was added or removed; may be
+     *         <code>null</code>.
+     */
+    public final String getCategoryId() {
+        return categoryId;
+    }
+
+    /**
+     * Returns the command identifier that was added or removed.
+     *
+     * @return The command identifier that was added or removed; may be
+     *         <code>null</code>.
+     */
+    public final String getCommandId() {
+        return commandId;
+    }
+
+    /**
+     * Returns the instance of the interface that changed.
+     *
+     * @return the instance of the interface that changed. Guaranteed not to be
+     *         <code>null</code>.
+     */
+    public final CommandManager getCommandManager() {
+        return commandManager;
+    }
+
+    /**
+     * Returns the command parameter type identifier that was added or removed.
+     *
+     * @return The command parameter type identifier that was added or removed;
+     *         may be <code>null</code>.
+     *
+     * @since 3.2
+     */
+    public final String getParameterTypeId() {
+        return parameterTypeId;
+    }
+
+    /**
+     * Returns whether the list of defined category identifiers has changed.
+     *
+     * @return <code>true</code> if the list of category identifiers has
+     *         changed; <code>false</code> otherwise.
+     */
+    public final bool isCategoryChanged() {
+        return (categoryId !is null);
+    }
+
+    /**
+     * Returns whether the category identifier became defined. Otherwise, the
+     * category identifier became undefined.
+     *
+     * @return <code>true</code> if the category identifier became defined;
+     *         <code>false</code> if the category identifier became undefined.
+     */
+    public final bool isCategoryDefined() {
+        return (((changedValues & CHANGED_CATEGORY_DEFINED) !is 0) && (categoryId !is null));
+    }
+
+    /**
+     * Returns whether the list of defined command identifiers has changed.
+     *
+     * @return <code>true</code> if the list of command identifiers has
+     *         changed; <code>false</code> otherwise.
+     */
+    public final bool isCommandChanged() {
+        return (commandId !is null);
+    }
+
+    /**
+     * Returns whether the command identifier became defined. Otherwise, the
+     * command identifier became undefined.
+     *
+     * @return <code>true</code> if the command identifier became defined;
+     *         <code>false</code> if the command identifier became undefined.
+     */
+    public final bool isCommandDefined() {
+        return (((changedValues & CHANGED_COMMAND_DEFINED) !is 0) && (commandId !is null));
+    }
+
+    /**
+     * Returns whether the list of defined command parameter type identifiers
+     * has changed.
+     *
+     * @return <code>true</code> if the list of command parameter type
+     *         identifiers has changed; <code>false</code> otherwise.
+     *
+     * @since 3.2
+     */
+    public final bool isParameterTypeChanged() {
+        return (parameterTypeId !is null);
+    }
+
+    /**
+     * Returns whether the command parameter type identifier became defined.
+     * Otherwise, the command parameter type identifier became undefined.
+     *
+     * @return <code>true</code> if the command parameter type identifier
+     *         became defined; <code>false</code> if the command parameter
+     *         type identifier became undefined.
+     *
+     * @since 3.2
+     */
+    public final bool isParameterTypeDefined() {
+        return (((changedValues & CHANGED_PARAMETER_TYPE_DEFINED) !is 0) && (parameterTypeId !is null));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/ExecutionEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,244 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.core.commands.ExecutionEvent;
+
+// import java.util.Collections;
+
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.ParameterType;
+import org.eclipse.core.commands.AbstractParameterValueConverter;
+import org.eclipse.core.commands.ParameterValueConversionException;
+
+import java.lang.all;
+import java.util.Map;
+import java.util.Collections;
+
+/**
+ * <p>
+ * The data object to pass to the command (and its handler) as it executes. This
+ * carries information about the current state of the application, and the
+ * application context in which the command was executed.
+ * </p>
+ * <p>
+ * An execution event carries three blocks of data: the parameters, the trigger,
+ * and the application context. How these blocks are used is application
+ * dependent. In the Eclipse workbench, the trigger is an SWT event, and the
+ * application context contains information about the selection and active part.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class ExecutionEvent {
+    /**
+     * The state of the application at the time the execution was triggered. In
+     * the Eclipse workbench, this might contain information about the active
+     * part of the active selection (for example). This value may be
+     * <code>null</code>.
+     */
+    private const Object applicationContext;
+
+    /**
+     * The command being executed. This value may be <code>null</code>.
+     */
+    private const Command command;
+
+    /**
+     * The parameters to qualify the execution. For handlers that normally
+     * prompt for additional information, these can be used to avoid prompting.
+     * This value may be empty, but it is never <code>null</code>.
+     */
+    private const Map parameters;
+
+    /**
+     * The object that triggered the execution. In an event-driven architecture,
+     * this is typically just another event. In the Eclipse workbench, this is
+     * typically an SWT event. This value may be <code>null</code>.
+     */
+    private const Object trigger;
+
+    /**
+     * Constructs a new instance of <code>ExecutionEvent</code> with no
+     * parameters, no trigger and no application context. This is just a
+     * convenience method.
+     *
+     * @since 3.2
+     */
+    public this() {
+        this(null, Collections.EMPTY_MAP, null, null);
+    }
+
+    /**
+     * Constructs a new instance of <code>ExecutionEvent</code>.
+     *
+     * @param parameters
+     *            The parameters to qualify the execution; must not be
+     *            <code>null</code>. This must be a map of parameter ids (<code>String</code>)
+     *            to parameter values (<code>String</code>).
+     * @param trigger
+     *            The object that triggered the execution; may be
+     *            <code>null</code>.
+     * @param applicationContext
+     *            The state of the application at the time the execution was
+     *            triggered; may be <code>null</code>.
+     * @deprecated use
+     *             {@link ExecutionEvent#ExecutionEvent(Command, Map, Object, Object)}
+     */
+    public this(Map parameters, Object trigger,
+            Object applicationContext) {
+        this(null, parameters, trigger, applicationContext);
+    }
+
+    /**
+     * Constructs a new instance of <code>ExecutionEvent</code>.
+     *
+     * @param command
+     *            The command being executed; may be <code>null</code>.
+     * @param parameters
+     *            The parameters to qualify the execution; must not be
+     *            <code>null</code>. This must be a map of parameter ids (<code>String</code>)
+     *            to parameter values (<code>String</code>).
+     * @param trigger
+     *            The object that triggered the execution; may be
+     *            <code>null</code>.
+     * @param applicationContext
+     *            The state of the application at the time the execution was
+     *            triggered; may be <code>null</code>.
+     * @since 3.2
+     */
+    public this(Command command, Map parameters,
+            Object trigger, Object applicationContext) {
+        if (parameters is null) {
+            throw new NullPointerException(
+                    "An execution event must have a non-null map of parameters"); //$NON-NLS-1$
+        }
+
+        this.command = command;
+        this.parameters = parameters;
+        this.trigger = trigger;
+        this.applicationContext = applicationContext;
+    }
+
+    /**
+     * Returns the state of the application at the time the execution was
+     * triggered.
+     *
+     * @return The application context; may be <code>null</code>.
+     */
+    public final Object getApplicationContext() {
+        return applicationContext;
+    }
+
+    /**
+     * Returns the command being executed.
+     *
+     * @return The command being executed.
+     * @since 3.2
+     */
+    public final Command getCommand() {
+        return command;
+    }
+
+    /**
+     * Returns the object represented by the string value of the parameter with
+     * the provided id.
+     * <p>
+     * This is intended to be used in the scope of an
+     * {@link IHandler#execute(ExecutionEvent)} method, so any problem getting
+     * the object value causes <code>ExecutionException</code> to be thrown.
+     * </p>
+     *
+     * @param parameterId
+     *            The id of a parameter to retrieve the object value of.
+     * @return The object value of the parameter with the provided id.
+     * @throws ExecutionException
+     *             if the parameter object value could not be obtained for any
+     *             reason
+     * @since 3.2
+     */
+    public final Object getObjectParameterForExecution(String parameterId) {
+        if (command is null) {
+            throw new ExecutionException(
+                    "No command is associated with this execution event"); //$NON-NLS-1$
+        }
+
+        try {
+            ParameterType parameterType = command
+                    .getParameterType(parameterId);
+            if (parameterType is null) {
+                throw new ExecutionException(
+                        "Command does not have a parameter type for the given parameter"); //$NON-NLS-1$
+            }
+            AbstractParameterValueConverter valueConverter = parameterType
+                    .getValueConverter();
+            if (valueConverter is null) {
+                throw new ExecutionException(
+                        "Command does not have a value converter"); //$NON-NLS-1$
+            }
+            String stringValue = getParameter(parameterId);
+            Object objectValue = valueConverter
+                    .convertToObject(stringValue);
+            return objectValue;
+        } catch (NotDefinedException e) {
+            throw new ExecutionException("Command is not defined", e); //$NON-NLS-1$
+        } catch (ParameterValueConversionException e) {
+            throw new ExecutionException(
+                    "The parameter string could not be converted to an object", e); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Returns the value of the parameter with the given id.
+     *
+     * @param parameterId
+     *            The id of the parameter to retrieve; may be <code>null</code>.
+     * @return The parameter value; <code>null</code> if the parameter cannot
+     *         be found.
+     */
+    public final String getParameter(String parameterId) {
+        return stringcast(parameters.get(parameterId));
+    }
+
+    /**
+     * Returns all of the parameters.
+     *
+     * @return The parameters; never <code>null</code>, but may be empty.
+     */
+    public final Map getParameters() {
+        return parameters;
+    }
+
+    /**
+     * Returns the object that triggered the execution
+     *
+     * @return The trigger; <code>null</code> if there was no trigger.
+     */
+    public final Object getTrigger() {
+        return trigger;
+    }
+
+    /**
+     * The string representation of this execution event -- for debugging
+     * purposes only. This string should not be shown to an end user.
+     *
+     * @return The string representation; never <code>null</code>.
+     */
+    public override final String toString() {
+        String parm;
+        foreach( k, v; parameters ){
+            parm ~= "{"~stringcast(k)~","~stringcast(v)~"}";
+        }
+        return Format( "ExecutionEvent({},{},{})", command, parm, trigger, applicationContext );
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/ExecutionException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.commands.ExecutionException;
+
+import org.eclipse.core.commands.common.CommandException;
+import java.lang.all;
+
+/**
+ * Signals that an exception occured during the execution of a command.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class ExecutionException : CommandException {
+
+    /**
+     * Generated serial version UID for this class.
+     *
+     * @since 3.1
+     */
+    private static final long serialVersionUID = 3258130262767448120L;
+
+    /**
+     * Creates a new instance of this class with the specified detail message.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     * @since 3.2
+     */
+    public this(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new instance of this class with the specified detail message
+     * and cause.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     * @param cause
+     *            the cause; may be <code>null</code>.
+     */
+    public this(String message, Exception cause) {
+        super(message, cause);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/HandlerEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.commands.HandlerEvent;
+
+import org.eclipse.core.commands.common.AbstractBitSetEvent;
+import org.eclipse.core.commands.IHandler;
+import java.lang.all;
+
+/**
+ * An instance of this class describes changes to an instance of
+ * <code>IHandler</code>.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see IHandlerListener#handlerChanged(HandlerEvent)
+ */
+public final class HandlerEvent : AbstractBitSetEvent {
+
+    /**
+     * The bit used to represent whether the handler has changed its enabled
+     * state.
+     */
+    private static final int CHANGED_ENABLED = 1;
+
+    /**
+     * The bit used to represent whether the handler has changed its handled
+     * state.
+     */
+    private static final int CHANGED_HANDLED = 1 << 1;
+
+    /**
+     * The handler that changed; this value is never <code>null</code>.
+     */
+    private final IHandler handler;
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param handler
+     *            the instance of the interface that changed; must not be
+     *            <code>null</code>.
+     * @param enabledChanged
+     *            Whether the enabled state of the handler has changed.
+     * @param handledChanged
+     *            Whether the handled state of the handler has changed.
+     */
+    public this(IHandler handler, bool enabledChanged,
+            bool handledChanged) {
+        if (handler is null) {
+            throw new NullPointerException();
+        }
+        this.handler = handler;
+
+        if (enabledChanged) {
+            changedValues |= CHANGED_ENABLED;
+        }
+        if (handledChanged) {
+            changedValues |= CHANGED_HANDLED;
+        }
+    }
+
+    /**
+     * Returns the instance of the interface that changed.
+     *
+     * @return the instance of the interface that changed. Guaranteed not to be
+     *         <code>null</code>.
+     */
+    public IHandler getHandler() {
+        return handler;
+    }
+
+    /**
+     * Returns whether or not the enabled property changed.
+     *
+     * @return <code>true</code>, iff the enabled property changed.
+     */
+    public bool isEnabledChanged() {
+        return ((changedValues & CHANGED_ENABLED) !is 0);
+    }
+
+    /**
+     * Returns whether or not the handled property changed.
+     *
+     * @return <code>true</code>, iff the handled property changed.
+     */
+    public bool isHandledChanged() {
+        return ((changedValues & CHANGED_HANDLED) !is 0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/ICategoryListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.ICategoryListener;
+
+import org.eclipse.core.commands.CategoryEvent;
+
+/**
+ * An instance of this interface can be used by clients to receive notification
+ * of changes to one or more instances of <code>Category</code>.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see Category#addCategoryListener(ICategoryListener)
+ * @see Category#removeCategoryListener(ICategoryListener)
+ */
+public interface ICategoryListener {
+
+    /**
+     * Notifies that one or more properties of an instance of
+     * <code>Category</code> have changed. Specific details are described in
+     * the <code>CategoryEvent</code>.
+     *
+     * @param categoryEvent
+     *            the category event. Guaranteed not to be <code>null</code>.
+     */
+    void categoryChanged(CategoryEvent categoryEvent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/ICommandListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.ICommandListener;
+
+import org.eclipse.core.commands.CommandEvent;
+
+/**
+ * An instance of this interface can be used by clients to receive notification
+ * of changes to one or more instances of <code>Command</code>.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see Command#addCommandListener(ICommandListener)
+ * @see Command#removeCommandListener(ICommandListener)
+ */
+public interface ICommandListener {
+
+    /**
+     * Notifies that one or more properties of an instance of
+     * <code>Command</code> have changed. Specific details are described in
+     * the <code>CommandEvent</code>.
+     *
+     * @param commandEvent
+     *            the command event. Guaranteed not to be <code>null</code>.
+     */
+    void commandChanged(CommandEvent commandEvent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/ICommandManagerListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.ICommandManagerListener;
+
+import org.eclipse.core.commands.CommandManagerEvent;
+/**
+ * An instance of this interface can be used by clients to receive notification
+ * of changes to one or more instances of <code>ICommandManager</code>.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see CommandManager#addCommandManagerListener(ICommandManagerListener)
+ * @see CommandManager#removeCommandManagerListener(ICommandManagerListener)
+ */
+public interface ICommandManagerListener {
+
+    /**
+     * Notifies that one or more properties of an instance of
+     * <code>ICommandManager</code> have changed. Specific details are
+     * described in the <code>CommandManagerEvent</code>.
+     *
+     * @param commandManagerEvent
+     *            the commandManager event. Guaranteed not to be
+     *            <code>null</code>.
+     */
+    void commandManagerChanged(CommandManagerEvent commandManagerEvent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/IExecutionListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.IExecutionListener;
+
+import org.eclipse.core.commands.NotHandledException;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.ExecutionEvent;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * A listener to the execution of commands. This listener will be notified if a
+ * command is about to execute, and when that execution completes. It is not
+ * possible for the listener to prevent the execution, only to respond to it in
+ * some way.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IExecutionListener {
+
+    /**
+     * Notifies the listener that an attempt was made to execute a command with
+     * no handler.
+     *
+     * @param commandId
+     *            The identifier of command that is not handled; never
+     *            <code>null</code>
+     * @param exception
+     *            The exception that occurred; never <code>null</code>.
+     */
+    public void notHandled(String commandId, NotHandledException exception);
+
+    /**
+     * Notifies the listener that a command has failed to complete execution.
+     *
+     * @param commandId
+     *            The identifier of the command that has executed; never
+     *            <code>null</code>.
+     * @param exception
+     *            The exception that occurred; never <code>null</code>.
+     */
+    public void postExecuteFailure(String commandId,
+            ExecutionException exception);
+
+    /**
+     * Notifies the listener that a command has completed execution
+     * successfully.
+     *
+     * @param commandId
+     *            The identifier of the command that has executed; never
+     *            <code>null</code>.
+     * @param returnValue
+     *            The return value from the command; may be <code>null</code>.
+     */
+    public void postExecuteSuccess(String commandId, Object returnValue);
+
+    /**
+     * Notifies the listener that a command is about to execute.
+     *
+     * @param commandId
+     *            The identifier of the command that is about to execute, never
+     *            <code>null</code>.
+     * @param event
+     *            The event that will be passed to the <code>execute</code>
+     *            method; never <code>null</code>.
+     */
+    public void preExecute(String commandId, ExecutionEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/IExecutionListenerWithChecks.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.IExecutionListenerWithChecks;
+
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.commands.IExecutionListener;
+import org.eclipse.core.commands.NotEnabledException;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * A listener to the execution of commands. This listener will be notified if
+ * someone tries to execute a command and it is not enabled or not defined. The
+ * listener also be notified if a command is about to execute, and when that
+ * execution completes. It is not possible for the listener to prevent the
+ * execution, only to respond to it in some way.
+ * </p>
+ * <p>
+ * Clients may implement, but must not extend.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface IExecutionListenerWithChecks : IExecutionListener {
+
+    /**
+     * Notifies the listener that an attempt was made to execute a command that
+     * is not defined.
+     *
+     * @param commandId
+     *            The identifier of command that is not defined; never
+     *            <code>null</code>
+     * @param exception
+     *            The exception that occurred; never <code>null</code>.
+     */
+    public void notDefined(String commandId, NotDefinedException exception);
+
+    /**
+     * Notifies the listener that an attempt was made to execute a command that
+     * is disabled.
+     *
+     * @param commandId
+     *            The identifier of command that is not enabled; never
+     *            <code>null</code>
+     * @param exception
+     *            The exception that occurred; never <code>null</code>.
+     */
+    public void notEnabled(String commandId, NotEnabledException exception);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/IHandler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.IHandler;
+
+import org.eclipse.core.commands.IHandlerListener;
+import org.eclipse.core.commands.ExecutionEvent;
+
+/**
+ * A handler is the pluggable piece of a command that handles execution. Each
+ * command can have zero or more handlers associated with it (in general), of
+ * which only one will be active at any given moment in time. When the command
+ * is asked to execute, it will simply pass that request on to its active
+ * handler, if any.
+ *
+ * @see AbstractHandler
+ * @since 3.1
+ */
+public interface IHandler {
+
+    /**
+     * Registers an instance of <code>IHandlerListener</code> to listen for
+     * changes to properties of this instance.
+     *
+     * @param handlerListener
+     *            the instance to register. Must not be <code>null</code>. If
+     *            an attempt is made to register an instance which is already
+     *            registered with this instance, no operation is performed.
+     */
+    void addHandlerListener(IHandlerListener handlerListener);
+
+    /**
+     * Disposes of this handler. This method is run once when the object is no
+     * longer referenced. This can be used as an opportunity to unhook listeners
+     * from other objects.
+     */
+    public void dispose();
+
+    /**
+     * Executes with the map of parameter values by name.
+     *
+     * @param event
+     *            An event containing all the information about the current
+     *            state of the application; must not be <code>null</code>.
+     * @return the result of the execution. Reserved for future use, must be
+     *         <code>null</code>.
+     * @throws ExecutionException
+     *             if an exception occurred during execution.
+     */
+    Object execute(ExecutionEvent event);
+
+    /**
+     * Returns whether this handler is capable of executing at this moment in
+     * time. If the enabled state is other than true clients should also
+     * consider implementing IHandler2 so they can be notified about framework
+     * execution contexts.
+     *
+     * @return <code>true</code> if the command is enabled; <code>false</code>
+     *         otherwise.
+     * @see IHandler2#setEnabled(Object)
+     */
+    public bool isEnabled();
+
+    /**
+     * Returns whether this handler is really capable of handling delegation. In
+     * the case of a handler that is a composition of other handlers, this reply
+     * is intended to indicate whether the handler is truly capable of receiving
+     * delegated responsibilities at this time.
+     *
+     * @return <code>true</code> if the handler is handled; <code>false</code>
+     *         otherwise.
+     */
+    public bool isHandled();
+
+    /**
+     * Unregisters an instance of <code>IHandlerListener</code> listening for
+     * changes to properties of this instance.
+     *
+     * @param handlerListener
+     *            the instance to unregister. Must not be <code>null</code>.
+     *            If an attempt is made to unregister an instance which is not
+     *            already registered with this instance, no operation is
+     *            performed.
+     */
+    void removeHandlerListener(IHandlerListener handlerListener);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/IHandler2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.core.commands.IHandler2;
+
+import org.eclipse.core.commands.IHandler;
+
+/**
+ * Extend the IHandler interface to provide some context for isEnabled()
+ * requests. Clients should use {@link AbstractHandler} unless they need to
+ * provide their own listener mechanism.
+ *
+ * @since 3.4
+ * @see AbstractHandler
+ */
+public interface IHandler2 : IHandler {
+    /**
+     * Called by the framework to allow the handler to update its enabled state.
+     *
+     * @param evaluationContext
+     *            the state to evaluate against. May be <code>null</code>
+     *            which indicates that the handler can query whatever model that
+     *            is necessary. This context must not be cached.
+     */
+    public void setEnabled(Object evaluationContext);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/IHandlerAttributes.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.IHandlerAttributes;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * Attribute constants that have special meanings within this package.  These
+ * attributes can be used to communicate extra information from the handler to
+ * either the command or the command manager.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IHandlerAttributes {
+
+    /**
+     * <p>
+     * The name of the attribute indicating whether the handler is handled.
+     * This is intended largely for backward compatibility with the workbench
+     * <code>RetargetAction</code> class.  It is used to indicate that while
+     * the handler is handling a command, it should not be treated as such.
+     * The command should act and behave as if it has no handler.
+     * </p>
+     */
+    public static const String ATTRIBUTE_HANDLED = "handled"; //$NON-NLS-1$
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/IHandlerListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.IHandlerListener;
+
+import org.eclipse.core.commands.HandlerEvent;
+
+/**
+ * An instance of this interface can be used by clients to receive notification
+ * of changes to one or more instances of <code>IHandler</code>.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see IHandler#addHandlerListener(IHandlerListener)
+ * @see IHandler#removeHandlerListener(IHandlerListener)
+ */
+public interface IHandlerListener {
+
+    /**
+     * Notifies that one or more properties of an instance of
+     * <code>IHandler</code> have changed. Specific details are described in
+     * the <code>HandlerEvent</code>.
+     *
+     * @param handlerEvent
+     *            the handler event. Guaranteed not to be <code>null</code>.
+     */
+    void handlerChanged(HandlerEvent handlerEvent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/INamedHandleStateIds.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.INamedHandleStateIds;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * State identifiers that are understood by named handle objects that implement
+ * {@link IObjectWithState}.
+ * </p>
+ * <p>
+ * Clients may implement or extend this class.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface INamedHandleStateIds {
+
+    /**
+     * The state id used for overriding the description of a named handle
+     * object. This state's value must return a {@link String}.
+     */
+    public static String DESCRIPTION = "DESCRIPTION"; //$NON-NLS-1$
+
+    /**
+     * The state id used for overriding the name of a named handle object. This
+     * state's value must return a {@link String}.
+     */
+    public static String NAME = "NAME"; //$NON-NLS-1$
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/IObjectWithState.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.IObjectWithState;
+
+import org.eclipse.core.commands.State;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * An object that holds zero or more state objects. This state information can
+ * be shared between different instances of <code>IObjectWithState</code>.
+ * </p>
+ * <p>
+ * Clients may implement, but must not extend this interface.
+ * </p>
+ *
+ * @see AbstractHandlerWithState
+ * @since 3.2
+ */
+public interface IObjectWithState {
+
+    /**
+     * Adds state to this object.
+     *
+     * @param id
+     *            The identifier indicating the type of state being added; must
+     *            not be <code>null</code>.
+     * @param state
+     *            The new state to add to this object; must not be
+     *            <code>null</code>.
+     */
+    public void addState(String id, State state);
+
+    /**
+     * Gets the state with the given id.
+     *
+     * @param stateId
+     *            The identifier of the state to retrieve; must not be
+     *            <code>null</code>.
+     * @return The state; may be <code>null</code> if there is no state with
+     *         the given id.
+     */
+    public State getState(String stateId);
+
+    /**
+     * Gets the identifiers for all of the state associated with this object.
+     *
+     * @return All of the state identifiers; may be empty, but never
+     *         <code>null</code>.
+     */
+    public String[] getStateIds();
+
+    /**
+     * Removes state from this object.
+     *
+     * @param stateId
+     *            The id of the state to remove from this object; must not be
+     *            <code>null</code>.
+     */
+    public void removeState(String stateId);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/IParameter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.IParameter;
+
+import org.eclipse.core.commands.IParameterValues;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * A parameter for a command. A parameter identifies a type of information that
+ * the command might accept. For example, a "Show View" command might accept the
+ * id of a view for display. This parameter also identifies possible values, for
+ * display in the user interface.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IParameter {
+
+    /**
+     * Returns the identifier for this parameter.
+     *
+     * @return The identifier; never <code>null</code>.
+     */
+    public String getId();
+
+    /**
+     * Returns the human-readable name for this parameter.
+     *
+     * @return The parameter name; never <code>null</code>.
+     */
+    public String getName();
+
+    /**
+     * Returns the values associated with this parameter.
+     *
+     * @return The values associated with this parameter. This must not be
+     *         <code>null</code>.
+     * @throws ParameterValuesException
+     *             If the values can't be retrieved for some reason.
+     */
+    public IParameterValues getValues();
+
+    /**
+     * Returns whether parameter is optional. Otherwise, it is required.
+     *
+     * @return <code>true</code> if the parameter is optional;
+     *         <code>false</code> if it is required.
+     */
+    public bool isOptional();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/IParameterTypeListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.IParameterTypeListener;
+
+import org.eclipse.core.commands.ParameterTypeEvent;
+
+import java.lang.all;
+
+/**
+ * An instance of this interface can be used by clients to receive notification
+ * of changes to one or more instances of {@link ParameterType}.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.2
+ * @see ParameterType#addListener(IParameterTypeListener)
+ * @see ParameterType#removeListener(IParameterTypeListener)
+ */
+public interface IParameterTypeListener {
+
+    /**
+     * Notifies that one or more properties of an instance of
+     * {@link ParameterType} have changed. Specific details are described in the
+     * {@link ParameterTypeEvent}.
+     *
+     * @param parameterTypeEvent
+     *            the event. Guaranteed not to be <code>null</code>.
+     */
+    void parameterTypeChanged(ParameterTypeEvent parameterTypeEvent);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/IParameterValues.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.IParameterValues;
+
+import java.lang.all;
+import java.util.Map;
+
+/**
+ * <p>
+ * The parameters for a command. This interface will only be consulted if the
+ * parameters need to be displayed to the user. Otherwise, they will be ignored.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IParameterValues {
+
+    /**
+     * Returns a map keyed by externalized names for parameter values. These
+     * names should be human-readable, and are generally for display to the user
+     * in a user interface of some sort. The values should be actual values that
+     * will be interpreted by the handler for the command.
+     *
+     * @return A map of the externalizable name of the parameter value (<code>String</code>)
+     *         to the actual value of the parameter (<code>String</code>).
+     */
+    public Map getParameterValues();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/IStateListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.IStateListener;
+
+import org.eclipse.core.commands.State;
+
+/**
+ * <p>
+ * A listener to changes in some state.
+ * </p>
+ * <p>
+ * Clients may implement, but must not extend this interface.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface IStateListener {
+
+    /**
+     * Handles a change to the value in some state.
+     *
+     * @param state
+     *            The state that has changed; never <code>null</code>. The
+     *            value for this state has been updated to the new value.
+     * @param oldValue
+     *            The old value; may be anything.
+     */
+    public void handleStateChange(State state, Object oldValue);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/ITypedParameter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.ITypedParameter;
+
+import org.eclipse.core.commands.ParameterType;
+
+/**
+ * A command parameter that has a declared type. This interface is intended to
+ * be implemented by implementors of {@link IParameter} that will support
+ * parameter types.
+ *
+ * @since 3.2
+ */
+public interface ITypedParameter {
+
+    /**
+     * Returns the {@link ParameterType} associated with a command parameter or
+     * <code>null</code> if the parameter does not declare a type.
+     * <p>
+     * Note that the parameter type returned may be undefined.
+     * </p>
+     *
+     * @return the parameter type associated with a command parameter or
+     *         <code>null</code> if the parameter does not declare a type
+     */
+    public ParameterType getParameterType();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/NamedHandleObjectWithState.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.NamedHandleObjectWithState;
+
+import org.eclipse.core.commands.common.NamedHandleObject;
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.commands.IObjectWithState;
+import org.eclipse.core.commands.State;
+import org.eclipse.core.commands.INamedHandleStateIds;
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+/**
+ * <p>
+ * A named handle object that can carry state with it. This state can be used to
+ * override the name or description.
+ * </p>
+ * <p>
+ * Clients may neither instantiate nor extend this class.
+ * </p>
+ *
+ * @since 3.2
+ */
+abstract class NamedHandleObjectWithState : NamedHandleObject,
+        IObjectWithState {
+
+    /**
+     * An empty string array, which can be returned from {@link #getStateIds()}
+     * if there is no state.
+     */
+    private static const String[] NO_STATE = null;
+
+    /**
+     * The map of states currently held by this command. If this command has no
+     * state, then this will be <code>null</code>.
+     */
+    private Map states = null;
+
+    /**
+     * Constructs a new instance of <code>NamedHandleObject<WithState/code>.
+     *
+     * @param id
+     *            The identifier for this handle; must not be <code>null</code>.
+     */
+    protected this(String id) {
+        super(id);
+    }
+
+    public void addState(String stateId, State state) {
+        if (state is null) {
+            throw new NullPointerException("Cannot add a null state"); //$NON-NLS-1$
+        }
+
+        if (states is null) {
+            states = new HashMap(3);
+        }
+        states.put(stateId, state);
+    }
+
+    public override final String getDescription() {
+        String description = super.getDescription(); // Trigger a NDE.
+
+        State descriptionState = getState(INamedHandleStateIds.DESCRIPTION);
+        if (descriptionState !is null) {
+            Object value = descriptionState.getValue();
+            if (value !is null) {
+                return value.toString();
+            }
+        }
+
+        return description;
+    }
+
+    public override final String getName() {
+        String name = super.getName(); // Trigger a NDE, if necessary.
+
+        State nameState = getState(INamedHandleStateIds.NAME);
+        if (nameState !is null) {
+            final Object value = nameState.getValue();
+            if (value !is null) {
+                return value.toString();
+            }
+        }
+
+        return name;
+    }
+
+    public final State getState(String stateId) {
+        if ((states is null) || (states.isEmpty())) {
+            return null;
+        }
+
+        return cast(State) states.get(stateId);
+    }
+
+    public final String[] getStateIds() {
+        if ((states is null) || (states.isEmpty())) {
+            return NO_STATE;
+        }
+
+        Set stateIds = states.keySet();
+        return stringcast( stateIds.toArray());
+    }
+
+    public void removeState(String id) {
+        if (id is null) {
+            throw new NullPointerException("Cannot remove a null id"); //$NON-NLS-1$
+        }
+
+        if (states !is null) {
+            states.remove(id);
+            if (states.isEmpty()) {
+                states = null;
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/NotEnabledException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.NotEnabledException;
+
+import org.eclipse.core.commands.common.CommandException;
+
+import java.lang.all;
+
+/**
+ * Signals that an attempt was made to execute a command that is not enabled.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.2
+ */
+public final class NotEnabledException : CommandException {
+
+    /**
+     * Generated serial version UID for this class.
+     *
+     * @since 3.1
+     */
+    private static final long serialVersionUID = 3257572788998124596L;
+
+    /**
+     * Creates a new instance of this class with the specified detail message.
+     *
+     * @param s
+     *            the detail message; may be <code>null</code>.
+     */
+    public this(String s) {
+        super(s);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/NotHandledException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.NotHandledException;
+
+import org.eclipse.core.commands.common.CommandException;
+
+import java.lang.all;
+
+/**
+ * Signals that an attempt was made to access the properties of an unhandled
+ * object.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class NotHandledException : CommandException {
+
+    /**
+     * Generated serial version UID for this class.
+     *
+     * @since 3.1
+     */
+    private static final long serialVersionUID = 3256446914827726904L;
+
+    /**
+     * Creates a new instance of this class with the specified detail message.
+     *
+     * @param s
+     *            the detail message.
+     */
+    public this(String s) {
+        super(s);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/ParameterType.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,299 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.ParameterType;
+
+import org.eclipse.core.commands.common.HandleObject;
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.internal.commands.util.Util;
+import org.eclipse.core.commands.AbstractParameterValueConverter;
+import org.eclipse.core.commands.IParameterTypeListener;
+import org.eclipse.core.commands.ParameterTypeEvent;
+
+import java.lang.all;
+import tango.text.convert.Format;
+
+/**
+ * <p>
+ * Provides information about the type of a command parameter. Clients can use a
+ * parameter type to check if an object matches the type of the parameter with
+ * {@link #isCompatible(Object)} and can get an
+ * {@link AbstractParameterValueConverter} to convert between objects matching
+ * the parameter type and strings that encode the object's identity.
+ * </p>
+ * <p>
+ * A command parameter is not required to declare a type. To determine if a
+ * given parameter has a type, check if an {@link IParameter} implements
+ * {@link ITypedParameter} and if so, use
+ * {@link ITypedParameter#getParameterType()} like this:
+ * </p>
+ *
+ * <pre>
+ *                   IParameter parameter = // ... get IParameter from Command
+ *                   if (parameter instanceof ITypedParameter) {
+ *                     ParameterType type = ((ITypedParameter)parameter).getParameterType();
+ *                     if (type !is null) {
+ *                       // this parameter has a ParameterType
+ *                     }
+ *                   }
+ * </pre>
+ *
+ * @see IParameter
+ * @see ITypedParameter#getParameterType()
+ * @since 3.2
+ */
+public final class ParameterType : HandleObject, Comparable {
+
+    /**
+     * TODO: this was copied from
+     * org.eclipse.core.internal.expressions.Expressions is there a better place
+     * to reference this?
+     *
+     * @param element
+     *            The element to test; may be <code>null</code>.
+     * @param type
+     *            The type against which we are testing;may be <code>null</code>.
+     * @return <code>true</code> if the <code>element</code> is an instance
+     *         of <code>type</code>; <code>false</code> otherwise.
+     */
+    private static final bool isInstanceOf(Object element,
+            String type) {
+        // null isn't an instanceof of anything.
+        if (element is null) {
+            return false;
+        }
+        return isSubtype(element.classinfo, type);
+    }
+
+    /**
+     * TODO: this was copied from
+     * org.eclipse.core.internal.expressions.Expressions is there a better place
+     * to reference this?
+     *
+     * @param clazz
+     *            The class to match; may be <code>null</code>.
+     * @param type
+     *            The type against which we are testing;may be <code>null</code>.
+     * @return <code>true</code> if the <code>element</code> is an instance
+     *         of <code>type</code>; <code>false</code> otherwise.
+     */
+    private static final bool isSubtype(ClassInfo clazz, String type) {
+        if (clazz.name.equals(type)) {
+            return true;
+        }
+        ClassInfo superClass = clazz.base;
+        if (superClass !is null && isSubtype(superClass, type)) {
+            return true;
+        }
+        Interface[] interfaces = clazz.interfaces;
+        for (int i = 0; i < interfaces.length; i++) {
+            if (isSubtype(interfaces[i].classinfo, type)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * An {@link AbstractParameterValueConverter} for converting parameter
+     * values between objects and strings. This may be <code>null</code>.
+     */
+    private /+transient+/ AbstractParameterValueConverter parameterTypeConverter;
+
+    /**
+     * A string specifying the object type of this parameter type. This will be
+     * <code>null</code> when the parameter type is undefined but never null
+     * when it is defined.
+     */
+    private /+transient+/ String type = null;
+
+    /**
+     * Constructs a new instance based on the given identifier. When a parameter
+     * type is first constructed, it is undefined. Parameter types should only
+     * be constructed by the {@link CommandManager} to ensure that the
+     * identifier remains unique.
+     *
+     * @param id
+     *            The identifier for this type. This value must not be
+     *            <code>null</code>, and must be unique amongst all parameter
+     *            types.
+     */
+    this(String id) {
+        super(id);
+    }
+
+    /**
+     * Adds a listener to this parameter type that will be notified when its
+     * state changes.
+     *
+     * @param listener
+     *            The listener to be added; must not be <code>null</code>.
+     */
+    public final void addListener(IParameterTypeListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Compares this parameter type with another object by comparing each of the
+     * non-transient attributes.
+     *
+     * @param object
+     *            The object with which to compare; must be an instance of
+     *            {@link ParameterType}.
+     * @return A negative integer, zero or a positive integer, if the object is
+     *         greater than, equal to or less than this parameter type.
+     */
+    public final int compareTo(Object object) {
+        ParameterType castedObject = cast(ParameterType) object;
+        int compareTo = Util.compare(defined, castedObject.defined);
+        if (compareTo is 0) {
+            compareTo = Util.compare(id, castedObject.id);
+        }
+        return compareTo;
+    }
+
+    /**
+     * <p>
+     * Defines this parameter type, setting the defined property to
+     * <code>true</code>.
+     * </p>
+     * <p>
+     * Notification is sent to all listeners that something has changed.
+     * </p>
+     *
+     * @param type
+     *            a string identifying the Java object type for this parameter
+     *            type; <code>null</code> is interpreted as
+     *            <code>"java.lang.Object"</code>
+     * @param parameterTypeConverter
+     *            an {@link AbstractParameterValueConverter} to perform
+     *            string/object conversions for parameter values; may be
+     *            <code>null</code>
+     */
+    public final void define(String type,
+            AbstractParameterValueConverter parameterTypeConverter) {
+
+        bool definedChanged = !this.defined;
+        this.defined = true;
+
+        this.type = (type is null) ? Object.classinfo.name : type;
+        this.parameterTypeConverter = parameterTypeConverter;
+
+        fireParameterTypeChanged(new ParameterTypeEvent(this, definedChanged));
+    }
+
+    /**
+     * Notifies all listeners that this parameter type has changed. This sends
+     * the given event to all of the listeners, if any.
+     *
+     * @param event
+     *            The event to send to the listeners; must not be
+     *            <code>null</code>.
+     */
+    private final void fireParameterTypeChanged(ParameterTypeEvent event) {
+        if (event is null) {
+            throw new NullPointerException(
+                    "Cannot send a null event to listeners."); //$NON-NLS-1$
+        }
+
+        if (!isListenerAttached()) {
+            return;
+        }
+
+        Object[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            IParameterTypeListener listener = cast(IParameterTypeListener) listeners[i];
+            listener.parameterTypeChanged(event);
+        }
+    }
+
+    /**
+     * Returns the value converter associated with this parameter, if any.
+     *
+     * @return The parameter value converter, or <code>null</code> if there is
+     *         no value converter for this parameter.
+     * @throws NotDefinedException
+     *             if the parameter type is not currently defined
+     */
+    public final AbstractParameterValueConverter getValueConverter() {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot use getValueConverter() with an undefined ParameterType"); //$NON-NLS-1$
+        }
+
+        return parameterTypeConverter;
+    }
+
+    /**
+     * Returns whether the provided value is compatible with this parameter
+     * type. An object is compatible with a parameter type if the object is an
+     * instance of the class defined as the parameter's type class.
+     *
+     * @param value
+     *            an object to check for compatibility with this parameter type;
+     *            may be <code>null</code>.
+     * @return <code>true</code> if the value is compatible with this type,
+     *         <code>false</code> otherwise
+     * @throws NotDefinedException
+     *             if the parameter type is not currently defined
+     */
+    public bool isCompatible(Object value) {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot use isCompatible() with an undefined ParameterType"); //$NON-NLS-1$
+        }
+        return isInstanceOf(value, type);
+    }
+
+    /**
+     * Unregisters listener for changes to properties of this parameter type.
+     *
+     * @param listener
+     *            the instance to unregister. Must not be <code>null</code>.
+     *            If an attempt is made to unregister an instance which is not
+     *            already registered with this instance, no operation is
+     *            performed.
+     */
+    public final void removeListener(IParameterTypeListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * The string representation of this parameter type. For debugging purposes
+     * only. This string should not be shown to an end user.
+     *
+     * @return The string representation; never <code>null</code>.
+     */
+    public override final String toString() {
+        if (string is null) {
+            string = Format( "ParameterType({},{})", id, defined );
+        }
+        return string;
+    }
+
+    /**
+     * Makes this parameter type become undefined. Notification is sent to all
+     * listeners.
+     */
+    public override final void undefine() {
+        string = null;
+
+        final bool definedChanged = defined;
+        defined = false;
+
+        type = null;
+        parameterTypeConverter = null;
+
+        fireParameterTypeChanged(new ParameterTypeEvent(this, definedChanged));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/ParameterTypeEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.ParameterTypeEvent;
+
+import org.eclipse.core.commands.common.AbstractHandleObjectEvent;
+import org.eclipse.core.commands.ParameterType;
+
+import java.lang.all;
+
+/**
+ * An instance of this class describes changes to an instance of
+ * {@link ParameterType}.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @see IParameterTypeListener#parameterTypeChanged(ParameterTypeEvent)
+ * @since 3.2
+ */
+public final class ParameterTypeEvent : AbstractHandleObjectEvent {
+
+    /**
+     * The parameter type that has changed. This value is never
+     * <code>null</code>.
+     */
+    private final ParameterType parameterType;
+
+    /**
+     * Constructs a new instance.
+     *
+     * @param parameterType
+     *            The parameter type that changed; must not be <code>null</code>.
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     */
+    this(ParameterType parameterType,
+            bool definedChanged) {
+
+        super(definedChanged);
+
+        if (parameterType is null) {
+            throw new NullPointerException();
+        }
+
+        this.parameterType = parameterType;
+    }
+
+    /**
+     * Returns the instance of the parameter type that changed.
+     *
+     * @return the instance of the parameter type that changed. Guaranteed not
+     *         to be <code>null</code>.
+     */
+    public final ParameterType getParameterType() {
+        return parameterType;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/ParameterValueConversionException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.ParameterValueConversionException;
+
+import org.eclipse.core.commands.common.CommandException;
+
+import java.lang.all;
+
+/**
+ * Signals that a problem occurred while converting a command parameter value
+ * from string to object, or object to string.
+ *
+ * @see AbstractParameterValueConverter
+ * @since 3.2
+ */
+public class ParameterValueConversionException : CommandException {
+
+    /**
+     * Generated serial version UID for this class.
+     */
+    private static final long serialVersionUID = 4703077729505066104L;
+
+    /**
+     * Creates a new instance of this class with the specified detail message.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     */
+    public this(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new instance of this class with the specified detail message
+     * and cause.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     * @param cause
+     *            the cause; may be <code>null</code>.
+     */
+    public this(String message,
+            Exception cause) {
+        super(message, cause);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/ParameterValuesException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.ParameterValuesException;
+
+import org.eclipse.core.commands.common.CommandException;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * Signals that a problem has occurred while trying to create an instance of
+ * <code>IParameterValues</code>. In applications based on the registry
+ * provided by core, this usually indicates a problem creating an
+ * <code>IExecutableExtension</code>. For other applications, this exception
+ * could be used to signify any general problem during initialization.
+ * </p>
+ *
+ * @since 3.1
+ *
+ */
+public final class ParameterValuesException : CommandException {
+
+    /**
+     * Generated serial version UID for this class.
+     */
+    private static final long serialVersionUID = 3618976793520845623L;
+
+    /**
+     * Creates a new instance of this class with the specified detail message
+     * and cause.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     * @param cause
+     *            the cause; may be <code>null</code>.
+     */
+    public this(String message, Exception cause) {
+        super(message, cause);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/Parameterization.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.Parameterization;
+
+import org.eclipse.core.commands.IParameter;
+import org.eclipse.core.internal.commands.util.Util;
+
+import java.lang.all;
+import java.util.Map;
+import java.util.Iterator;
+
+/**
+ * <p>
+ * A parameter with a specific value. This is usually a part of a
+ * <code>ParameterizedCommand</code>, which is used to refer to a command
+ * with a collection of parameterizations.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class Parameterization {
+
+    /**
+     * The constant integer hash code value meaning the hash code has not yet
+     * been computed.
+     */
+    private static const int HASH_CODE_NOT_COMPUTED = -1;
+
+    /**
+     * A factor for computing the hash code for all parameterized commands.
+     */
+    private static const int HASH_FACTOR = 89;
+
+    /**
+     * The seed for the hash code for all parameterized commands.
+     */
+    private static const int HASH_INITIAL;
+
+    static this(){
+        HASH_INITIAL = java.lang.all.toHash( Parameterization.classinfo.name );
+    }
+    /**
+     * The hash code for this object. This value is computed lazily, and marked
+     * as invalid when one of the values on which it is based changes.
+     */
+    private /+transient+/ hash_t hashCode = HASH_CODE_NOT_COMPUTED;
+
+    /**
+     * The parameter that is being parameterized. This value is never
+     * <code>null</code>.
+     */
+    private const IParameter parameter;
+
+    /**
+     * The value that defines the parameterization. This value may be
+     * <code>null</code>.
+     */
+    private const String value;
+
+    /**
+     * Constructs a new instance of <code>Parameterization</code>.
+     *
+     * @param parameter
+     *            The parameter that is being parameterized; must not be
+     *            <code>null</code>.
+     * @param value
+     *            The value for the parameter; may be <code>null</code>.
+     */
+    public this(IParameter parameter, String value) {
+        if (parameter is null) {
+            throw new NullPointerException(
+                    "You cannot parameterize a null parameter"); //$NON-NLS-1$
+        }
+
+        this.parameter = parameter;
+        this.value = value;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public override final int opEquals(Object object) {
+        if (this is object) {
+            return true;
+        }
+
+        if (!(cast(Parameterization)object)) {
+            return false;
+        }
+
+        Parameterization parameterization = cast(Parameterization) object;
+        if (!(Util.equals(this.parameter.getId(), parameterization.parameter
+                .getId()))) {
+            return false;
+        }
+
+        return Util.equals(this.value, parameterization.value);
+    }
+
+    /**
+     * Returns the parameter that is being parameterized.
+     *
+     * @return The parameter; never <code>null</code>.
+     */
+    public final IParameter getParameter() {
+        return parameter;
+    }
+
+    /**
+     * Returns the value for the parameter in this parameterization.
+     *
+     * @return The value; may be <code>null</code>.
+     */
+    public final String getValue() {
+        return value;
+    }
+
+    /**
+     * Returns the human-readable name for the current value, if any. If the
+     * name cannot be found, then it simply returns the value. It also ensures
+     * that any <code>null</code> values are converted into an empty string.
+     *
+     * @return The human-readable name of the value; never <code>null</code>.
+     * @throws ParameterValuesException
+     *             If the parameter needed to be initialized, but couldn't be.
+     */
+    public final String getValueName() {
+        Map parameterValues = parameter.getValues().getParameterValues();
+        Iterator parameterValueItr = parameterValues.entrySet()
+                .iterator();
+        String returnValue = null;
+        while (parameterValueItr.hasNext()) {
+            Map.Entry entry = cast(Map.Entry) parameterValueItr.next();
+            String currentValue = stringcast( entry.getValue());
+            if (Util.equals(value, currentValue)) {
+                returnValue = stringcast( entry.getKey());
+                break;
+            }
+        }
+
+        if (returnValue is null) {
+            return Util.ZERO_LENGTH_STRING;
+        }
+
+        return returnValue;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    public override final hash_t toHash() {
+        if (hashCode is HASH_CODE_NOT_COMPUTED) {
+            hashCode = HASH_INITIAL * HASH_FACTOR + Util.toHash(cast(Object)parameter);
+            hashCode = hashCode * HASH_FACTOR + Util.toHash(value);
+            if (hashCode is HASH_CODE_NOT_COMPUTED) {
+                hashCode++;
+            }
+        }
+        return hashCode;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/ParameterizedCommand.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,725 @@
+/*******************************************************************************
+ * 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
+ *     Benjamin Muskalla - bug 222861 [Commands] ParameterizedCommand#equals broken
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.core.commands.ParameterizedCommand;
+
+import org.eclipse.core.commands.AbstractParameterValueConverter;
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.CommandManager;
+import org.eclipse.core.commands.IParameter;
+import org.eclipse.core.commands.IParameterValues;
+import org.eclipse.core.commands.ParameterType;
+import org.eclipse.core.commands.Parameterization;
+import org.eclipse.core.commands.ParameterValuesException;
+import org.eclipse.core.commands.ParameterValueConversionException;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.internal.commands.util.Util;
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * <p>
+ * A command that has had one or more of its parameters specified. This class
+ * serves as a utility class for developers that need to manipulate commands
+ * with parameters. It handles the behaviour of generating a parameter map and a
+ * human-readable name.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class ParameterizedCommand : Comparable {
+    /**
+     * The constant integer hash code value meaning the hash code has not yet
+     * been computed.
+     */
+    private static const int HASH_CODE_NOT_COMPUTED = -1;
+
+    /**
+     * A factor for computing the hash code for all parameterized commands.
+     */
+    private static const int HASH_FACTOR = 89;
+
+    /**
+     * The seed for the hash code for all parameterized commands.
+     */
+    private static const int HASH_INITIAL;
+
+    static this(){
+        HASH_INITIAL = java.lang.all.toHash(ParameterizedCommand.classinfo.name );
+    }
+    /**
+     * The index of the parameter id in the parameter values.
+     *
+     * @deprecated no longer used
+     */
+    public static const int INDEX_PARAMETER_ID = 0;
+
+    /**
+     * The index of the human-readable name of the parameter itself, in the
+     * parameter values.
+     *
+     * @deprecated no longer used
+     */
+    public static const int INDEX_PARAMETER_NAME = 1;
+
+    /**
+     * The index of the human-readable name of the value of the parameter for
+     * this command.
+     *
+     * @deprecated no longer used
+     */
+    public static const int INDEX_PARAMETER_VALUE_NAME = 2;
+
+    /**
+     * The index of the value of the parameter that the command can understand.
+     *
+     * @deprecated no longer used
+     */
+    public static const int INDEX_PARAMETER_VALUE_VALUE = 3;
+
+    /**
+     * Escapes special characters in the command id, parameter ids and parameter
+     * values for {@link #serialize()}. The special characters
+     * {@link CommandManager#PARAMETER_START_CHAR},
+     * {@link CommandManager#PARAMETER_END_CHAR},
+     * {@link CommandManager#ID_VALUE_CHAR},
+     * {@link CommandManager#PARAMETER_SEPARATOR_CHAR} and
+     * {@link CommandManager#ESCAPE_CHAR} are escaped by prepending a
+     * {@link CommandManager#ESCAPE_CHAR} character.
+     *
+     * @param rawText
+     *            a <code>String</code> to escape special characters in for
+     *            serialization.
+     * @return a <code>String</code> representing <code>rawText</code> with
+     *         special serialization characters escaped
+     * @since 3.2
+     */
+    private static final String escape(String rawText) {
+
+        // defer initialization of a StringBuffer until we know we need one
+        StringBuffer buffer;
+
+        for (int i = 0; i < rawText.length; i++) {
+
+            char c = rawText.charAt(i);
+            switch (c) {
+            case CommandManager.PARAMETER_START_CHAR:
+            case CommandManager.PARAMETER_END_CHAR:
+            case CommandManager.ID_VALUE_CHAR:
+            case CommandManager.PARAMETER_SEPARATOR_CHAR:
+            case CommandManager.ESCAPE_CHAR:
+                if (buffer is null) {
+                    buffer = new StringBuffer(rawText.substring(0, i));
+                }
+                buffer.append(CommandManager.ESCAPE_CHAR);
+                buffer.append(c);
+                break;
+            default:
+                if (buffer !is null) {
+                    buffer.append(c);
+                }
+                break;
+            }
+
+        }
+
+        if (buffer is null) {
+            return rawText;
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Generates every possible combination of parameter values for the given
+     * parameters. Parameters values that cannot be initialized are just
+     * ignored. Optional parameters are considered.
+     *
+     * @param startIndex
+     *            The index in the <code>parameters</code> that we should
+     *            process. This must be a valid index.
+     * @param parameters
+     *            The parameters in to process; must not be <code>null</code>.
+     * @return A collection (<code>Collection</code>) of combinations (<code>List</code>
+     *         of <code>Parameterization</code>).
+     */
+    private static final Collection expandParameters(int startIndex,
+            IParameter[] parameters) {
+        int nextIndex = startIndex + 1;
+        bool noMoreParameters = (nextIndex >= parameters.length);
+
+        IParameter parameter = parameters[startIndex];
+        List parameterizations = new ArrayList();
+        if (parameter.isOptional()) {
+            parameterizations.add( cast(Object) null);
+        }
+
+        IParameterValues values = null;
+        try {
+            values = parameter.getValues();
+        } catch (ParameterValuesException e) {
+            if (noMoreParameters) {
+                return parameterizations;
+            }
+
+            // Make recursive call
+            return expandParameters(nextIndex, parameters);
+        }
+
+        Map parameterValues = values.getParameterValues();
+        Iterator parameterValueItr = parameterValues.entrySet()
+                .iterator();
+        while (parameterValueItr.hasNext()) {
+            Map.Entry entry = cast(Map.Entry) parameterValueItr.next();
+            Parameterization parameterization = new Parameterization(
+                    parameter, stringcast( entry.getValue()));
+            parameterizations.add(parameterization);
+        }
+
+        // Check if another iteration will produce any more names.
+        int parameterizationCount = parameterizations.size();
+        if (noMoreParameters) {
+            // This is it, so just return the current parameterizations.
+            for (int i = 0; i < parameterizationCount; i++) {
+                Parameterization parameterization = cast(Parameterization) parameterizations
+                        .get(i);
+                List combination = new ArrayList();
+                combination.add(parameterization);
+                parameterizations.set(i, cast(Object)combination);
+            }
+            return parameterizations;
+        }
+
+        // Make recursive call
+        Collection suffixes = expandParameters(nextIndex, parameters);
+        while (suffixes.remove(cast(Object)null)) {
+            // just keep deleting the darn things.
+        }
+        if (suffixes.isEmpty()) {
+            // This is it, so just return the current parameterizations.
+            for (int i = 0; i < parameterizationCount; i++) {
+                Parameterization parameterization = cast(Parameterization) parameterizations
+                        .get(i);
+                List combination = new ArrayList();
+                combination.add(parameterization);
+                parameterizations.set(i,cast(Object) combination);
+            }
+            return parameterizations;
+        }
+        Collection returnValue = new ArrayList();
+        Iterator suffixItr = suffixes.iterator();
+        while (suffixItr.hasNext()) {
+            List combination = cast(List) suffixItr.next();
+            int combinationSize = combination.size();
+            for (int i = 0; i < parameterizationCount; i++) {
+                Parameterization parameterization = cast(Parameterization) parameterizations
+                        .get(i);
+                List newCombination = new ArrayList(combinationSize + 1);
+                newCombination.add(parameterization);
+                newCombination.addAll(combination);
+                returnValue.add(cast(Object)newCombination);
+            }
+        }
+
+        return returnValue;
+    }
+
+    /**
+     * <p>
+     * Generates all the possible combinations of command parameterizations for
+     * the given command. If the command has no parameters, then this is simply
+     * a parameterized version of that command. If a parameter is optional, both
+     * the included and not included cases are considered.
+     * </p>
+     * <p>
+     * If one of the parameters cannot be loaded due to a
+     * <code>ParameterValuesException</code>, then it is simply ignored.
+     * </p>
+     *
+     * @param command
+     *            The command for which the parameter combinations should be
+     *            generated; must not be <code>null</code>.
+     * @return A collection of <code>ParameterizedCommand</code> instances
+     *         representing all of the possible combinations. This value is
+     *         never empty and it is never <code>null</code>.
+     * @throws NotDefinedException
+     *             If the command is not defined.
+     */
+    public static final Collection generateCombinations(Command command) {
+        IParameter[] parameters = command.getParameters();
+        if (parameters is null) {
+            return Collections
+                    .singleton(new ParameterizedCommand(command, null));
+        }
+
+        Collection expansion = expandParameters(0, parameters);
+        Collection combinations = new ArrayList(expansion.size());
+        Iterator expansionItr = expansion.iterator();
+        while (expansionItr.hasNext()) {
+            List combination = cast(List) expansionItr.next();
+            if (combination is null) {
+                combinations.add(new ParameterizedCommand(command, null));
+            } else {
+                while (combination.remove(cast(Object)null)) {
+                    // Just keep removing while there are null entries left.
+                }
+                if (combination.isEmpty()) {
+                    combinations.add(new ParameterizedCommand(command, null));
+                } else {
+                    Parameterization[] parameterizations = arraycast!(Parameterization)( combination
+                            .toArray());
+                    combinations.add(new ParameterizedCommand(command,
+                            parameterizations));
+                }
+            }
+        }
+
+        return combinations;
+    }
+
+    /**
+     * Take a command and a map of parameter IDs to values, and generate the
+     * appropriate parameterized command.
+     *
+     * @param command
+     *            The command object. Must not be <code>null</code>.
+     * @param parameters
+     *            A map of String parameter ids to objects. May be
+     *            <code>null</code>.
+     * @return the parameterized command, or <code>null</code> if it could not
+     *         be generated
+     * @since 3.4
+     */
+    public static final ParameterizedCommand generateCommand(Command command,
+            Map parameters) {
+        // no parameters
+        if (parameters is null || parameters.isEmpty()) {
+            return new ParameterizedCommand(command, null);
+        }
+
+        try {
+            ArrayList parms = new ArrayList();
+            Iterator i = parameters.keySet().iterator();
+
+            // iterate over given parameters
+            while (i.hasNext()) {
+                String key = stringcast( i.next() );
+                IParameter parameter = null;
+                // get the parameter from the command
+                parameter = command.getParameter(key);
+
+                // if the parameter is defined add it to the parameter list
+                if (parameter is null) {
+                    return null;
+                }
+                ParameterType parameterType = command.getParameterType(key);
+                if (parameterType is null) {
+                    parms.add(new Parameterization(parameter,
+                            stringcast( parameters.get(key))));
+                } else {
+                    AbstractParameterValueConverter valueConverter = parameterType
+                            .getValueConverter();
+                    if (valueConverter !is null) {
+                        String val = valueConverter.convertToString( parameters
+                                .get(key));
+                        parms.add(new Parameterization(parameter, val));
+                    } else {
+                        parms.add(new Parameterization(parameter,
+                                stringcast(parameters.get(key))));
+                    }
+                }
+            }
+
+            // convert the parameters to an Parameterization array and create
+            // the command
+            return new ParameterizedCommand(command, arraycast!(Parameterization)( parms
+                    .toArray()));
+        } catch (NotDefinedException e) {
+        } catch (ParameterValueConversionException e) {
+        }
+        return null;
+    }
+
+    /**
+     * The base command which is being parameterized. This value is never
+     * <code>null</code>.
+     */
+    private const Command command;
+
+    /**
+     * The hash code for this object. This value is computed lazily, and marked
+     * as invalid when one of the values on which it is based changes.
+     */
+    private /+transient+/ int hashCode = HASH_CODE_NOT_COMPUTED;
+
+    /**
+     * This is an array of parameterization defined for this command. This value
+     * may be <code>null</code> if the command has no parameters.
+     */
+    private const Parameterization[] parameterizations;
+
+    private String name;
+
+    /**
+     * Constructs a new instance of <code>ParameterizedCommand</code> with
+     * specific values for zero or more of its parameters.
+     *
+     * @param command
+     *            The command that is parameterized; must not be
+     *            <code>null</code>.
+     * @param parameterizations
+     *            An array of parameterizations binding parameters to values for
+     *            the command. This value may be <code>null</code>.
+     */
+    public this(Command command,
+            Parameterization[] parameterizations) {
+        if (command is null) {
+            throw new NullPointerException(
+                    "A parameterized command cannot have a null command"); //$NON-NLS-1$
+        }
+
+        this.command = command;
+        IParameter[] parms = null;
+        try {
+            parms = command.getParameters();
+        } catch (NotDefinedException e) {
+            // This should not happen.
+        }
+        if (parameterizations !is null && parameterizations.length>0 && parms !is null) {
+            int parmIndex = 0;
+            Parameterization[] params = new Parameterization[parameterizations.length];
+            for (int j = 0; j < parms.length; j++) {
+                for (int i = 0; i < parameterizations.length; i++) {
+                    Parameterization pm = parameterizations[i];
+                    if ((cast(Object)parms[j]).opEquals(cast(Object)pm.getParameter())) {
+                        params[parmIndex++] = pm;
+                    }
+                }
+            }
+            this.parameterizations = params;
+        } else {
+            this.parameterizations = null;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Comparable#compareTo(java.lang.Object)
+     */
+    public final int compareTo(Object object) {
+        ParameterizedCommand command = cast(ParameterizedCommand) object;
+        bool thisDefined = this.command.isDefined();
+        bool otherDefined = command.command.isDefined();
+        if (!thisDefined || !otherDefined) {
+            return Util.compare(thisDefined, otherDefined);
+        }
+
+        try {
+            int compareTo = getName() < command.getName();
+            if (compareTo is 0) {
+                return getId() < command.getId();
+            }
+            return compareTo;
+        } catch (NotDefinedException e) {
+            throw new Exception (
+                    "Concurrent modification of a command's defined state"); //$NON-NLS-1$
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public override final int opEquals(Object object) {
+        if (this is object) {
+            return true;
+        }
+
+        if (!(cast(ParameterizedCommand)object)) {
+            return false;
+        }
+
+        ParameterizedCommand command = cast(ParameterizedCommand) object;
+        if (!Util.equals(this.command, command.command)) {
+            return false;
+        }
+
+        return Util.equals(this.parameterizations, command.parameterizations);
+    }
+
+    /**
+     * Executes this command with its parameters. This method will succeed
+     * regardless of whether the command is enabled or defined. It is
+     * preferrable to use {@link #executeWithChecks(Object, Object)}.
+     *
+     * @param trigger
+     *            The object that triggered the execution; may be
+     *            <code>null</code>.
+     * @param applicationContext
+     *            The state of the application at the time the execution was
+     *            triggered; may be <code>null</code>.
+     * @return The result of the execution; may be <code>null</code>.
+     * @throws ExecutionException
+     *             If the handler has problems executing this command.
+     * @throws NotHandledException
+     *             If there is no handler.
+     * @deprecated Please use {@link #executeWithChecks(Object, Object)}
+     *             instead.
+     */
+    public final Object execute(Object trigger,
+            Object applicationContext) {
+        return command.execute(new ExecutionEvent(command, getParameterMap(),
+                trigger, applicationContext));
+    }
+
+    /**
+     * Executes this command with its parameters. This does extra checking to
+     * see if the command is enabled and defined. If it is not both enabled and
+     * defined, then the execution listeners will be notified and an exception
+     * thrown.
+     *
+     * @param trigger
+     *            The object that triggered the execution; may be
+     *            <code>null</code>.
+     * @param applicationContext
+     *            The state of the application at the time the execution was
+     *            triggered; may be <code>null</code>.
+     * @return The result of the execution; may be <code>null</code>.
+     * @throws ExecutionException
+     *             If the handler has problems executing this command.
+     * @throws NotDefinedException
+     *             If the command you are trying to execute is not defined.
+     * @throws NotEnabledException
+     *             If the command you are trying to execute is not enabled.
+     * @throws NotHandledException
+     *             If there is no handler.
+     * @since 3.2
+     */
+    public final Object executeWithChecks(Object trigger,
+            Object applicationContext) {
+        return command.executeWithChecks(new ExecutionEvent(command,
+                getParameterMap(), trigger, applicationContext));
+    }
+
+    /**
+     * Returns the base command. It is possible for more than one parameterized
+     * command to have the same identifier.
+     *
+     * @return The command; never <code>null</code>, but may be undefined.
+     */
+    public final Command getCommand() {
+        return command;
+    }
+
+    /**
+     * Returns the command's base identifier. It is possible for more than one
+     * parameterized command to have the same identifier.
+     *
+     * @return The command id; never <code>null</code>.
+     */
+    public final String getId() {
+        return command.getId();
+    }
+
+    /**
+     * Returns a human-readable representation of this command with all of its
+     * parameterizations.
+     *
+     * @return The human-readable representation of this parameterized command;
+     *         never <code>null</code>.
+     * @throws NotDefinedException
+     *             If the underlying command is not defined.
+     */
+    public final String getName() {
+        if (name is null) {
+            StringBuffer nameBuffer = new StringBuffer();
+            nameBuffer.append(command.getName());
+            if (parameterizations !is null) {
+                nameBuffer.append(" ("); //$NON-NLS-1$
+                int parameterizationCount = parameterizations.length;
+                for (int i = 0; i < parameterizationCount; i++) {
+                    Parameterization parameterization = parameterizations[i];
+                    nameBuffer
+                            .append(parameterization.getParameter().getName());
+                    nameBuffer.append(": "); //$NON-NLS-1$
+                    try {
+                        nameBuffer.append(parameterization.getValueName());
+                    } catch (ParameterValuesException e) {
+                        /*
+                         * Just let it go for now. If someone complains we can
+                         * add more info later.
+                         */
+                    }
+
+                    // If there is another item, append a separator.
+                    if (i + 1 < parameterizationCount) {
+                        nameBuffer.append(", "); //$NON-NLS-1$
+                    }
+                }
+                nameBuffer.append(')');
+            }
+            name = nameBuffer.toString();
+        }
+        return name;
+    }
+
+    /**
+     * Returns the parameter map, as can be used to construct an
+     * <code>ExecutionEvent</code>.
+     *
+     * @return The map of parameter ids (<code>String</code>) to parameter
+     *         values (<code>String</code>). This map is never
+     *         <code>null</code>, but may be empty.
+     */
+    public final Map getParameterMap() {
+        if ((parameterizations is null) || (parameterizations.length is 0)) {
+            return Collections.EMPTY_MAP;
+        }
+
+        Map parameterMap = new HashMap();
+        for (int i = 0; i < parameterizations.length; i++) {
+            Parameterization parameterization = parameterizations[i];
+            parameterMap.put(parameterization.getParameter().getId(),
+                    parameterization.getValue());
+        }
+        return parameterMap;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#hashCode()
+     */
+    public override final hash_t toHash() {
+        if (hashCode is HASH_CODE_NOT_COMPUTED) {
+            hashCode = HASH_INITIAL * HASH_FACTOR + Util.toHash(command);
+            hashCode = hashCode * HASH_FACTOR;
+            if (parameterizations !is null) {
+                for (int i = 0; i < parameterizations.length; i++) {
+                    hashCode += Util.toHash(parameterizations[i]);
+                }
+            }
+            if (hashCode is HASH_CODE_NOT_COMPUTED) {
+                hashCode++;
+            }
+        }
+        return hashCode;
+    }
+
+    /**
+     * Returns a {@link String} containing the command id, parameter ids and
+     * parameter values for this {@link ParameterizedCommand}. The returned
+     * {@link String} can be stored by a client and later used to reconstruct an
+     * equivalent {@link ParameterizedCommand} using the
+     * {@link CommandManager#deserialize(String)} method.
+     * <p>
+     * The syntax of the returned {@link String} is as follows:
+     * </p>
+     *
+     * <blockquote>
+     * <code>serialization = <u>commandId</u> [ '(' parameters ')' ]</code><br>
+     * <code>parameters = parameter [ ',' parameters ]</code><br>
+     * <code>parameter = <u>parameterId</u> [ '=' <u>parameterValue</u> ]</code>
+     * </blockquote>
+     *
+     * <p>
+     * In the syntax above, sections inside square-brackets are optional. The
+     * characters in single quotes (<code>(</code>, <code>)</code>,
+     * <code>,</code> and <code>=</code>) indicate literal characters.
+     * </p>
+     * <p>
+     * <code><u>commandId</u></code> represents the command id encoded with
+     * separator characters escaped. <code><u>parameterId</u></code> and
+     * <code><u>parameterValue</u></code> represent the parameter ids and
+     * values encoded with separator characters escaped. The separator
+     * characters <code>(</code>, <code>)</code>, <code>,</code> and
+     * <code>=</code> are escaped by prepending a <code>%</code>. This
+     * requires <code>%</code> to be escaped, which is also done by prepending
+     * a <code>%</code>.
+     * </p>
+     * <p>
+     * The order of the parameters is not defined (and not important). A missing
+     * <code><u>parameterValue</u></code> indicates that the value of the
+     * parameter is <code>null</code>.
+     * </p>
+     * <p>
+     * For example, the string shown below represents a serialized parameterized
+     * command that can be used to show the Resource perspective:
+     * </p>
+     * <p>
+     * <code>org.eclipse.ui.perspectives.showPerspective(org.eclipse.ui.perspectives.showPerspective.perspectiveId=org.eclipse.ui.resourcePerspective)</code>
+     * </p>
+     * <p>
+     * This example shows the more general form with multiple parameters,
+     * <code>null</code> value parameters, and escaped <code>=</code> in the
+     * third parameter value.
+     * </p>
+     * <p>
+     * <code>command.id(param1.id=value1,param2.id,param3.id=esc%=val3)</code>
+     * </p>
+     *
+     * @return A string containing the escaped command id, parameter ids and
+     *         parameter values; never <code>null</code>.
+     * @see CommandManager#deserialize(String)
+     * @since 3.2
+     */
+    public final String serialize() {
+        String escapedId = escape(getId());
+
+        if ((parameterizations is null) || (parameterizations.length is 0)) {
+            return escapedId;
+        }
+
+        StringBuffer buffer = new StringBuffer(escapedId);
+        buffer.append(CommandManager.PARAMETER_START_CHAR);
+
+        for (int i = 0; i < parameterizations.length; i++) {
+
+            if (i > 0) {
+                // insert separator between parameters
+                buffer.append(CommandManager.PARAMETER_SEPARATOR_CHAR);
+            }
+
+            Parameterization parameterization = parameterizations[i];
+            String parameterId = parameterization.getParameter().getId();
+            String escapedParameterId = escape(parameterId);
+
+            buffer.append(escapedParameterId);
+
+            String parameterValue = parameterization.getValue();
+            if (parameterValue !is null) {
+                String escapedParameterValue = escape(parameterValue);
+                buffer.append(CommandManager.ID_VALUE_CHAR);
+                buffer.append(escapedParameterValue);
+            }
+        }
+
+        buffer.append(CommandManager.PARAMETER_END_CHAR);
+
+        return buffer.toString();
+    }
+
+    public override final String toString() {
+        return Format( "ParameterizedCommand({},{})", command, parameterizations);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/SerializationException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.commands.SerializationException;
+
+import org.eclipse.core.commands.common.CommandException;
+
+import java.lang.all;
+
+/**
+ * Signals that an exception occured while serializing a
+ * {@link ParameterizedCommand} to a string or deserializing a String to a
+ * {@link ParameterizedCommand}.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.2
+ */
+public final class SerializationException : CommandException {
+
+    /**
+     * Generated serial version UID for this class.
+     */
+    private static final long serialVersionUID = 2691599674561684949L;
+
+    /**
+     * Creates a new instance of this class with the specified detail message.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     */
+    public this(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new instance of this class with the specified detail message
+     * and cause.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     * @param cause
+     *            the cause; may be <code>null</code>.
+     */
+    public this(String message, Exception cause) {
+        super(message, cause);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/State.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.State;
+
+import org.eclipse.core.commands.common.EventManager;
+import org.eclipse.core.internal.commands.util.Util;
+import org.eclipse.core.commands.IStateListener;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * A piece of state information that can be shared between objects, and might be
+ * persisted between sessions. This can be used for commands that toggle between
+ * two states and wish to pass this state information between different
+ * handlers.
+ * </p>
+ * <p>
+ * This state object can either be used as a single state object shared between
+ * several commands, or one state object per command -- depending on the needs
+ * of the application.
+ * </p>
+ * <p>
+ * Clients may instantiate or extend this class.
+ * </p>
+ *
+ * @since 3.2
+ */
+public class State : EventManager {
+
+    /**
+     * The identifier of the state; may be <code>null</code> if it has not
+     * been initialized.
+     */
+    private String id;
+
+    /**
+     * The value held by this state; may be anything at all.
+     */
+    private Object value;
+
+    /**
+     * Adds a listener to changes for this state.
+     *
+     * @param listener
+     *            The listener to add; must not be <code>null</code>.
+     */
+    public void addListener(IStateListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Disposes of this state. This allows the state to unregister itself with
+     * any managers or as a listener.
+     */
+    public void dispose() {
+        // The default implementation does nothing.
+    }
+
+    /**
+     * Notifies listeners to this state that it has changed in some way.
+     *
+     * @param oldValue
+     *            The old value; may be anything.
+     */
+    protected final void fireStateChanged(Object oldValue) {
+        final Object[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            IStateListener listener = cast(IStateListener) listeners[i];
+            listener.handleStateChange(this, oldValue);
+        }
+    }
+
+    /**
+     * Returns the identifier for this state.
+     *
+     * @return The id; may be <code>null</code>.
+     */
+    public final String getId() {
+        return id;
+    }
+
+    /**
+     * The current value associated with this state. This can be any type of
+     * object, but implementations will usually restrict this value to a
+     * particular type.
+     *
+     * @return The current value; may be anything.
+     */
+
+    public Object getValue() {
+        return value;
+    }
+
+    /**
+     * Removes a listener to changes from this state.
+     *
+     * @param listener
+     *            The listener to remove; must not be <code>null</code>.
+     */
+
+    public void removeListener(IStateListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Sets the identifier for this object.  This method should only be called
+     * by the command framework.  Clients should not call this method.
+     *
+     * @param id
+     *            The id; must not be <code>null</code>.
+     */
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /**
+     * Sets the value for this state object.
+     *
+     * @param value
+     *            The value to set; may be anything.
+     */
+    public void setValue(Object value) {
+        if (!Util.equals(this.value, value)) {
+            final Object oldValue = this.value;
+            this.value = value;
+            fireStateChanged(oldValue);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/common/AbstractBitSetEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.common.AbstractBitSetEvent;
+
+/**
+ * <p>
+ * An event that carries with it two or more boolean values.  This provides a
+ * single integer value which can then be used as a bit set.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class AbstractBitSetEvent {
+
+    /**
+     * A collection of bits representing whether certain values have changed. A
+     * bit is set (i.e., <code>1</code>) if the corresponding property has
+     * changed. It can be assumed that this value will be correctly initialized
+     * by the superconstructor.
+     */
+    protected int changedValues = 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/common/AbstractHandleObjectEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.common.AbstractHandleObjectEvent;
+
+import org.eclipse.core.commands.common.AbstractBitSetEvent;
+
+/**
+ * <p>
+ * An event fired from a <code>NamedHandleObject</code>. This provides
+ * notification of changes to the defined state, the name and the description.
+ * </p>
+ *
+ * @since 3.2
+ */
+public abstract class AbstractHandleObjectEvent : AbstractBitSetEvent {
+
+    /**
+     * The bit used to represent whether the category has changed its defined
+     * state.
+     */
+    protected static const int CHANGED_DEFINED = 1;
+
+    /**
+     * The last used bit so that subclasses can add more properties.
+     */
+    protected static const int LAST_BIT_USED_ABSTRACT_HANDLE = CHANGED_DEFINED;
+
+    /**
+     * Constructs a new instance of <code>AbstractHandleObjectEvent</code>.
+     *
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     */
+    protected this( bool definedChanged) {
+        if (definedChanged) {
+            changedValues |= CHANGED_DEFINED;
+        }
+    }
+
+    /**
+     * Returns whether or not the defined property changed.
+     *
+     * @return <code>true</code>, iff the defined property changed.
+     */
+    public final bool isDefinedChanged() {
+        return ((changedValues & CHANGED_DEFINED) !is 0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/common/AbstractNamedHandleEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+module org.eclipse.core.commands.common.AbstractNamedHandleEvent;
+
+import org.eclipse.core.commands.common.AbstractHandleObjectEvent;
+
+/**
+ * <p>
+ * An event fired from a <code>NamedHandleObject</code>. This provides
+ * notification of changes to the defined state, the name and the description.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class AbstractNamedHandleEvent :
+        AbstractHandleObjectEvent {
+
+    /**
+     * The bit used to represent whether the category has changed its
+     * description.
+     */
+    protected static const int CHANGED_DESCRIPTION = 1 << LAST_BIT_USED_ABSTRACT_HANDLE;
+
+    /**
+     * The bit used to represent whether the category has changed its name.
+     */
+    protected static const int CHANGED_NAME = 1 << LAST_BIT_USED_ABSTRACT_HANDLE;
+
+    /**
+     * The last used bit so that subclasses can add more properties.
+     */
+    protected static const int LAST_USED_BIT = CHANGED_NAME;
+
+    /**
+     * Constructs a new instance of <code>AbstractHandleObjectEvent</code>.
+     *
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     * @param descriptionChanged
+     *            <code>true</code>, iff the description property changed.
+     * @param nameChanged
+     *            <code>true</code>, iff the name property changed.
+     */
+    protected this(bool definedChanged,
+            bool descriptionChanged, bool nameChanged) {
+        super(definedChanged);
+
+        if (descriptionChanged) {
+            changedValues |= CHANGED_DESCRIPTION;
+        }
+        if (nameChanged) {
+            changedValues |= CHANGED_NAME;
+        }
+    }
+
+    /**
+     * Returns whether or not the description property changed.
+     *
+     * @return <code>true</code>, iff the description property changed.
+     */
+    public final bool isDescriptionChanged() {
+        return ((changedValues & CHANGED_DESCRIPTION) !is 0);
+    }
+
+    /**
+     * Returns whether or not the name property changed.
+     *
+     * @return <code>true</code>, iff the name property changed.
+     */
+    public final bool isNameChanged() {
+        return ((changedValues & CHANGED_NAME) !is 0);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/common/CommandException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.common.CommandException;
+
+import java.lang.all;
+
+/**
+ * Signals that an exception occured within the command architecture.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public abstract class CommandException : Exception {
+
+    /**
+     * Generated serial version UID for this class.
+     * 
+     * @since 3.4
+     */
+    private static final long serialVersionUID = 5389763628699257234L;
+    
+    /**
+     * This member variable is required here to allow us to compile against JCL
+     * foundation libraries.  The value may be <code>null</code>.
+     */
+//     private Exception cause;
+
+    /**
+     * Creates a new instance of this class with the specified detail message.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     */
+    public this(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new instance of this class with the specified detail message
+     * and cause.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     * @param cause
+     *            the cause; may be <code>null</code>.
+     */
+    public this(String message, Exception cause) {
+        super(message);
+        // don't pass the cause to super, to allow compilation against JCL Foundation
+        this.next = cause;
+    }
+
+    /**
+     * Returns the cause of this throwable or <code>null</code> if the
+     * cause is nonexistent or unknown.
+     *
+     * @return the cause or <code>null</code>
+     */
+    public /+override+/ Exception getCause() {
+        return next;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/common/EventManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.common.EventManager;
+
+import org.eclipse.core.runtime.ListenerList;
+
+/**
+ * <p>
+ * A manager to which listeners can be attached. This handles the management of
+ * a list of listeners -- optimizing memory and performance. All the methods on
+ * this class are guaranteed to be thread-safe.
+ * </p>
+ * <p>
+ * Clients may extend.
+ * </p>
+ *
+ * @since 3.2
+ */
+public abstract class EventManager {
+
+    /**
+     * An empty array that can be returned from a call to
+     * {@link #getListeners()} when {@link #listenerList} is <code>null</code>.
+     */
+    private static const Object[] EMPTY_ARRAY = null;
+
+    /**
+     * A collection of objects listening to changes to this manager. This
+     * collection is <code>null</code> if there are no listeners.
+     */
+    private /+transient+/ ListenerList listenerList = null;
+
+    /**
+     * Adds a listener to this manager that will be notified when this manager's
+     * state changes.
+     *
+     * @param listener
+     *            The listener to be added; must not be <code>null</code>.
+     */
+    protected synchronized final void addListenerObject(Object listener) {
+        if (listenerList is null) {
+            listenerList = new ListenerList(ListenerList.IDENTITY);
+        }
+
+        listenerList.add(listener);
+    }
+
+    /**
+     * Clears all of the listeners from the listener list.
+     */
+    protected synchronized final void clearListeners() {
+        if (listenerList !is null) {
+            listenerList.clear();
+        }
+    }
+
+    /**
+     * Returns the listeners attached to this event manager.
+     *
+     * @return The listeners currently attached; may be empty, but never
+     *         <code>null</code>
+     */
+    protected final Object[] getListeners() {
+        ListenerList list = listenerList;
+        if (list is null) {
+            return EMPTY_ARRAY;
+        }
+
+        return list.getListeners();
+    }
+
+    /**
+     * Whether one or more listeners are attached to the manager.
+     *
+     * @return <code>true</code> if listeners are attached to the manager;
+     *         <code>false</code> otherwise.
+     */
+    protected final bool isListenerAttached() {
+        return listenerList !is null;
+    }
+
+    /**
+     * Removes a listener from this manager.
+     *
+     * @param listener
+     *            The listener to be removed; must not be <code>null</code>.
+     */
+    protected synchronized final void removeListenerObject(Object listener) {
+        if (listenerList !is null) {
+            listenerList.remove(listener);
+
+            if (listenerList.isEmpty()) {
+                listenerList = null;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/common/HandleObject.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.commands.common.HandleObject;
+
+import org.eclipse.core.internal.commands.util.Util;
+import org.eclipse.core.commands.common.IIdentifiable;
+import org.eclipse.core.commands.common.EventManager;
+import java.lang.all;
+
+/**
+ * <p>
+ * An object that can exist in one of two states: defined and undefined. This is
+ * used by APIs that want to give a handle to an object, even though the object
+ * does not fully exist yet. This way, users can attach listeners to objects
+ * before they come into existence. It also protects the API from users that do
+ * not release references when they should.
+ * </p>
+ * <p>
+ * To enforce good coding practice, all handle objects must implement
+ * <code>equals</code> and <code>toString</code>. Please use
+ * <code>string</code> to cache the result for <code>toString</code> once
+ * calculated.
+ * </p>
+ * <p>
+ * All handle objects are referred to using a single identifier. This identifier
+ * is a instance of <code>String</code>. It is important that this identifier
+ * remain unique within whatever context that handle object is being used. For
+ * example, there should only ever be one instance of <code>Command</code>
+ * with a given identifier.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class HandleObject : EventManager,
+        IIdentifiable {
+
+    /**
+     * The constant integer hash code value meaning the hash code has not yet
+     * been computed.
+     */
+    private static const int HASH_CODE_NOT_COMPUTED = -1;
+
+    /**
+     * A factor for computing the hash code for all schemes.
+     */
+    private static const int HASH_FACTOR = 89;
+
+    /**
+     * The seed for the hash code for all schemes.
+     */
+    private static const int HASH_INITIAL;
+
+    static this(){
+        HASH_INITIAL =
+        //typeid(char[]).getHash(& HandleObject.classinfo.name );
+        java.lang.all.toHash(HandleObject.classinfo.name);
+    }
+    /**
+     * Whether this object is defined. A defined object is one that has been
+     * fully initialized. By default, all objects start as undefined.
+     */
+    protected /+transient+/ bool defined = false;
+
+    /**
+     * The hash code for this object. This value is computed lazily, and marked
+     * as invalid when one of the values on which it is based changes.
+     */
+    private /+transient+/ int hashCode = HASH_CODE_NOT_COMPUTED;
+
+    /**
+     * The identifier for this object. This identifier should be unique across
+     * all objects of the same type and should never change. This value will
+     * never be <code>null</code>.
+     */
+    protected /+final+/ String id;
+
+    /**
+     * The string representation of this object. This string is for debugging
+     * purposes only, and is not meant to be displayed to the user. This value
+     * is computed lazily, and is cleared if one of its dependent values
+     * changes.
+     */
+    protected /+transient+/ String string = null;
+
+    /**
+     * Constructs a new instance of <code>HandleObject</code>.
+     *
+     * @param id
+     *            The id of this handle; must not be <code>null</code>.
+     */
+    protected this(String id) {
+        if (id is null) {
+            throw new NullPointerException(
+                    "Cannot create a handle with a null id"); //$NON-NLS-1$
+        }
+
+        this.id = id;
+    }
+
+    /**
+     * Tests whether this object is equal to another object. A handle object is
+     * only equal to another handle object with the same id and the same class.
+     *
+     * @param object
+     *            The object with which to compare; may be <code>null</code>.
+     * @return <code>true</code> if the objects are equal; <code>false</code>
+     *         otherwise.
+     */
+    public override int opEquals(Object object) {
+        // Check if they're the same.
+        if (object is this) {
+            return true;
+        }
+
+        // Check if they're the same type.
+        if (!(cast(HandleObject)object)) {
+            return false;
+        }
+
+        // Check each property in turn.
+        final HandleObject handle= cast(HandleObject) object;
+        return Util.equals(id, handle.id)
+                && (this.classinfo is handle.classinfo);
+    }
+
+    public /+override+/ final String getId() {
+        return id;
+    }
+
+    /**
+     * Computes the hash code for this object based on the id.
+     *
+     * @return The hash code for this object.
+     */
+    public final override hash_t toHash() {
+        if (hashCode is HASH_CODE_NOT_COMPUTED) {
+            hashCode = HASH_INITIAL * HASH_FACTOR + Util.toHash(id);
+            if (hashCode is HASH_CODE_NOT_COMPUTED) {
+                hashCode++;
+            }
+        }
+        return hashCode;
+    }
+
+    /**
+     * Whether this instance is defined. A defined instance is one that has been
+     * fully initialized. This allows objects to effectively disappear even
+     * though other objects may still have references to them.
+     *
+     * @return <code>true</code> if this object is defined; <code>false</code>
+     *         otherwise.
+     */
+    public final bool isDefined() {
+        return defined;
+    }
+
+    /**
+     * The string representation of this object -- for debugging purposes only.
+     * This string should not be shown to an end user.
+     *
+     * @return The string representation; never <code>null</code>.
+     */
+    public override abstract String toString();
+
+    /**
+     * Makes this object becomes undefined. This method should make any defined
+     * properties <code>null</code>. It should also send notification to any
+     * listeners that these properties have changed.
+     */
+    public abstract void undefine();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/common/HandleObjectManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.common.HandleObjectManager;
+
+import org.eclipse.core.commands.common.HandleObject;
+import org.eclipse.core.commands.common.EventManager;
+import java.lang.all;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * <p>
+ * A manager of {@link HandleObject} instances. This has some common behaviour
+ * which is shared between all such managers.
+ * </p>
+ * <p>
+ * Clients may extend.
+ * </p>
+ *
+ * @since 3.2
+ */
+public abstract class HandleObjectManager : EventManager {
+
+    /**
+     * The set of handle objects that are defined. This value may be empty, but
+     * it is never <code>null</code>.
+     */
+    protected const Set definedHandleObjects;
+
+    /**
+     * The map of identifiers (<code>String</code>) to handle objects (
+     * <code>HandleObject</code>). This collection may be empty, but it is
+     * never <code>null</code>.
+     */
+    protected const Map handleObjectsById;
+
+    public this(){
+        definedHandleObjects = new HashSet();
+        handleObjectsById = new HashMap();
+    }
+
+    /**
+     * Verifies that the identifier is valid. Exceptions will be thrown if the
+     * identifier is invalid in some way.
+     *
+     * @param id
+     *            The identifier to validate; may be anything.
+     */
+    protected final void checkId(String id) {
+        if (id is null) {
+            throw new NullPointerException(
+                    "A handle object may not have a null identifier"); //$NON-NLS-1$
+        }
+
+        if (id.length < 1) {
+            throw new IllegalArgumentException(
+                    "The handle object must not have a zero-length identifier"); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Returns the set of identifiers for those handle objects that are defined.
+     *
+     * @return The set of defined handle object identifiers; this value may be
+     *         empty, but it is never <code>null</code>.
+     */
+    protected final HashSet getDefinedHandleObjectIds() {
+        HashSet definedHandleObjectIds = new HashSet(definedHandleObjects
+                .size());
+        Iterator handleObjectItr = definedHandleObjects.iterator();
+        while (handleObjectItr.hasNext()) {
+            HandleObject handleObject = cast(HandleObject) handleObjectItr
+                    .next();
+            String id = handleObject.getId();
+            definedHandleObjectIds.add(id);
+        }
+        return definedHandleObjectIds;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/common/IIdentifiable.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.core.commands.common.IIdentifiable;
+import java.lang.all;
+/**
+ * <p>
+ * An object that is unique identifiable based on the combination of its class
+ * and its identifier.
+ * </p>
+ *
+ * @see HandleObject
+ * @since 3.2
+ */
+public interface IIdentifiable {
+
+    /**
+     * Returns the identifier for this object.
+     *
+     * @return The identifier; never <code>null</code>.
+     */
+    String getId();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/common/NamedHandleObject.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.commands.common.NamedHandleObject;
+
+import org.eclipse.core.commands.common.HandleObject;
+import org.eclipse.core.commands.common.NotDefinedException;
+import java.lang.all;
+
+/**
+ * A handle object that carries with it a name and a description. This type of
+ * handle object is quite common across the commands code base. For example,
+ * <code>Command</code>, <code>Context</code> and <code>Scheme</code>.
+ *
+ * @since 3.1
+ */
+public abstract class NamedHandleObject : HandleObject {
+
+    /**
+     * The description for this handle. This value may be <code>null</code> if
+     * the handle is undefined or has no description.
+     */
+    protected String description = null;
+
+    /**
+     * The name of this handle. This valud should not be <code>null</code>
+     * unless the handle is undefined.
+     */
+    protected String name = null;
+
+    /**
+     * Constructs a new instance of <code>NamedHandleObject</code>.
+     *
+     * @param id
+     *            The identifier for this handle; must not be <code>null</code>.
+     */
+    protected this(String id) {
+        super(id);
+    }
+
+    /**
+     * Returns the description for this handle.
+     *
+     * @return The description; may be <code>null</code> if there is no
+     *         description.
+     * @throws NotDefinedException
+     *             If the handle is not currently defined.
+     */
+    public String getDescription() {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot get a description from an undefined object. " //$NON-NLS-1$
+                    ~ id);
+        }
+
+        return description;
+    }
+
+    /**
+     * Returns the name for this handle.
+     *
+     * @return The name for this handle; never <code>null</code>.
+     * @throws NotDefinedException
+     *             If the handle is not currently defined.
+     */
+    public String getName() {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot get the name from an undefined object. " //$NON-NLS-1$
+                    ~ id);
+        }
+
+        return name;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/common/NamedHandleObjectComparator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.core.commands.common.NamedHandleObjectComparator;
+
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.commands.common.NamedHandleObject;
+import org.eclipse.core.internal.commands.util.Util;
+import java.lang.all;
+/**
+ * Comparator for instances of <code>NamedHandleObject</code> for display to
+ * an end user. The comparison is based on the name of the instances.
+ *
+ * @since 3.2
+ */
+public class NamedHandleObjectComparator /+: Comparator+/ {
+
+    /**
+     * Compares to instances of NamedHandleObject based on their names. This is
+     * useful is they are display to an end user.
+     *
+     * @param left
+     *            The first obect to compare; may be <code>null</code>.
+     * @param right
+     *            The second object to compare; may be <code>null</code>.
+     * @return <code>-1</code> if <code>left</code> is <code>null</code>
+     *         and <code>right</code> is not <code>null</code>;
+     *         <code>0</code> if they are both <code>null</code>;
+     *         <code>1</code> if <code>left</code> is not <code>null</code>
+     *         and <code>right</code> is <code>null</code>. Otherwise, the
+     *         result of <code>left.compareTo(right)</code>.
+     */
+    public final int compare(Object left, Object right) {
+        NamedHandleObject a = cast(NamedHandleObject) left;
+        NamedHandleObject b = cast(NamedHandleObject) right;
+
+        String aName = null;
+        try {
+            aName = a.getName();
+        } catch (NotDefinedException e) {
+            // Leave aName as null.
+        }
+        String bName = null;
+        try {
+            bName = b.getName();
+        } catch (NotDefinedException e) {
+            // Leave bName as null.
+        }
+
+        return Util.compare(aName, bName);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/common/NotDefinedException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.common.NotDefinedException;
+
+import org.eclipse.core.commands.common.CommandException;
+import java.lang.all;
+
+/**
+ * Signals that an attempt was made to access the properties of an undefined
+ * object.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class NotDefinedException : CommandException {
+
+    /**
+     * Generated serial version UID for this class.
+     *
+     * @since 3.1
+     */
+    private static final long serialVersionUID = 3257572788998124596L;
+
+    /**
+     * Creates a new instance of this class with the specified detail message.
+     *
+     * @param s
+     *            the detail message; may be <code>null</code>.
+     */
+    public this(String s) {
+        super(s);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/contexts/Context.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,289 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.core.commands.contexts.Context;
+
+import org.eclipse.core.commands.contexts.ContextEvent;
+import org.eclipse.core.commands.contexts.IContextListener;
+import org.eclipse.core.commands.common.NamedHandleObject;
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.internal.commands.util.Util;
+
+import java.lang.all;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.HashSet;
+
+/**
+ * <p>
+ * A context is an answer to the question "when". Other services can listen for
+ * the activation and deactivation of contexts, and change their own state in
+ * response to these changes. For example, Eclipse's key binding service listens
+ * to context activation and deactivation to determine which key bindings should
+ * be active.
+ * </p>
+ * <p>
+ * An instance of this interface can be obtained from an instance of
+ * <code>ContextManager</code> for any identifier, whether or not an context
+ * with that identifier is defined in the extension registry.
+ * </p>
+ * <p>
+ * The handle-based nature of this API allows it to work well with runtime
+ * plugin activation and deactivation. If a context is defined, that means that
+ * its corresponding plug-in is active. If the plug-in is then deactivated, the
+ * context will still exist but it will be undefined. An attempts to use an
+ * undefined context will result in a <code>NotDefinedException</code> being
+ * thrown.
+ * </p>
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see ContextManager
+ */
+public final class Context : NamedHandleObject, Comparable {
+
+    /**
+     * The collection of all objects listening to changes on this context. This
+     * value is <code>null</code> if there are no listeners.
+     */
+    private Set listeners = null;
+
+    /**
+     * The parent identifier for this context. The meaning of a parent is
+     * dependent on the system using contexts. This value can be
+     * <code>null</code> if the context has no parent.
+     */
+    private String parentId = null;
+
+    /**
+     * Constructs a new instance of <code>Context</code>.
+     *
+     * @param id
+     *            The id for this context; must not be <code>null</code>.
+     */
+    this(String id) {
+        super(id);
+    }
+
+    /**
+     * Registers an instance of <code>IContextListener</code> to listen for
+     * changes to properties of this instance.
+     *
+     * @param listener
+     *            the instance to register. Must not be <code>null</code>. If
+     *            an attempt is made to register an instance which is already
+     *            registered with this instance, no operation is performed.
+     */
+    public final void addContextListener(IContextListener listener) {
+        if (listener is null) {
+            throw new NullPointerException();
+        }
+
+        if (listeners is null) {
+            listeners = new HashSet();
+        }
+
+        listeners.add(cast(Object)listener);
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Comparable#compareTo(java.lang.Object)
+     */
+    public final int compareTo(Object object) {
+        Context scheme = cast(Context) object;
+        int compareTo = Util.compare(this.id, scheme.id);
+        if (compareTo is 0) {
+            compareTo = Util.compare(this.name, scheme.name);
+            if (compareTo is 0) {
+                compareTo = Util.compare(this.parentId, scheme.parentId);
+                if (compareTo is 0) {
+                    compareTo = Util.compare(this.description,
+                            scheme.description);
+                    if (compareTo is 0) {
+                        compareTo = Util.compare(this.defined, scheme.defined);
+                    }
+                }
+            }
+        }
+
+        return compareTo;
+    }
+
+    /**
+     * <p>
+     * Defines this context by giving it a name, and possibly a description and
+     * a parent identifier as well. The defined property automatically becomes
+     * <code>true</code>.
+     * </p>
+     * <p>
+     * Notification is sent to all listeners that something has changed.
+     * </p>
+     *
+     * @param name
+     *            The name of this context; must not be <code>null</code>.
+     * @param description
+     *            The description for this context; may be <code>null</code>.
+     * @param parentId
+     *            The parent identifier for this context; may be
+     *            <code>null</code>.
+     */
+    public final void define(String name, String description,
+            String parentId) {
+        if (name is null) {
+            throw new NullPointerException(
+                    "The name of a scheme cannot be null"); //$NON-NLS-1$
+        }
+
+        bool definedChanged = !this.defined;
+        this.defined = true;
+
+        bool nameChanged = !Util.equals(this.name, name);
+        this.name = name;
+
+        bool descriptionChanged = !Util.equals(this.description,
+                description);
+        this.description = description;
+
+        bool parentIdChanged = !Util.equals(this.parentId, parentId);
+        this.parentId = parentId;
+
+        fireContextChanged(new ContextEvent(this, definedChanged, nameChanged,
+                descriptionChanged, parentIdChanged));
+    }
+
+    /**
+     * Notifies all listeners that this context has changed. This sends the
+     * given event to all of the listeners, if any.
+     *
+     * @param event
+     *            The event to send to the listeners; must not be
+     *            <code>null</code>.
+     */
+    private final void fireContextChanged(ContextEvent event) {
+        if (event is null) {
+            throw new NullPointerException(
+                    "Cannot send a null event to listeners."); //$NON-NLS-1$
+        }
+
+        if (listeners is null) {
+            return;
+        }
+
+        Iterator listenerItr = listeners.iterator();
+        while (listenerItr.hasNext()) {
+            IContextListener listener = cast(IContextListener) listenerItr
+                    .next();
+            listener.contextChanged(event);
+        }
+    }
+
+    /**
+     * Returns the identifier of the parent of this instance.
+     * <p>
+     * Notification is sent to all registered listeners if this property
+     * changes.
+     * </p>
+     *
+     * @return the identifier of the parent of this instance. May be
+     *         <code>null</code>.
+     * @throws NotDefinedException
+     *             if this instance is not defined.
+     */
+    public final String getParentId() {
+        if (!defined) {
+            throw new NotDefinedException(
+                    "Cannot get the parent identifier from an undefined context. " //$NON-NLS-1$
+                    ~ id);
+        }
+
+        return parentId;
+    }
+
+    /**
+     * Unregisters an instance of <code>IContextListener</code> listening for
+     * changes to properties of this instance.
+     *
+     * @param contextListener
+     *            the instance to unregister. Must not be <code>null</code>.
+     *            If an attempt is made to unregister an instance which is not
+     *            already registered with this instance, no operation is
+     *            performed.
+     */
+    public final void removeContextListener(
+            IContextListener contextListener) {
+        if (contextListener is null) {
+            throw new NullPointerException("Cannot remove a null listener."); //$NON-NLS-1$
+        }
+
+        if (listeners is null) {
+            return;
+        }
+
+        listeners.remove(cast(Object)contextListener);
+
+        if (listeners.isEmpty()) {
+            listeners = null;
+        }
+    }
+
+    /**
+     * The string representation of this context -- for debugging purposes only.
+     * This string should not be shown to an end user.
+     *
+     * @return The string representation; never <code>null</code>.
+     */
+    public override final String toString() {
+        if (string is null) {
+            final StringBuffer stringBuffer = new StringBuffer();
+            stringBuffer.append("Context("); //$NON-NLS-1$
+            stringBuffer.append(id);
+            stringBuffer.append(',');
+            stringBuffer.append(name);
+            stringBuffer.append(',');
+            stringBuffer.append(description);
+            stringBuffer.append(',');
+            stringBuffer.append(parentId);
+            stringBuffer.append(',');
+            stringBuffer.append(defined ? "true" : "false");
+            stringBuffer.append(')');
+            string = stringBuffer.toString();
+        }
+        return string;
+    }
+
+    /**
+     * Makes this context become undefined. This has the side effect of changing
+     * the name, description and parent identifier to <code>null</code>.
+     * Notification is sent to all listeners.
+     */
+    public override final void undefine() {
+        string = null;
+
+        bool definedChanged = defined;
+        defined = false;
+
+        bool nameChanged = name !is null;
+        name = null;
+
+        bool descriptionChanged = description !is null;
+        description = null;
+
+        bool parentIdChanged = parentId !is null;
+        parentId = null;
+
+        fireContextChanged(new ContextEvent(this, definedChanged, nameChanged,
+                descriptionChanged, parentIdChanged));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/contexts/ContextEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.core.commands.contexts.ContextEvent;
+
+import org.eclipse.core.commands.contexts.Context;
+import org.eclipse.core.commands.common.AbstractNamedHandleEvent;
+import java.lang.all;
+
+/**
+ * An instance of this class describes changes to an instance of
+ * <code>IContext</code>.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see IContextListener#contextChanged(ContextEvent)
+ */
+public final class ContextEvent : AbstractNamedHandleEvent {
+
+    /**
+     * The bit used to represent whether the context has changed its parent.
+     */
+    private static const int CHANGED_PARENT_ID = LAST_USED_BIT << 1;
+
+    /**
+     * The context that has changed. This value is never <code>null</code>.
+     */
+    private Context context;
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param context
+     *            the instance of the interface that changed; must not be
+     *            <code>null</code>.
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     * @param nameChanged
+     *            <code>true</code>, iff the name property changed.
+     * @param descriptionChanged
+     *            <code>true</code>, iff the description property changed.
+     * @param parentIdChanged
+     *            <code>true</code>, iff the parentId property changed.
+     */
+    public this(Context context, bool definedChanged,
+            bool nameChanged, bool descriptionChanged,
+            bool parentIdChanged) {
+        super(definedChanged, descriptionChanged, nameChanged);
+
+        if (context is null) {
+            throw new NullPointerException();
+        }
+        this.context = context;
+
+        if (parentIdChanged) {
+            changedValues |= CHANGED_PARENT_ID;
+        }
+    }
+
+    /**
+     * Returns the instance of the interface that changed.
+     *
+     * @return the instance of the interface that changed. Guaranteed not to be
+     *         <code>null</code>.
+     */
+    public final Context getContext() {
+        return context;
+    }
+
+    /**
+     * Returns whether or not the parentId property changed.
+     *
+     * @return <code>true</code>, iff the parentId property changed.
+     */
+    public final bool isParentIdChanged() {
+        return ((changedValues & CHANGED_PARENT_ID) !is 0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/contexts/ContextManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,330 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.contexts.ContextManager;
+
+import org.eclipse.core.commands.contexts.IContextListener;
+import org.eclipse.core.commands.contexts.IContextManagerListener;
+import org.eclipse.core.commands.contexts.ContextEvent;
+import org.eclipse.core.commands.contexts.ContextManagerEvent;
+import org.eclipse.core.commands.contexts.Context;
+import org.eclipse.core.commands.common.HandleObjectManager;
+import org.eclipse.core.commands.util.Tracing;
+import org.eclipse.core.internal.commands.util.Util;
+
+import java.lang.all;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
+
+/**
+ * <p>
+ * A context manager tracks the sets of defined and enabled contexts within the
+ * application. The manager sends notification events to listeners when these
+ * sets change. It is also possible to retrieve any given context with its
+ * identifier.
+ * </p>
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class ContextManager : HandleObjectManager,
+        IContextListener {
+
+    private static const String DEFER_EVENTS = "org.eclipse.ui.internal.contexts.deferEvents"; //$NON-NLS-1$
+    private static const String SEND_EVENTS = "org.eclipse.ui.internal.contexts.sendEvents"; //$NON-NLS-1$
+
+    /**
+     * This flag can be set to <code>true</code> if the context manager should
+     * print information to <code>System.out</code> when certain boundary
+     * conditions occur.
+     */
+    public static bool DEBUG = false;
+
+    /**
+     * The set of active context identifiers. This value may be empty, but it is
+     * never <code>null</code>.
+     */
+    private Set activeContextIds;
+    private static Set EMPTY_SET;
+
+    // allow the ContextManager to send one event for a larger delta
+    private bool caching = false;
+
+    private int cachingRef = 0;
+
+    private bool activeContextsChange = false;
+
+    private Set oldIds = null;
+
+    public this(){
+        activeContextIds = new HashSet();
+        if( EMPTY_SET is null ){
+            EMPTY_SET = new HashSet();
+        }
+    }
+
+    /**
+     * Activates a context in this context manager.
+     *
+     * @param contextId
+     *            The identifier of the context to activate; must not be
+     *            <code>null</code>.
+     */
+    public final void addActiveContext(String contextId) {
+        if (DEFER_EVENTS.equals(contextId)) {
+            cachingRef++;
+            if (cachingRef is 1 ) {
+                setEventCaching(true);
+            }
+            return;
+        } else if (SEND_EVENTS.equals(contextId)) {
+            cachingRef--;
+            if (cachingRef is 0) {
+                setEventCaching(false);
+            }
+            return;
+        }
+
+        if (activeContextIds.contains(contextId)) {
+            return;
+        }
+        activeContextsChange = true;
+
+        if (caching) {
+            activeContextIds.add(contextId);
+        } else {
+            Set previouslyActiveContextIds = new HashSet(activeContextIds);
+            activeContextIds.add(contextId);
+
+            fireContextManagerChanged(new ContextManagerEvent(this, null,
+                    false, true, previouslyActiveContextIds));
+        }
+
+        if (DEBUG) {
+            Tracing.printTrace("CONTEXTS", activeContextIds.toString()); //$NON-NLS-1$
+        }
+
+    }
+
+    /**
+     * Adds a listener to this context manager. The listener will be notified
+     * when the set of defined contexts changes. This can be used to track the
+     * global appearance and disappearance of contexts.
+     *
+     * @param listener
+     *            The listener to attach; must not be <code>null</code>.
+     */
+    public final void addContextManagerListener(
+            IContextManagerListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    public final void contextChanged(ContextEvent contextEvent) {
+        if (contextEvent.isDefinedChanged()) {
+            Context context = contextEvent.getContext();
+            String contextId = context.getId();
+            bool contextIdAdded = context.isDefined();
+            if (contextIdAdded) {
+                definedHandleObjects.add(context);
+            } else {
+                definedHandleObjects.remove(context);
+            }
+            if (isListenerAttached()) {
+                fireContextManagerChanged(new ContextManagerEvent(this,
+                        contextId, contextIdAdded, false, null));
+            }
+        }
+    }
+
+    /**
+     * Notifies all of the listeners to this manager that the set of defined
+     * context identifiers has changed.
+     *
+     * @param event
+     *            The event to send to all of the listeners; must not be
+     *            <code>null</code>.
+     */
+    private final void fireContextManagerChanged(ContextManagerEvent event) {
+        if (event is null) {
+            throw new NullPointerException();
+        }
+
+        Object[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            IContextManagerListener listener = cast(IContextManagerListener) listeners[i];
+            listener.contextManagerChanged(event);
+        }
+    }
+
+    /**
+     * Returns the set of active context identifiers.
+     *
+     * @return The set of active context identifiers; this value may be
+     *         <code>null</code> if no active contexts have been set yet. If
+     *         the set is not <code>null</code>, then it contains only
+     *         instances of <code>String</code>.
+     */
+    public final Set getActiveContextIds() {
+        return Collections.unmodifiableSet(activeContextIds);
+    }
+
+    /**
+     * Gets the context with the given identifier. If no such context currently
+     * exists, then the context will be created (but be undefined).
+     *
+     * @param contextId
+     *            The identifier to find; must not be <code>null</code>.
+     * @return The context with the given identifier; this value will never be
+     *         <code>null</code>, but it might be undefined.
+     * @see Context
+     */
+    public final Context getContext(String contextId) {
+        checkId(contextId);
+
+        Context context = cast(Context) handleObjectsById.get(contextId);
+        if (context is null) {
+            context = new Context(contextId);
+            handleObjectsById.put(contextId, context);
+            context.addContextListener(this);
+        }
+
+        return context;
+    }
+
+    /**
+     * Returns the set of identifiers for those contexts that are defined.
+     *
+     * @return The set of defined context identifiers; this value may be empty,
+     *         but it is never <code>null</code>.
+     */
+    public final Set getDefinedContextIds() {
+        return getDefinedHandleObjectIds();
+    }
+
+    /**
+     * Returns the those contexts that are defined.
+     *
+     * @return The defined contexts; this value may be empty, but it is never
+     *         <code>null</code>.
+     * @since 3.2
+     */
+    public final Context[] getDefinedContexts() {
+        return arraycast!(Context)( definedHandleObjects
+                .toArray(/+new Context[definedHandleObjects.size()]+/));
+    }
+
+    /**
+     * Deactivates a context in this context manager.
+     *
+     * @param contextId
+     *            The identifier of the context to deactivate; must not be
+     *            <code>null</code>.
+     */
+    public final void removeActiveContext(String contextId) {
+        if (!activeContextIds.contains(contextId)) {
+            return;
+        }
+
+        activeContextsChange = true;
+        if (caching) {
+            activeContextIds.remove(contextId);
+        } else {
+            Set previouslyActiveContextIds = new HashSet(activeContextIds);
+            activeContextIds.remove(contextId);
+
+            fireContextManagerChanged(new ContextManagerEvent(this, null,
+                    false, true, previouslyActiveContextIds));
+        }
+
+        if (DEBUG) {
+            Tracing.printTrace("CONTEXTS", activeContextIds.toString()); //$NON-NLS-1$
+        }
+    }
+    /**
+     * Removes a listener from this context manager.
+     *
+     * @param listener
+     *            The listener to be removed; must not be <code>null</code>.
+     */
+    public final void removeContextManagerListener(
+            IContextManagerListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Changes the set of active contexts for this context manager. The whole
+     * set is required so that internal consistency can be maintained and so
+     * that excessive recomputations do nothing occur.
+     *
+     * @param activeContextIds
+     *            The new set of active context identifiers; may be
+     *            <code>null</code>.
+     */
+    public final void setActiveContextIds(Set activeContextIds) {
+        if (Util.equals(cast(Object)this.activeContextIds, cast(Object)activeContextIds)) {
+            return;
+        }
+
+        activeContextsChange = true;
+
+        Set previouslyActiveContextIds = this.activeContextIds;
+        if (activeContextIds !is null) {
+            this.activeContextIds = new HashSet();
+            this.activeContextIds.addAll(activeContextIds);
+        } else {
+            this.activeContextIds = null;
+        }
+
+        if (DEBUG) {
+            Tracing.printTrace("CONTEXTS", (activeContextIds is null) ? "none" //$NON-NLS-1$ //$NON-NLS-2$
+                    : activeContextIds.toString());
+        }
+
+        if (!caching) {
+            fireContextManagerChanged(new ContextManagerEvent(this, null,
+                    false, true, previouslyActiveContextIds));
+        }
+    }
+
+    /**
+     * Set the manager to cache context id changes.
+     *
+     * @param cache
+     *            <code>true</code> to turn caching on, <code>false</code>
+     *            to turn caching off and send an event if necessary.
+     * @since 3.3
+     */
+    private void setEventCaching(bool cache) {
+        if (caching is cache) {
+            return;
+        }
+        caching = cache;
+        bool fireChange = activeContextsChange;
+        Set holdOldIds = (oldIds is null?Collections.EMPTY_SET:oldIds);
+
+        if (caching) {
+            oldIds = new HashSet(activeContextIds);
+        } else {
+            oldIds = null;
+        }
+        activeContextsChange = false;
+
+        if (!caching && fireChange) {
+            fireContextManagerChanged(new ContextManagerEvent(this, null,
+                    false, true, holdOldIds));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/contexts/ContextManagerEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.contexts.ContextManagerEvent;
+
+import org.eclipse.core.commands.contexts.ContextManager;
+import org.eclipse.core.commands.common.AbstractBitSetEvent;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * <p>
+ * An event indicating that the set of defined context identifiers has changed.
+ * </p>
+ *
+ * @since 3.1
+ * @see IContextManagerListener#contextManagerChanged(ContextManagerEvent)
+ */
+public final class ContextManagerEvent : AbstractBitSetEvent {
+
+    /**
+     * The bit used to represent whether the set of defined contexts has
+     * changed.
+     */
+    private static const int CHANGED_CONTEXT_DEFINED = 1 << 1;
+
+    /**
+     * The bit used to represent whether the set of active contexts has changed.
+     */
+    private static const int CHANGED_CONTEXTS_ACTIVE = 1;
+
+    /**
+     * The context identifier that was added or removed from the list of defined
+     * context identifiers.
+     */
+    private const String contextId;
+
+    /**
+     * The context manager that has changed.
+     */
+    private const ContextManager contextManager;
+
+    /**
+     * The set of context identifiers (strings) that were active before the
+     * change occurred. If the active contexts did not changed, then this value
+     * is <code>null</code>.
+     */
+    private const Set previouslyActiveContextIds;
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param contextManager
+     *            the instance of the interface that changed; must not be
+     *            <code>null</code>.
+     * @param contextId
+     *            The context identifier that was added or removed; may be
+     *            <code>null</code> if the active contexts are changing.
+     * @param contextIdAdded
+     *            Whether the context identifier became defined (otherwise, it
+     *            became undefined).
+     * @param activeContextsChanged
+     *            Whether the list of active contexts has changed.
+     * @param previouslyActiveContextIds
+     *            the set of identifiers of previously active contexts. This set
+     *            may be empty. If this set is not empty, it must only contain
+     *            instances of <code>String</code>. This set must be
+     *            <code>null</code> if activeContextChanged is
+     *            <code>false</code> and must not be null if
+     *            activeContextChanged is <code>true</code>.
+     */
+    public this(ContextManager contextManager,
+            String contextId, bool contextIdAdded,
+            bool activeContextsChanged,
+            Set previouslyActiveContextIds) {
+        if (contextManager is null) {
+            throw new NullPointerException();
+        }
+
+        this.contextManager = contextManager;
+        this.contextId = contextId;
+        this.previouslyActiveContextIds = previouslyActiveContextIds;
+
+        if (contextIdAdded) {
+            changedValues |= CHANGED_CONTEXT_DEFINED;
+        }
+        if (activeContextsChanged) {
+            changedValues |= CHANGED_CONTEXTS_ACTIVE;
+        }
+    }
+
+    /**
+     * Returns the context identifier that was added or removed.
+     *
+     * @return The context identifier that was added or removed. This value may
+     *         be <code>null</code> if no context identifier was added or
+     *         removed.
+     */
+    public final String getContextId() {
+        return contextId;
+    }
+
+    /**
+     * Returns the instance of the interface that changed.
+     *
+     * @return the instance of the interface that changed. Guaranteed not to be
+     *         <code>null</code>.
+     */
+    public final ContextManager getContextManager() {
+        return contextManager;
+    }
+
+    /**
+     * Returns the set of identifiers to previously active contexts.
+     *
+     * @return the set of identifiers to previously active contexts. This set
+     *         may be empty. If this set is not empty, it is guaranteed to only
+     *         contain instances of <code>String</code>. This set is
+     *         guaranteed to be <code>null</code> if
+     *         haveActiveContextChanged() is <code>false</code> and is
+     *         guaranteed to not be <code>null</code> if
+     *         haveActiveContextsChanged() is <code>true</code>.
+     */
+    public final Set getPreviouslyActiveContextIds() {
+        return previouslyActiveContextIds;
+    }
+
+    /**
+     * Returns whether the active context identifiers have changed.
+     *
+     * @return <code>true</code> if the collection of active contexts changed;
+     *         <code>false</code> otherwise.
+     */
+    public final bool isActiveContextsChanged() {
+        return ((changedValues & CHANGED_CONTEXTS_ACTIVE) !is 0);
+    }
+
+    /**
+     * Returns whether the list of defined context identifiers has changed.
+     *
+     * @return <code>true</code> if the list of context identifiers has
+     *         changed; <code>false</code> otherwise.
+     */
+    public final bool isContextChanged() {
+        return (contextId !is null);
+    }
+
+    /**
+     * Returns whether the context identifier became defined. Otherwise, the
+     * context identifier became undefined.
+     *
+     * @return <code>true</code> if the context identifier became defined;
+     *         <code>false</code> if the context identifier became undefined.
+     */
+    public final bool isContextDefined() {
+        return (((changedValues & CHANGED_CONTEXT_DEFINED) !is 0) && (contextId !is null));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/contexts/IContextListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.core.commands.contexts.IContextListener;
+
+import org.eclipse.core.commands.contexts.ContextEvent;
+
+/**
+ * An instance of this interface can be used by clients to receive notification
+ * of changes to one or more instances of <code>IContext</code>.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see Context#addContextListener(IContextListener)
+ * @see Context#removeContextListener(IContextListener)
+ */
+public interface IContextListener {
+
+    /**
+     * Notifies that one or more properties of an instance of
+     * <code>IContext</code> have changed. Specific details are described in
+     * the <code>ContextEvent</code>.
+     *
+     * @param contextEvent
+     *            the context event. Guaranteed not to be <code>null</code>.
+     */
+    void contextChanged(ContextEvent contextEvent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/contexts/IContextManagerListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.core.commands.contexts.IContextManagerListener;
+
+import org.eclipse.core.commands.contexts.ContextManagerEvent;
+
+/**
+ * An instance of this interface can be used by clients to receive notification
+ * of changes to one or more instances of <code>IContextManager</code>.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see ContextManager#addContextManagerListener(IContextManagerListener)
+ * @see ContextManager#removeContextManagerListener(IContextManagerListener)
+ */
+public interface IContextManagerListener {
+
+    /**
+     * Notifies that one or more properties of an instance of
+     * <code>IContextManager</code> have changed. Specific details are
+     * described in the <code>ContextManagerEvent</code>.
+     *
+     * @param contextManagerEvent
+     *            the context manager event. Guaranteed not to be
+     *            <code>null</code>.
+     */
+    void contextManagerChanged(ContextManagerEvent contextManagerEvent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/AbstractOperation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.AbstractOperation;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.commands.operations.IUndoContext;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * <p>
+ * Abstract implementation for an undoable operation. At a minimum, subclasses
+ * should implement behavior for
+ * {@link IUndoableOperation#execute(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.core.runtime.IAdaptable)},
+ * {@link IUndoableOperation#redo(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.core.runtime.IAdaptable)},
+ * and
+ * {@link IUndoableOperation#undo(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.core.runtime.IAdaptable)}.
+ * </p>
+ *
+ * @see org.eclipse.core.commands.operations.IUndoableOperation
+ *
+ * @since 3.1
+ */
+public abstract class AbstractOperation : IUndoableOperation {
+    List contexts;
+
+    private String label = ""; //$NON-NLS-1$
+
+    /**
+     * Construct an operation that has the specified label.
+     *
+     * @param label
+     *            the label to be used for the operation. Should never be
+     *            <code>null</code>.
+     */
+    public this(String label) {
+        Assert.isNotNull(label);
+        this.label = label;
+        contexts = new ArrayList();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#addContext(org.eclipse.core.commands.operations.IUndoContext)
+     *
+     * <p> Subclasses may override this method. </p>
+     */
+    public void addContext(IUndoContext context) {
+        if (!contexts.contains(cast(Object)context)) {
+            contexts.add(cast(Object)context);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#canExecute()
+     *      <p> Default implementation. Subclasses may override this method.
+     *      </p>
+     *
+     */
+    public bool canExecute() {
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#canRedo()
+     *      <p> Default implementation. Subclasses may override this method.
+     *      </p>
+     */
+    public bool canRedo() {
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#canUndo()
+     *      <p> Default implementation. Subclasses may override this method.
+     *      </p>
+     */
+    public bool canUndo() {
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#dispose()
+     *      <p> Default implementation. Subclasses may override this method.
+     *      </p>
+     */
+    public void dispose() {
+        // nothing to dispose.
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#execute(org.eclipse.core.runtime.IProgressMonitor,
+     *      org.eclipse.core.runtime.IAdaptable)
+     */
+    public abstract IStatus execute(IProgressMonitor monitor, IAdaptable info);
+
+    public final IUndoContext[] getContexts() {
+        return arraycast!(IUndoContext)(contexts.toArray());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#getLabel()
+     *      <p> Default implementation. Subclasses may override this method.
+     *      </p>
+     */
+    public String getLabel() {
+        return label;
+    }
+
+    /**
+     * Set the label of the operation to the specified name.
+     *
+     * @param name
+     *            the string to be used for the label. Should never be
+     *            <code>null</code>.
+     */
+    public void setLabel(String name) {
+        label = name;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#hasContext(org.eclipse.core.commands.operations.IUndoContext)
+     */
+    public final bool hasContext(IUndoContext context) {
+        Assert.isNotNull(cast(Object)context);
+        for (int i = 0; i < contexts.size(); i++) {
+            IUndoContext otherContext = cast(IUndoContext)contexts.get(i);
+            // have to check both ways because one context may be more general
+            // in
+            // its matching rules than another.
+            if (context.matches(otherContext) || otherContext.matches(context)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#redo(org.eclipse.core.runtime.IProgressMonitor,
+     *      org.eclipse.core.runtime.IAdaptable)
+     */
+    public abstract IStatus redo(IProgressMonitor monitor, IAdaptable info);
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#removeContext(org.eclipse.core.commands.operations.IUndoContext)
+     *      <p> Default implementation. Subclasses may override this method.
+     *      </p>
+     */
+
+    public void removeContext(IUndoContext context) {
+        contexts.remove(cast(Object)context);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#undo(org.eclipse.core.runtime.IProgressMonitor,
+     *      org.eclipse.core.runtime.IAdaptable)
+     */
+    public abstract IStatus undo(IProgressMonitor monitor, IAdaptable info);
+
+    /**
+     * The string representation of this operation. Used for debugging purposes
+     * only. This string should not be shown to an end user.
+     *
+     * @return The string representation.
+     */
+    public override String toString() {
+        StringBuffer stringBuffer = new StringBuffer();
+        stringBuffer.append(getLabel());
+        stringBuffer.append("("); //$NON-NLS-1$
+        IUndoContext[] contexts = getContexts();
+        for (int i = 0; i < contexts.length; i++) {
+            stringBuffer.append((cast(Object)contexts[i]).toString());
+            if (i !is contexts.length - 1) {
+                stringBuffer.append(',');
+            }
+        }
+        stringBuffer.append(')');
+        return stringBuffer.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/DefaultOperationHistory.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1399 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.core.commands.operations.DefaultOperationHistory;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.util.Tracing;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.commands.operations.IUndoContext;
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.commands.operations.ICompositeOperation;
+import org.eclipse.core.commands.operations.IOperationApprover;
+import org.eclipse.core.commands.operations.IOperationApprover2;
+import org.eclipse.core.commands.operations.IOperationHistoryListener;
+import org.eclipse.core.commands.operations.OperationHistoryEvent;
+import org.eclipse.core.commands.operations.IAdvancedUndoableOperation;
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * <p>
+ * A base implementation of IOperationHistory that implements a linear undo and
+ * redo model . The most recently added operation is available for undo, and the
+ * most recently undone operation is available for redo.
+ * </p>
+ * <p>
+ * If the operation eligible for undo is not in a state where it can be undone,
+ * then no undo is available. No other operations are considered. Likewise, if
+ * the operation available for redo cannot be redone, then no redo is available.
+ * </p>
+ * <p>
+ * Implementations for the direct undo and redo of a specified operation are
+ * available. If a strict linear undo is to be enforced, than an
+ * IOperationApprover should be installed that prevents undo and redo of any
+ * operation that is not the most recently undone or redone operation in all of
+ * its undo contexts.
+ * </p>
+ * <p>
+ * The data structures used by the DefaultOperationHistory are synchronized, and
+ * entry points that modify the undo and redo history concurrently are also
+ * synchronized. This means that the DefaultOperationHistory is relatively
+ * "thread-friendly" in its implementation. Outbound notifications or operation
+ * approval requests will occur on the thread that initiated the request.
+ * Clients may use DefaultOperationHistory API from any thread; however,
+ * listeners or operation approvers that receive notifications from the
+ * DefaultOperationHistory must be prepared to receive these notifications from
+ * a background thread. Any UI access occurring inside these notifications must
+ * be properly synchronized using the techniques specified by the client's
+ * widget library.
+ * </p>
+ *
+ * <p>
+ * This implementation is not intended to be subclassed.
+ * </p>
+ *
+ * @see org.eclipse.core.commands.operations.IOperationHistory
+ * @see org.eclipse.core.commands.operations.IOperationApprover
+ *
+ * @since 3.1
+ */
+public final class DefaultOperationHistory : IOperationHistory {
+    /**
+     * This flag can be set to <code>true</code> if the history should print
+     * information to <code>System.out</code> whenever notifications about
+     * changes to the history occur. This flag should be used for debug purposes
+     * only.
+     */
+    public static bool DEBUG_OPERATION_HISTORY_NOTIFICATION = false;
+
+    /**
+     * This flag can be set to <code>true</code> if the history should print
+     * information to <code>System.out</code> whenever an unexpected condition
+     * arises. This flag should be used for debug purposes only.
+     */
+    public static bool DEBUG_OPERATION_HISTORY_UNEXPECTED = false;
+
+    /**
+     * This flag can be set to <code>true</code> if the history should print
+     * information to <code>System.out</code> whenever an undo context is
+     * disposed. This flag should be used for debug purposes only.
+     */
+    public static bool DEBUG_OPERATION_HISTORY_DISPOSE = false;
+
+    /**
+     * This flag can be set to <code>true</code> if the history should print
+     * information to <code>System.out</code> during the open/close sequence.
+     * This flag should be used for debug purposes only.
+     */
+    public static bool DEBUG_OPERATION_HISTORY_OPENOPERATION = false;
+
+    /**
+     * This flag can be set to <code>true</code> if the history should print
+     * information to <code>System.out</code> whenever an operation is not
+     * approved. This flag should be used for debug purposes only.
+     */
+    public static bool DEBUG_OPERATION_HISTORY_APPROVAL = false;
+
+    static const int DEFAULT_LIMIT = 20;
+
+    /**
+     * the list of {@link IOperationApprover}s
+     */
+    ListenerList approvers;
+
+    /**
+     * a map of undo limits per context
+     */
+    private Map limits;
+
+    /**
+     * the list of {@link IOperationHistoryListener}s
+     */
+    ListenerList listeners;
+
+    /**
+     * the list of operations available for redo, LIFO
+     */
+    private List redoList;
+
+    /**
+     * the list of operations available for undo, LIFO
+     */
+    private List undoList;
+
+    /**
+     * a lock that is used to synchronize access between the undo and redo
+     * history
+     */
+    final Object undoRedoHistoryLock;
+
+    /**
+     * An operation that is "absorbing" all other operations while it is open.
+     * When this is not null, other operations added or executed are added to
+     * this composite.
+     *
+     */
+    private ICompositeOperation openComposite = null;
+
+    /**
+     * a lock that is used to synchronize access to the open composite.
+     */
+    const Object openCompositeLock;
+
+    /**
+     * Create an instance of DefaultOperationHistory.
+     */
+    public this() {
+        openCompositeLock = new Object();
+        undoRedoHistoryLock = new Object();
+        approvers = new ListenerList(ListenerList.IDENTITY);
+        limits =  Collections.synchronizedMap(new HashMap());
+        listeners = new ListenerList(ListenerList.IDENTITY);
+        redoList = Collections.synchronizedList(new ArrayList());
+        undoList = Collections.synchronizedList(new ArrayList());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#add(org.eclipse.core.commands.operations.IUndoableOperation)
+     */
+    public void add(IUndoableOperation operation) {
+        Assert.isNotNull( cast(Object)operation);
+
+        /*
+         * If we are in the middle of executing an open batching operation, and
+         * this is not that operation, then we need only add the context of the
+         * new operation to the batch. The operation itself is disposed since we
+         * will never undo or redo it. We consider it to be triggered by the
+         * batching operation and assume that its undo will be triggered by the
+         * batching operation undo.
+         */
+        synchronized (openCompositeLock) {
+            if (openComposite !is null && openComposite !is operation) {
+                openComposite.add(operation);
+                return;
+            }
+        }
+
+        if (checkUndoLimit(operation)) {
+            synchronized (undoRedoHistoryLock) {
+                undoList.add(cast(Object)operation);
+            }
+            notifyAdd(operation);
+
+            // flush redo stack for related contexts
+            IUndoContext[] contexts = operation.getContexts();
+            for (int i = 0; i < contexts.length; i++) {
+                flushRedo(contexts[i]);
+            }
+        } else {
+            // Dispose the operation since we will not have a reference to it.
+            operation.dispose();
+        }
+    }
+
+    /**
+     * <p>
+     * Add the specified approver to the list of operation approvers consulted
+     * by the operation history before an undo or redo is allowed to proceed.
+     * This method has no effect if the instance being added is already in the
+     * list.
+     * </p>
+     * <p>
+     * Operation approvers must be prepared to receive these the operation
+     * approval messages from a background thread. Any UI access occurring
+     * inside the implementation must be properly synchronized using the
+     * techniques specified by the client's widget library.
+     * </p>
+     *
+     * @param approver
+     *            the IOperationApprover to be added as an approver.
+     *
+     */
+
+    public void addOperationApprover(IOperationApprover approver) {
+        approvers.add(cast(Object)approver);
+    }
+
+    /**
+     * <p>
+     * Add the specified listener to the list of operation history listeners
+     * that are notified about changes in the history or operations that are
+     * executed, undone, or redone. This method has no effect if the instance
+     * being added is already in the list.
+     * </p>
+     * <p>
+     * Operation history listeners must be prepared to receive notifications
+     * from a background thread. Any UI access occurring inside the
+     * implementation must be properly synchronized using the techniques
+     * specified by the client's widget library.
+     * </p>
+     *
+     * @param listener
+     *            the IOperationHistoryListener to be added as a listener.
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistoryListener
+     * @see org.eclipse.core.commands.operations.OperationHistoryEvent
+     */
+    public void addOperationHistoryListener(IOperationHistoryListener listener) {
+        listeners.add(cast(Object)listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#canRedo(org.eclipse.core.commands.operations.IUndoContext)
+     */
+    public bool canRedo(IUndoContext context) {
+        // null context is allowed and passed through
+        IUndoableOperation operation = getRedoOperation(context);
+        return (operation !is null && operation.canRedo());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#canUndo(org.eclipse.core.commands.operations.IUndoContext)
+     */
+    public bool canUndo(IUndoContext context) {
+        // null context is allowed and passed through
+        IUndoableOperation operation = getUndoOperation(context);
+        return (operation !is null && operation.canUndo());
+    }
+
+    /**
+     * Check the redo limit before adding an operation. In theory the redo limit
+     * should never be reached, because the redo items are transferred from the
+     * undo history, which has the same limit. The redo history is cleared
+     * whenever a new operation is added. We check for completeness since
+     * implementations may change over time.
+     *
+     * Return a bool indicating whether the redo should proceed.
+     */
+    private bool checkRedoLimit(IUndoableOperation operation) {
+        IUndoContext[] contexts = operation.getContexts();
+        for (int i = 0; i < contexts.length; i++) {
+            int limit = getLimit(contexts[i]);
+            if (limit > 0) {
+                forceRedoLimit(contexts[i], limit - 1);
+            } else {
+                // this context has a 0 limit
+                operation.removeContext(contexts[i]);
+            }
+        }
+        return operation.getContexts().length > 0;
+    }
+
+    /**
+     * Check the undo limit before adding an operation. Return a bool
+     * indicating whether the undo should proceed.
+     */
+    private bool checkUndoLimit(IUndoableOperation operation) {
+        IUndoContext[] contexts = operation.getContexts();
+        for (int i = 0; i < contexts.length; i++) {
+            int limit = getLimit(contexts[i]);
+            if (limit > 0) {
+                forceUndoLimit(contexts[i], limit - 1);
+            } else {
+                // this context has a 0 limit
+                operation.removeContext(contexts[i]);
+            }
+        }
+        return operation.getContexts().length > 0;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#dispose(org.eclipse.core.commands.operations.IUndoContext,
+     *      bool, bool, bool)
+     */
+    public void dispose(IUndoContext context, bool flushUndo_,
+            bool flushRedo_, bool flushContext_) {
+        // dispose of any limit that was set for the context if it is not to be
+        // used again.
+        if (flushContext_) {
+            if (DEBUG_OPERATION_HISTORY_DISPOSE) {
+                Tracing.printTrace("OPERATIONHISTORY", "Flushing context " //$NON-NLS-1$//$NON-NLS-2$
+                        ~ (cast(Object)context).toString );
+            }
+            flushUndo(context);
+            flushRedo(context);
+            limits.remove(cast(Object)context);
+            return;
+        }
+        if (flushUndo_) {
+            flushUndo(context);
+        }
+        if (flushRedo_) {
+            flushRedo(context);
+        }
+
+    }
+
+    /**
+     * Perform the redo. All validity checks have already occurred.
+     *
+     * @param monitor
+     * @param operation
+     */
+    private IStatus doRedo(IProgressMonitor monitor, IAdaptable info,
+            IUndoableOperation operation) {
+
+        IStatus status = getRedoApproval(operation, info);
+        if (status.isOK()) {
+            notifyAboutToRedo(operation);
+            try {
+                status = operation.redo(monitor, info);
+            } catch (OperationCanceledException e) {
+                status = Status.CANCEL_STATUS;
+            } catch (ExecutionException e) {
+                notifyNotOK(operation);
+                if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "ExecutionException while redoing " ~ (cast(Object)operation).toString ); //$NON-NLS-1$
+                }
+                throw e;
+            } catch (Exception e) {
+                notifyNotOK(operation);
+                if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "Exception while redoing " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+                }
+                throw new ExecutionException(
+                        "While redoing the operation, an exception occurred", e); //$NON-NLS-1$
+            }
+        }
+
+        // if successful, the operation is removed from the redo history and
+        // placed back in the undo history.
+        if (status.isOK()) {
+            bool addedToUndo = true;
+            synchronized (undoRedoHistoryLock) {
+                redoList.remove(cast(Object)operation);
+                if (checkUndoLimit(operation)) {
+                    undoList.add(cast(Object)operation);
+                } else {
+                    addedToUndo = false;
+                }
+            }
+            // dispose the operation since we could not add it to the
+            // stack and will no longer have a reference to it.
+            if (!addedToUndo) {
+                operation.dispose();
+            }
+
+            // notify listeners must happen after history is updated
+            notifyRedone(operation);
+        } else {
+            notifyNotOK(operation, status);
+        }
+
+        return status;
+    }
+
+    /**
+     * Perform the undo. All validity checks have already occurred.
+     *
+     * @param monitor
+     * @param operation
+     */
+    private IStatus doUndo(IProgressMonitor monitor, IAdaptable info,
+            IUndoableOperation operation) {
+        IStatus status = getUndoApproval(operation, info);
+        if (status.isOK()) {
+            notifyAboutToUndo(operation);
+            try {
+                status = operation.undo(monitor, info);
+            } catch (OperationCanceledException e) {
+                status = Status.CANCEL_STATUS;
+            } catch (ExecutionException e) {
+                notifyNotOK(operation);
+                if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "ExecutionException while undoing " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+                }
+                throw e;
+            } catch (Exception e) {
+                notifyNotOK(operation);
+                if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "Exception while undoing " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+                }
+                throw new ExecutionException(
+                        "While undoing the operation, an exception occurred", e); //$NON-NLS-1$
+            }
+        }
+        // if successful, the operation is removed from the undo history and
+        // placed in the redo history.
+        if (status.isOK()) {
+            bool addedToRedo = true;
+            synchronized (undoRedoHistoryLock) {
+                undoList.remove(cast(Object)operation);
+                if (checkRedoLimit(operation)) {
+                    redoList.add(cast(Object)operation);
+                } else {
+                    addedToRedo = false;
+                }
+            }
+            // dispose the operation since we could not add it to the
+            // stack and will no longer have a reference to it.
+            if (!addedToRedo) {
+                operation.dispose();
+            }
+            // notification occurs after the undo and redo histories are
+            // adjusted
+            notifyUndone(operation);
+        } else {
+            notifyNotOK(operation, status);
+        }
+        return status;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#execute(org.eclipse.core.commands.operations.IUndoableOperation,
+     *      org.eclipse.core.runtime.IProgressMonitor,
+     *      org.eclipse.core.runtime.IAdaptable)
+     */
+    public IStatus execute(IUndoableOperation operation,
+            IProgressMonitor monitor, IAdaptable info) {
+        Assert.isNotNull(cast(Object)operation);
+
+        // error if operation is invalid
+        if (!operation.canExecute()) {
+            return IOperationHistory.OPERATION_INVALID_STATUS;
+        }
+
+        // check with the operation approvers
+        IStatus status = getExecuteApproval(operation, info);
+        if (!status.isOK()) {
+            // not approved. No notifications are sent, just return the status.
+            return status;
+        }
+
+        /*
+         * If we are in the middle of an open composite, then we will add this
+         * operation to the open operation rather than add the operation to the
+         * history. We will still execute it.
+         */
+        bool merging = false;
+        synchronized (openCompositeLock) {
+            if (openComposite !is null) {
+                // the composite shouldn't be executed explicitly while it is
+                // still
+                // open
+                if (openComposite is operation) {
+                    return IOperationHistory.OPERATION_INVALID_STATUS;
+                }
+                openComposite.add(operation);
+                merging = true;
+            }
+        }
+
+        /*
+         * Execute the operation
+         */
+        if (!merging) {
+            notifyAboutToExecute(operation);
+        }
+        try {
+            status = operation.execute(monitor, info);
+        } catch (OperationCanceledException e) {
+            status = Status.CANCEL_STATUS;
+        } catch (ExecutionException e) {
+            notifyNotOK(operation);
+            throw e;
+        } catch (Exception e) {
+            notifyNotOK(operation);
+            throw new ExecutionException(
+                    "While executing the operation, an exception occurred", e); //$NON-NLS-1$
+        }
+
+        // if successful, the notify listeners are notified and the operation is
+        // added to the history
+        if (!merging) {
+            if (status.isOK()) {
+                notifyDone(operation);
+                add(operation);
+            } else {
+                notifyNotOK(operation, status);
+                // dispose the operation since we did not add it to the stack
+                // and will no longer have a reference to it.
+                operation.dispose();
+            }
+        }
+        // all other severities are not interpreted. Simply return the status.
+        return status;
+    }
+
+    /*
+     * Filter the specified list to include only the specified undo context.
+     */
+    private IUndoableOperation[] filter(List list, IUndoContext context) {
+        /*
+         * This method is used whenever there is a need to filter the undo or
+         * redo history on a particular context. Currently there are no caches
+         * kept to optimize repeated requests for the same filter. If benchmarks
+         * show this to be a common pattern that causes performances problems,
+         * we could implement a filtered cache here that is nullified whenever
+         * the global history changes.
+         */
+
+        List filtered = new ArrayList();
+        Iterator iterator = list.iterator();
+        synchronized (undoRedoHistoryLock) {
+            while (iterator.hasNext()) {
+                IUndoableOperation operation = cast(IUndoableOperation) iterator
+                        .next();
+                if (operation.hasContext(context)) {
+                    filtered.add(cast(Object)operation);
+                }
+            }
+        }
+        return arraycast!(IUndoableOperation)( filtered
+                .toArray());
+    }
+
+    /*
+     * Flush the redo stack of all operations that have the given context.
+     */
+    private void flushRedo(IUndoContext context) {
+        if (DEBUG_OPERATION_HISTORY_DISPOSE) {
+            Tracing.printTrace("OPERATIONHISTORY", "Flushing redo history for " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)context).toString );
+        }
+
+        IUndoableOperation[] filtered = filter(redoList, context);
+        for (int i = 0; i < filtered.length; i++) {
+            IUndoableOperation operation =  cast(IUndoableOperation) filtered[i];
+            if (context is GLOBAL_UNDO_CONTEXT
+                    || operation.getContexts().length is 1) {
+                // remove the operation if it only has the context or we are
+                // flushing all
+                redoList.remove(cast(Object)operation);
+                internalRemove(operation);
+            } else {
+                // remove the reference to the context.
+                // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=161786
+                // It is not enough to simply remove the context. There could
+                // be one or more contexts that match the one we are trying to
+                // dispose.
+                IUndoContext[] contexts = operation.getContexts();
+                for (int j = 0; j < contexts.length; j++) {
+                    if (contexts[j].matches(context)) {
+                        operation.removeContext(contexts[j]);
+                    }
+                }
+                if (operation.getContexts().length is 0) {
+                    redoList.remove(cast(Object)operation);
+                    internalRemove(operation);
+                }
+            }
+        }
+    }
+
+    /*
+     * Flush the undo stack of all operations that have the given context.
+     */
+    private void flushUndo(IUndoContext context) {
+        if (DEBUG_OPERATION_HISTORY_DISPOSE) {
+            Tracing.printTrace("OPERATIONHISTORY", "Flushing undo history for " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)context).toString );
+        }
+
+        // Get all operations that have the context (or one that matches)
+        IUndoableOperation[] filtered = filter(undoList, context);
+        for (int i = 0; i < filtered.length; i++) {
+            IUndoableOperation operation = cast(IUndoableOperation) filtered[i];
+            if (context is GLOBAL_UNDO_CONTEXT
+                    || operation.getContexts().length is 1) {
+                // remove the operation if it only has the context or we are
+                // flushing all
+                undoList.remove(cast(Object)operation);
+                internalRemove(operation);
+            } else {
+                // remove the reference to the context.
+                // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=161786
+                // It is not enough to simply remove the context. There could
+                // be one or more contexts that match the one we are trying to
+                // dispose.
+                IUndoContext[] contexts = operation.getContexts();
+                for (int j = 0; j < contexts.length; j++) {
+                    if (contexts[j].matches(context)) {
+                        operation.removeContext(contexts[j]);
+                    }
+                }
+                if (operation.getContexts().length is 0) {
+                    undoList.remove(cast(Object)operation);
+                    internalRemove(operation);
+                }
+            }
+        }
+        /*
+         * There may be an open composite. If it has this context, then the
+         * context must be removed. If it has only this context or we are
+         * flushing all operations, then null it out and notify that we are
+         * ending it. We don't remove it since it was never added.
+         */
+        ICompositeOperation endedComposite = null;
+        synchronized (openCompositeLock) {
+            if (openComposite !is null) {
+                if (openComposite.hasContext(context)) {
+                    if (context is GLOBAL_UNDO_CONTEXT
+                            || openComposite.getContexts().length is 1) {
+                        endedComposite = openComposite;
+                        openComposite = null;
+                    } else {
+                        openComposite.removeContext(context);
+                    }
+                }
+            }
+        }
+        // notify outside of the synchronized block.
+        if (endedComposite !is null) {
+            notifyNotOK(endedComposite);
+        }
+    }
+
+    /*
+     * Force the redo history for the given context to contain max or less
+     * items.
+     */
+    private void forceRedoLimit(IUndoContext context, int max) {
+        IUndoableOperation[] filtered = filter(redoList, context);
+        int size = filtered.length;
+        if (size > 0) {
+            int index = 0;
+            while (size > max) {
+                IUndoableOperation removed = cast(IUndoableOperation)filtered[index];
+                if (context is GLOBAL_UNDO_CONTEXT
+                        || removed.getContexts().length is 1) {
+                    /*
+                     * remove the operation if we are enforcing a global limit
+                     * or if the operation only has the specified context
+                     */
+                    redoList.remove(cast(Object)removed);
+                    internalRemove(removed);
+                } else {
+                    /*
+                     * if the operation has multiple contexts and we've reached
+                     * the limit for only one of them, then just remove the
+                     * context, not the operation.
+                     */
+                    removed.removeContext(context);
+                }
+                size--;
+                index++;
+            }
+        }
+    }
+
+    /*
+     * Force the undo history for the given context to contain max or less
+     * items.
+     */
+    private void forceUndoLimit(IUndoContext context, int max) {
+        IUndoableOperation[] filtered = filter(undoList, context);
+        int size = filtered.length;
+        if (size > 0) {
+            int index = 0;
+            while (size > max) {
+                IUndoableOperation removed = cast(IUndoableOperation)filtered[index];
+                if (context is GLOBAL_UNDO_CONTEXT
+                        || removed.getContexts().length is 1) {
+                    /*
+                     * remove the operation if we are enforcing a global limit
+                     * or if the operation only has the specified context
+                     */
+                    undoList.remove(cast(Object)removed);
+                    internalRemove(removed);
+                } else {
+                    /*
+                     * if the operation has multiple contexts and we've reached
+                     * the limit for only one of them, then just remove the
+                     * context, not the operation.
+                     */
+                    removed.removeContext(context);
+                }
+                size--;
+                index++;
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#getLimit()
+     */
+    public int getLimit(IUndoContext context) {
+        if (!limits.containsKey(cast(Object)context)) {
+            return DEFAULT_LIMIT;
+        }
+        return (cast(Integer) (limits.get(cast(Object)context))).intValue();
+    }
+
+    /*
+     * Consult the IOperationApprovers to see if the proposed redo should be
+     * allowed.
+     */
+    private IStatus getRedoApproval(IUndoableOperation operation,
+            IAdaptable info) {
+
+        Object[] approverArray = approvers.getListeners();
+
+        for (int i = 0; i < approverArray.length; i++) {
+            IOperationApprover approver = cast(IOperationApprover) approverArray[i];
+            IStatus approval = approver.proceedRedoing(operation, this, info);
+            if (!approval.isOK()) {
+                if (DEBUG_OPERATION_HISTORY_APPROVAL) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "Redo not approved by " ~ (cast(Object)approver).toString //$NON-NLS-1$
+                                    ~ "for operation " ~ (cast(Object)operation).toString //$NON-NLS-1$
+                                    ~ " approved by " ~ (cast(Object)approval).toString); //$NON-NLS-1$
+                }
+                return approval;
+            }
+        }
+        return Status.OK_STATUS;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#getRedoHistory(org.eclipse.core.commands.operations.IUndoContext)
+     */
+    public IUndoableOperation[] getRedoHistory(IUndoContext context) {
+        Assert.isNotNull(cast(Object)context);
+        return filter(redoList, context);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#getOperation(org.eclipse.core.commands.operations.IUndoContext)
+     */
+    public IUndoableOperation getRedoOperation(IUndoContext context) {
+        Assert.isNotNull(cast(Object)context);
+        synchronized (undoRedoHistoryLock) {
+            for (int i = redoList.size() - 1; i >= 0; i--) {
+                IUndoableOperation operation = cast(IUndoableOperation) redoList
+                        .get(i);
+                if (operation.hasContext(context)) {
+                    return operation;
+                }
+            }
+        }
+        return null;
+    }
+
+    /*
+     * Consult the IOperationApprovers to see if the proposed undo should be
+     * allowed.
+     */
+    private IStatus getUndoApproval(IUndoableOperation operation,
+            IAdaptable info) {
+
+        final Object[] approverArray = approvers.getListeners();
+
+        for (int i = 0; i < approverArray.length; i++) {
+            IOperationApprover approver = cast(IOperationApprover) approverArray[i];
+            IStatus approval = approver.proceedUndoing(operation, this, info);
+            if (!approval.isOK()) {
+                if (DEBUG_OPERATION_HISTORY_APPROVAL) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "Undo not approved by " ~ (cast(Object)approver).toString //$NON-NLS-1$
+                                    ~ "for operation " ~ (cast(Object)operation ).toString//$NON-NLS-1$
+                                    ~ " with status " ~ (cast(Object)approval).toString); //$NON-NLS-1$
+                }
+                return approval;
+            }
+        }
+        return Status.OK_STATUS;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#getUndoHistory(org.eclipse.core.commands.operations.IUndoContext)
+     */
+    public IUndoableOperation[] getUndoHistory(IUndoContext context) {
+        Assert.isNotNull(cast(Object)context);
+        return filter(undoList, context);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#getUndoOperation(org.eclipse.core.commands.operations.IUndoContext)
+     */
+    public IUndoableOperation getUndoOperation(IUndoContext context) {
+        Assert.isNotNull(cast(Object)context);
+        synchronized (undoRedoHistoryLock) {
+            for (int i = undoList.size() - 1; i >= 0; i--) {
+                IUndoableOperation operation = cast(IUndoableOperation) undoList
+                        .get(i);
+                if (operation.hasContext(context)) {
+                    return operation;
+                }
+            }
+        }
+        return null;
+    }
+
+    /*
+     * Consult the IOperationApprovers to see if the proposed execution should
+     * be allowed.
+     *
+     * @since 3.2
+     */
+    private IStatus getExecuteApproval(IUndoableOperation operation,
+            IAdaptable info) {
+
+        final Object[] approverArray = approvers.getListeners();
+
+        for (int i = 0; i < approverArray.length; i++) {
+            if ( auto apro = cast(IOperationApprover2)approverArray[i]  ) {
+                IOperationApprover2 approver = apro;
+                IStatus approval = approver.proceedExecuting(operation, this,
+                        info);
+                if (!approval.isOK()) {
+                    if (DEBUG_OPERATION_HISTORY_APPROVAL) {
+                        Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                                "Execute not approved by " ~ (cast(Object)approver).toString //$NON-NLS-1$
+                                        ~ "for operation " ~ (cast(Object)operation).toString //$NON-NLS-1$
+                                        ~ " with status " ~ (cast(Object)approval).toString); //$NON-NLS-1$
+                    }
+                    return approval;
+                }
+            }
+        }
+        return Status.OK_STATUS;
+    }
+
+    /*
+     * Remove the operation by disposing it and notifying listeners.
+     */
+    private void internalRemove(IUndoableOperation operation) {
+        operation.dispose();
+        notifyRemoved(operation);
+    }
+
+    /*
+     * Notify listeners of an operation event.
+     */
+    private void notifyListeners(OperationHistoryEvent event) {
+        if ( auto e = cast(IAdvancedUndoableOperation)event.getOperation()  ) {
+            IAdvancedUndoableOperation advancedOp = e;
+            SafeRunner.run(new class(advancedOp) ISafeRunnable {
+                IAdvancedUndoableOperation advancedOp_;
+                this(IAdvancedUndoableOperation a){ advancedOp_=a;}
+                public void handleException(Exception exception) {
+                    if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                        Tracing.printTrace(
+                                        "OPERATIONHISTORY", //$NON-NLS-1$
+                                        "Exception during notification callback " ~ exception.toString); //$NON-NLS-1$
+                    }
+                }
+
+                public void run() {
+                    advancedOp_.aboutToNotify(event);
+                }
+            });
+        }
+        Object[] listenerArray = listeners.getListeners();
+        for (int i = 0; i < listenerArray.length; i++) {
+            IOperationHistoryListener listener = cast(IOperationHistoryListener) listenerArray[i];
+            SafeRunner.run(new class(listener) ISafeRunnable {
+                IOperationHistoryListener listener_;
+                this(IOperationHistoryListener a){ listener_=a; }
+                public void handleException(Exception exception) {
+                    if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                        Tracing.printTrace(
+                                        "OPERATIONHISTORY", //$NON-NLS-1$
+                                        "Exception during notification callback " ~ exception.toString); //$NON-NLS-1$
+                    }
+                }
+
+                public void run() {
+                    listener_.historyNotification(event);
+                }
+            });
+        }
+    }
+
+    private void notifyAboutToExecute(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "ABOUT_TO_EXECUTE " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)operation).toString);
+        }
+
+        notifyListeners(new OperationHistoryEvent(
+                OperationHistoryEvent.ABOUT_TO_EXECUTE, this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation is about to redo.
+     */
+    private void notifyAboutToRedo(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "ABOUT_TO_REDO " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)operation).toString);
+        }
+
+        notifyListeners(new OperationHistoryEvent(
+                OperationHistoryEvent.ABOUT_TO_REDO, this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation is about to undo.
+     */
+    private void notifyAboutToUndo(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "ABOUT_TO_UNDO " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)operation).toString);
+        }
+
+        notifyListeners(new OperationHistoryEvent(
+                OperationHistoryEvent.ABOUT_TO_UNDO, this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation has been added.
+     */
+    private void notifyAdd(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "OPERATION_ADDED " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)operation).toString);
+        }
+
+        notifyListeners(new OperationHistoryEvent(
+                OperationHistoryEvent.OPERATION_ADDED, this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation is done executing.
+     */
+    private void notifyDone(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "DONE " ~ (cast(Object)operation).toString); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        notifyListeners(new OperationHistoryEvent(OperationHistoryEvent.DONE,
+                this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation did not succeed after an attempt to
+     * execute, undo, or redo was made.
+     */
+    private void notifyNotOK(IUndoableOperation operation) {
+        notifyNotOK(operation, null);
+    }
+
+    /*
+     * Notify listeners that an operation did not succeed after an attempt to
+     * execute, undo, or redo was made. Include the status associated with the
+     * attempt.
+     *
+     * @since 3.2
+     */
+    private void notifyNotOK(IUndoableOperation operation, IStatus status) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "OPERATION_NOT_OK " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)operation).toString);
+        }
+
+        notifyListeners(new OperationHistoryEvent(
+                OperationHistoryEvent.OPERATION_NOT_OK, this, operation, status));
+    }
+
+    /*
+     * Notify listeners that an operation was redone.
+     */
+    private void notifyRedone(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "REDONE " ~ (cast(Object)operation).toString); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        notifyListeners(new OperationHistoryEvent(OperationHistoryEvent.REDONE,
+                this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation has been removed from the history.
+     */
+    private void notifyRemoved(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "OPERATION_REMOVED " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)operation).toString);
+        }
+
+        notifyListeners(new OperationHistoryEvent(
+                OperationHistoryEvent.OPERATION_REMOVED, this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation has been undone.
+     */
+    private void notifyUndone(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "UNDONE " ~ (cast(Object)operation).toString); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        notifyListeners(new OperationHistoryEvent(OperationHistoryEvent.UNDONE,
+                this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation has been undone.
+     */
+    private void notifyChanged(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "OPERATION_CHANGED " //$NON-NLS-1$//$NON-NLS-2$
+                    ~ (cast(Object)operation).toString);
+        }
+
+        notifyListeners(new OperationHistoryEvent(
+                OperationHistoryEvent.OPERATION_CHANGED, this, operation));
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#redo(org.eclipse.core.commands.operations.IUndoContext,
+     *      org.eclipse.core.runtime.IProgressMonitor,
+     *      org.eclipse.core.runtime.IAdaptable)
+     */
+    public IStatus redo(IUndoContext context, IProgressMonitor monitor,
+            IAdaptable info) {
+        Assert.isNotNull(cast(Object)context);
+        IUndoableOperation operation = getRedoOperation(context);
+
+        // info if there is no operation
+        if (operation is null) {
+            return IOperationHistory.NOTHING_TO_REDO_STATUS;
+        }
+
+        // error if operation is invalid
+        if (!operation.canRedo()) {
+            if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                        "Redo operation not valid - " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+            }
+
+            return IOperationHistory.OPERATION_INVALID_STATUS;
+        }
+
+        return doRedo(monitor, info, operation);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#redoOperation(org.eclipse.core.commands.operations.IUndoableOperation,
+     *      org.eclipse.core.runtime.IProgressMonitor,
+     *      org.eclipse.core.runtime.IAdaptable)
+     */
+
+    public IStatus redoOperation(IUndoableOperation operation,
+            IProgressMonitor monitor, IAdaptable info) {
+        Assert.isNotNull(cast(Object)operation);
+        IStatus status;
+        if (operation.canRedo()) {
+            status = doRedo(monitor, info, operation);
+        } else {
+            if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                        "Redo operation not valid - " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+            }
+            status = IOperationHistory.OPERATION_INVALID_STATUS;
+        }
+        return status;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#removeOperationApprover(org.eclipse.core.commands.operations.IOperationApprover)
+     */
+    public void removeOperationApprover(IOperationApprover approver) {
+        approvers.remove(cast(Object)approver);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#removeOperationHistoryListener(org.eclipse.core.commands.operations.IOperationHistoryListener)
+     */
+    public void removeOperationHistoryListener(
+            IOperationHistoryListener listener) {
+        listeners.remove(cast(Object)listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#replaceOperation(org.eclipse.core.commands.operations.IUndoableOperation,
+     *      org.eclipse.core.commands.operations.IUndoableOperation [])
+     */
+    public void replaceOperation(IUndoableOperation operation,
+            IUndoableOperation[] replacements) {
+        // check the undo history first.
+        bool inUndo = false;
+        synchronized (undoRedoHistoryLock) {
+            int index = undoList.indexOf(cast(Object)operation);
+            if (index > -1) {
+                inUndo = true;
+                undoList.remove(cast(Object)operation);
+                // notify listeners after the lock on undoList is released
+                ArrayList allContexts = new ArrayList(replacements.length);
+                for (int i = 0; i < replacements.length; i++) {
+                    IUndoContext[] opContexts = replacements[i].getContexts();
+                    for (int j = 0; j < opContexts.length; j++) {
+                        allContexts.add(cast(Object)opContexts[j]);
+                    }
+                    undoList.add(index, cast(Object)replacements[i]);
+                    // notify listeners after the lock on the history is
+                    // released
+                }
+                // recheck all the limits. We do this at the end so the index
+                // doesn't change during replacement
+                for (int i = 0; i < allContexts.size(); i++) {
+                    IUndoContext context = cast(IUndoContext) allContexts.get(i);
+                    forceUndoLimit(context, getLimit(context));
+                }
+            }
+        }
+        if (inUndo) {
+            // notify listeners of operations added and removed
+            internalRemove(operation);
+            for (int i = 0; i < replacements.length; i++) {
+                notifyAdd(replacements[i]);
+            }
+            return;
+        }
+
+        // operation was not in the undo history. Check the redo history.
+
+        synchronized (undoRedoHistoryLock) {
+            int index = redoList.indexOf(cast(Object)operation);
+            if (index is -1) {
+                return;
+            }
+            ArrayList allContexts = new ArrayList(replacements.length);
+            redoList.remove(cast(Object)operation);
+            // notify listeners after we release the lock on redoList
+            for (int i = 0; i < replacements.length; i++) {
+                IUndoContext[] opContexts = replacements[i].getContexts();
+                for (int j = 0; j < opContexts.length; j++) {
+                    allContexts.add(cast(Object)opContexts[j]);
+                }
+                redoList.add(index, cast(Object)replacements[i]);
+                // notify listeners after we release the lock on redoList
+            }
+            // recheck all the limits. We do this at the end so the index
+            // doesn't change during replacement
+            for (int i = 0; i < allContexts.size(); i++) {
+                IUndoContext context = cast(IUndoContext) allContexts.get(i);
+                forceRedoLimit(context, getLimit(context));
+            }
+        }
+        // send listener notifications after we release the lock on the history
+        internalRemove(operation);
+        for (int i = 0; i < replacements.length; i++) {
+            notifyAdd(replacements[i]);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#setLimit(org.eclipse.core.commands.operations.IUndoContext,
+     *      int)
+     */
+    public void setLimit(IUndoContext context, int limit) {
+        Assert.isTrue(limit >= 0);
+        /*
+         * The limit checking methods interpret a null context as a global limit
+         * to be enforced. We do not wish to support a global limit in this
+         * implementation, so we throw an exception for a null context. The rest
+         * of the implementation can handle a null context, so subclasses can
+         * override this if a global limit is desired.
+         */
+        Assert.isNotNull(cast(Object)context);
+        limits.put(cast(Object)context, new Integer(limit));
+        forceUndoLimit(context, limit);
+        forceRedoLimit(context, limit);
+
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#undo(org.eclipse.core.commands.operations.IUndoContext,
+     *      org.eclipse.core.runtime.IProgressMonitor,
+     *      org.eclipse.core.runtime.IAdaptable)
+     */
+    public IStatus undo(IUndoContext context, IProgressMonitor monitor,
+            IAdaptable info) {
+        Assert.isNotNull(cast(Object)context);
+        IUndoableOperation operation = getUndoOperation(context);
+
+        // info if there is no operation
+        if (operation is null) {
+            return IOperationHistory.NOTHING_TO_UNDO_STATUS;
+        }
+
+        // error if operation is invalid
+        if (!operation.canUndo()) {
+            if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                        "Undo operation not valid - " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+            }
+            return IOperationHistory.OPERATION_INVALID_STATUS;
+        }
+
+        return doUndo(monitor, info, operation);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#undoOperation(org.eclipse.core.commands.operations.IUndoableOperation,
+     *      org.eclipse.core.runtime.IProgressMonitor,
+     *      org.eclipse.core.runtime.IAdaptable)
+     */
+    public IStatus undoOperation(IUndoableOperation operation,
+            IProgressMonitor monitor, IAdaptable info) {
+        Assert.isNotNull(cast(Object)operation);
+        IStatus status;
+        if (operation.canUndo()) {
+            status = doUndo(monitor, info, operation);
+        } else {
+            if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                        "Undo operation not valid - " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+            }
+            status = IOperationHistory.OPERATION_INVALID_STATUS;
+        }
+        return status;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#openOperation(org.eclipse.core.commands.operations.ICompositeOperation)
+     */
+    public void openOperation(ICompositeOperation operation, int mode) {
+        synchronized (openCompositeLock) {
+            if (openComposite !is null && openComposite !is operation) {
+                // unexpected nesting of operations.
+                if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "Open operation called while another operation is open.  old: " //$NON-NLS-1$
+                                    ~ (cast(Object)openComposite).toString ~ "; new:  " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+                }
+
+                throw new IllegalStateException(
+                        "Cannot open an operation while one is already open"); //$NON-NLS-1$
+            }
+            openComposite = operation;
+        }
+        if (DEBUG_OPERATION_HISTORY_OPENOPERATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "Opening operation " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)openComposite).toString);
+        }
+
+        if (mode is EXECUTE) {
+            notifyAboutToExecute(openComposite);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#closeOperation(bool,
+     *      bool)
+     */
+    public void closeOperation(bool operationOK, bool addToHistory,
+            int mode) {
+        ICompositeOperation endedComposite = null;
+
+        synchronized (openCompositeLock) {
+            if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                if (openComposite is null) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "Attempted to close operation when none was open"); //$NON-NLS-1$
+                    return;
+                }
+            }
+            // notifications will occur outside the synchonized block
+            if (openComposite !is null) {
+                if (DEBUG_OPERATION_HISTORY_OPENOPERATION) {
+                    Tracing.printTrace("OPERATIONHISTORY", "Closing operation " //$NON-NLS-1$ //$NON-NLS-2$
+                            ~ (cast(Object)openComposite).toString);
+                }
+                endedComposite = openComposite;
+                openComposite = null;
+            }
+        }
+        // any mode other than EXECUTE was triggered by a request to undo or
+        // redo something already in the history, so undo and redo
+        // notification will occur at the end of that sequence.
+        if (endedComposite !is null) {
+            if (operationOK) {
+                if (mode is EXECUTE) {
+                    notifyDone(endedComposite);
+                }
+                if (addToHistory) {
+                    add(endedComposite);
+                }
+            } else {
+                if (mode is EXECUTE) {
+                    notifyNotOK(endedComposite);
+                }
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistory#operationChanged(org.eclipse.core.commands.operations.IUndoableOperation)
+     */
+    public void operationChanged(IUndoableOperation operation) {
+        if (undoList.contains(cast(Object)operation) || redoList.contains(cast(Object)operation)) {
+            notifyChanged(operation);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IAdvancedUndoableOperation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.IAdvancedUndoableOperation;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+
+import org.eclipse.core.commands.operations.OperationHistoryEvent;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * IAdvancedUndoableOperation defines an interface for undoable operations that
+ * modify one or more elements in a model and attempt to keep model listeners up
+ * to date with changes that occur in the undo and redo history involving particular
+ * model elements.  It also defines methods for computing the validity of an operation
+ * for undo or redo before attempting to perform the undo or redo.
+ * </p>
+ * <p>
+ * This interface is intended to be used by legacy frameworks that are adapting
+ * their original undo and redo support to this framework. The methods in this
+ * interface allow legacy clients to maintain features not supported in the
+ * basic operations framework.
+ * </p>
+ *
+ * @since 3.1
+ *
+ */
+public interface IAdvancedUndoableOperation {
+
+    /**
+     * <p>
+     * An operation history notification about this operation is about to be
+     * sent to operation history listeners. Any preparation needed before
+     * listeners are notified about this operation should be performed here.
+     *
+     * <p>
+     * This method has been added to support legacy undo frameworks that are
+     * adapting to IUndoableOperation. Operations that previously relied on
+     * notification from their containing history or stack before any listeners
+     * are notified about changes to the operation should implement this
+     * interface.
+     *
+     * @param event
+     *            the event that is about to be sent with the pending
+     *            notification
+     *
+     */
+    void aboutToNotify(OperationHistoryEvent event);
+
+    /**
+     * <p>
+     * Return an array of objects that are affected by executing, undoing, or
+     * redoing this operation. If it cannot be determined which objects are
+     * affected, return null.
+     * </p>
+     *
+     * @return the array of Objects modified by this operation, or
+     *         <code>null</code> if the affected objects cannot be determined.
+     */
+    Object[] getAffectedObjects();
+
+    /**
+     * Return a status indicating the projected outcome of undoing the receiver.
+     *
+     * This method should be used to report the possible outcome of an undo and
+     * is used when computing the validity of an undo is too expensive to
+     * perform in {@link IUndoableOperation#canUndo()}. It is not called by the
+     * operation history, but instead is used by clients (such as implementers
+     * of {@link IOperationApprover}) who wish to perform advanced validation of
+     * an operation before attempting to undo it.
+     *
+     * If the result of this method is the discovery that an operation can in
+     * fact not be undone, then the operation is expected to correctly answer
+     * <code>false</code> on subsequent calls to
+     * {@link IUndoableOperation#canUndo()}.
+     *
+     * @param monitor
+     *            the progress monitor (or <code>null</code>) to use for
+     *            reporting progress to the user while computing the validity.
+     *
+     * @return the IStatus indicating the validity of the undo. The status
+     *         severity should be set to <code>OK</code> if the undo can
+     *         successfully be performed, and <code>ERROR</code> if it
+     *         cannnot. Any other status is assumed to represent an ambiguous
+     *         state.
+     * @throws ExecutionException
+     *             if an exception occurs while computing the validity.
+     */
+    IStatus computeUndoableStatus(IProgressMonitor monitor);
+
+    /**
+     * Return a status indicating the projected outcome of redoing the receiver.
+     *
+     * This method should be used to report the possible outcome of a redo and
+     * is used when computing the validity of a redo is too expensive to perform
+     * in {@link IUndoableOperation#canRedo()}. It is not called by the
+     * operation history, but instead is used by clients (such as implementers
+     * of {@link IOperationApprover}) who wish to perform advanced validation of
+     * an operation before attempting to redo it.
+     *
+     * If the result of this method is the discovery that an operation can in
+     * fact not be redone, then the operation is expected to correctly answer
+     * <code>false</code> on subsequent calls to
+     * {@link IUndoableOperation#canRedo()}.
+     *
+     * @param monitor
+     *            the progress monitor (or <code>null</code>) to use for
+     *            reporting progress to the user while computing the validity.
+     *
+     * @return the IStatus indicating the validity of the redo. The status
+     *         severity should be set to <code>OK</code> if the redo can
+     *         successfully be performed, and <code>ERROR</code> if it
+     *         cannnot. Any other status is assumed to represent an ambiguous
+     *         state.
+     * @throws ExecutionException
+     *             if an exception occurs while computing the validity.
+     */
+    IStatus computeRedoableStatus(IProgressMonitor monitor);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IAdvancedUndoableOperation2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.core.commands.operations.IAdvancedUndoableOperation2;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * IAdvancedUndoableOperation2 defines a method for computing the validity of
+ * executing an operation before attempting to execute it. It also defines a way
+ * for clients to specify that computing status should be done quietly, without
+ * consulting the user. This interface is useful when implementing
+ * {@link IOperationApprover2}, or any other object that performs validation of
+ * the undo history. It also allows operations to specify whether they should be
+ * run in the UI thread.
+ * </p>
+ *
+ * @since 3.3
+ *
+ */
+public interface IAdvancedUndoableOperation2 {
+    /**
+     * Return a status indicating the projected outcome of executing the
+     * receiver.
+     *
+     * This method should be used to report the possible outcome of executing an
+     * operation when computing the validity of an execute is too expensive to
+     * perform in {@link IUndoableOperation#canExecute()}. It is not called by
+     * the operation history, but instead is used by clients (such as
+     * implementers of {@link IOperationApprover2}) who wish to perform
+     * advanced validation of an operation before attempting to execute it.
+     *
+     * If the result of this method is the discovery that an operation can in
+     * fact not be executed, then the operation is expected to correctly answer
+     * <code>false</code> on subsequent calls to
+     * {@link IUndoableOperation#canExecute()}.
+     *
+     * @param monitor
+     *            the progress monitor (or <code>null</code>) to use for
+     *            reporting progress to the user while computing the validity.
+     *
+     * @return the IStatus indicating the validity of the execute. The status
+     *         severity should be set to <code>OK</code> if the execute can
+     *         successfully be performed, and <code>ERROR</code> if it cannot.
+     *         Any other severity is assumed to represent an ambiguous state.
+     * @throws ExecutionException
+     *             if an exception occurs while computing the validity.
+     */
+    IStatus computeExecutionStatus(IProgressMonitor monitor);
+
+    /**
+     * Set a bool that instructs whether the computation of the receiver's
+     * execution, undo, or redo status should quietly compute status without
+     * consulting or prompting the user. The default value is <code>false</code>.
+     * This flag should only be set to <code>true</code> while the execution,
+     * undo, or redo status computations are being performed in the background,
+     * and should be restored to <code>false</code> when complete.
+     * <p>
+     * If the status computation methods typically need to consult the user in
+     * order to determine the severity of a particular situation, the least
+     * severe status that could be chosen by the user should be returned when
+     * this flag is <code>true</code>. This can help to prevent overzealous
+     * disposal of the operation history when an operation is in an ambiguous
+     * state. Typically, the status computation methods are invoked with this
+     * flag set to <code>false</code> just before the actual execution, undo,
+     * or redo occurs, so the user can be consulted for the final outcome.
+     *
+     * @param quiet
+     *            <code>true</code> if it is inappropriate to consult or
+     *            otherwise prompt the user while computing status, and
+     *            <code>false</code> if the user may be prompted.
+     *
+     * @see #computeExecutionStatus(IProgressMonitor)
+     * @see IAdvancedUndoableOperation#computeUndoableStatus(IProgressMonitor)
+     * @see IAdvancedUndoableOperation#computeRedoableStatus(IProgressMonitor)
+     */
+    public void setQuietCompute(bool quiet);
+
+    /**
+     * Return a bool that instructs whether the operation should be executed,
+     * undone, or redone in a background thread.
+     *
+     * @return <code>true</code> if the operation should be run in the
+     *         background, <code>false</code> if it should not.
+     */
+    public bool runInBackground();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/ICompositeOperation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.ICompositeOperation;
+
+import org.eclipse.core.commands.operations.IUndoableOperation;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * ICompositeOperation defines an undoable operation that is composed of child
+ * operations. Requests to execute, undo, or redo a composite result in the the
+ * execution, undo, or redo of the composite as a whole. Similarly, a request to
+ * dispose the composite should result in all child operations being disposed.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface ICompositeOperation : IUndoableOperation {
+
+    /**
+     * <p>
+     * Add the specified operation as a child of this operation.
+     * </p>
+     *
+     * @param operation
+     *            the operation to be added. If the operation instance has
+     *            already been added, this method will have no effect.
+     */
+    void add(IUndoableOperation operation);
+
+    /**
+     * <p>
+     * Remove the specified operation from this operation.
+     * </p>
+     * <p>
+     * The composite operation should dispose the operation as part of removing
+     * it.
+     * </p>
+     *
+     * @param operation
+     *            the operation to be removed. The operation should be disposed
+     *            by the receiver. This method will have no effect if the
+     *            operation instance is not already a child.
+     */
+    void remove(IUndoableOperation operation);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IContextReplacingOperation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.IContextReplacingOperation;
+
+import org.eclipse.core.commands.operations.IUndoContext;
+
+import java.lang.all;
+
+/**
+ * IContextReplacingOperation defines an interface for undoable operations that
+ * can replace one undo context with another undo context. It is used by
+ * operations, such as composite operations, where removing and adding an undo
+ * context would not have the same semantic as replacing one undo context with
+ * another.
+ *
+ * @since 3.2
+ *
+ */
+public interface IContextReplacingOperation {
+
+    /**
+     * Replace the undo context of the receiver with the provided replacement
+     * undo context.
+     * <p>
+     * This message has no effect if the original undo context is not present in
+     * the receiver.
+     *
+     * @param original the undo context which is to be replaced
+     * @param replacement the undo context which is replacing the original
+     *
+     */
+    void replaceContext(IUndoContext original, IUndoContext replacement);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IOperationApprover.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.IOperationApprover;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IStatus;
+
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.commands.operations.IOperationHistory;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * IOperationApprover defines an interface for approving the undo or redo of a
+ * particular operation within an operation history. Operations that are
+ * candidates for undo or redo have already been validated against their current
+ * state and according to the rules of the history.
+ * </p>
+ * <p>
+ * By the time an IOperationApprover is consulted, the undo has already been
+ * requested. Approvers should return an <code>IStatus</code> object with
+ * severity <code>OK</code> if the operation should proceed, and any other
+ * severity if it should not. When an operation is not approved, it is expected
+ * that the object not allowing the operation has already consulted the user if
+ * necessary or otherwise provided any necessary information to the user about
+ * the fact that the operation is not approved.
+ * </p>
+ * <p>
+ * Operation approvers must be prepared to receive the approval messages from a
+ * background thread. Any UI access occurring inside the implementation must be
+ * properly synchronized using the techniques specified by the client's widget
+ * library.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IOperationApprover {
+
+    /**
+     * Return a status indicating whether the specified operation should be
+     * redone. Any status that does not have severity <code>IStatus.OK</code>
+     * will not be approved. Implementers should not assume that the redo will
+     * be performed when the status is <code>OK</code>, since other operation
+     * approvers may veto the redo.
+     *
+     * @param operation
+     *            the operation to be redone
+     * @param history
+     *            the history redoing the operation
+     * @param info
+     *            the IAdaptable (or <code>null</code>) provided by the
+     *            caller in order to supply UI information for prompting the
+     *            user if necessary. When this parameter is not
+     *            <code>null</code>, it should minimally contain an adapter
+     *            for the org.eclipse.swt.widgets.Shell.class. Even if UI
+     *            information is provided, the implementation of this method
+     *            must be prepared for being called from a background thread.
+     *            Any UI access must be properly synchronized using the
+     *            techniques specified by the client's widget library.
+     * @return the IStatus describing whether the operation is approved. The
+     *         redo will not proceed if the status severity is not
+     *         <code>OK</code>, and the caller requesting the redo will be
+     *         returned the status that caused the rejection. Any other status
+     *         severities will not be interpreted by the history.
+     */
+    IStatus proceedRedoing(IUndoableOperation operation,
+            IOperationHistory history, IAdaptable info);
+
+    /**
+     * Return a status indicating whether the specified operation should be
+     * undone. Any status that does not have severity <code>IStatus.OK</code>
+     * will not be approved. Implementers should not assume that the undo will
+     * be performed when the status is <code>OK</code>, since other operation
+     * approvers can veto the undo.
+     *
+     * @param operation
+     *            the operation to be undone
+     * @param history
+     *            the history undoing the operation
+     * @param info
+     *            the IAdaptable (or <code>null</code>) provided by the
+     *            caller in order to supply UI information for prompting the
+     *            user if necessary. When this parameter is not
+     *            <code>null</code>, it should minimally contain an adapter
+     *            for the org.eclipse.swt.widgets.Shell.class. Even if UI
+     *            information is provided, the implementation of this method
+     *            must be prepared for being called from a background thread.
+     *            Any UI access must be properly synchronized using the
+     *            techniques specified by the client's widget library.
+     * @return the IStatus describing whether the operation is approved. The
+     *         undo will not proceed if the status severity is not
+     *         <code>OK</code>, and the caller requesting the undo will be
+     *         returned the status that caused the rejection. Any other status
+     *         severities will not be interpreted by the history.
+     */
+    IStatus proceedUndoing(IUndoableOperation operation,
+            IOperationHistory history, IAdaptable info);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IOperationApprover2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.core.commands.operations.IOperationApprover2;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IStatus;
+
+import org.eclipse.core.commands.operations.IOperationApprover;
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.commands.operations.IOperationHistory;
+
+import java.lang.all;
+
+/**
+ * Extends {@link IOperationApprover} to approve the execution of a particular
+ * operation within an operation history. Operations that are candidates for
+ * execution have already been validated against their current state and
+ * according to the rules of the history. Prior to 3.2, an operation approver
+ * was only consulted for undo and redo of an operation, not its initial
+ * execution.
+ * <p>
+ * By the time an IOperationApprover2 is consulted, the execution has already
+ * been requested and it has been determined that the operation is valid.
+ * Approvers should return an <code>IStatus</code> object with severity
+ * <code>OK</code> if the operation should proceed, and any other severity if
+ * it should not. When an operation is not approved, it is expected that the
+ * object not allowing the operation has already consulted the user if necessary
+ * or otherwise provided any necessary information to the user about the fact
+ * that the operation is not approved.
+ * </p>
+ * <p>
+ * Like {@link IOperationApprover}, implementers of this extension must be
+ * prepared to receive the approval messages from a background thread. Any UI
+ * access occurring inside the implementation must be properly synchronized
+ * using the techniques specified by the client's widget library.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface IOperationApprover2 : IOperationApprover {
+    /**
+     * Return a status indicating whether the specified operation should be
+     * executed. Any status that does not have severity <code>IStatus.OK</code>
+     * will not be approved. Implementers should not assume that the execution
+     * will be performed when the status is <code>OK</code>, since other
+     * operation approvers may veto the execution.
+     *
+     * @param operation
+     *            the operation to be executed
+     * @param history
+     *            the history performing the execution of the operation
+     * @param info
+     *            the IAdaptable (or <code>null</code>) provided by the
+     *            caller in order to supply UI information for prompting the
+     *            user if necessary. When this parameter is not
+     *            <code>null</code>, it should minimally contain an adapter
+     *            for the org.eclipse.swt.widgets.Shell.class. Even if UI
+     *            information is provided, the implementation of this method
+     *            must be prepared for being called from a background thread.
+     *            Any UI access must be properly synchronized using the
+     *            techniques specified by the client's widget library.
+     * @return the IStatus describing whether the operation is approved. The
+     *         execution will not proceed if the status severity is not
+     *         <code>OK</code>, and the caller requesting the execution will
+     *         be returned the status that caused the rejection. Any other
+     *         status severities will not be interpreted by the history.
+     */
+    IStatus proceedExecuting(IUndoableOperation operation,
+            IOperationHistory history, IAdaptable info);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IOperationHistory.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,712 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.IOperationHistory;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.internal.commands.operations.GlobalUndoContext;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+
+import org.eclipse.core.commands.operations.IUndoContext;
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.commands.operations.IOperationApprover;
+import org.eclipse.core.commands.operations.IOperationApprover2;
+import org.eclipse.core.commands.operations.IOperationHistoryListener;
+import org.eclipse.core.commands.operations.ICompositeOperation;
+import org.eclipse.core.commands.operations.OperationStatus;
+
+import java.lang.all;
+
+static this(){
+    IOperationHistory.GLOBAL_UNDO_CONTEXT = new GlobalUndoContext();
+    IOperationHistory.NOTHING_TO_REDO_STATUS = new OperationStatus(
+        IStatus.INFO, OperationStatus.DEFAULT_PLUGIN_ID,
+        OperationStatus.NOTHING_TO_REDO, "No operation to redo", null); //$NON-NLS-1$
+    IOperationHistory.NOTHING_TO_UNDO_STATUS = new OperationStatus(
+        IStatus.INFO, OperationStatus.DEFAULT_PLUGIN_ID,
+        OperationStatus.NOTHING_TO_UNDO, "No operation to undo", null); //$NON-NLS-1$
+    IOperationHistory.OPERATION_INVALID_STATUS = new OperationStatus(
+        IStatus.ERROR, OperationStatus.DEFAULT_PLUGIN_ID,
+        OperationStatus.OPERATION_INVALID, "Operation is not valid", null); //$NON-NLS-1$
+}
+
+/**
+ * <p>
+ * IOperationHistory tracks a history of operations that can be undone or
+ * redone. Operations are added to the history once they have been initially
+ * executed. Clients may choose whether to have the operations history perform
+ * the initial execution or to simply add an already-executed operation to the
+ * history.
+ * </p>
+ * <p>
+ * Once operations are added to the history, the methods
+ * {@link #canRedo(IUndoContext)} and {@link #canUndo(IUndoContext)} are used to
+ * determine whether there is an operation available for undo and redo in a
+ * given undo context. The context-based protocol implies that there is only one
+ * operation that can be undone or redone at a given time in a given context.
+ * This is typical of a linear undo model, when only the most recently executed
+ * operation is available for undo. When this protocol is used, a linear model
+ * is enforced by the history.
+ * </p>
+ * <p>
+ * It is up to clients to determine how to maintain a history that is invalid or
+ * stale. For example, when the most recent operation for a context cannot be
+ * performed, clients may wish to dispose the history for that context.
+ * </p>
+ * <p>
+ * Additional protocol allows direct undo and redo of a specified operation,
+ * regardless of its position in the history. When a more flexible undo model is
+ * supported, these methods can be implemented to undo and redo directly
+ * specified operations. If an implementer of IOperationHistory does not allow
+ * direct undo and redo, these methods can return a status indicating that it is
+ * not allowed.
+ * </p>
+ * <p>
+ * Listeners ({@link IOperationHistoryListener}) can listen for notifications
+ * about changes in the history (operations added or removed), and for
+ * notification before and after any operation is executed, undone or redone.
+ * Notification of operation execution only occurs when clients direct the
+ * history to execute the operation. If the operation is added after it is
+ * executed, there can be no notification of its execution.
+ * </p>
+ * <p>
+ * {@link IOperationApprover} defines an interface for approving an undo or redo
+ * before it occurs. This is useful for injecting policy-decisions into the undo
+ * model - whether direct undo and redo are supported, or warning the user about
+ * certain kinds of operations. It can also be used when clients maintain state
+ * related to an operation and need to determine whether an undo or redo will
+ * cause any conflicts with their local state.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IOperationHistory {
+
+    /**
+     * An operation is to be opened or closed for execution. (value is 1).
+     */
+    public static const int EXECUTE = 1;
+
+    /**
+     * An operation is to be opened for undo. (value is 2).
+     */
+    public static const int UNDO = 2;
+
+    /**
+     * An operation is to be opened for redo. (value is 3).
+     */
+    public static const int REDO = 3;
+
+    /**
+     * An undo context that can be used to refer to the global undo history.
+     * This context is not intended to be assigned to operations. Instead, it is
+     * used for querying the history or performing an undo or redo on the entire
+     * history, regardless of each operation's undo contexts.
+     */
+    public static /+const+/ IUndoContext GLOBAL_UNDO_CONTEXT;
+
+    /**
+     * An operation info status describing the condition that there is no
+     * available operation for redo.
+     */
+    public static /+const+/ IStatus NOTHING_TO_REDO_STATUS; //$NON-NLS-1$
+
+    /**
+     * An operation info status describing the condition that there is no
+     * available operation for undo.
+     */
+    public static /+const+/ IStatus NOTHING_TO_UNDO_STATUS; //$NON-NLS-1$
+
+    /**
+     * An operation error status describing the condition that the operation
+     * available for execution, undo or redo is not in a valid state for the
+     * action to be performed.
+     */
+    public static /+const+/ IStatus OPERATION_INVALID_STATUS; //$NON-NLS-1$
+
+    /**
+     * <p>
+     * Add the specified operation to the history without executing it. The
+     * operation should have already been executed by the time it is added to
+     * the history. Listeners will be notified that the operation was added to
+     * the history (<code>OPERATION_ADDED</code>).
+     * </p>
+     *
+     * @param operation
+     *            the operation to be added to the history
+     */
+    void add(IUndoableOperation operation);
+
+    /**
+     * <p>
+     * Add the specified approver to the list of operation approvers consulted
+     * by the operation history before an undo or redo is attempted.
+     * </p>
+     *
+     * @param approver
+     *            the IOperationApprover to be added as an approver.the instance
+     *            to remove. Must not be <code>null</code>. If an attempt is
+     *            made to register an instance which is already registered with
+     *            this instance, this method has no effect.
+     *
+     * @see org.eclipse.core.commands.operations.IOperationApprover
+     */
+    void addOperationApprover(IOperationApprover approver);
+
+    /**
+     * <p>
+     * Add the specified listener to the list of operation history listeners
+     * that are notified about changes in the history or operations that are
+     * executed, undone, or redone.
+     * </p>
+     *
+     * @param listener
+     *            the IOperationHistoryListener to be added as a listener. Must
+     *            not be <code>null</code>. If an attempt is made to register
+     *            an instance which is already registered with this instance,
+     *            this method has no effect.
+     *
+     * @see org.eclipse.core.commands.operations.IOperationHistoryListener
+     * @see org.eclipse.core.commands.operations.OperationHistoryEvent
+     */
+    void addOperationHistoryListener(IOperationHistoryListener listener);
+
+    /**
+     * <p>
+     * Close the current operation. If the operation has successfully completed,
+     * send listeners a <code>DONE</code>, <code>UNDONE</code>, or
+     * <code>REDONE</code> notification, depending on the mode. Otherwise send
+     * an <code>OPERATION_NOT_OK</code> notification. Add the operation to the
+     * history if specified and send an <code>OPERATION_ADDED</code>
+     * notification.
+     * </p>
+     * <p>
+     * Any operations that are executed and added after this operation is closed
+     * will no longer be considered part of this operation.
+     * </p>
+     * <p>
+     * This method has no effect if the caller has not previously called
+     * {@link #openOperation}.
+     * </p>
+     *
+     * @param operationOK
+     *            <code>true</code> if the operation successfully completed.
+     *            Listeners should be notified with <code>DONE</code>,
+     *            <code>UNDONE</code>, or <code>REDONE</code>.
+     *            <code>false</code> if the operation did not successfully
+     *            complete. Listeners should be notified with
+     *            <code>OPERATION_NOT_OK</code>.
+     * @param addToHistory
+     *            <code>true</code> if the operation should be added to the
+     *            history, <code>false</code> if it should not. If the
+     *            <code>operationOK</code> parameter is <code>false</code>,
+     *            the operation will never be added to the history.
+     * @param mode
+     *            the mode the operation was opened in. Can be one of
+     *            <code>EXECUTE</code>, <code>UNDO</code>, or
+     *            <code>REDO</code>. This determines what notifications are
+     *            sent.
+     */
+    void closeOperation(bool operationOK, bool addToHistory, int mode);
+
+    /**
+     * <p>
+     * Return whether there is a valid redoable operation available in the given
+     * context.
+     * </p>
+     *
+     * @param context
+     *            the context to be checked
+     * @return <code>true</code> if there is a redoable operation,
+     *         <code>false</code> otherwise.
+     */
+
+    bool canRedo(IUndoContext context);
+
+    /**
+     * <p>
+     * Return whether there is a valid undoable operation available in the given
+     * context
+     * </p>
+     *
+     * @param context
+     *            the context to be checked
+     * @return <code>true</code> if there is an undoable operation,
+     *         <code>false</code> otherwise.
+     */
+    bool canUndo(IUndoContext context);
+
+    /**
+     * <p>
+     * Dispose of the specified context in the history. All operations that have
+     * only the given context will be disposed. References to the context in
+     * operations that have more than one context will also be removed. A
+     * history notification for the removal of each operation being disposed
+     * will be sent.
+     * </p>
+     *
+     * @param context
+     *            the context to be disposed
+     * @param flushUndo
+     *            <code>true</code> if the context should be flushed from the
+     *            undo history, <code>false</code> if it should not
+     * @param flushRedo
+     *            <code>true</code> if the context should be flushed from the
+     *            redo history, <code>false</code> if it should not.
+     * @param flushContext
+     *            <code>true</code> if the context is no longer in use and
+     *            references to it should be flushed.
+     */
+    void dispose(IUndoContext context, bool flushUndo, bool flushRedo,
+            bool flushContext);
+
+    /**
+     * <p>
+     * Execute the specified operation and add it to the operations history if
+     * successful. This method is used by clients who wish operation history
+     * listeners to receive notifications before and after the execution of the
+     * operation. Execution of the operation is subject to approval by any
+     * registered {@link IOperationApprover2}. If execution is approved,
+     * listeners will be notified before (<code>ABOUT_TO_EXECUTE</code>) and
+     * after (<code>DONE</code> or <code>OPERATION_NOT_OK</code>).
+     * </p>
+     * <p>
+     * If the operation successfully executes, an additional notification that
+     * the operation has been added to the history (<code>OPERATION_ADDED</code>)
+     * will be sent.
+     * </p>
+     *
+     * @param operation
+     *            the operation to be executed and then added to the history
+     *
+     * @param monitor
+     *            the progress monitor to be used (or <code>null</code>)
+     *            during the operation.
+     *
+     * @param info
+     *            the IAdaptable (or <code>null</code>) provided by the
+     *            caller in order to supply UI information for prompting the
+     *            user if necessary. When this parameter is not
+     *            <code>null</code>, it should minimally contain an adapter
+     *            for the org.eclipse.swt.widgets.Shell.class.
+     *
+     * @return the IStatus indicating whether the execution succeeded.
+     *
+     * <p>
+     * The severity code in the returned status describes whether the operation
+     * succeeded and whether it was added to the history. <code>OK</code>
+     * severity indicates that the execute operation was successful and that the
+     * operation has been added to the history. Listeners will receive
+     * notifications about the operation's success (<code>DONE</code>) and
+     * about the operation being added to the history (<code>OPERATION_ADDED</code>).
+     * </p>
+     * <p>
+     * <code>CANCEL</code> severity indicates that the user cancelled the
+     * operation and that the operation was not added to the history.
+     * <code>ERROR</code> severity indicates that the operation did not
+     * successfully execute and that it was not added to the history. Any other
+     * severity code is not specifically interpreted by the history, and the
+     * operation will not be added to the history. For all severities other than
+     * <code>OK</code>, listeners will receive the
+     * <code>OPERATION_NOT_OK</code> notification instead of the
+     * <code>DONE</code> notification if the execution was approved and
+     * attempted.
+     * </p>
+     *
+     * @throws ExecutionException
+     *             if an exception occurred during execution.
+     *
+     */
+    IStatus execute(IUndoableOperation operation, IProgressMonitor monitor,
+            IAdaptable info);
+
+    /**
+     * <p>
+     * Return the limit on the undo and redo history for a particular context.
+     * </p>
+     *
+     * @param context
+     *            the context whose limit is requested
+     *
+     * @return the undo and redo history limit for the specified context.
+     */
+    int getLimit(IUndoContext context);
+
+    /**
+     * <p>
+     * Get the array of operations in the redo history for a the specified undo
+     * context. The operations are in the order that they were added to the
+     * history, with the most recently undone operation appearing last in the
+     * array. This history is used LIFO (last in, first out) when successive
+     * "Redo" commands are invoked.
+     *
+     * </p>
+     *
+     * @param context
+     *            the context for the redo
+     * @return the array of operations in the history
+     */
+    IUndoableOperation[] getRedoHistory(IUndoContext context);
+
+    /**
+     * <p>
+     * Get the operation that will next be redone in the given undo context.
+     * </p>
+     *
+     * @param context
+     *            the context for the redo
+     * @return the operation to be redone or <code>null</code> if there is no
+     *         operation available. There is no guarantee that the returned
+     *         operation is valid for redo.
+     */
+    IUndoableOperation getRedoOperation(IUndoContext context);
+
+    /**
+     * <p>
+     * Get the array of operations in the undo history for the specified undo
+     * context. The operations are in the order that they were added to the
+     * history, with the most recently added operation appearing last in the
+     * array. This history is used LIFO (last in, first out) when successive
+     * "Undo" commands are invoked.
+     * </p>
+     *
+     * @param context
+     *            the context for the undo
+     * @return the array of operations in the history
+     */
+    IUndoableOperation[] getUndoHistory(IUndoContext context);
+
+    /**
+     * <p>
+     * Open this composite operation and consider it an operation that contains
+     * other related operations. Consider all operations that are subsequently
+     * executed or added to be part of this operation. When an operation is
+     * opened, listeners will immediately receive a notification for the opened
+     * operation. The specific notification depends on the mode in which the
+     * operation is opened (<code>ABOUT_TO_EXECUTE</code>,
+     * <code>ABOUT_TO_UNDO</code>, <code>ABOUT_TO_REDO</code>).
+     * Notifications for any other execute or add while this operation is open
+     * will not occur. Instead, those operations will be added to the current
+     * operation.
+     * </p>
+     * <p>
+     * Note: This method is intended to be used by legacy undo frameworks that
+     * do not expect related undo operations to appear in the same undo history
+     * as the triggering undo operation. When an operation is open, any
+     * subsequent requests to execute, add, undo, or redo another operation will
+     * result in that operation being added to the open operation. Once the
+     * operation is closed, the composite will be considered an atomic
+     * operation. Clients should not modify the composite directly (by adding
+     * and removing children) while it is open.
+     * </p>
+     * <p>
+     * When a composite is open, operations that are added to the history will
+     * be considered part of the open operation instead. Operations that are
+     * executed while a composite is open will first be executed and then added
+     * to the composite.
+     * </p>
+     * <p>
+     * Open operations cannot be nested. If this method is called when a
+     * different operation is open, it is presumed to be an application coding
+     * error and this method will throw an IllegalStateException.
+     * </p>
+     *
+     * @param operation
+     *            the composite operation to be considered as the parent for all
+     *            subsequent operations.
+     * @param mode
+     *            the mode the operation is executing in. Can be one of
+     *            <code>EXECUTE</code>, <code>UNDO</code>, or
+     *            <code>REDO</code>. This determines what notifications are
+     *            sent.
+     */
+    void openOperation(ICompositeOperation operation, int mode);
+
+    /**
+     * <p>
+     * The specified operation has changed in some way since it was added to the
+     * operation history. Notify listeners with an OPERATION_CHANGED event.
+     * </p>
+     *
+     * @param operation
+     *            the operation that has changed.
+     *
+     */
+    void operationChanged(IUndoableOperation operation);
+
+    /**
+     * <p>
+     * Get the operation that will next be undone in the given undo context.
+     * </p>
+     *
+     * @param context
+     *            the context for the undo
+     * @return the operation to be undone or <code>null</code> if there is no
+     *         operation available. There is no guarantee that the available
+     *         operation is valid for the undo.
+     */
+    IUndoableOperation getUndoOperation(IUndoContext context);
+
+    /**
+     * <p>
+     * Redo the most recently undone operation in the given context. The redo of
+     * the operation is subject to approval by any registered
+     * {@link IOperationApprover} before it is attempted.
+     * </p>
+     *
+     * @param context
+     *            the context to be redone
+     * @param monitor
+     *            the progress monitor to be used for the redo, or
+     *            <code>null</code> if no progress monitor is provided.
+     * @param info
+     *            the IAdaptable (or <code>null</code>) provided by the
+     *            caller in order to supply UI information for prompting the
+     *            user if necessary. When this parameter is not
+     *            <code>null</code>, it should minimally contain an adapter
+     *            for the org.eclipse.swt.widgets.Shell.class.
+     * @return the IStatus indicating whether the redo succeeded.
+     *
+     * <p>
+     * The severity code in the returned status describes whether the operation
+     * succeeded and whether it remains in the history. <code>OK</code>
+     * severity indicates that the redo operation was successful and (since
+     * release 3.2), that the operation will be placed in the undo history.
+     * (Prior to 3.2, a successfully redone operation would not be placed on the
+     * undo history if it could not be undone. Since 3.2, this is relaxed, and
+     * all successfully redone operations are placed in the undo history.)
+     * Listeners will receive the <code>REDONE</code> notification.
+     * </p>
+     * <p>
+     * Other severity codes (<code>CANCEL</code>, <code>ERROR</code>,
+     * <code>INFO</code>, etc.) are not specifically interpreted by the
+     * history. The operation will remain in the history and the returned status
+     * is simply passed back to the caller. For all severities other than
+     * <code>OK</code>, listeners will receive the
+     * <code>OPERATION_NOT_OK</code> notification instead of the
+     * <code>REDONE</code> notification if the redo was approved and
+     * attempted.
+     * </p>
+     *
+     * @throws ExecutionException
+     *             if an exception occurred during redo.
+     *
+     */
+    IStatus redo(IUndoContext context, IProgressMonitor monitor, IAdaptable info);
+
+    /**
+     * <p>
+     * Redo the specified operation. The redo of the operation is subject to
+     * approval by any registered {@link IOperationApprover} before it is
+     * attempted.
+     * </p>
+     *
+     * @param operation
+     *            the operation to be redone
+     * @param monitor
+     *            the progress monitor to be used for the redo, or code>null</code>
+     *            if no progress monitor is provided
+     * @param info
+     *            the IAdaptable (or <code>null</code>) provided by the
+     *            caller in order to supply UI information for prompting the
+     *            user if necessary. When this parameter is not <code>null</code>,
+     *            it should minimally contain an adapter for the
+     *            org.eclipse.swt.widgets.Shell.class.
+     *
+     * @return the IStatus indicating whether the redo succeeded.
+     *
+     * <p>
+     * The severity code in the returned status describes whether the operation
+     * succeeded and whether it remains in the history. <code>OK</code>
+     * severity indicates that the redo operation was successful, and (since
+     * release 3.2), that the operation will be placed in the undo history.
+     * (Prior to 3.2, a successfully redone operation would not be placed on the
+     * undo history if it could not be undone. Since 3.2, this is relaxed, and
+     * all successfully redone operations are placed in the undo history.)
+     * Listeners will receive the <code>REDONE</code> notification.
+     * </p>
+     * <p>
+     * Other severity codes (<code>CANCEL</code>, <code>ERROR</code>,
+     * <code>INFO</code>, etc.) are not specifically interpreted by the
+     * history. The operation will remain in the history and the returned status
+     * is simply passed back to the caller. For all severities other than <code>OK</code>,
+     * listeners will receive the <code>OPERATION_NOT_OK</code> notification
+     * instead of the <code>REDONE</code> notification if the redo was
+     * approved and attempted.
+     * </p>
+     *
+     * @throws ExecutionException
+     *             if an exception occurred during redo.
+     */
+    IStatus redoOperation(IUndoableOperation operation,
+            IProgressMonitor monitor, IAdaptable info);
+
+    /**
+     * <p>
+     * Remove the specified operation approver from the list of operation
+     * approvers that are consulted before an operation is undone or redone.
+     * </p>
+     *
+     * @param approver
+     *            the IOperationApprover to be removed. Must not be
+     *            <code>null</code>. If an attempt is made to remove an
+     *            instance which is not already registered with this instance,
+     *            this method has no effect.
+     */
+    void removeOperationApprover(IOperationApprover approver);
+
+    /**
+     * <p>
+     * Remove the specified listener from the list of operation history
+     * listeners.
+     * </p>
+     *
+     * @param listener
+     *            The IOperationHistoryListener to be removed. Must not be
+     *            <code>null</code>. If an attempt is made to remove an
+     *            instance which is not already registered with this instance,
+     *            this method has no effect.
+     */
+    void removeOperationHistoryListener(IOperationHistoryListener listener);
+
+    /**
+     * <p>
+     * Replace the specified operation in the undo or redo history with the
+     * provided list of replacements. This protocol is typically used when a
+     * composite is broken up into its atomic parts. The replacements will be
+     * inserted so that the first replacement will be the first of the
+     * replacements to be undone or redone. Listeners will be notified about the
+     * removal of the replaced element and the addition of each replacement.
+     * </p>
+     *
+     * @param operation
+     *            The IUndoableOperation to be replaced
+     * @param replacements
+     *            the array of IUndoableOperation to replace the first operation
+     */
+    void replaceOperation(IUndoableOperation operation,
+            IUndoableOperation[] replacements);
+
+    /**
+     * <p>
+     * Set the limit on the undo and redo history for a particular context.
+     * </p>
+     *
+     * @param context
+     *            the context whose limit is being set.
+     *
+     * @param limit
+     *            the maximum number of operations that should be kept in the
+     *            undo or redo history for the specified context. Must not be
+     *            negative.
+     */
+    void setLimit(IUndoContext context, int limit);
+
+    /**
+     * <p>
+     * Undo the most recently executed operation in the given context. The undo
+     * of the operation is subject to approval by any registered
+     * {@link IOperationApprover} before it is attempted.
+     * </p>
+     *
+     * @param context
+     *            the context to be undone
+     * @param monitor
+     *            the progress monitor to be used for the undo, or
+     *            <code>null</code> if no progress monitor is provided.
+     * @param info
+     *            the IAdaptable (or <code>null</code>) provided by the
+     *            caller in order to supply UI information for prompting the
+     *            user if necessary. When this parameter is not
+     *            <code>null</code>, it should minimally contain an adapter
+     *            for the org.eclipse.swt.widgets.Shell.class.
+     *
+     * @return the IStatus indicating whether the undo succeeded.
+     *
+     * <p>
+     * The severity code in the returned status describes whether the operation
+     * succeeded and whether it remains in the history. <code>OK</code>
+     * severity indicates that the undo operation was successful, and (since
+     * release 3.2), that the operation will be placed on the redo history.
+     * (Prior to 3.2, a successfully undone operation would not be placed on the
+     * redo history if it could not be redone. Since 3.2, this is relaxed, and
+     * all successfully undone operations are placed in the redo history.)
+     * Listeners will receive the <code>UNDONE</code> notification.
+     * </p>
+     * <p>
+     * Other severity codes (<code>CANCEL</code>, <code>ERROR</code>,
+     * <code>INFO</code>, etc.) are not specifically interpreted by the
+     * history. The operation will remain in the history and the returned status
+     * is simply passed back to the caller. For all severities other than
+     * <code>OK</code>, listeners will receive the
+     * <code>OPERATION_NOT_OK</code> notification instead of the
+     * <code>UNDONE</code> notification if the undo was approved and
+     * attempted.
+     * </p>
+     *
+     * @throws ExecutionException
+     *             if an exception occurred during undo.
+     */
+
+    IStatus undo(IUndoContext context, IProgressMonitor monitor, IAdaptable info);
+
+    /**
+     * <p>
+     * Undo the specified operation. The undo of the operation is subject to
+     * approval by any registered {@link IOperationApprover} before it is
+     * attempted.
+     * </p>
+     *
+     * @param operation
+     *            the operation to be undone
+     * @param monitor
+     *            the progress monitor to be used for the undo, or
+     *            <code>null</code> if no progress monitor is provided
+     * @param info
+     *            the IAdaptable (or <code>null</code>) provided by the
+     *            caller in order to supply UI information for prompting the
+     *            user if necessary. When this parameter is not
+     *            <code>null</code>, it should minimally contain an adapter
+     *            for the org.eclipse.swt.widgets.Shell.class.
+     *
+     * @return the IStatus indicating whether the undo succeeded.
+     *
+     * <p>
+     * The severity code in the returned status describes whether the operation
+     * succeeded and whether it remains in the history. <code>OK</code>
+     * severity indicates that the undo operation was successful, and (since
+     * release 3.2), that the operation will be placed on the redo history.
+     * (Prior to 3.2, a successfully undone operation would not be placed on the
+     * redo history if it could not be redone. Since 3.2, this is relaxed, and
+     * all successfully undone operations are placed in the redo history.)
+     * Listeners will receive the <code>UNDONE</code> notification.
+     * </p>
+     * <p>
+     * Other severity codes (<code>CANCEL</code>, <code>ERROR</code>,
+     * <code>INFO</code>, etc.) are not specifically interpreted by the
+     * history. The operation will remain in the history and the returned status
+     * is simply passed back to the caller. For all severities other than
+     * <code>OK</code>, listeners will receive the
+     * <code>OPERATION_NOT_OK</code> notification instead of the
+     * <code>UNDONE</code> notification if the undo was approved and
+     * attempted.
+     * </p>
+     *
+     * @throws ExecutionException
+     *             if an exception occurred during undo.
+     */
+    IStatus undoOperation(IUndoableOperation operation,
+            IProgressMonitor monitor, IAdaptable info);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IOperationHistoryListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.IOperationHistoryListener;
+
+import org.eclipse.core.commands.operations.OperationHistoryEvent;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * This interface is used to listen to notifications from an IOperationHistory.
+ * The supplied OperationHistoryEvent describes the particular notification.
+ * </p>
+ * <p>
+ * Operation history listeners must be prepared to receive notifications from a
+ * background thread. Any UI access occurring inside the implementation must be
+ * properly synchronized using the techniques specified by the client's widget
+ * library.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IOperationHistoryListener {
+    /**
+     * Something of note has happened in the IOperationHistory. Listeners should
+     * check the supplied event for details.
+     *
+     * @param event
+     *            the OperationHistoryEvent that describes the particular
+     *            notification.
+     */
+    void historyNotification(OperationHistoryEvent event);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IUndoContext.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.IUndoContext;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * An undo context is used to "tag" operations as being applicable to a certain
+ * context. The undo context is used to filter the history of operations
+ * available for undo or redo so that only operations appropriate for a given
+ * undo context are shown when the application is presenting that context.
+ * </p>
+ * <p>
+ * The scope of an undo context is defined by the application that is making
+ * undo and redo of operations available. Undo contexts may be related to
+ * application models, or may be associated with UI objects that are providing
+ * undo and redo support.
+ * </p>
+ * <p>
+ * An undo context may be defined as "matching" another context. This allows
+ * applications to provide specialized implementations of an undo context that
+ * will appear in the operation history for their matching context.
+ *
+ * @since 3.1
+ */
+
+public interface IUndoContext {
+
+    /**
+     * Get the label that describes the undo context.
+     *
+     * @return the label for the context.
+     */
+    public String getLabel();
+
+    /**
+     * Return whether the specified context is considered a match for the
+     * receiving context. When a context matches another context, operations
+     * that have the context are considered to also have the matching context.
+     *
+     * @param context
+     *            the context to be checked against the receiving context.
+     *
+     * @return <code>true</code> if the receiving context can be considered a
+     *         match for the specified context, and <code>false</code> if it
+     *         cannot.
+     */
+    public bool matches(IUndoContext context);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/IUndoableOperation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,245 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.IUndoableOperation;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+
+import org.eclipse.core.commands.operations.IUndoContext;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * IUndoableOperation defines an operation that can be executed, undone, and
+ * redone. Operations typically have fully defined parameters. That is, they are
+ * usually created after the user has been queried for any input needed to
+ * define the operation.
+ * </p>
+ * <p>
+ * Operations determine their ability to execute, undo, or redo according to the
+ * current state of the application. They do not make decisions about their
+ * validity based on where they occur in the operation history. That is left to
+ * the particular operation history.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IUndoableOperation {
+
+    /**
+     * <p>
+     * Add the specified context to the operation. If a context equal to the
+     * specified context is already present, do not add it again. Note that
+     * determining whether a context is already present is based on equality,
+     * not whether the context matches ({@link IUndoContext#matches(IUndoContext)})
+     * another context.
+     * </p>
+     *
+     * @param context
+     *            the context to be added
+     */
+    void addContext(IUndoContext context);
+
+    /**
+     * <p>
+     * Returns whether the operation can be executed in its current state.
+     * </p>
+     *
+     * <p>
+     * Note: The computation for this method must be fast, as it is called
+     * frequently. If necessary, this method can be optimistic in its
+     * computation (returning true) and later perform more time-consuming
+     * computations during the actual execution of the operation, returning the
+     * appropriate status if the operation cannot actually execute at that time.
+     * </p>
+     *
+     * @return <code>true</code> if the operation can be executed;
+     *         <code>false</code> otherwise.
+     */
+    bool canExecute();
+
+    /**
+     * <p>
+     * Returns whether the operation can be redone in its current state.
+     * </p>
+     *
+     * <p>
+     * Note: The computation for this method must be fast, as it is called
+     * frequently. If necessary, this method can be optimistic in its
+     * computation (returning true) and later perform more time-consuming
+     * computations during the actual redo of the operation, returning the
+     * appropriate status if the operation cannot actually be redone at that
+     * time.
+     * </p>
+     *
+     * @return <code>true</code> if the operation can be redone;
+     *         <code>false</code> otherwise.
+     */
+    bool canRedo();
+
+    /**
+     * <p>
+     * Returns whether the operation can be undone in its current state.
+     * </p>
+     *
+     * <p>
+     * Note: The computation for this method must be fast, as it is called
+     * frequently. If necessary, this method can be optimistic in its
+     * computation (returning true) and later perform more time-consuming
+     * computations during the actual undo of the operation, returning the
+     * appropriate status if the operation cannot actually be undone at that
+     * time.
+     * </p>
+     *
+     * @return <code>true</code> if the operation can be undone;
+     *         <code>false</code> otherwise.
+     */
+    bool canUndo();
+
+    /**
+     * Dispose of the operation. This method is used when the operation is no
+     * longer kept in the history. Implementers of this method typically
+     * unregister any listeners.
+     *
+     */
+    void dispose();
+
+    /**
+     * Execute the operation. This method should only be called the first time
+     * that an operation is executed.
+     *
+     * @param monitor
+     *            the progress monitor (or <code>null</code>) to use for
+     *            reporting progress to the user.
+     * @param info
+     *            the IAdaptable (or <code>null</code>) provided by the
+     *            caller in order to supply UI information for prompting the
+     *            user if necessary. When this parameter is not
+     *            <code>null</code>, it should minimally contain an adapter
+     *            for the org.eclipse.swt.widgets.Shell.class.
+     *
+     * @return the IStatus of the execution. The status severity should be set
+     *         to <code>OK</code> if the operation was successful, and
+     *         <code>ERROR</code> if it was not. Any other status is assumed
+     *         to represent an incompletion of the execution.
+     * @throws ExecutionException
+     *             if an exception occurred during execution.
+     */
+    IStatus execute(IProgressMonitor monitor, IAdaptable info);
+
+    /**
+     * <p>
+     * Returns the array of contexts that have been assigned to the operation.
+     * </p>
+     * <p>
+     * This method may be called by the operation history from inside a
+     * synchronized block. To avoid deadlock conditions, implementers of this
+     * method must avoid dispatching and waiting on threads that modify the
+     * operation history during this method.
+     * </p>
+     *
+     * @return the array of contexts
+     */
+    IUndoContext[] getContexts();
+
+    /**
+     * Return the label that should be used to show the name of the operation to
+     * the user. This label is typically combined with the command strings shown
+     * to the user in "Undo" and "Redo" user interfaces.  
+     *
+     * @return the String label.  Should never be <code>null</code>.
+     */
+    String getLabel();
+
+    /**
+     * <p>
+     * Returns whether the operation has a matching context for the specified
+     * context.
+     * </p>
+     * <p>
+     * This method may be called by the operation history from inside a
+     * synchronized block. To avoid deadlock conditions, implementers of this
+     * method must avoid dispatching and waiting on threads that modify the
+     * operation history during this method.
+     * </p>
+     *
+     * @see IUndoContext#matches(IUndoContext)
+     *
+     * @param context
+     *            the context in question
+     * @return <code>true</code> if the context is present, <code>false</code>
+     *         if it is not.
+     */
+    bool hasContext(IUndoContext context);
+
+    /**
+     * Redo the operation. This method should only be called after an operation
+     * has been undone.
+     *
+     * @param monitor
+     *            the progress monitor (or <code>null</code>) to use for
+     *            reporting progress to the user.
+     * @param info
+     *            the IAdaptable (or <code>null</code>) provided by the
+     *            caller in order to supply UI information for prompting the
+     *            user if necessary. When this parameter is not
+     *            <code>null</code>, it should minimally contain an adapter
+     *            for the org.eclipse.swt.widgets.Shell.class.
+     * @return the IStatus of the redo. The status severity should be set to
+     *         <code>OK</code> if the redo was successful, and
+     *         <code>ERROR</code> if it was not. Any other status is assumed
+     *         to represent an incompletion of the redo.
+     * @throws ExecutionException
+     *             if an exception occurred during redo.
+     */
+
+    IStatus redo(IProgressMonitor monitor, IAdaptable info);
+
+    /**
+     * Remove the specified context from the operation. This method has no
+     * effect if the context is not equal to another context in the context
+     * list. Note that determining whether a context is present when removing it
+     * is based on equality, not whether the context matches ({@link
+     * IUndoContext#matches(IUndoContext)}) another context.
+     *
+     * @param context
+     *            the context to be removed
+     */
+    void removeContext(IUndoContext context);
+
+    /**
+     * Undo the operation. This method should only be called after an operation
+     * has been executed.
+     *
+     * @param monitor
+     *            the progress monitor (or <code>null</code>) to use for
+     *            reporting progress to the user.
+     * @param info
+     *            the IAdaptable (or <code>null</code>) provided by the
+     *            caller in order to supply UI information for prompting the
+     *            user if necessary. When this parameter is not
+     *            <code>null</code>, it should minimally contain an adapter
+     *            for the org.eclipse.swt.widgets.Shell.class.
+     * @return the IStatus of the undo. The status severity should be set to
+     *         <code>OK</code> if the redo was successful, and
+     *         <code>ERROR</code> if it was not. Any other status is assumed
+     *         to represent an incompletion of the undo.
+     * @throws ExecutionException
+     *             if an exception occurred during undo.
+     */
+    IStatus undo(IProgressMonitor monitor, IAdaptable info);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/LinearUndoEnforcer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.LinearUndoEnforcer;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.core.commands.operations.LinearUndoViolationDetector;
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.commands.operations.IUndoContext;
+import org.eclipse.core.commands.operations.IOperationHistory;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * An operation approver that enforces a strict linear undo. It does not allow
+ * the undo or redo of any operation that is not the latest available operation
+ * in all of its undo contexts.  This class may be instantiated by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class LinearUndoEnforcer : LinearUndoViolationDetector {
+    /**
+     * Create an instance of LinearUndoEnforcer.
+     */
+    public this() {
+        super();
+    }
+
+    /*
+     * Return whether a linear redo violation is allowable.  A linear redo violation
+     * is defined as a request to redo a particular operation even if it is not the most
+     * recently added operation to the redo history.
+     */
+    protected override IStatus allowLinearRedoViolation(IUndoableOperation operation,
+            IUndoContext context, IOperationHistory history, IAdaptable uiInfo) {
+        return Status.CANCEL_STATUS;
+    }
+
+    /*
+     * Return whether a linear undo violation is allowable.  A linear undo violation
+     * is defined as a request to undo a particular operation even if it is not the most
+     * recently added operation to the undo history.
+     */
+    protected override IStatus allowLinearUndoViolation(IUndoableOperation operation,
+            IUndoContext context, IOperationHistory history, IAdaptable uiInfo) {
+        return Status.CANCEL_STATUS;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/LinearUndoViolationDetector.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.LinearUndoViolationDetector;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.core.commands.operations.IOperationApprover;
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.commands.operations.IUndoContext;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * An abstract class for detecting violations in a strict linear undo/redo
+ * model. Once a violation is detected, subclasses implement the specific
+ * behavior for indicating whether or not the undo/redo should proceed.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class LinearUndoViolationDetector : IOperationApprover {
+
+    /**
+     * Create an instance of LinearUndoViolationDetector.
+     */
+    public this() {
+    }
+
+    /**
+     * Return a status indicating whether a linear redo violation is allowable.
+     * A linear redo violation is defined as a request to redo a particular
+     * operation even if it is not the most recently added operation to the redo
+     * history.
+     *
+     * @param operation
+     *            the operation for which a linear redo violation has been
+     *            detected.
+     * @param context
+     *            the undo context in which the linear redo violation exists
+     * @param history
+     *            the operation history containing the operation
+     * @param info
+     *            the IAdaptable (or <code>null</code>) provided by the
+     *            caller in order to supply UI information for prompting the
+     *            user if necessary. When this parameter is not
+     *            <code>null</code>, it should minimally contain an adapter
+     *            for the org.eclipse.swt.widgets.Shell.class.
+     *
+     * @return the IStatus describing whether the redo violation is allowed. The
+     *         redo will not proceed if the status severity is not
+     *         <code>OK</code>, and the caller requesting the redo will be
+     *         returned the status that caused the rejection. Specific status
+     *         severities will not be interpreted by the history.
+     */
+
+    protected abstract IStatus allowLinearRedoViolation(
+            IUndoableOperation operation, IUndoContext context,
+            IOperationHistory history, IAdaptable info);
+
+    /**
+     * Return a status indicating whether a linear undo violation is allowable.
+     * A linear undo violation is defined as a request to undo a particular
+     * operation even if it is not the most recently added operation to the undo
+     * history.
+     *
+     * @param operation
+     *            the operation for which a linear undo violation has been
+     *            detected.
+     * @param context
+     *            the undo context in which the linear undo violation exists
+     * @param history
+     *            the operation history containing the operation
+     * @param info
+     *            the IAdaptable (or <code>null</code>) provided by the
+     *            caller in order to supply UI information for prompting the
+     *            user if necessary. When this parameter is not
+     *            <code>null</code>, it should minimally contain an adapter
+     *            for the org.eclipse.swt.widgets.Shell.class.
+     *
+     * @return the IStatus describing whether the undo violation is allowed. The
+     *         undo will not proceed if the status severity is not
+     *         <code>OK</code>, and the caller requesting the undo will be
+     *         returned the status that caused the rejection. Specific status
+     *         severities will not be interpreted by the history.
+     */
+    protected abstract IStatus allowLinearUndoViolation(
+            IUndoableOperation operation, IUndoContext context,
+            IOperationHistory history, IAdaptable info);
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationApprover#proceedRedoing(org.eclipse.core.commands.operations.IUndoableOperation,
+     *      org.eclipse.core.commands.operations.IOperationHistory,
+     *      org.eclipse.core.runtime.IAdaptable)
+     */
+    public final IStatus proceedRedoing(IUndoableOperation operation,
+            IOperationHistory history, IAdaptable info) {
+        IUndoContext[] contexts = operation.getContexts();
+        for (int i = 0; i < contexts.length; i++) {
+            IUndoContext context = contexts[i];
+            if (history.getRedoOperation(context) !is operation) {
+                IStatus status = allowLinearRedoViolation(operation, context,
+                        history, info);
+                if (!status.isOK()) {
+                    return status;
+                }
+            }
+        }
+        return Status.OK_STATUS;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IOperationApprover#proceedUndoing(org.eclipse.core.commands.operations.IUndoableOperation,
+     *      org.eclipse.core.commands.operations.IOperationHistory,
+     *      org.eclipse.core.runtime.IAdaptable)
+     */
+
+    public final IStatus proceedUndoing(IUndoableOperation operation,
+            IOperationHistory history, IAdaptable info) {
+        IUndoContext[] contexts = operation.getContexts();
+        for (int i = 0; i < contexts.length; i++) {
+            IUndoContext context = contexts[i];
+            if (history.getUndoOperation(context) !is operation) {
+                IStatus status = allowLinearUndoViolation(operation, context,
+                        history, info);
+                if (!status.isOK()) {
+                    return status;
+                }
+            }
+        }
+        return Status.OK_STATUS;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/ObjectUndoContext.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.ObjectUndoContext;
+
+import org.eclipse.core.commands.operations.UndoContext;
+import org.eclipse.core.commands.operations.IUndoContext;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * <p>
+ * An undo context that can be used to represent any given object. Clients
+ * can add matching contexts to this context.  This class may be instantiated
+ * by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class ObjectUndoContext : UndoContext {
+
+    private Object object;
+
+    private String label;
+
+    private List children;
+
+    /**
+     * Construct an operation context that represents the given object.
+     *
+     * @param object
+     *            the object to be represented.
+     */
+    public this(Object object) {
+        this(object, null);
+    }
+
+    /**
+     * Construct an operation context that represents the given object and has a
+     * specialized label.
+     *
+     * @param object
+     *            the object to be represented.
+     * @param label
+     *            the label for the context
+     */
+    public this(Object object, String label) {
+        this.object = object;
+        this.label = label;
+        children = new ArrayList();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoContext#getLabel()
+     */
+    public override String getLabel() {
+        if (label !is null) {
+            return label;
+        }
+        if (object !is null) {
+            return object.toString();
+        }
+        return super.getLabel();
+    }
+
+    /**
+     * Return the object that is represented by this context.
+     *
+     * @return the object represented by this context.
+     */
+    public Object getObject() {
+        return object;
+    }
+
+    /**
+     * Add the specified context as a match of this context. Contexts added as
+     * matches of this context will be interpreted as a match of this context
+     * when the history is filtered for a particular context. Adding a match
+     * allows components to create their own contexts for implementing
+     * specialized behavior, yet have their operations appear in a more
+     * global context.
+     *
+     * @param context
+     *            the context to be added as a match of this context
+     */
+    public void addMatch(IUndoContext context) {
+        children.add(cast(Object)context);
+    }
+
+    /**
+     * Remove the specified context as a match of this context. The context will
+     * no longer be interpreted as a match of this context when the history is
+     * filtered for a particular context. This method has no effect if the
+     * specified context was never previously added as a match.
+     *
+     * @param context
+     *            the context to be removed from the list of matches for this
+     *            context
+     */
+    public void removeMatch(IUndoContext context) {
+        children.remove(cast(Object)context);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoContext#matches(IUndoContext
+     *      context)
+     */
+    public override bool matches(IUndoContext context) {
+        // Check first for explicit matches that have been assigned.
+        if (children.contains(cast(Object)context)) {
+            return true;
+        }
+        // Contexts for equal objects are considered matching
+        if ( null !is cast(ObjectUndoContext)context  && getObject() !is null) {
+            return getObject().opEquals((cast(ObjectUndoContext)context).getObject()) !is 0;
+        }
+        // Use the normal matching implementation
+        return super.matches(context);
+    }
+
+    /**
+     * The string representation of this operation.  Used for debugging purposes only.
+     * This string should not be shown to an end user.
+     *
+     * @return The string representation.
+     */
+    public override String toString() {
+        return getLabel();
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/OperationHistoryEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.OperationHistoryEvent;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.commands.operations.IUndoableOperation;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * OperationHistoryEvent is used to communicate changes that occur in a
+ * DefaultOperationHistory, including the addition or removal of operations, and
+ * the execution, undo, and redo of operations.
+ * </p>
+ * <p>
+ * Operation history listeners must be prepared to receive notifications from a
+ * background thread. Any UI access occurring inside the implementation must be
+ * properly synchronized using the techniques specified by the client's widget
+ * library.
+ * </p>
+ *
+ *
+ * @since 3.1
+ */
+public final class OperationHistoryEvent {
+
+    /**
+     * ABOUT_TO_EXECUTE indicates that an operation is about to execute.
+     * Listeners should prepare for the execution as appropriate. Listeners will
+     * receive a DONE notification if the operation is successful, or an
+     * OPERATION_NOT_OK notification if the execution is cancelled or otherwise
+     * fails. This notification is only received for those operations executed
+     * by the operation history. Operations that are added to the history after
+     * execution do not trigger these notifications.
+     *
+     * If the operation successfully executes, clients will also receive a
+     * notification that it has been added to the history.
+     *
+     * (value is 1).
+     */
+
+    public static const int ABOUT_TO_EXECUTE = 1;
+
+    /**
+     * ABOUT_TO_REDO indicates that an operation is about to be redone.
+     * Listeners should prepare for the redo as appropriate. Listeners will
+     * receive a REDONE notification if the operation is successful, or an
+     * OPERATION_NOT_OK notification if the redo is cancelled or otherwise
+     * fails.
+     *
+     * (value is 2).
+     */
+    public static const int ABOUT_TO_REDO = 2;
+
+    /**
+     * ABOUT_TO_UNDO indicates that an operation is about to be undone.
+     * Listeners should prepare for the undo as appropriate. Listeners will
+     * receive an UNDONE notification if the operation is successful, or an
+     * OPERATION_NOT_OK notification if the undo is cancelled or otherwise
+     * fails.
+     *
+     * (value is 3).
+     */
+    public static const int ABOUT_TO_UNDO = 3;
+
+    /**
+     * DONE indicates that an operation has been executed. Listeners can take
+     * appropriate action, such as revealing any relevant state in the UI. This
+     * notification is only received for those operations executed by the
+     * operation history. Operations that are added to the history after
+     * execution do not trigger this notification.
+     *
+     * Clients will also receive a notification that the operation has been
+     * added to the history.
+     *
+     * (value is 4).
+     */
+    public static const int DONE = 4;
+
+    /**
+     * OPERATION_ADDED indicates that an operation was added to the history.
+     * Listeners can use this notification to add their undo context to a new
+     * operation as appropriate or otherwise record the operation.
+     *
+     * (value is 5).
+     */
+    public static const int OPERATION_ADDED = 5;
+
+    /**
+     * OPERATION_CHANGED indicates that an operation has changed in some way
+     * since it was added to the operations history.
+     *
+     * (value is 6).
+     */
+    public static const int OPERATION_CHANGED = 6;
+
+    /**
+     * OPERATION_NOT_OK indicates that an operation was attempted and not
+     * successful. Listeners typically use this when they have prepared for an
+     * execute, undo, or redo, and need to know that the operation did not
+     * successfully complete. For example, listeners that turn redraw off before
+     * an operation is undone would turn redraw on when the operation completes,
+     * or when this notification is received, since there will be no
+     * notification of the completion.
+     *
+     * (value is 7).
+     */
+    public static const int OPERATION_NOT_OK = 7;
+
+    /**
+     * OPERATION_REMOVED indicates an operation was removed from the history.
+     * Listeners typically remove any record of the operation that they may have
+     * kept in their own state. The operation has been disposed by the time
+     * listeners receive this notification.
+     *
+     * (value is 8).
+     */
+    public static const int OPERATION_REMOVED = 8;
+
+    /**
+     * REDONE indicates that an operation was redone. Listeners can take
+     * appropriate action, such as revealing any relevant state in the UI.
+     *
+     * (value is 9).
+     */
+    public static const int REDONE = 9;
+
+    /**
+     * UNDONE indicates that an operation was undone. Listeners can take
+     * appropriate action, such as revealing any relevant state in the UI.
+     *
+     * (value is 10).
+     */
+    public static const int UNDONE = 10;
+
+    private int code = 0;
+
+    private IOperationHistory history;
+
+    private IUndoableOperation operation;
+
+    /* @since 3.2 */
+    private IStatus status;
+
+    /**
+     * Construct an event for the specified operation history.
+     *
+     * @param code
+     *            the event code to be used.
+     * @param history
+     *            the history triggering the event.
+     * @param operation
+     *            the operation involved in the event.
+     */
+    public this(int code, IOperationHistory history,
+            IUndoableOperation operation) {
+        this(code, history, operation, null);
+    }
+
+    /**
+     * Construct an event for the specified operation history.
+     *
+     * @param code
+     *            the event code to be used.
+     * @param history
+     *            the history triggering the event.
+     * @param operation
+     *            the operation involved in the event.
+     * @param status
+     *            the status associated with the event, or null if no status is
+     *            available.
+     *
+     * @since 3.2
+     */
+    public this(int code, IOperationHistory history,
+            IUndoableOperation operation, IStatus status) {
+        if (history is null) {
+            throw new NullPointerException();
+        }
+        if (operation is null) {
+            throw new NullPointerException();
+        }
+        this.code = code;
+        this.history = history;
+        this.operation = operation;
+        this.status = status;
+    }
+
+    /**
+     * Return the type of event that is occurring.
+     *
+     * @return the type code indicating the type of event.
+     */
+    public int getEventType() {
+        return code;
+    }
+
+    /**
+     * Return the operation history that triggered this event.
+     *
+     * @return the operation history
+     */
+
+    public IOperationHistory getHistory() {
+        return history;
+    }
+
+    /**
+     * Return the operation associated with this event.
+     *
+     * @return the operation
+     */
+
+    public IUndoableOperation getOperation() {
+        return operation;
+    }
+
+    /**
+     * Return the status associated with this event.
+     *
+     * @return the status associated with this event. The status may be null.
+     *
+     * @since 3.2
+     */
+
+    public IStatus getStatus() {
+        return status;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/OperationHistoryFactory.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.OperationHistoryFactory;
+
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.commands.operations.DefaultOperationHistory;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * This class is used to maintain the instance of the operation history that
+ * should be used by classes that access the undo or redo history and add
+ * undoable operations to the history.
+ *
+ * <p>
+ * It is intended that an application can create an operation history appropriate
+ * for its needs and set it into this class.  Otherwise, a default operation history
+ * will be created.  The operation history may only be set one time.  All classes that
+ * access an operations history use this class to obtain the correct instance.
+ *
+ * @since 3.1
+ */
+public final class OperationHistoryFactory {
+
+    private static IOperationHistory operationHistory;
+
+    /**
+     * Return the operation history to be used for managing undoable operations.
+     *
+     * @return the operation history to be used for executing, undoing, and
+     *         redoing operations.
+     */
+    public static IOperationHistory getOperationHistory() {
+        if (operationHistory is null) {
+            operationHistory = new DefaultOperationHistory();
+        }
+        return operationHistory;
+    }
+
+    /**
+     * Set the operation history to be used for managing undoable operations.
+     * This method may only be called one time, and must be called before any
+     * request to get the history.  Attempts to set the operation history will
+     * be ignored after it has been already set, or after a default one has
+     * been created.
+     *
+     * @param history
+     *            the operation history to be used for executing, undoing, and
+     *            redoing operations.
+     */
+    public static void setOperationHistory(IOperationHistory history) {
+        // If one has already been set or created, ignore this request.
+        if (operationHistory is null) {
+            operationHistory = history;
+        }
+    }
+
+    private this() {
+        // may not be instantiated
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/OperationStatus.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.OperationStatus;
+
+import org.eclipse.core.runtime.Status;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * OperationStatus describes the status of a request to execute, undo, or redo
+ * an operation.  This class may be instantiated by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class OperationStatus : Status {
+    /**
+     * NOTHING_TO_REDO indicates there was no operation available for redo.
+     *
+     * (value is 1).
+     */
+    public static const int NOTHING_TO_REDO = 1;
+
+    /**
+     * NOTHING_TO_UNDO indicates there was no operation available for undo.
+     *
+     * (value is 2).
+     */
+    public static const int NOTHING_TO_UNDO = 2;
+
+    /**
+     * OPERATION_INVALID indicates that the operation available for undo or redo
+     * is not in a state to successfully perform the undo or redo.
+     *
+     * (value is 3).
+     */
+    public static const int OPERATION_INVALID = 3;
+
+    /**
+     * DEFAULT_PLUGIN_ID identifies the default plugin reporting the status.
+     *
+     * (value is "org.eclipse.core.commands").
+     */
+    static String DEFAULT_PLUGIN_ID = "org.eclipse.core.commands"; //$NON-NLS-1$
+
+    /**
+     * Creates a new operation status, specifying all properties.
+     *
+     * @param severity
+     *            the severity for the status
+     * @param pluginId
+     *            the unique identifier of the relevant plug-in
+     * @param code
+     *            the informational code for the status
+     * @param message
+     *            a human-readable message, localized to the current locale
+     * @param exception
+     *            a low-level exception, or <code>null</code> if not
+     *            applicable
+     */
+    public this(int severity, String pluginId, int code, String message, Exception exception) {
+        super(severity, pluginId, code, message, exception);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/TriggeredOperations.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,467 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.TriggeredOperations;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.core.commands.operations.AbstractOperation;
+import org.eclipse.core.commands.operations.ICompositeOperation;
+import org.eclipse.core.commands.operations.IAdvancedUndoableOperation;
+import org.eclipse.core.commands.operations.IContextReplacingOperation;
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.commands.operations.IUndoContext;
+import org.eclipse.core.commands.operations.OperationHistoryEvent;
+
+import java.lang.all;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Triggered operations are a specialized implementation of a composite
+ * operation that keeps track of operations triggered by the execution of some
+ * primary operation. The composite knows which operation was the trigger for
+ * subsequent operations, and adds all triggered operations as children. When
+ * execution, undo, or redo is performed, only the triggered operation is
+ * executed, undone, or redone if it is still present. If the trigger is removed
+ * from the triggered operations, then the child operations will replace the
+ * triggered operations in the history.
+ * <p>
+ * This class may be instantiated by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class TriggeredOperations : AbstractOperation,
+        ICompositeOperation, IAdvancedUndoableOperation,
+        IContextReplacingOperation {
+
+    private IUndoableOperation triggeringOperation;
+
+    private IOperationHistory history;
+
+    private List children;
+
+    /**
+     * Construct a composite triggered operations using the specified undoable
+     * operation as the trigger. Use the label of this trigger as the label of
+     * the operation.
+     *
+     * @param operation
+     *            the operation that will trigger other operations.
+     * @param history
+     *            the operation history containing the triggered operations.
+     */
+    public this(IUndoableOperation operation,
+            IOperationHistory history) {
+        super(operation.getLabel());
+        children = new ArrayList();
+        triggeringOperation = operation;
+        recomputeContexts();
+        this.history = history;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#add(org.eclipse.core.commands.operations.IUndoableOperation)
+     */
+    public void add(IUndoableOperation operation) {
+        children.add(cast(Object)operation);
+        recomputeContexts();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#remove(org.eclipse.core.commands.operations.IUndoableOperation)
+     */
+    public void remove(IUndoableOperation operation) {
+        if (operation is triggeringOperation) {
+            // the triggering operation is being removed, so we must replace
+            // this composite with its individual triggers.
+            triggeringOperation = null;
+            // save the children before replacing the operation, since this
+            // operation will be disposed as part of replacing it. We don't want
+            // the children to be disposed since they are to replace this
+            // operation.
+            List childrenToRestore = new ArrayList(children);
+            children = new ArrayList(0);
+            recomputeContexts();
+            operation.dispose();
+            // now replace the triggering operation
+            history.replaceOperation(this, arraycast!(IUndoableOperation)(childrenToRestore.toArray()));
+        } else {
+            children.remove(cast(Object)operation);
+            operation.dispose();
+            recomputeContexts();
+        }
+    }
+
+    /**
+     * Remove the specified context from the receiver. This method is typically
+     * invoked when the history is being flushed for a certain context. In the
+     * case of triggered operations, if the only context for the triggering
+     * operation is being removed, then the triggering operation must be
+     * replaced in the operation history with the atomic operations that it
+     * triggered. If the context being removed is not the only context for the
+     * triggering operation, the triggering operation will remain, and the
+     * children will each be similarly checked.
+     *
+     * @param context
+     *            the undo context being removed from the receiver.
+     */
+    public override void removeContext(IUndoContext context) {
+
+        bool recompute = false;
+        // first check to see if we are removing the only context of the
+        // triggering operation
+        if (triggeringOperation !is null
+                && triggeringOperation.hasContext(context)) {
+            if (triggeringOperation.getContexts().length is 1) {
+                remove(triggeringOperation);
+                return;
+            }
+            triggeringOperation.removeContext(context);
+            recompute = true;
+        }
+        // the triggering operation remains, check all the children
+        auto toBeRemoved = new ArrayList();
+        for (int i = 0; i < children.size(); i++) {
+            IUndoableOperation child = cast(IUndoableOperation) children.get(i);
+            if (child.hasContext(context)) {
+                if (child.getContexts().length is 1) {
+                    toBeRemoved.add(cast(Object)child);
+                } else {
+                    child.removeContext(context);
+                }
+                recompute = true;
+            }
+        }
+        for (int i = 0; i < toBeRemoved.size(); i++) {
+            remove(cast(IUndoableOperation) toBeRemoved.get(i));
+        }
+        if (recompute) {
+            recomputeContexts();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#execute(org.eclipse.core.runtime.IProgressMonitor,
+     *      org.eclipse.core.runtime.IAdaptable)
+     */
+    public override IStatus execute(IProgressMonitor monitor, IAdaptable info) {
+        if (triggeringOperation !is null) {
+            history.openOperation(this, IOperationHistory.EXECUTE);
+            try {
+                IStatus status = triggeringOperation.execute(monitor, info);
+                history.closeOperation(status.isOK(), false,
+                        IOperationHistory.EXECUTE);
+                return status;
+            } catch (ExecutionException e) {
+                history.closeOperation(false, false, IOperationHistory.EXECUTE);
+                throw e;
+            } catch (RuntimeException e) {
+                history.closeOperation(false, false, IOperationHistory.EXECUTE);
+                throw e;
+            }
+
+        }
+        return IOperationHistory.OPERATION_INVALID_STATUS;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#redo(org.eclipse.core.runtime.IProgressMonitor,
+     *      org.eclipse.core.runtime.IAdaptable)
+     */
+    public override IStatus redo(IProgressMonitor monitor, IAdaptable info) {
+        if (triggeringOperation !is null) {
+            history.openOperation(this, IOperationHistory.REDO);
+            List childrenToRestore = new ArrayList(children);
+            try {
+                removeAllChildren();
+                IStatus status = triggeringOperation.redo(monitor, info);
+                if (!status.isOK()) {
+                    children = childrenToRestore;
+                }
+                history.closeOperation(status.isOK(), false,
+                        IOperationHistory.REDO);
+                return status;
+            } catch (ExecutionException e) {
+                children = childrenToRestore;
+                history.closeOperation(false, false, IOperationHistory.REDO);
+                throw e;
+            } catch (RuntimeException e) {
+                children = childrenToRestore;
+                history.closeOperation(false, false, IOperationHistory.REDO);
+                throw e;
+            }
+        }
+        return IOperationHistory.OPERATION_INVALID_STATUS;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#undo(org.eclipse.core.runtime.IProgressMonitor,
+     *      org.eclipse.core.runtime.IAdaptable)
+     */
+    public override IStatus undo(IProgressMonitor monitor, IAdaptable info) {
+        if (triggeringOperation !is null) {
+            history.openOperation(this, IOperationHistory.UNDO);
+            List childrenToRestore = new ArrayList(children);
+            try {
+                removeAllChildren();
+                IStatus status = triggeringOperation.undo(monitor, info);
+                if (!status.isOK()) {
+                    children = childrenToRestore;
+                }
+                history.closeOperation(status.isOK(), false,
+                        IOperationHistory.UNDO);
+                return status;
+            } catch (ExecutionException e) {
+                children = childrenToRestore;
+                history.closeOperation(false, false, IOperationHistory.UNDO);
+                throw e;
+            } catch (RuntimeException e) {
+                children = childrenToRestore;
+                history.closeOperation(false, false, IOperationHistory.UNDO);
+                throw e;
+            }
+        }
+        return IOperationHistory.OPERATION_INVALID_STATUS;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#canUndo()
+     */
+    public override bool canUndo() {
+        if (triggeringOperation !is null) {
+            return triggeringOperation.canUndo();
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#canExecute()
+     */
+    public override bool canExecute() {
+        if (triggeringOperation !is null) {
+            return triggeringOperation.canExecute();
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IUndoableOperation#canRedo()
+     */
+    public override bool canRedo() {
+        if (triggeringOperation !is null) {
+            return triggeringOperation.canRedo();
+        }
+        return false;
+    }
+
+    /*
+     * Dispose all operations in the receiver.
+     */
+    public override void dispose() {
+        for (int i = 0; i < children.size(); i++) {
+            (cast(IUndoableOperation)children.get(i)).dispose();
+        }
+        if (triggeringOperation !is null) {
+            triggeringOperation.dispose();
+        }
+    }
+
+    /*
+     * Recompute contexts in light of some change in the children
+     */
+    private void recomputeContexts() {
+        ArrayList allContexts = new ArrayList();
+        if (triggeringOperation !is null) {
+            IUndoContext[] contexts = triggeringOperation.getContexts();
+            for (int i = 0; i < contexts.length; i++) {
+                allContexts.add(cast(Object)contexts[i]);
+            }
+        }
+        for (int i = 0; i < children.size(); i++) {
+            IUndoContext[] contexts = (cast(IUndoableOperation)children.get(i))
+                    .getContexts();
+            for (int j = 0; j < contexts.length; j++) {
+                if (!allContexts.contains(cast(Object)contexts[j])) {
+                    allContexts.add(cast(Object)contexts[j]);
+                }
+            }
+        }
+        contexts = allContexts;
+
+    }
+
+    /*
+     * Remove all non-triggering children
+     */
+    private void removeAllChildren() {
+        IUndoableOperation[] nonTriggers = arraycast!(IUndoableOperation)(children
+                .toArray());
+        for (int i = 0; i < nonTriggers.length; i++) {
+            children.remove(cast(Object)nonTriggers[i]);
+            nonTriggers[i].dispose();
+        }
+    }
+
+    /**
+     * Return the operation that triggered the other operations in this
+     * composite.
+     *
+     * @return the IUndoableOperation that triggered the other children.
+     */
+    public IUndoableOperation getTriggeringOperation() {
+        return triggeringOperation;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IAdvancedModelOperation#getAffectedObjects()
+     */
+    public Object[] getAffectedObjects() {
+        if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation  ) {
+            return trg
+                    .getAffectedObjects();
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IAdvancedModelOperation#aboutToNotify(org.eclipse.core.commands.operations.OperationHistoryEvent)
+     */
+    public void aboutToNotify(OperationHistoryEvent event) {
+        if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation ) {
+            trg.aboutToNotify(event);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IAdvancedUndoableOperation#computeUndoableStatus(org.eclipse.core.runtime.IProgressMonitor)
+     */
+    public IStatus computeUndoableStatus(IProgressMonitor monitor) {
+        if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation ) {
+            try {
+                return trg.computeUndoableStatus(monitor);
+            } catch (OperationCanceledException e) {
+                return Status.CANCEL_STATUS;
+            }
+        }
+        return Status.OK_STATUS;
+
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.commands.operations.IAdvancedUndoableOperation#computeRedoableStatus(org.eclipse.core.runtime.IProgressMonitor)
+     */
+    public IStatus computeRedoableStatus(IProgressMonitor monitor) {
+        if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation ) {
+            try {
+                return trg.computeRedoableStatus(monitor);
+            } catch (OperationCanceledException e) {
+                return Status.CANCEL_STATUS;
+            }
+        }
+        return Status.OK_STATUS;
+
+    }
+
+    /**
+     * Replace the undo context of the receiver with the provided replacement
+     * undo context. In the case of triggered operations, all contained
+     * operations are checked and any occurrence of the original context is
+     * replaced with the new undo context.
+     * <p>
+     * This message has no effect if the original undo context is not present in
+     * the receiver.
+     *
+     * @param original
+     *            the undo context which is to be replaced
+     * @param replacement
+     *            the undo context which is replacing the original
+     * @since 3.2
+     */
+    public void replaceContext(IUndoContext original, IUndoContext replacement) {
+
+        // first check the triggering operation
+        if (triggeringOperation !is null
+                && triggeringOperation.hasContext(original)) {
+            if ( auto trg = cast(IContextReplacingOperation)triggeringOperation ) {
+                trg.replaceContext(original, replacement);
+            } else {
+                triggeringOperation.removeContext(original);
+                triggeringOperation.addContext(replacement);
+            }
+        }
+        // Now check all the children
+        for (int i = 0; i < children.size(); i++) {
+            IUndoableOperation child = cast(IUndoableOperation)children.get(i);
+            if (child.hasContext(original)) {
+                if ( auto c = cast(IContextReplacingOperation)child ) {
+                    c.replaceContext(
+                            original, replacement);
+                } else {
+                    child.removeContext(original);
+                    child.addContext(replacement);
+                }
+            }
+        }
+        recomputeContexts();
+    }
+
+    /**
+     * Add the specified context to the operation. Overridden in
+     * TriggeredOperations to add the specified undo context to the triggering
+     * operation.
+     *
+     * @param context
+     *            the context to be added
+     *
+     * @since 3.2
+     */
+    public override void addContext(IUndoContext context) {
+        if (triggeringOperation !is null) {
+            triggeringOperation.addContext(context);
+            recomputeContexts();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/operations/UndoContext.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.operations.UndoContext;
+
+import org.eclipse.core.commands.operations.IUndoContext;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * A simple, lightweight undo context that can be used to tag any operation. It
+ * does not provided a specialized label. This class may be instantiated by
+ * clients. This class may also be subclassed.
+ * </p>
+ *
+ * @since 3.1
+ */
+public class UndoContext : IUndoContext {
+
+    /**
+     * <p>
+     * Get the label that describes the undo context. The default implementation
+     * returns the empty String. Subclasses may override.
+     * </p>
+     *
+     * @return the label for the context.
+     */
+    public String getLabel() {
+        return ""; //$NON-NLS-1$
+    }
+
+    /**
+     * <p>
+     * Return whether the specified context is considered a match for the
+     * receiving context. When a context matches another context, operations
+     * that have the context are considered to also have the matching context.
+     * The default implementation checks whether the supplied context is
+     * identical to this context. Subclasses may override.
+     * </p>
+     *
+     * @param context
+     *            the context to be checked against the receiving context.
+     *
+     * @return <code>true</code> if the receiving context can be considered a
+     *         match for the specified context, and <code>false</code> if it
+     *         cannot.
+     */
+    public bool matches(IUndoContext context) {
+        return context is this;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/util/Tracing.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.commands.util.Tracing;
+
+import java.lang.all;
+import tango.util.log.Trace;
+
+/**
+ * <p>
+ * A utility class for printing tracing output to the console.
+ * </p>
+ * <p>
+ * Clients must not extend or instantiate this class.
+ * </p>
+ *
+ * @since 3.2
+ */
+public final class Tracing {
+
+    /**
+     * The separator to place between the component and the message.
+     */
+    public static const String SEPARATOR = " >>> "; //$NON-NLS-1$
+
+    /**
+     * <p>
+     * Prints a tracing message to standard out. The message is prefixed by a
+     * component identifier and some separator. See the example below.
+     * </p>
+     *
+     * <pre>
+     *        BINDINGS &gt;&gt; There are 4 deletion markers
+     * </pre>
+     *
+     * @param component
+     *            The component for which this tracing applies; may be
+     *            <code>null</code>
+     * @param message
+     *            The message to print to standard out; may be <code>null</code>.
+     */
+    public static final void printTrace(String component,
+            String message) {
+        StringBuffer buffer = new StringBuffer();
+        if (component.length !is 0) {
+            buffer.append(component);
+        }
+        if ((component.length !is 0) && (message.length !is 0)) {
+            buffer.append(SEPARATOR);
+        }
+        if (message.length !is 0) {
+            buffer.append(message);
+        }
+        Trace.formatln( "{}", buffer.toString());
+    }
+
+    /**
+     * This class is not intended to be instantiated.
+     */
+    private this() {
+        // Do nothing.
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/internal/commands/operations/GlobalUndoContext.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.internal.commands.operations.GlobalUndoContext;
+
+import org.eclipse.core.commands.operations.IUndoContext;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * An operation context that matches to any context.  It can be used to
+ * get an unfiltered (global) history.
+ * </p>
+ *
+ * @since 3.1
+ */
+public class GlobalUndoContext : IUndoContext {
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.commands.operations.IUndoContext#getLabel()
+     */
+    public String getLabel() {
+        return "Global Undo Context"; //$NON-NLS-1$
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.commands.operations.IUndoContext#matches(IUndoContext context)
+     */
+    public bool matches(IUndoContext context) {
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/internal/commands/util/Util.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,395 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.core.internal.commands.util.Util;
+
+import java.lang.all;
+
+// import java.util.Collections;
+// import java.util.HashMap;
+// import java.util.HashSet;
+// import java.util.Iterator;
+// import java.util.Map;
+// import java.util.Set;
+// import java.util.SortedMap;
+// import java.util.SortedSet;
+// import java.util.TreeMap;
+// import java.util.TreeSet;
+
+/**
+ * A class providing utility functions for the commands plug-in.
+ *
+ * @since 3.1
+ */
+public final class Util {
+/++
+    /**
+     * A shared, unmodifiable, empty, sorted map. This value is guaranteed to
+     * always be the same.
+     */
+    public const static SortedMap EMPTY_SORTED_MAP = Collections
+            .unmodifiableSortedMap(new TreeMap());
+
+    /**
+     * A shared, unmodifiable, empty, sorted set. This value is guaranteed to
+     * always be the same.
+     */
+    public final static SortedSet EMPTY_SORTED_SET = Collections
+            .unmodifiableSortedSet(new TreeSet());
+++/
+    /**
+     * A shared, zero-length string -- for avoiding non-externalized string
+     * tags. This value is guaranteed to always be the same.
+     */
+    public final static String ZERO_LENGTH_STRING = "\0"[ 0 .. 0 ]; //$NON-NLS-1$
+/++
+
+    /**
+     * Asserts the the given object is an instance of the given class --
+     * optionally allowing the object to be <code>null</code>.
+     *
+     * @param object
+     *            The object for which the type should be checked.
+     * @param c
+     *            The class that the object must be; fails if the class is
+     *            <code>null</code>.
+     * @param allowNull
+     *            Whether the object being <code>null</code> will not cause a
+     *            failure.
+     */
+    public static final void assertInstance(final Object object, final Class c,
+            final bool allowNull) {
+        if (object is null && allowNull) {
+            return;
+        }
+
+        if (object is null || c is null) {
+            throw new NullPointerException();
+        } else if (!c.isInstance(object)) {
+            throw new IllegalArgumentException();
+        }
+    }
+++/
+    /**
+     * Compares two bool values. <code>false</code> is considered to be
+     * less than <code>true</code>.
+     *
+     * @param left
+     *            The left value to compare.
+     * @param right
+     *            The right value to compare.
+     * @return <code>-1</code> if <code>left</code> is <code>false</code>
+     *         and <code>right</code> is <code>true</code>;<code>0</code>
+     *         if they are equal; <code>1</code> if <code>left</code> is
+     *         <code>true</code> and <code>right</code> is
+     *         <code>false</code>
+     */
+    public static final int compare(bool left, bool right) {
+        return left is false ? (right is true ? -1 : 0) : (right is true ? 0
+                : 1);
+    }
+
+    /**
+     * Compares two comparable objects, but with protection against
+     * <code>null</code>.
+     *
+     * @param left
+     *            The left value to compare; may be <code>null</code>.
+     * @param right
+     *            The right value to compare; may be <code>null</code>.
+     * @return <code>-1</code> if <code>left</code> is <code>null</code>
+     *         and <code>right</code> is not <code>null</code>;
+     *         <code>0</code> if they are both <code>null</code>;
+     *         <code>1</code> if <code>left</code> is not <code>null</code>
+     *         and <code>right</code> is <code>null</code>. Otherwise, the
+     *         result of <code>left.compareTo(right)</code>.
+     */
+    public static final int compare(Comparable left,
+            Comparable right) {
+        if (left is null && right is null) {
+            return 0;
+        } else if (left is null) {
+            return -1;
+        } else if (right is null) {
+            return 1;
+        } else {
+            return left.compareTo( cast(Object)right);
+        }
+    }
+
+    /**
+     * Compares two integer values. This method fails if the distance between
+     * <code>left</code> and <code>right</code> is greater than
+     * <code>Integer.MAX_VALUE</code>.
+     *
+     * @param left
+     *            The left value to compare.
+     * @param right
+     *            The right value to compare.
+     * @return <code>left - right</code>
+     */
+    public static final int compare(int left, int right) {
+        return left - right;
+    }
+
+    /**
+     * Compares two objects that are not otherwise comparable. If neither object
+     * is <code>null</code>, then the string representation of each object is
+     * used.
+     *
+     * @param left
+     *            The left value to compare. The string representation of this
+     *            value must not be <code>null</code>.
+     * @param right
+     *            The right value to compare. The string representation of this
+     *            value must not be <code>null</code>.
+     * @return <code>-1</code> if <code>left</code> is <code>null</code>
+     *         and <code>right</code> is not <code>null</code>;
+     *         <code>0</code> if they are both <code>null</code>;
+     *         <code>1</code> if <code>left</code> is not <code>null</code>
+     *         and <code>right</code> is <code>null</code>. Otherwise, the
+     *         result of
+     *         <code>left.toString().compareTo(right.toString())</code>.
+     */
+    public static final int compare(Object left, Object right) {
+        if (left is null && right is null) {
+            return 0;
+        } else if (left is null) {
+            return -1;
+        } else if (right is null) {
+            return 1;
+        } else {
+            return left.toString() < (right.toString());
+        }
+    }
+    public static final int compare(Object[] left, Object[] right) {
+        if (left.length !is right.length ) {
+            return left.length < right.length;
+        }
+        for( int i = 0; i < left.length; i++ ){
+            int res = left[i].opCmp(right[i]);
+            if( res !is 0 ){
+                return res;
+            }
+        }
+        return 0;
+    }
+    public static final int compare(String left, String right) {
+        return left < right;
+    }
+
+    /**
+     * Decides whether two booleans are equal.
+     *
+     * @param left
+     *            The first bool to compare; may be <code>null</code>.
+     * @param right
+     *            The second bool to compare; may be <code>null</code>.
+     * @return <code>true</code> if the booleans are equal; <code>false</code>
+     *         otherwise.
+     */
+    public static bool equals(bool left, bool right) {
+        return left is right;
+    }
+
+    /**
+     * Decides whether two objects are equal -- defending against
+     * <code>null</code>.
+     *
+     * @param left
+     *            The first object to compare; may be <code>null</code>.
+     * @param right
+     *            The second object to compare; may be <code>null</code>.
+     * @return <code>true</code> if the objects are equals; <code>false</code>
+     *         otherwise.
+     */
+    public static bool equals(Object left, Object right) {
+        return left is null ? right is null : ((right !is null) && (left
+                .opEquals(right)) !is 0 );
+    }
+    public static bool equals(String left, String right) {
+        return left == right;
+    }
+    public static bool equals(String[] left, String[] right) {
+        if( left.length !is right.length ){
+            return false;
+        }
+        for( int i = 0; i < left.length; i++ ){
+            if( !equals( left[i], right[i] )){
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Tests whether two arrays of objects are equal to each other. The arrays
+     * must not be <code>null</code>, but their elements may be
+     * <code>null</code>.
+     *
+     * @param leftArray
+     *            The left array to compare; may be <code>null</code>, and
+     *            may be empty and may contain <code>null</code> elements.
+     * @param rightArray
+     *            The right array to compare; may be <code>null</code>, and
+     *            may be empty and may contain <code>null</code> elements.
+     * @return <code>true</code> if the arrays are equal length and the
+     *         elements at the same position are equal; <code>false</code>
+     *         otherwise.
+     */
+    public static bool equals(Object[] leftArray,
+            Object[] rightArray) {
+        if (leftArray is null) {
+            return (rightArray is null);
+        } else if (rightArray is null) {
+            return false;
+        }
+
+        if (leftArray.length !is rightArray.length) {
+            return false;
+        }
+
+        for (int i = 0; i < leftArray.length; i++) {
+            Object left = leftArray[i];
+            Object right = rightArray[i];
+            bool equal = (left is null) ? (right is null) : (left
+                    .opEquals(right) !is 0);
+            if (!equal) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Computes the hash code for an integer.
+     *
+     * @param i
+     *            The integer for which a hash code should be computed.
+     * @return <code>i</code>.
+     */
+    public static final hash_t toHash(int i) {
+        return i;
+    }
+
+    /**
+     * Computes the hash code for an object, but with defense against
+     * <code>null</code>.
+     *
+     * @param object
+     *            The object for which a hash code is needed; may be
+     *            <code>null</code>.
+     * @return The hash code for <code>object</code>; or <code>0</code> if
+     *         <code>object</code> is <code>null</code>.
+     */
+    public static final hash_t toHash( Object object) {
+        return object !is null ? object.toHash() : 0;
+    }
+    public static final hash_t toHash( char[] str) {
+        return str !is null ? java.lang.all.toHash( str ) : 0;
+    }
+/++
+
+    /**
+     * Makes a type-safe copy of the given map. This method should be used when
+     * a map is crossing an API boundary (i.e., from a hostile plug-in into
+     * internal code, or vice versa).
+     *
+     * @param map
+     *            The map which should be copied; must not be <code>null</code>.
+     * @param keyClass
+     *            The class that all the keys must be; must not be
+     *            <code>null</code>.
+     * @param valueClass
+     *            The class that all the values must be; must not be
+     *            <code>null</code>.
+     * @param allowNullKeys
+     *            Whether <code>null</code> keys should be allowed.
+     * @param allowNullValues
+     *            Whether <code>null</code> values should be allowed.
+     * @return A copy of the map; may be empty, but never <code>null</code>.
+     */
+    public static final Map safeCopy(final Map map, final Class keyClass,
+            final Class valueClass, final bool allowNullKeys,
+            final bool allowNullValues) {
+        if (map is null || keyClass is null || valueClass is null) {
+            throw new NullPointerException();
+        }
+
+        final Map copy = Collections.unmodifiableMap(new HashMap(map));
+        final Iterator iterator = copy.entrySet().iterator();
+
+        while (iterator.hasNext()) {
+            final Map.Entry entry = (Map.Entry) iterator.next();
+            assertInstance(entry.getKey(), keyClass, allowNullKeys);
+            assertInstance(entry.getValue(), valueClass, allowNullValues);
+        }
+
+        return map;
+    }
+
+    /**
+     * Makes a type-safe copy of the given set. This method should be used when
+     * a set is crossing an API boundary (i.e., from a hostile plug-in into
+     * internal code, or vice versa).
+     *
+     * @param set
+     *            The set which should be copied; must not be <code>null</code>.
+     * @param c
+     *            The class that all the values must be; must not be
+     *            <code>null</code>.
+     * @return A copy of the set; may be empty, but never <code>null</code>.
+     *         None of its element will be <code>null</code>.
+     */
+    public static final Set safeCopy(final Set set, final Class c) {
+        return safeCopy(set, c, false);
+    }
+
+    /**
+     * Makes a type-safe copy of the given set. This method should be used when
+     * a set is crossing an API boundary (i.e., from a hostile plug-in into
+     * internal code, or vice versa).
+     *
+     * @param set
+     *            The set which should be copied; must not be <code>null</code>.
+     * @param c
+     *            The class that all the values must be; must not be
+     *            <code>null</code>.
+     * @param allowNullElements
+     *            Whether null values should be allowed.
+     * @return A copy of the set; may be empty, but never <code>null</code>.
+     */
+    public static final Set safeCopy(final Set set, final Class c,
+            final bool allowNullElements) {
+        if (set is null || c is null) {
+            throw new NullPointerException();
+        }
+
+        final Set copy = Collections.unmodifiableSet(new HashSet(set));
+        final Iterator iterator = copy.iterator();
+
+        while (iterator.hasNext()) {
+            assertInstance(iterator.next(), c, allowNullElements);
+        }
+
+        return set;
+    }
+++/
+    /**
+     * The utility class is meant to just provide static members.
+     */
+    private this() {
+        // Should not be called.
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Deadlock.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.internal.jobs.Deadlock;
+
+import java.lang.JThread;
+
+import java.lang.all;
+
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * The deadlock class stores information about a deadlock that just occurred.
+ * It contains an array of the threads that were involved in the deadlock
+ * as well as the thread that was chosen to be suspended and an array of locks
+ * held by that thread that are going to be suspended to resolve the deadlock.
+ */
+class Deadlock {
+    //all the threads which are involved in the deadlock
+    private JThread[] threads;
+    //the thread whose locks will be suspended to resolve deadlock
+    private JThread candidate;
+    //the locks that will be suspended
+    private ISchedulingRule[] locks;
+
+    public this(JThread[] threads, ISchedulingRule[] locks, JThread candidate) {
+        this.threads = threads;
+        this.locks = locks;
+        this.candidate = candidate;
+    }
+
+    public ISchedulingRule[] getLocks() {
+        return locks;
+    }
+
+    public JThread getCandidate() {
+        return candidate;
+    }
+
+    public JThread[] getThreads() {
+        return threads;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/DeadlockDetector.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,722 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 org.eclipse.core.internal.jobs.DeadlockDetector;
+
+import java.lang.JThread;
+import tango.io.Stdout;
+import tango.text.convert.Format;
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Set;
+
+import org.eclipse.core.internal.runtime.RuntimeLog;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ILock;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.internal.jobs.Deadlock;
+import org.eclipse.core.internal.jobs.JobManager;
+
+/**
+ * Stores all the relationships between locks (rules are also considered locks),
+ * and the threads that own them. All the relationships are stored in a 2D integer array.
+ * The rows in the array are threads, while the columns are locks.
+ * Two corresponding arrayLists store the actual threads and locks.
+ * The index of a thread in the first arrayList is the index of the row in the graph.
+ * The index of a lock in the second arrayList is the index of the column in the graph.
+ * An entry greater than 0 in the graph is the number of times a thread in the entry's row
+ * acquired the lock in the entry's column.
+ * An entry of -1 means that the thread is waiting to acquire the lock.
+ * An entry of 0 means that the thread and the lock have no relationship.
+ *
+ * The difference between rules and locks is that locks can be suspended, while
+ * rules are implicit locks and as such cannot be suspended.
+ * To resolve deadlock, the graph will first try to find a thread that only owns
+ * locks. Failing that, it will find a thread in the deadlock that owns at least
+ * one lock and suspend it.
+ *
+ * Deadlock can only occur among locks, or among locks in combination with rules.
+ * Deadlock among rules only is impossible. Therefore, in any deadlock one can always
+ * find a thread that owns at least one lock that can be suspended.
+ *
+ * The implementation of the graph assumes that a thread can only own 1 rule at
+ * any one time. It can acquire that rule several times, but a thread cannot
+ * acquire 2 non-conflicting rules at the same time.
+ *
+ * The implementation of the graph will sometimes also find and resolve bogus deadlocks.
+ *      graph:              assuming this rule hierarchy:
+ *         R2 R3 L1                     R1
+ *      J1  1  0  0                    /  \
+ *      J2  0  1 -1                   R2  R3
+ *      J3 -1  0  1
+ *
+ * If in the above situation job4 decides to acquire rule1, then the graph will transform
+ * to the following:
+ *         R2 R3 R1 L1
+ *      J1  1  0  1  0
+ *      J2  1  1  1 -1
+ *      J3 -1  0  0  1
+ *      J4  0  0 -1  0
+ *
+ * and the graph will assume that job2 and job3 are deadlocked and suspend lock1 of job3.
+ * The reason the deadlock is bogus is that the deadlock is unlikely to actually happen (the threads
+ * are currently not deadlocked, but might deadlock later on when it is too late to detect it)
+ * Therefore, in order to make sure that no deadlock is possible,
+ * the deadlock will still be resolved at this point.
+ */
+class DeadlockDetector {
+    private static int NO_STATE = 0;
+    //state variables in the graph
+    private static int WAITING_FOR_LOCK = -1;
+    //empty matrix
+    private static const int[][] EMPTY_MATRIX = null;
+    //matrix of relationships between threads and locks
+    private int[][] graph = EMPTY_MATRIX;
+    //index is column in adjacency matrix for the lock
+    private final ArrayList locks;
+    //index is row in adjacency matrix for the thread
+    private final ArrayList lockThreads;
+    //whether the graph needs to be resized
+    private bool resize = false;
+
+    public this(){
+        locks = new ArrayList();
+        lockThreads = new ArrayList();
+    }
+
+    /**
+     * Recursively check if any of the threads that prevent the current thread from running
+     * are actually deadlocked with the current thread.
+     * Add the threads that form deadlock to the deadlockedThreads list.
+     */
+    private bool addCycleThreads(ArrayList deadlockedThreads, JThread next) {
+        //get the thread that block the given thread from running
+        JThread[] blocking = blockingThreads(next);
+        //if the thread is not blocked by other threads, then it is not part of a deadlock
+        if (blocking.length is 0)
+            return false;
+        bool inCycle = false;
+        for (int i = 0; i < blocking.length; i++) {
+            //if we have already visited the given thread, then we found a cycle
+            if (deadlockedThreads.contains(blocking[i])) {
+                inCycle = true;
+            } else {
+                //otherwise, add the thread to our list and recurse deeper
+                deadlockedThreads.add(blocking[i]);
+                //if the thread is not part of a cycle, remove it from the list
+                if (addCycleThreads(deadlockedThreads, blocking[i]))
+                    inCycle = true;
+                else
+                    deadlockedThreads.remove(blocking[i]);
+            }
+        }
+        return inCycle;
+    }
+
+    /**
+     * Get the thread(s) that own the lock this thread is waiting for.
+     */
+    private JThread[] blockingThreads(JThread current) {
+        //find the lock this thread is waiting for
+        ISchedulingRule lock = cast(ISchedulingRule) getWaitingLock(current);
+        return getThreadsOwningLock(lock);
+    }
+
+    /**
+     * Check that the addition of a waiting thread did not produce deadlock.
+     * If deadlock is detected return true, else return false.
+     */
+    private bool checkWaitCycles(int[] waitingThreads, int lockIndex) {
+        /**
+         * find the lock that this thread is waiting for
+         * recursively check if this is a cycle (i.e. a thread waiting on itself)
+         */
+        for (int i = 0; i < graph.length; i++) {
+            if (graph[i][lockIndex] > NO_STATE) {
+                if (waitingThreads[i] > NO_STATE) {
+                    return true;
+                }
+                //keep track that we already visited this thread
+                waitingThreads[i]++;
+                for (int j = 0; j < graph[i].length; j++) {
+                    if (graph[i][j] is WAITING_FOR_LOCK) {
+                        if (checkWaitCycles(waitingThreads, j))
+                            return true;
+                    }
+                }
+                //this thread is not involved in a cycle yet, so remove the visited flag
+                waitingThreads[i]--;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true IFF the matrix contains a row for the given thread.
+     * (meaning the given thread either owns locks or is waiting for locks)
+     */
+    bool contains(JThread t) {
+        return lockThreads.contains(t);
+    }
+
+    /**
+     * A new rule was just added to the graph.
+     * Find a rule it conflicts with and update the new rule with the number of times
+     * it was acquired implicitly when threads acquired conflicting rule.
+     */
+    private void fillPresentEntries(ISchedulingRule newLock, int lockIndex) {
+        //fill in the entries for the new rule from rules it conflicts with
+        for (int j = 0; j < locks.size(); j++) {
+            if ((j !is lockIndex) && (newLock.isConflicting(cast(ISchedulingRule) locks.get(j)))) {
+                for (int i = 0; i < graph.length; i++) {
+                    if ((graph[i][j] > NO_STATE) && (graph[i][lockIndex] is NO_STATE))
+                        graph[i][lockIndex] = graph[i][j];
+                }
+            }
+        }
+        //now back fill the entries for rules the current rule conflicts with
+        for (int j = 0; j < locks.size(); j++) {
+            if ((j !is lockIndex) && (newLock.isConflicting(cast(ISchedulingRule) locks.get(j)))) {
+                for (int i = 0; i < graph.length; i++) {
+                    if ((graph[i][lockIndex] > NO_STATE) && (graph[i][j] is NO_STATE))
+                        graph[i][j] = graph[i][lockIndex];
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns all the locks owned by the given thread
+     */
+    private Object[] getOwnedLocks(JThread current) {
+        ArrayList ownedLocks = new ArrayList(1);
+        int index = indexOf(current, false);
+
+        for (int j = 0; j < graph[index].length; j++) {
+            if (graph[index][j] > NO_STATE)
+                ownedLocks.add(locks.get(j));
+        }
+        if (ownedLocks.size() is 0)
+            Assert.isLegal(false, "A thread with no locks is part of a deadlock."); //$NON-NLS-1$
+        return ownedLocks.toArray();
+    }
+
+    /**
+     * Returns an array of threads that form the deadlock (usually 2).
+     */
+    private JThread[] getThreadsInDeadlock(JThread cause) {
+        ArrayList deadlockedThreads = new ArrayList(2);
+        /**
+         * if the thread that caused deadlock doesn't own any locks, then it is not part
+         * of the deadlock (it just caused it because of a rule it tried to acquire)
+         */
+        if (ownsLocks(cause))
+            deadlockedThreads.add(cause);
+        addCycleThreads(deadlockedThreads, cause);
+        return arraycast!(JThread)( deadlockedThreads.toArray());
+    }
+
+    /**
+     * Returns the thread(s) that own the given lock.
+     */
+    private JThread[] getThreadsOwningLock(ISchedulingRule rule) {
+        if (rule is null)
+            return new JThread[0];
+        int lockIndex = indexOf(rule, false);
+        ArrayList blocking = new ArrayList(1);
+        for (int i = 0; i < graph.length; i++) {
+            if (graph[i][lockIndex] > NO_STATE)
+                blocking.add(lockThreads.get(i));
+        }
+        if ((blocking.size() is 0) && (JobManager.DEBUG_LOCKS))
+            Stdout.formatln(Format("Lock {} is involved in deadlock but is not owned by any thread.", rule )); //$NON-NLS-1$ //$NON-NLS-2$
+        if ((blocking.size() > 1) && (cast(ILock)rule ) && (JobManager.DEBUG_LOCKS))
+            Stdout.formatln(Format("Lock {} is owned by more than 1 thread, but it is not a rule.", rule )); //$NON-NLS-1$ //$NON-NLS-2$
+        return arraycast!(JThread)( blocking.toArray());
+    }
+
+    /**
+     * Returns the lock the given thread is waiting for.
+     */
+    private Object getWaitingLock(JThread current) {
+        int index = indexOf(current, false);
+        //find the lock that this thread is waiting for
+        for (int j = 0; j < graph[index].length; j++) {
+            if (graph[index][j] is WAITING_FOR_LOCK)
+                return locks.get(j);
+        }
+        //it can happen that a thread is not waiting for any lock (it is not really part of the deadlock)
+        return null;
+    }
+
+    /**
+     * Returns the index of the given lock in the lock array. If the lock is
+     * not present in the array, it is added to the end.
+     */
+    private int indexOf(ISchedulingRule lock, bool add) {
+        int index = locks.indexOf(cast(Object)lock);
+        if ((index < 0) && add) {
+            locks.add(cast(Object)lock);
+            resize = true;
+            index = locks.size() - 1;
+        }
+        return index;
+    }
+
+    /**
+     * Returns the index of the given thread in the thread array. If the thread
+     * is not present in the array, it is added to the end.
+     */
+    private int indexOf(JThread owner, bool add) {
+        int index = lockThreads.indexOf(owner);
+        if ((index < 0) && add) {
+            lockThreads.add(owner);
+            resize = true;
+            index = lockThreads.size() - 1;
+        }
+        return index;
+    }
+
+    /**
+     * Returns true IFF the adjacency matrix is empty.
+     */
+    bool isEmpty() {
+        return (locks.size() is 0) && (lockThreads.size() is 0) && (graph.length is 0);
+    }
+
+    /**
+     * The given lock was acquired by the given thread.
+     */
+    void lockAcquired(JThread owner, ISchedulingRule lock) {
+        int lockIndex = indexOf(lock, true);
+        int threadIndex = indexOf(owner, true);
+        if (resize)
+            resizeGraph();
+        if (graph[threadIndex][lockIndex] is WAITING_FOR_LOCK)
+            graph[threadIndex][lockIndex] = NO_STATE;
+        /**
+         * acquire all locks that conflict with the given lock
+         * or conflict with a lock the given lock will acquire implicitly
+         * (locks are acquired implicitly when a conflicting lock is acquired)
+         */
+        ArrayList conflicting = new ArrayList(1);
+        //only need two passes through all the locks to pick up all conflicting rules
+        int NUM_PASSES = 2;
+        conflicting.add(cast(Object)lock);
+        graph[threadIndex][lockIndex]++;
+        for (int i = 0; i < NUM_PASSES; i++) {
+            for (int k = 0; k < conflicting.size(); k++) {
+                ISchedulingRule current = cast(ISchedulingRule) conflicting.get(k);
+                for (int j = 0; j < locks.size(); j++) {
+                    ISchedulingRule possible = cast(ISchedulingRule) locks.get(j);
+                    if (current.isConflicting(possible) && !conflicting.contains(cast(Object)possible)) {
+                        conflicting.add(cast(Object)possible);
+                        graph[threadIndex][j]++;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * The given lock was released by the given thread. Update the graph.
+     */
+    void lockReleased(JThread owner, ISchedulingRule lock) {
+        int lockIndex = indexOf(lock, false);
+        int threadIndex = indexOf(owner, false);
+        //make sure the lock and thread exist in the graph
+        if (threadIndex < 0) {
+            if (JobManager.DEBUG_LOCKS)
+                Stdout.formatln("[lockReleased] Lock {} was already released by thread {}", lock, owner.getName()); //$NON-NLS-1$ //$NON-NLS-2$
+            return;
+        }
+        if (lockIndex < 0) {
+            if (JobManager.DEBUG_LOCKS)
+                Stdout.formatln("[lockReleased] Thread {} already released lock {}", owner.getName(), lock); //$NON-NLS-1$ //$NON-NLS-2$
+            return;
+        }
+        //if this lock was suspended, set it to NO_STATE
+        if ((cast(ILock)lock ) && (graph[threadIndex][lockIndex] is WAITING_FOR_LOCK)) {
+            graph[threadIndex][lockIndex] = NO_STATE;
+            return;
+        }
+        //release all locks that conflict with the given lock
+        //or release all rules that are owned by the given thread, if we are releasing a rule
+        for (int j = 0; j < graph[threadIndex].length; j++) {
+            if ((lock.isConflicting(cast(ISchedulingRule) locks.get(j))) || (!(cast(ILock)lock ) && !(cast(ILock)locks.get(j)) && (graph[threadIndex][j] > NO_STATE))) {
+                if (graph[threadIndex][j] is NO_STATE) {
+                    if (JobManager.DEBUG_LOCKS)
+                        Stdout.formatln("[lockReleased] More releases than acquires for thread {} and lock {}", owner.getName(), lock); //$NON-NLS-1$ //$NON-NLS-2$
+                } else {
+                    graph[threadIndex][j]--;
+                }
+            }
+        }
+        //if this thread just released the given lock, try to simplify the graph
+        if (graph[threadIndex][lockIndex] is NO_STATE)
+            reduceGraph(threadIndex, lock);
+    }
+
+    /**
+     * The given scheduling rule is no longer used because the job that invoked it is done.
+     * Release this rule regardless of how many times it was acquired.
+     */
+    void lockReleasedCompletely(JThread owner, ISchedulingRule rule) {
+        int ruleIndex = indexOf(rule, false);
+        int threadIndex = indexOf(owner, false);
+        //need to make sure that the given thread and rule were not already removed from the graph
+        if (threadIndex < 0) {
+            if (JobManager.DEBUG_LOCKS)
+                Stdout.formatln("[lockReleasedCompletely] Lock {} was already released by thread {}", rule, owner.getName()); //$NON-NLS-1$ //$NON-NLS-2$
+            return;
+        }
+        if (ruleIndex < 0) {
+            if (JobManager.DEBUG_LOCKS)
+                Stdout.formatln("[lockReleasedCompletely] Thread {} already released lock {}", owner.getName(), rule); //$NON-NLS-1$ //$NON-NLS-2$
+            return;
+        }
+        /**
+         * set all rules that are owned by the given thread to NO_STATE
+         * (not just rules that conflict with the rule we are releasing)
+         * if we are releasing a lock, then only update the one entry for the lock
+         */
+        for (int j = 0; j < graph[threadIndex].length; j++) {
+            if (!(cast(ILock)locks.get(j) ) && (graph[threadIndex][j] > NO_STATE))
+                graph[threadIndex][j] = NO_STATE;
+        }
+        reduceGraph(threadIndex, rule);
+    }
+
+    /**
+     * The given thread could not get the given lock and is waiting for it.
+     * Update the graph.
+     */
+    Deadlock lockWaitStart(JThread client, ISchedulingRule lock) {
+        setToWait(client, lock, false);
+        int lockIndex = indexOf(lock, false);
+        int[] temp = new int[lockThreads.size()];
+        //check if the addition of the waiting thread caused deadlock
+        if (!checkWaitCycles(temp, lockIndex))
+            return null;
+        //there is a deadlock in the graph
+        JThread[] threads = getThreadsInDeadlock(client);
+        JThread candidate = resolutionCandidate(threads);
+        ISchedulingRule[] locksToSuspend = realLocksForThread(candidate);
+        Deadlock deadlock = new Deadlock(threads, locksToSuspend, candidate);
+        //find a thread whose locks can be suspended to resolve the deadlock
+        if (JobManager.DEBUG_LOCKS)
+            reportDeadlock(deadlock);
+        if (JobManager.DEBUG_DEADLOCK)
+            throw new IllegalStateException(Format("Deadlock detected. Caused by thread {}.", client.getName())); //$NON-NLS-1$
+        // Update the graph to indicate that the locks will now be suspended.
+        // To indicate that the lock will be suspended, we set the thread to wait for the lock.
+        // When the lock is forced to be released, the entry will be cleared.
+        for (int i = 0; i < locksToSuspend.length; i++)
+            setToWait(deadlock.getCandidate(), locksToSuspend[i], true);
+        return deadlock;
+    }
+
+    /**
+     * The given thread has stopped waiting for the given lock.
+     * Update the graph.
+     */
+    void lockWaitStop(JThread owner, ISchedulingRule lock) {
+        int lockIndex = indexOf(lock, false);
+        int threadIndex = indexOf(owner, false);
+        //make sure the thread and lock exist in the graph
+        if (threadIndex < 0) {
+            if (JobManager.DEBUG_LOCKS)
+                Stdout.formatln("Thread {} was already removed.", owner.getName() ); //$NON-NLS-1$ //$NON-NLS-2$
+            return;
+        }
+        if (lockIndex < 0) {
+            if (JobManager.DEBUG_LOCKS)
+                Stdout.formatln("Lock {} was already removed.", lock ); //$NON-NLS-1$ //$NON-NLS-2$
+            return;
+        }
+        if (graph[threadIndex][lockIndex] !is WAITING_FOR_LOCK)
+            Assert.isTrue(false, Format("Thread {} was not waiting for lock {} so it could not time out.", owner.getName(), (cast(Object)lock).toString())); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        graph[threadIndex][lockIndex] = NO_STATE;
+        reduceGraph(threadIndex, lock);
+    }
+
+    /**
+     * Returns true IFF the given thread owns a single lock
+     */
+    private bool ownsLocks(JThread cause) {
+        int threadIndex = indexOf(cause, false);
+        for (int j = 0; j < graph[threadIndex].length; j++) {
+            if (graph[threadIndex][j] > NO_STATE)
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true IFF the given thread owns a single real lock.
+     * A real lock is a lock that can be suspended.
+     */
+    private bool ownsRealLocks(JThread owner) {
+        int threadIndex = indexOf(owner, false);
+        for (int j = 0; j < graph[threadIndex].length; j++) {
+            if (graph[threadIndex][j] > NO_STATE) {
+                Object lock = locks.get(j);
+                if (cast(ILock)lock )
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return true IFF this thread owns rule locks (i.e. implicit locks which
+     * cannot be suspended)
+     */
+    private bool ownsRuleLocks(JThread owner) {
+        int threadIndex = indexOf(owner, false);
+        for (int j = 0; j < graph[threadIndex].length; j++) {
+            if (graph[threadIndex][j] > NO_STATE) {
+                Object lock = locks.get(j);
+                if (!(cast(ILock)lock ))
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns an array of real locks that are owned by the given thread.
+     * Real locks are locks that implement the ILock interface and can be suspended.
+     */
+    private ISchedulingRule[] realLocksForThread(JThread owner) {
+        int threadIndex = indexOf(owner, false);
+        ArrayList ownedLocks = new ArrayList(1);
+        for (int j = 0; j < graph[threadIndex].length; j++) {
+            if ((graph[threadIndex][j] > NO_STATE) && (cast(ILock)locks.get(j) ))
+                ownedLocks.add(locks.get(j));
+        }
+        if (ownedLocks.size() is 0)
+            Assert.isLegal(false, "A thread with no real locks was chosen to resolve deadlock."); //$NON-NLS-1$
+        return arraycast!(ISchedulingRule)( ownedLocks.toArray());
+    }
+
+    /**
+     * The matrix has been simplified. Check if any unnecessary rows or columns
+     * can be removed.
+     */
+    private void reduceGraph(int row, ISchedulingRule lock) {
+        int numLocks = locks.size();
+        bool[] emptyColumns = new bool[numLocks];
+
+        /**
+         * find all columns that could possibly be empty
+         * (consist of locks which conflict with the given lock, or of locks which are rules)
+         */
+        for (int j = 0; j < numLocks; j++) {
+            if ((lock.isConflicting(cast(ISchedulingRule) locks.get(j))) || !(cast(ILock)locks.get(j)))
+                emptyColumns[j] = true;
+        }
+
+        bool rowEmpty = true;
+        int numEmpty = 0;
+        //check if the given row is empty
+        for (int j = 0; j < graph[row].length; j++) {
+            if (graph[row][j] !is NO_STATE) {
+                rowEmpty = false;
+                break;
+            }
+        }
+        /**
+         * Check if the possibly empty columns are actually empty.
+         * If a column is actually empty, remove the corresponding lock from the list of locks
+         * Start at the last column so that when locks are removed from the list,
+         * the index of the remaining locks is unchanged. Store the number of empty columns.
+         */
+        for (int j = emptyColumns.length - 1; j >= 0; j--) {
+            for (int i = 0; i < graph.length; i++) {
+                if (emptyColumns[j] && (graph[i][j] !is NO_STATE)) {
+                    emptyColumns[j] = false;
+                    break;
+                }
+            }
+            if (emptyColumns[j]) {
+                locks.remove(j);
+                numEmpty++;
+            }
+        }
+        //if no columns or rows are empty, return right away
+        if ((numEmpty is 0) && (!rowEmpty))
+            return;
+
+        if (rowEmpty)
+            lockThreads.remove(row);
+
+        //new graph (the list of locks and the list of threads are already updated)
+        final int numThreads = lockThreads.size();
+        numLocks = locks.size();
+        //optimize empty graph case
+        if (numThreads is 0 && numLocks is 0) {
+            graph = EMPTY_MATRIX;
+            return;
+        }
+        int[][] tempGraph = new int[][](numThreads,numLocks);
+
+        //the number of rows we need to skip to get the correct entry from the old graph
+        int numRowsSkipped = 0;
+        for (int i = 0; i < graph.length - numRowsSkipped; i++) {
+            if ((i is row) && rowEmpty) {
+                numRowsSkipped++;
+                //check if we need to skip the last row
+                if (i >= graph.length - numRowsSkipped)
+                    break;
+            }
+            //the number of columns we need to skip to get the correct entry from the old graph
+            //needs to be reset for every new row
+            int numColsSkipped = 0;
+            for (int j = 0; j < graph[i].length - numColsSkipped; j++) {
+                while (emptyColumns[j + numColsSkipped]) {
+                    numColsSkipped++;
+                    //check if we need to skip the last column
+                    if (j >= graph[i].length - numColsSkipped)
+                        break;
+                }
+                //need to break out of the outer loop
+                if (j >= graph[i].length - numColsSkipped)
+                    break;
+                tempGraph[i][j] = graph[i + numRowsSkipped][j + numColsSkipped];
+            }
+        }
+        graph = tempGraph;
+        Assert.isTrue(numThreads is graph.length, "Rows and threads don't match."); //$NON-NLS-1$
+        Assert.isTrue(numLocks is ((graph.length > 0) ? graph[0].length : 0), "Columns and locks don't match."); //$NON-NLS-1$
+    }
+
+    /**
+     * Adds a 'deadlock detected' message to the log with a stack trace.
+     */
+    private void reportDeadlock(Deadlock deadlock) {
+        String msg = "Deadlock detected. All locks owned by thread " ~ deadlock.getCandidate().getName() ~ " will be suspended."; //$NON-NLS-1$ //$NON-NLS-2$
+        MultiStatus main = new MultiStatus(JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, msg, new IllegalStateException());
+        JThread[] threads = deadlock.getThreads();
+        for (int i = 0; i < threads.length; i++) {
+            Object[] ownedLocks = getOwnedLocks(threads[i]);
+            Object waitLock = getWaitingLock(threads[i]);
+            StringBuffer buf = new StringBuffer("Thread "); //$NON-NLS-1$
+            buf.append(threads[i].getName());
+            buf.append(" has locks: "); //$NON-NLS-1$
+            for (int j = 0; j < ownedLocks.length; j++) {
+                buf.append(Format("{}",ownedLocks[j]));
+                buf.append((j < ownedLocks.length - 1) ? ", " : " "); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+            buf.append("and is waiting for lock "); //$NON-NLS-1$
+            buf.append(Format("{}",waitLock));
+            Status child = new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, buf.toString(), null);
+            main.add(child);
+        }
+        RuntimeLog.log(main);
+    }
+
+    /**
+     * The number of threads/locks in the graph has changed. Update the
+     * underlying matrix.
+     */
+    private void resizeGraph() {
+        // a new row and/or a new column was added to the graph.
+        // since new rows/columns are always added to the end, just transfer
+        // old entries to the new graph, with the same indices.
+        final int newRows = lockThreads.size();
+        final int newCols = locks.size();
+        //optimize 0x0 and 1x1 matrices
+        if (newRows is 0 && newCols is 0) {
+            graph = EMPTY_MATRIX;
+            return;
+        }
+        int[][] tempGraph = new int[][](newRows,newCols);
+        for (int i = 0; i < graph.length; i++)
+            System.arraycopy(graph[i], 0, tempGraph[i], 0, graph[i].length);
+        graph = tempGraph;
+        resize = false;
+    }
+
+    /**
+     * Get the thread whose locks can be suspended. (i.e. all locks it owns are
+     * actual locks and not rules). Return the first thread in the array by default.
+     */
+    private JThread resolutionCandidate(JThread[] candidates) {
+        //first look for a candidate that has no scheduling rules
+        for (int i = 0; i < candidates.length; i++) {
+            if (!ownsRuleLocks(candidates[i]))
+                return candidates[i];
+        }
+        //next look for any candidate with a real lock (a lock that can be suspended)
+        for (int i = 0; i < candidates.length; i++) {
+            if (ownsRealLocks(candidates[i]))
+                return candidates[i];
+        }
+        //unnecessary, return the first entry in the array by default
+        return candidates[0];
+    }
+
+    /**
+     * The given thread is waiting for the given lock. Update the graph.
+     */
+    private void setToWait(JThread owner, ISchedulingRule lock, bool suspend) {
+        bool needTransfer = false;
+        /**
+         * if we are adding an entry where a thread is waiting on a scheduling rule,
+         * then we need to transfer all positive entries for a conflicting rule to the
+         * newly added rule in order to synchronize the graph.
+         */
+        if (!suspend && !(cast(ILock)lock))
+            needTransfer = true;
+        int lockIndex = indexOf(lock, !suspend);
+        int threadIndex = indexOf(owner, !suspend);
+        if (resize)
+            resizeGraph();
+
+        graph[threadIndex][lockIndex] = WAITING_FOR_LOCK;
+        if (needTransfer)
+            fillPresentEntries(lock, lockIndex);
+    }
+
+    /**
+     * Prints out the current matrix to standard output.
+     * Only used for debugging.
+     */
+    public String toDebugString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append(" :: \n"); //$NON-NLS-1$
+        for (int j = 0; j < locks.size(); j++) {
+            sb.append(" ");
+            sb.append( locks.get(j).toString );
+            sb.append(",");
+        }
+        sb.append("\n");
+        for (int i = 0; i < graph.length; i++) {
+            sb.append(" ");
+            sb.append( (cast(JThread) lockThreads.get(i)).getName() );
+            sb.append(" : ");
+            for (int j = 0; j < graph[i].length; j++) {
+                sb.append(" ");
+                sb.append(Integer.toString(graph[i][j])); //$NON-NLS-1$
+                sb.append(",");
+            }
+            sb.append("\n");
+        }
+        sb.append("-------\n"); //$NON-NLS-1$
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/ImplicitJobs.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,283 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.internal.jobs.ImplicitJobs;
+
+import tango.text.convert.Format;
+import java.lang.JThread;
+import tango.io.Stdout;
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+
+import org.eclipse.core.internal.runtime.RuntimeLog;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+
+import org.eclipse.core.internal.jobs.JobManager;
+import org.eclipse.core.internal.jobs.ThreadJob;
+import org.eclipse.core.internal.jobs.InternalJob;
+
+/**
+ * Implicit jobs are jobs that are running by virtue of a JobManager.begin/end
+ * pair. They act like normal jobs, except they are tied to an arbitrary thread
+ * of the client's choosing, and they can be nested.
+ */
+class ImplicitJobs {
+    /**
+     * Cached unused instance that can be reused
+     */
+    private ThreadJob jobCache = null;
+    protected JobManager manager;
+
+    /**
+     * Set of suspended scheduling rules.
+     */
+    private const Set suspendedRules;
+
+    /**
+     * Maps (Thread->ThreadJob), threads to the currently running job for that
+     * thread.
+     */
+    private final Map threadJobs;
+
+    this(JobManager manager) {
+        this.manager = manager;
+        suspendedRules = new HashSet(20);
+        threadJobs = new HashMap(20);
+    }
+
+    /* (Non-javadoc)
+     * @see IJobManager#beginRule
+     */
+    void begin(ISchedulingRule rule, IProgressMonitor monitor, bool suspend) {
+        if (JobManager.DEBUG_BEGIN_END)
+            JobManager.debug_(Format("Begin rule: {}", rule)); //$NON-NLS-1$
+        final JThread getThis = JThread.currentThread();
+        ThreadJob threadJob;
+        synchronized (this) {
+            threadJob = cast(ThreadJob) threadJobs.get(getThis);
+            if (threadJob !is null) {
+                //nested rule, just push on stack and return
+                threadJob.push(rule);
+                return;
+            }
+            //no need to schedule a thread job for a null rule
+            if (rule is null)
+                return;
+            //create a thread job for this thread, use the rule from the real job if it has one
+            Job realJob = manager.currentJob();
+            if (realJob !is null && realJob.getRule() !is null)
+                threadJob = newThreadJob(realJob.getRule());
+            else {
+                threadJob = newThreadJob(rule);
+                threadJob.acquireRule = true;
+            }
+            //don't acquire rule if it is a suspended rule
+            if (isSuspended(rule))
+                threadJob.acquireRule = false;
+            //indicate if it is a system job to ensure isBlocking works correctly
+            threadJob.setRealJob(realJob);
+            threadJob.setThread(getThis);
+        }
+        try {
+            threadJob.push(rule);
+            //join the thread job outside sync block
+            if (threadJob.acquireRule) {
+                //no need to re-acquire any locks because the thread did not wait to get this lock
+                if (manager.runNow_package(threadJob))
+                    manager.getLockManager().addLockThread(JThread.currentThread(), rule);
+                else
+                    threadJob = threadJob.joinRun(monitor);
+            }
+        } finally {
+            //remember this thread job  - only do this
+            //after the rule is acquired because it is ok for this thread to acquire
+            //and release other rules while waiting.
+            synchronized (this) {
+                threadJobs.put(getThis, threadJob);
+                if (suspend)
+                    suspendedRules.add(cast(Object)rule);
+            }
+            if (threadJob.isBlocked) {
+                threadJob.isBlocked = false;
+                manager.reportUnblocked(monitor);
+            }
+        }
+    }
+
+    /* (Non-javadoc)
+     * @see IJobManager#endRule
+     */
+    synchronized void end(ISchedulingRule rule, bool resume) {
+        if (JobManager.DEBUG_BEGIN_END)
+            JobManager.debug_(Format("End rule: {}", rule)); //$NON-NLS-1$
+        ThreadJob threadJob = cast(ThreadJob) threadJobs.get(JThread.currentThread());
+        if (threadJob is null)
+            Assert.isLegal(rule is null, Format("endRule without matching beginRule: {}", rule)); //$NON-NLS-1$
+        else if (threadJob.pop(rule)) {
+            endThreadJob(threadJob, resume);
+        }
+    }
+
+    /**
+     * Called when a worker thread has finished running a job. At this
+     * point, the worker thread must not own any scheduling rules
+     * @param lastJob The last job to run in this thread
+     */
+    void endJob(InternalJob lastJob) {
+        final JThread getThis = JThread.currentThread();
+        IStatus error;
+        synchronized (this) {
+            ThreadJob threadJob = cast(ThreadJob) threadJobs.get(getThis);
+            if (threadJob is null) {
+                if (lastJob.getRule() !is null)
+                    notifyWaitingThreadJobs();
+                return;
+            }
+            String msg = Format("Worker thread ended job: {}, but still holds rule: {}", lastJob, threadJob ); //$NON-NLS-1$ //$NON-NLS-2$
+            error = new Status(IStatus.ERROR, JobManager.PI_JOBS, 1, msg, null);
+            //end the thread job
+            endThreadJob(threadJob, false);
+        }
+        try {
+            RuntimeLog.log(error);
+        } catch (RuntimeException e) {
+            //failed to log, so print to console instead
+            Stderr.formatln("{}", error.getMessage());
+        }
+    }
+
+    private void endThreadJob(ThreadJob threadJob, bool resume) {
+        JThread getThis = JThread.currentThread();
+        //clean up when last rule scope exits
+        threadJobs.remove(getThis);
+        ISchedulingRule rule = threadJob.getRule();
+        if (resume && rule !is null)
+            suspendedRules.remove(cast(Object)rule);
+        //if this job had a rule, then we are essentially releasing a lock
+        //note it is safe to do this even if the acquire was aborted
+        if (threadJob.acquireRule) {
+            manager.getLockManager().removeLockThread(getThis, rule);
+            notifyWaitingThreadJobs();
+        }
+        //if the job was started, we need to notify job manager to end it
+        if (threadJob.isRunning())
+            manager.endJob_package(threadJob, Status.OK_STATUS, false);
+        recycle(threadJob);
+    }
+
+    /**
+     * Returns true if this rule has been suspended, and false otherwise.
+     */
+    private bool isSuspended(ISchedulingRule rule) {
+        if (suspendedRules.size() is 0)
+            return false;
+        for (Iterator it = suspendedRules.iterator(); it.hasNext();)
+            if ((cast(ISchedulingRule) it.next()).contains(rule))
+                return true;
+        return false;
+    }
+
+    /**
+     * Returns a new or reused ThreadJob instance.
+     */
+    private ThreadJob newThreadJob(ISchedulingRule rule) {
+        if (jobCache !is null) {
+            ThreadJob job = jobCache;
+            job.setRule(rule);
+            job.acquireRule = job.isRunning_ = false;
+            job.realJob = null;
+            jobCache = null;
+            return job;
+        }
+        return new ThreadJob(manager, rule);
+    }
+
+    /**
+     * A job has just finished that was holding a scheduling rule, and the
+     * scheduling rule is now free.  Wake any blocked thread jobs so they can
+     * compete for the newly freed lock
+     */
+    private void notifyWaitingThreadJobs() {
+        synchronized (ThreadJob.mutex) {
+            ThreadJob.condition.notifyAll();
+        }
+    }
+
+    /**
+     * Indicates that a thread job is no longer in use and can be reused.
+     */
+    private void recycle(ThreadJob job) {
+        if (jobCache is null && job.recycle())
+            jobCache = job;
+    }
+
+    /**
+     * Implements IJobManager#resume(ISchedulingRule)
+     * @param rule
+     */
+    void resume(ISchedulingRule rule) {
+        //resume happens as a consequence of freeing the last rule in the stack
+        end(rule, true);
+        if (JobManager.DEBUG_BEGIN_END)
+            JobManager.debug_(Format("Resume rule: {}", rule)); //$NON-NLS-1$
+    }
+
+    /**
+     * Implements IJobManager#suspend(ISchedulingRule, IProgressMonitor)
+     * @param rule
+     * @param monitor
+     */
+    void suspend(ISchedulingRule rule, IProgressMonitor monitor) {
+        if (JobManager.DEBUG_BEGIN_END)
+            JobManager.debug_(Format("Suspend rule: {}", rule)); //$NON-NLS-1$
+        //the suspend job will be remembered once the rule is acquired
+        begin(rule, monitor, true);
+    }
+
+    /**
+     * Implements IJobManager#transferRule(ISchedulingRule, Thread)
+     */
+    synchronized void transfer(ISchedulingRule rule, JThread destinationThread) {
+        //nothing to do for null
+        if (rule is null)
+            return;
+        JThread getThis = JThread.currentThread();
+        //nothing to do if transferring to the same thread
+        if (getThis is destinationThread)
+            return;
+        //ensure destination thread doesn't already have a rule
+        ThreadJob job = cast(ThreadJob) threadJobs.get(destinationThread);
+        Assert.isLegal(job is null);
+        //ensure calling thread owns the job being transferred
+        job = cast(ThreadJob) threadJobs.get(getThis);
+        Assert.isNotNull(job);
+        Assert.isLegal(job.getRule() is rule);
+        //transfer the thread job without ending it
+        job.setThread(destinationThread);
+        threadJobs.remove(getThis);
+        threadJobs.put(destinationThread, job);
+        //transfer lock
+        if (job.acquireRule) {
+            manager.getLockManager().removeLockThread(getThis, rule);
+            manager.getLockManager().addLockThread(destinationThread, rule);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/InternalJob.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,629 @@
+/*******************************************************************************
+ * 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.internal.jobs.InternalJob;
+
+import java.lang.all;
+import java.util.Map;
+import java.util.Set;
+import java.lang.JThread;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.jobs.MultiRule;
+
+import org.eclipse.core.internal.jobs.JobManager;
+import org.eclipse.core.internal.jobs.ObjectMap;
+
+import tango.text.convert.Format;
+
+/**
+ * Internal implementation class for jobs. Clients must not implement this class
+ * directly.  All jobs must be subclasses of the API <code>org.eclipse.core.runtime.jobs.Job</code> class.
+ */
+public abstract class InternalJob : PlatformObject, Comparable {
+    /**
+     * Job state code (value 16) indicating that a job has been removed from
+     * the wait queue and is about to start running. From an API point of view,
+     * this is the same as RUNNING.
+     */
+    static const int ABOUT_TO_RUN = 0x10;
+
+    /**
+     * Job state code (value 32) indicating that a job has passed scheduling
+     * precondition checks and is about to be added to the wait queue. From an API point of view,
+     * this is the same as WAITING.
+     */
+    static const int ABOUT_TO_SCHEDULE = 0x20;
+    /**
+     * Job state code (value 8) indicating that a job is blocked by another currently
+     * running job.  From an API point of view, this is the same as WAITING.
+     */
+    static const int BLOCKED = 0x08;
+
+    //flag mask bits
+    private static const int M_STATE = 0xFF;
+    private static const int M_SYSTEM = 0x0100;
+    private static const int M_USER = 0x0200;
+
+    /*
+     * flag on a job indicating that it was about to run, but has been canceled
+     */
+    private static const int M_ABOUT_TO_RUN_CANCELED = 0x0400;
+
+    private static JobManager manager_;
+    protected static JobManager manager(){
+        if( manager_ is null ){
+            synchronized( InternalJob.classinfo ){
+                if( manager_ is null ){
+                    manager_ = JobManager.getInstance();
+                }
+            }
+        }
+        return manager_;
+    }
+    private static int nextJobNumber = 0;
+
+    /**
+     * Start time constant indicating a job should be started at
+     * a time in the infinite future, causing it to sleep forever.
+     */
+    static const long T_INFINITE = Long.MAX_VALUE;
+    /**
+     * Start time constant indicating that the job has no start time.
+     */
+    static const long T_NONE = -1;
+
+    private /+volatile+/ int flags = Job.NONE;
+    private const int jobNumber;
+    private ListenerList listeners = null;
+    private IProgressMonitor monitor;
+    private String name;
+    /**
+     * The job ahead of me in a queue or list.
+     */
+    private InternalJob next_;
+    /**
+     * The job behind me in a queue or list.
+     */
+    private InternalJob previous_;
+    private int priority = Job.LONG;
+    /**
+     * Arbitrary properties (key,value) pairs, attached
+     * to a job instance by a third party.
+     */
+    private ObjectMap properties;
+    private IStatus result;
+    private ISchedulingRule schedulingRule;
+    /**
+     * If the job is waiting, this represents the time the job should start by.
+     * If this job is sleeping, this represents the time the job should wake up.
+     * If this job is running, this represents the delay automatic rescheduling,
+     * or -1 if the job should not be rescheduled.
+     */
+    private long startTime;
+
+    /**
+     * Stamp added when a job is added to the wait queue. Used to ensure
+     * jobs in the wait queue maintain their insertion order even if they are
+     * removed from the wait queue temporarily while blocked
+     */
+    private long waitQueueStamp = T_NONE;
+
+    /*
+     * The thread that is currently running this job
+     */
+    private /+volatile+/ JThread thread = null;
+
+    protected this(String name) {
+        Assert.isNotNull(name);
+        jobNumber = nextJobNumber++;
+        this.name = name;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#addJobListener(IJobChangeListener)
+     */
+    protected void addJobChangeListener(IJobChangeListener listener) {
+        if (listeners is null)
+            listeners = new ListenerList(ListenerList.IDENTITY);
+        listeners.add(cast(Object)listener);
+    }
+    package void addJobChangeListener_package(IJobChangeListener listener) {
+        addJobChangeListener(listener);
+    }
+
+    /**
+     * Adds an entry at the end of the list of which this item is the head.
+     */
+    final void addLast(InternalJob entry) {
+        InternalJob last = this;
+        //find the end of the queue
+        while (last.previous_ !is null)
+            last = last.previous_;
+        //add the new entry to the end of the queue
+        last.previous_ = entry;
+        entry.next_ = last;
+        entry.previous_ = null;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#belongsTo(Object)
+     */
+    protected bool belongsTo(Object family) {
+        return false;
+    }
+    package bool belongsTo_package(Object family) {
+        return belongsTo(family);
+    }
+
+    /* (non-Javadoc)
+     * @see Job#cancel()
+     */
+    protected package bool cancel() {
+        return manager.cancel_package(this);
+    }
+
+    /* (non-Javadoc)
+     * @see Job#canceling()
+     */
+    protected package void canceling() {
+        //default implementation does nothing
+    }
+
+    /* (on-Javadoc)
+     * @see java.lang.Comparable#compareTo(java.lang.Object)
+     */
+    public final int compareTo(Object otherJob) {
+        return (cast(InternalJob) otherJob).startTime >= startTime ? 1 : -1;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#done(IStatus)
+     */
+    protected void done(IStatus endResult) {
+        manager.endJob_package(this, endResult, true);
+    }
+
+    /**
+     * Returns the job listeners that are only listening to this job.  Returns
+     * <code>null</code> if this job has no listeners.
+     */
+    final ListenerList getListeners() {
+        return listeners;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#getName()
+     */
+    protected String getName() {
+        return name;
+    }
+    package String getName_package() {
+        return name;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#getPriority()
+     */
+    protected package int getPriority() {
+        return priority;
+    }
+
+    /**
+     * Returns the job's progress monitor, or null if it is not running.
+     */
+    final IProgressMonitor getProgressMonitor() {
+        return monitor;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#getProperty
+     */
+    protected Object getProperty(QualifiedName key) {
+        // thread safety: (Concurrency001 - copy on write)
+        Map temp = properties;
+        if (temp is null)
+            return null;
+        return temp.get(key);
+    }
+
+    /* (non-Javadoc)
+     * @see Job#getResult
+     */
+    protected IStatus getResult() {
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#getRule
+     */
+    protected package ISchedulingRule getRule() {
+        return schedulingRule;
+    }
+    package ISchedulingRule getRule_package() {
+        return getRule();
+    }
+
+    /**
+     * Returns the time that this job should be started, awakened, or
+     * rescheduled, depending on the current state.
+     * @return time in milliseconds
+     */
+    final long getStartTime() {
+        return startTime;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#getState()
+     */
+    protected int getState() {
+        int state = flags & M_STATE;
+        switch (state) {
+            //blocked state is equivalent to waiting state for clients
+            case BLOCKED :
+                return Job.WAITING;
+            case ABOUT_TO_RUN :
+                return Job.RUNNING;
+            case ABOUT_TO_SCHEDULE :
+                return Job.WAITING;
+            default :
+                return state;
+        }
+    }
+    package int getState_package() {
+        return getState();
+    }
+
+    /* (non-javadoc)
+     * @see Job.getThread
+     */
+    protected JThread getThread() {
+        return thread;
+    }
+    package JThread getThread_package() {
+        return getThread();
+    }
+
+    /**
+     * Returns the raw job state, including internal states no exposed as API.
+     */
+    final int internalGetState() {
+        return flags & M_STATE;
+    }
+
+    /**
+     * Must be called from JobManager#setPriority
+     */
+    final void internalSetPriority(int newPriority) {
+        this.priority = newPriority;
+    }
+
+    /**
+     * Must be called from JobManager#setRule
+     */
+    final void internalSetRule(ISchedulingRule rule) {
+        this.schedulingRule = rule;
+    }
+
+    /**
+     * Must be called from JobManager#changeState
+     */
+    final void internalSetState(int i) {
+        flags = (flags & ~M_STATE) | i;
+    }
+
+    /**
+     * Returns whether this job was canceled when it was about to run
+     */
+    final bool isAboutToRunCanceled() {
+        return (flags & M_ABOUT_TO_RUN_CANCELED) !is 0;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#isBlocking()
+     */
+    protected bool isBlocking() {
+        return manager.isBlocking_package(this);
+    }
+
+    /**
+     * Returns true if this job conflicts with the given job, and false otherwise.
+     */
+    final bool isConflicting(InternalJob otherJob) {
+        ISchedulingRule otherRule = otherJob.getRule();
+        if (schedulingRule is null || otherRule is null)
+            return false;
+        //if one of the rules is a compound rule, it must be asked the question.
+        if (schedulingRule.classinfo is MultiRule.classinfo)
+            return schedulingRule.isConflicting(otherRule);
+        return otherRule.isConflicting(schedulingRule);
+    }
+
+    /* (non-javadoc)
+     * @see Job.isSystem()
+     */
+    protected bool isSystem() {
+        return (flags & M_SYSTEM) !is 0;
+    }
+    package bool isSystem_package() {
+        return isSystem();
+    }
+
+    /* (non-javadoc)
+     * @see Job.isUser()
+     */
+    protected bool isUser() {
+        return (flags & M_USER) !is 0;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#join()
+     */
+    protected void join() {
+        manager.join_package(this);
+    }
+
+    /**
+     * Returns the next_ entry (ahead of this one) in the list, or null if there is no next_ entry
+     */
+    final InternalJob next() {
+        return next_;
+    }
+
+    /**
+     * Returns the previous_ entry (behind this one) in the list, or null if there is no previous_ entry
+     */
+    final InternalJob previous() {
+        return previous_;
+    }
+
+    /**
+     * Removes this entry from any list it belongs to.  Returns the receiver.
+     */
+    final InternalJob remove() {
+        if (next_ !is null)
+            next_.setPrevious(previous_);
+        if (previous_ !is null)
+            previous_.setNext(next_);
+        next_ = previous_ = null;
+        return this;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#removeJobListener(IJobChangeListener)
+     */
+    protected void removeJobChangeListener(IJobChangeListener listener) {
+        if (listeners !is null)
+            listeners.remove(cast(Object)listener);
+    }
+    package void removeJobChangeListener_package(IJobChangeListener listener) {
+        removeJobChangeListener(listener);
+    }
+
+    /* (non-Javadoc)
+     * @see Job#run(IProgressMonitor)
+     */
+    protected abstract IStatus run(IProgressMonitor progressMonitor);
+    package IStatus run_package(IProgressMonitor progressMonitor){
+        return run(progressMonitor);
+    }
+
+    /* (non-Javadoc)
+     * @see Job#schedule(long)
+     */
+    protected void schedule(long delay) {
+        if (shouldSchedule())
+            manager.schedule_package(this, delay, false);
+    }
+    package void schedule_package(long delay) {
+        schedule(delay);
+    }
+
+    /**
+     * Sets whether this job was canceled when it was about to run
+     */
+    final void setAboutToRunCanceled(bool value) {
+        flags = value ? flags | M_ABOUT_TO_RUN_CANCELED : flags & ~M_ABOUT_TO_RUN_CANCELED;
+
+    }
+
+    /* (non-Javadoc)
+     * @see Job#setName(String)
+     */
+    protected void setName(String name) {
+        Assert.isNotNull(name);
+        this.name = name;
+    }
+
+    /**
+     * Sets the next_ entry in this linked list of jobs.
+     * @param entry
+     */
+    final void setNext(InternalJob entry) {
+        this.next_ = entry;
+    }
+
+    /**
+     * Sets the previous_ entry in this linked list of jobs.
+     * @param entry
+     */
+    final void setPrevious(InternalJob entry) {
+        this.previous_ = entry;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#setPriority(int)
+     */
+    protected void setPriority(int newPriority) {
+        switch (newPriority) {
+            case Job.INTERACTIVE :
+            case Job.SHORT :
+            case Job.LONG :
+            case Job.BUILD :
+            case Job.DECORATE :
+                manager.setPriority_package(this, newPriority);
+                break;
+            default :
+                throw new IllegalArgumentException(Integer.toString(newPriority));
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see Job#setProgressGroup(IProgressMonitor, int)
+     */
+    protected void setProgressGroup(IProgressMonitor group, int ticks) {
+        Assert.isNotNull(cast(Object)group);
+        IProgressMonitor pm = manager.createMonitor_package(this, group, ticks);
+        if (pm !is null)
+            setProgressMonitor(pm);
+    }
+
+    /**
+     * Sets the progress monitor to use for the next_ execution of this job,
+     * or for clearing the monitor when a job completes.
+     * @param monitor a progress monitor
+     */
+    final void setProgressMonitor(IProgressMonitor monitor) {
+        this.monitor = monitor;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#setProperty(QualifiedName,Object)
+     */
+    protected void setProperty(QualifiedName key, Object value) {
+        // thread safety: (Concurrency001 - copy on write)
+        if (value is null) {
+            if (properties is null)
+                return;
+            ObjectMap temp = cast(ObjectMap) properties.clone();
+            temp.remove(key);
+            if (temp.isEmpty())
+                properties = null;
+            else
+                properties = temp;
+        } else {
+            ObjectMap temp = properties;
+            if (temp is null)
+                temp = new ObjectMap(5);
+            else
+                temp = cast(ObjectMap) properties.clone();
+            temp.put(key, value);
+            properties = temp;
+        }
+    }
+
+    /**
+     * Sets or clears the result of an execution of this job.
+     * @param result a result status, or <code>null</code>
+     */
+    final void setResult(IStatus result) {
+        this.result = result;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#setRule(ISchedulingRule)
+     */
+    protected void setRule(ISchedulingRule rule) {
+        manager.setRule(this, rule);
+    }
+
+    /**
+     * Sets a time to start, wake up, or schedule this job,
+     * depending on the current state
+     * @param time a time in milliseconds
+     */
+    final void setStartTime(long time) {
+        startTime = time;
+    }
+
+    /* (non-javadoc)
+     * @see Job.setSystem
+     */
+    protected void setSystem(bool value) {
+        if (getState() !is Job.NONE)
+            throw new IllegalStateException();
+        flags = value ? flags | M_SYSTEM : flags & ~M_SYSTEM;
+    }
+
+    /* (non-javadoc)
+     * @see Job.setThread
+     */
+    protected void setThread(JThread thread) {
+        this.thread = thread;
+    }
+    package void setThread_package(JThread thread) {
+        setThread(thread);
+    }
+
+    /* (non-javadoc)
+     * @see Job.setUser
+     */
+    protected void setUser(bool value) {
+        if (getState() !is Job.NONE)
+            throw new IllegalStateException();
+        flags = value ? flags | M_USER : flags & ~M_USER;
+    }
+
+    /* (Non-javadoc)
+     * @see Job#shouldSchedule
+     */
+    protected bool shouldSchedule() {
+        return true;
+    }
+    package bool shouldSchedule_package() {
+        return shouldSchedule();
+    }
+
+    /* (non-Javadoc)
+     * @see Job#sleep()
+     */
+    protected bool sleep() {
+        return manager.sleep_package(this);
+    }
+
+    /* (non-Javadoc)
+     * Prints a string-based representation of this job instance.
+     * For debugging purposes only.
+     */
+    public String toString() {
+        return Format( "{}({})", getName(), jobNumber ); //$NON-NLS-1$//$NON-NLS-2$
+    }
+
+    /* (non-Javadoc)
+     * @see Job#wakeUp(long)
+     */
+    protected void wakeUp(long delay) {
+        manager.wakeUp_package(this, delay);
+    }
+
+    /**
+     * @param waitQueueStamp The waitQueueStamp to set.
+     */
+    void setWaitQueueStamp(long waitQueueStamp) {
+        this.waitQueueStamp = waitQueueStamp;
+    }
+
+    /**
+     * @return Returns the waitQueueStamp.
+     */
+    long getWaitQueueStamp() {
+        return waitQueueStamp;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobActivator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.internal.jobs.JobActivator;
+
+import java.lang.all;
+import java.util.Set;
+
+// import org.osgi.framework.BundleActivator;
+// import org.osgi.framework.BundleContext;
+// import org.osgi.framework.ServiceRegistration;
+
+import org.eclipse.core.runtime.jobs.IJobManager;
+
+/**
+ * The Jobs plugin class.
+ */
+public class JobActivator /+: BundleActivator+/ {
+
+    /**
+     * Eclipse property. Set to <code>false</code> to avoid registering JobManager
+     * as an OSGi service.
+     */
+    private static const String PROP_REGISTER_JOB_SERVICE = "eclipse.service.jobs"; //$NON-NLS-1$
+/++
+    /**
+     * The bundle associated this plug-in
+     */
+    private static BundleContext bundleContext;
+
+    /**
+     * This plugin provides a JobManager service.
+     */
+    private ServiceRegistration jobManagerService = null;
+
+    /**
+     * This method is called upon plug-in activation
+     */
+    public void start(BundleContext context) throws Exception {
+        bundleContext = context;
+        JobOSGiUtils.getDefault().openServices();
+
+        bool shouldRegister = !"false".equalsIgnoreCase(context.getProperty(PROP_REGISTER_JOB_SERVICE)); //$NON-NLS-1$
+        if (shouldRegister)
+            registerServices();
+    }
+
+    /**
+     * This method is called when the plug-in is stopped
+     */
+    public void stop(BundleContext context) throws Exception {
+        unregisterServices();
+        JobManager.shutdown();
+        JobOSGiUtils.getDefault().closeServices();
+        bundleContext = null;
+    }
+
+    static BundleContext getContext() {
+        return bundleContext;
+    }
+
+    private void registerServices() {
+        jobManagerService = bundleContext.registerService(IJobManager.class.getName(), JobManager.getInstance(), new Hashtable());
+    }
+
+    private void unregisterServices() {
+        if (jobManagerService !is null) {
+            jobManagerService.unregister();
+            jobManagerService = null;
+        }
+    }
+++/
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobChangeEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.internal.jobs.JobChangeEvent;
+
+import java.lang.all;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.Job;
+
+public class JobChangeEvent : IJobChangeEvent {
+    /**
+     * The job on which this event occurred.
+     */
+    Job job = null;
+    /**
+     * The result returned by the job's run method, or <code>null</code> if
+     * not applicable.
+     */
+    IStatus result = null;
+    /**
+     * The amount of time to wait after scheduling the job before it should be run,
+     * or <code>-1</code> if not applicable for this type of event.
+     */
+    long delay = -1;
+    /**
+     * Whether this job is being immediately rescheduled.
+     */
+    bool reschedule = false;
+
+    /* (non-Javadoc)
+     * Method declared on IJobChangeEvent
+     */
+    public long getDelay() {
+        return delay;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IJobChangeEvent
+     */
+    public Job getJob() {
+        return job;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IJobChangeEvent
+     */
+    public IStatus getResult() {
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobListeners.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,192 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.internal.jobs.JobListeners;
+
+import java.lang.all;
+
+import org.eclipse.core.internal.runtime.RuntimeLog;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import org.eclipse.core.runtime.jobs.Job;
+import org.osgi.util.NLS;
+import org.eclipse.core.internal.jobs.JobChangeEvent;
+import org.eclipse.core.internal.jobs.InternalJob;
+import org.eclipse.core.internal.jobs.JobOSGiUtils;
+import org.eclipse.core.internal.jobs.JobManager;
+import org.eclipse.core.internal.jobs.JobMessages;
+
+/**
+ * Responsible for notifying all job listeners about job lifecycle events.  Uses a
+ * specialized iterator to ensure the complex iteration logic is contained in one place.
+ */
+class JobListeners {
+    interface IListenerDoit {
+        public void notify(IJobChangeListener listener, IJobChangeEvent event);
+    }
+
+    private const IListenerDoit aboutToRun_;
+    private const IListenerDoit awake_;
+    private const IListenerDoit done_;
+    private const IListenerDoit running_;
+    private const IListenerDoit scheduled_;
+    private const IListenerDoit sleeping_;
+    /**
+     * The global job listeners.
+     */
+    protected const ListenerList global;
+
+    this(){
+        aboutToRun_ = new class IListenerDoit {
+            public void notify(IJobChangeListener listener, IJobChangeEvent event) {
+                listener.aboutToRun(event);
+            }
+        };
+        awake_ = new class IListenerDoit {
+            public void notify(IJobChangeListener listener, IJobChangeEvent event) {
+                listener.awake(event);
+            }
+        };
+        done_ = new class IListenerDoit {
+            public void notify(IJobChangeListener listener, IJobChangeEvent event) {
+                listener.done(event);
+            }
+        };
+        running_ = new class IListenerDoit {
+            public void notify(IJobChangeListener listener, IJobChangeEvent event) {
+                listener.running(event);
+            }
+        };
+        scheduled_ = new class IListenerDoit {
+            public void notify(IJobChangeListener listener, IJobChangeEvent event) {
+                listener.scheduled(event);
+            }
+        };
+        sleeping_ = new class IListenerDoit {
+            public void notify(IJobChangeListener listener, IJobChangeEvent event) {
+                listener.sleeping(event);
+            }
+        };
+        global = new ListenerList(ListenerList.IDENTITY);
+    }
+
+    /**
+     * TODO Could use an instance pool to re-use old event objects
+     */
+    static JobChangeEvent newEvent(Job job) {
+        JobChangeEvent instance = new JobChangeEvent();
+        instance.job = job;
+        return instance;
+    }
+
+    static JobChangeEvent newEvent(Job job, IStatus result) {
+        JobChangeEvent instance = new JobChangeEvent();
+        instance.job = job;
+        instance.result = result;
+        return instance;
+    }
+
+    static JobChangeEvent newEvent(Job job, long delay) {
+        JobChangeEvent instance = new JobChangeEvent();
+        instance.job = job;
+        instance.delay = delay;
+        return instance;
+    }
+
+    /**
+     * Process the given doit for all global listeners and all local listeners
+     * on the given job.
+     */
+    private void doNotify(IListenerDoit doit, IJobChangeEvent event) {
+        //notify all global listeners
+        Object[] listeners = global.getListeners();
+        int size = listeners.length;
+        for (int i = 0; i < size; i++) {
+            try {
+                if (listeners[i] !is null)
+                    doit.notify(cast(IJobChangeListener) listeners[i], event);
+            } catch (Exception e) {
+                handleException(listeners[i], e);
+//             } catch (LinkageError e) {
+//                 handleException(listeners[i], e);
+            }
+        }
+        //notify all local listeners
+        ListenerList list = (cast(InternalJob) event.getJob()).getListeners();
+        listeners = list is null ? null : list.getListeners();
+        if (listeners is null)
+            return;
+        size = listeners.length;
+        for (int i = 0; i < size; i++) {
+            try {
+                if (listeners[i] !is null)
+                    doit.notify(cast(IJobChangeListener) listeners[i], event);
+            } catch (Exception e) {
+                handleException(listeners[i], e);
+//             } catch (LinkageError e) {
+//                 handleException(listeners[i], e);
+            }
+        }
+    }
+
+    private void handleException(Object listener, Exception e) {
+        //this code is roughly copied from InternalPlatform.run(ISafeRunnable),
+        //but in-lined here for performance reasons
+        if (cast(OperationCanceledException)e )
+            return;
+        String pluginId = JobOSGiUtils.getDefault().getBundleId(listener);
+        if (pluginId is null)
+            pluginId = JobManager.PI_JOBS;
+        String message = NLS.bind(JobMessages.meta_pluginProblems, pluginId);
+        RuntimeLog.log(new Status(IStatus.ERROR, pluginId, JobManager.PLUGIN_ERROR, message, e));
+    }
+
+    public void add(IJobChangeListener listener) {
+        global.add(cast(Object)listener);
+    }
+
+    public void remove(IJobChangeListener listener) {
+        global.remove(cast(Object)listener);
+    }
+
+    public void aboutToRun(Job job) {
+        doNotify(aboutToRun_, newEvent(job));
+    }
+
+    public void awake(Job job) {
+        doNotify(awake_, newEvent(job));
+    }
+
+    public void done(Job job, IStatus result, bool reschedule) {
+        JobChangeEvent event = newEvent(job, result);
+        event.reschedule = reschedule;
+        doNotify(done_, event);
+    }
+
+    public void running(Job job) {
+        doNotify(running_, newEvent(job));
+    }
+
+    public void scheduled(Job job, long delay, bool reschedule) {
+        JobChangeEvent event = newEvent(job, delay);
+        event.reschedule = reschedule;
+        doNotify(scheduled_, event);
+    }
+
+    public void sleeping(Job job) {
+        doNotify(sleeping_, newEvent(job));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1364 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.internal.jobs.JobManager;
+
+import java.lang.all;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.HashSet;
+import tango.io.Stdout;
+import tango.text.convert.Format;
+import tango.time.WallClock;
+import tango.time.Time;
+import java.lang.JThread;
+import tango.text.convert.Format;
+
+//don't use ICU because this is used for debugging only (see bug 135785)
+// import java.text.DateFormat;
+// import java.text.FieldPosition;
+// import java.text.SimpleDateFormat;
+
+import org.eclipse.core.internal.runtime.RuntimeLog;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IProgressMonitorWithBlocking;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import org.eclipse.core.runtime.jobs.IJobManager;
+import org.eclipse.core.runtime.jobs.ILock;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+import org.eclipse.core.runtime.jobs.LockListener;
+import org.eclipse.core.runtime.jobs.ProgressProvider;
+import org.osgi.util.NLS;
+
+import org.eclipse.core.internal.jobs.ImplicitJobs;
+import org.eclipse.core.internal.jobs.WorkerPool;
+import org.eclipse.core.internal.jobs.JobListeners;
+import org.eclipse.core.internal.jobs.LockManager;
+import org.eclipse.core.internal.jobs.JobQueue;
+import org.eclipse.core.internal.jobs.InternalJob;
+import org.eclipse.core.internal.jobs.ThreadJob;
+import org.eclipse.core.internal.jobs.JobOSGiUtils;
+import org.eclipse.core.internal.jobs.Worker;
+import org.eclipse.core.internal.jobs.Semaphore;
+import org.eclipse.core.internal.jobs.JobChangeEvent;
+import org.eclipse.core.internal.jobs.JobMessages;
+import org.eclipse.core.internal.jobs.JobStatus;
+
+/**
+ * Implementation of API type IJobManager
+ *
+ * Implementation note: all the data structures of this class are protected
+ * by a single lock object held as a private field in this class.  The JobManager
+ * instance itself is not used because this class is publicly reachable, and third
+ * party clients may try to synchronize on it.
+ *
+ * The WorkerPool class uses its own monitor for synchronizing its data
+ * structures. To avoid deadlock between the two classes, the JobManager
+ * must NEVER call the worker pool while its own monitor is held.
+ */
+public class JobManager : IJobManager {
+
+    /**
+     * The unique identifier constant of this plug-in.
+     */
+    public static const String PI_JOBS = "org.eclipse.core.jobs"; //$NON-NLS-1$
+
+    /**
+     * Status code constant indicating an error occurred while running a plug-in.
+     * For backward compatibility with Platform.PLUGIN_ERROR left at (value = 2).
+     */
+    public static const int PLUGIN_ERROR = 2;
+
+    private static const String OPTION_DEADLOCK_ERROR = PI_JOBS ~ "/jobs/errorondeadlock"; //$NON-NLS-1$
+    private static const String OPTION_DEBUG_BEGIN_END = PI_JOBS ~ "/jobs/beginend"; //$NON-NLS-1$
+    private static const String OPTION_DEBUG_JOBS = PI_JOBS ~ "/jobs"; //$NON-NLS-1$
+    private static const String OPTION_DEBUG_JOBS_TIMING = PI_JOBS ~ "/jobs/timing"; //$NON-NLS-1$
+    private static const String OPTION_LOCKS = PI_JOBS ~ "/jobs/locks"; //$NON-NLS-1$
+    private static const String OPTION_SHUTDOWN = PI_JOBS ~ "/jobs/shutdown"; //$NON-NLS-1$
+
+    static bool DEBUG = false;
+    static bool DEBUG_BEGIN_END = false;
+    static bool DEBUG_DEADLOCK = false;
+    static bool DEBUG_LOCKS = false;
+    static bool DEBUG_TIMING = false;
+    static bool DEBUG_SHUTDOWN = false;
+//     private static DateFormat DEBUG_FORMAT;
+
+    /**
+     * The singleton job manager instance. It must be a singleton because
+     * all job instances maintain a reference (as an optimization) and have no way
+     * of updating it.
+     */
+    private static JobManager instance = null;
+    /**
+     * Scheduling rule used for validation of client-defined rules.
+     */
+    private static ISchedulingRule nullRule;
+    private static void initNullRule(){
+        if( nullRule !is null ) return;
+        nullRule = new class ISchedulingRule {
+            public bool contains(ISchedulingRule rule) {
+                return rule is this;
+            }
+
+            public bool isConflicting(ISchedulingRule rule) {
+                return rule is this;
+            }
+        };
+    }
+
+    /**
+     * True if this manager is active, and false otherwise.  A job manager
+     * starts out active, and becomes inactive if it has been shutdown
+     * and not restarted.
+     */
+    private /+volatile+/ bool active = true;
+
+    const ImplicitJobs implicitJobs;
+
+    private const JobListeners jobListeners;
+
+    /**
+     * The lock for synchronizing all activity in the job manager.  To avoid deadlock,
+     * this lock must never be held for extended periods, and must never be
+     * held while third party code is being called.
+     */
+    private const Object lock;
+
+    private const LockManager lockManager;
+
+    /**
+     * The pool of worker threads.
+     */
+    private WorkerPool pool;
+
+    private ProgressProvider progressProvider = null;
+    /**
+     * Jobs that are currently running. Should only be modified from changeState
+     */
+    private const HashSet running;
+
+    /**
+     * Jobs that are sleeping.  Some sleeping jobs are scheduled to wake
+     * up at a given start time, while others will sleep indefinitely until woken.
+     * Should only be modified from changeState
+     */
+    private const JobQueue sleeping;
+    /**
+     * True if this manager has been suspended, and false otherwise.  A job manager
+     * starts out not suspended, and becomes suspended when <code>suspend</code>
+     * is invoked. Once suspended, no jobs will start running until <code>resume</code>
+     * is called.
+     */
+    private bool suspended = false;
+
+    /**
+     * jobs that are waiting to be run. Should only be modified from changeState
+     */
+    private const JobQueue waiting;
+
+    /**
+     * Counter to record wait queue insertion order.
+     */
+    private long waitQueueCounter;
+
+    public static void debug_(String msg) {
+        StringBuffer msgBuf = new StringBuffer(msg.length + 40);
+        if (DEBUG_TIMING) {
+            //lazy initialize to avoid overhead when not debugging
+//             if (DEBUG_FORMAT is null)
+//                 DEBUG_FORMAT = new SimpleDateFormat("HH:mm:ss.SSS"); //$NON-NLS-1$
+//             DEBUG_FORMAT.format(new Date(), msgBuf, new FieldPosition(0));
+            auto time = WallClock.now();
+            msgBuf.append(Format("{:d2}:{:d2}:{:d2}:{:d3}",
+                time.time.span.hours,
+                time.time.span.minutes,
+                time.time.span.seconds,
+                time.time.span.millis ));
+            msgBuf.append('-');
+        }
+        msgBuf.append('[');
+        msgBuf.append(JThread.currentThread().toString());
+        msgBuf.append(']');
+        msgBuf.append(msg);
+        Stdout.formatln( "{}", msgBuf.toString());
+    }
+
+    /**
+     * Returns the job manager singleton. For internal use only.
+     */
+    static synchronized JobManager getInstance() {
+        if (instance is null)
+            new JobManager();
+        return instance;
+    }
+
+    /**
+     * For debugging purposes only
+     */
+    private static String printJobName(Job job) {
+        if (cast(ThreadJob)job ) {
+            Job realJob = (cast(ThreadJob) job).realJob;
+            if (realJob !is null)
+                return realJob.classinfo.name;
+            return Format("ThreadJob on rule: {}", job.getRule()); //$NON-NLS-1$
+        }
+        return job.classinfo.name;
+    }
+
+    /**
+     * For debugging purposes only
+     */
+    public static String printState(int state) {
+        switch (state) {
+            case Job.NONE :
+                return "NONE"; //$NON-NLS-1$
+            case Job.WAITING :
+                return "WAITING"; //$NON-NLS-1$
+            case Job.SLEEPING :
+                return "SLEEPING"; //$NON-NLS-1$
+            case Job.RUNNING :
+                return "RUNNING"; //$NON-NLS-1$
+            case InternalJob.BLOCKED :
+                return "BLOCKED"; //$NON-NLS-1$
+            case InternalJob.ABOUT_TO_RUN :
+                return "ABOUT_TO_RUN"; //$NON-NLS-1$
+            case InternalJob.ABOUT_TO_SCHEDULE :
+                return "ABOUT_TO_SCHEDULE";//$NON-NLS-1$
+            default:
+        }
+        return "UNKNOWN"; //$NON-NLS-1$
+    }
+
+    /**
+     * Note that although this method is not API, clients have historically used
+     * it to force jobs shutdown in cases where OSGi shutdown does not occur.
+     * For this reason, this method should be considered near-API and should not
+     * be changed if at all possible.
+     */
+    public static void shutdown() {
+        if (instance !is null) {
+            instance.doShutdown();
+            instance = null;
+        }
+    }
+
+    private this() {
+        // SWT instance init
+        implicitJobs = new ImplicitJobs(this);
+        jobListeners = new JobListeners();
+        lock = new Object();
+        lockManager = new LockManager();
+
+        instance = this;
+
+        initDebugOptions();
+        synchronized (lock) {
+            waiting = new JobQueue(false);
+            sleeping = new JobQueue(true);
+            running = new HashSet(10);
+            pool = new WorkerPool(this);
+        }
+        pool.setDaemon(JobOSGiUtils.getDefault().useDaemonThreads());
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.IJobManager#addJobListener(org.eclipse.core.runtime.jobs.IJobChangeListener)
+     */
+    public void addJobChangeListener(IJobChangeListener listener) {
+        jobListeners.add(listener);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.IJobManager#beginRule(org.eclipse.core.runtime.jobs.ISchedulingRule, org.eclipse.core.runtime.IProgressMonitor)
+     */
+    public void beginRule(ISchedulingRule rule, IProgressMonitor monitor) {
+        validateRule(rule);
+        implicitJobs.begin(rule, monitorFor(monitor), false);
+    }
+
+    /**
+     * Cancels a job
+     */
+    protected bool cancel(InternalJob job) {
+        IProgressMonitor monitor = null;
+        synchronized (lock) {
+            switch (job.getState_package()) {
+                case Job.NONE :
+                    return true;
+                case Job.RUNNING :
+                    //cannot cancel a job that has already started (as opposed to ABOUT_TO_RUN)
+                    if (job.internalGetState() is Job.RUNNING) {
+                        monitor = job.getProgressMonitor();
+                        break;
+                    }
+                    //signal that the job should be canceled before it gets a chance to run
+                    job.setAboutToRunCanceled(true);
+                    return true;
+                default :
+                    changeState(job, Job.NONE);
+            }
+        }
+        //call monitor outside sync block
+        if (monitor !is null) {
+            if (!monitor.isCanceled()) {
+                monitor.setCanceled(true);
+                job.canceling();
+            }
+            return false;
+        }
+        //only notify listeners if the job was waiting or sleeping
+        jobListeners.done(cast(Job) job, Status.CANCEL_STATUS, false);
+        return true;
+    }
+    package bool cancel_package(InternalJob job) {
+        return cancel(job);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.IJobManager#cancel(java.lang.String)
+     */
+    public void cancel(Object family) {
+        //don't synchronize because cancel calls listeners
+        for (Iterator it = select(family).iterator(); it.hasNext();)
+            cancel(cast(InternalJob) it.next());
+    }
+
+    /**
+     * Atomically updates the state of a job, adding or removing from the
+     * necessary queues or sets.
+     */
+    private void changeState(InternalJob job, int newState) {
+        bool blockedJobs = false;
+        synchronized (lock) {
+            int oldState = job.internalGetState();
+            switch (oldState) {
+                case Job.NONE :
+                case InternalJob.ABOUT_TO_SCHEDULE :
+                    break;
+                case InternalJob.BLOCKED :
+                    //remove this job from the linked list of blocked jobs
+                    job.remove();
+                    break;
+                case Job.WAITING :
+                    try {
+                        waiting.remove(job);
+                    } catch (RuntimeException e) {
+                        Assert.isLegal(false, "Tried to remove a job that wasn't in the queue"); //$NON-NLS-1$
+                    }
+                    break;
+                case Job.SLEEPING :
+                    try {
+                        sleeping.remove(job);
+                    } catch (RuntimeException e) {
+                        Assert.isLegal(false, "Tried to remove a job that wasn't in the queue"); //$NON-NLS-1$
+                    }
+                    break;
+                case Job.RUNNING :
+                case InternalJob.ABOUT_TO_RUN :
+                    running.remove(job);
+                    //add any blocked jobs back to the wait queue
+                    InternalJob blocked = job.previous();
+                    job.remove();
+                    blockedJobs = blocked !is null;
+                    while (blocked !is null) {
+                        InternalJob previous = blocked.previous();
+                        changeState(blocked, Job.WAITING);
+                        blocked = previous;
+                    }
+                    break;
+                default :
+                    Assert.isLegal(false, Format("Invalid job state: {}, state: {}", job, oldState)); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+            job.internalSetState(newState);
+            switch (newState) {
+                case Job.NONE :
+                    job.setStartTime(InternalJob.T_NONE);
+                    job.setWaitQueueStamp(InternalJob.T_NONE);
+                case InternalJob.BLOCKED :
+                    break;
+                case Job.WAITING :
+                    waiting.enqueue(job);
+                    break;
+                case Job.SLEEPING :
+                    try {
+                        sleeping.enqueue(job);
+                    } catch (RuntimeException e) {
+                        throw new RuntimeException(Format("Error changing from state: ", oldState)); //$NON-NLS-1$
+                    }
+                    break;
+                case Job.RUNNING :
+                case InternalJob.ABOUT_TO_RUN :
+                    job.setStartTime(InternalJob.T_NONE);
+                    job.setWaitQueueStamp(InternalJob.T_NONE);
+                    running.add(job);
+                    break;
+                case InternalJob.ABOUT_TO_SCHEDULE :
+                    break;
+                default :
+                    Assert.isLegal(false, Format("Invalid job state: {}, state: {}", job, newState)); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+        }
+        //notify queue outside sync block
+        if (blockedJobs)
+            pool.jobQueued();
+    }
+
+    /**
+     * Returns a new progress monitor for this job, belonging to the given
+     * progress group.  Returns null if it is not a valid time to set the job's group.
+     */
+    protected IProgressMonitor createMonitor(InternalJob job, IProgressMonitor group, int ticks) {
+        synchronized (lock) {
+            //group must be set before the job is scheduled
+            //this includes the ABOUT_TO_SCHEDULE state, during which it is still
+            //valid to set the progress monitor
+            if (job.getState_package() !is Job.NONE)
+                return null;
+            IProgressMonitor monitor = null;
+            if (progressProvider !is null)
+                monitor = progressProvider.createMonitor(cast(Job) job, group, ticks);
+            if (monitor is null)
+                monitor = new NullProgressMonitor();
+            return monitor;
+        }
+    }
+    package IProgressMonitor createMonitor_package(InternalJob job, IProgressMonitor group, int ticks) {
+        return createMonitor(job, group, ticks);
+    }
+
+    /**
+     * Returns a new progress monitor for this job.  Never returns null.
+     */
+    private IProgressMonitor createMonitor(Job job) {
+        IProgressMonitor monitor = null;
+        if (progressProvider !is null)
+            monitor = progressProvider.createMonitor(job);
+        if (monitor is null)
+            monitor = new NullProgressMonitor();
+        return monitor;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.IJobManager#createProgressGroup()
+     */
+    public IProgressMonitor createProgressGroup() {
+        if (progressProvider !is null)
+            return progressProvider.createProgressGroup();
+        return new NullProgressMonitor();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.IJobManager#currentJob()
+     */
+    public Job currentJob() {
+        JThread current = JThread.currentThread();
+        if (cast(Worker)current )
+            return (cast(Worker) current).currentJob();
+        synchronized (lock) {
+            for (Iterator it = running.iterator(); it.hasNext();) {
+                Job job = cast(Job) it.next();
+                if (job.getThread() is current)
+                    return job;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the delay in milliseconds that a job with a given priority can
+     * tolerate waiting.
+     */
+    private long delayFor(int priority) {
+        //these values may need to be tweaked based on machine speed
+        switch (priority) {
+            case Job.INTERACTIVE :
+                return 0L;
+            case Job.SHORT :
+                return 50L;
+            case Job.LONG :
+                return 100L;
+            case Job.BUILD :
+                return 500L;
+            case Job.DECORATE :
+                return 1000L;
+            default :
+                Assert.isTrue(false, Format("Job has invalid priority: {}", priority)); //$NON-NLS-1$
+                return 0;
+        }
+    }
+
+    /**
+     * Performs the scheduling of a job.  Does not perform any notifications.
+     */
+    private void doSchedule(InternalJob job, long delay) {
+        synchronized (lock) {
+            //if it's a decoration job with no rule, don't run it right now if the system is busy
+            if (job.getPriority() is Job.DECORATE && job.getRule() is null) {
+                long minDelay = running.size() * 100;
+                delay = Math.max(delay, minDelay);
+            }
+            if (delay > 0) {
+                job.setStartTime(System.currentTimeMillis() + delay);
+                changeState(job, Job.SLEEPING);
+            } else {
+                job.setStartTime(System.currentTimeMillis() + delayFor(job.getPriority()));
+                job.setWaitQueueStamp(waitQueueCounter++);
+                changeState(job, Job.WAITING);
+            }
+        }
+    }
+
+    /**
+     * Shuts down the job manager.  Currently running jobs will be told
+     * to stop, but worker threads may still continue processing.
+     * (note: This implemented IJobManager.shutdown which was removed
+     * due to problems caused by premature shutdown)
+     */
+    private void doShutdown() {
+        Job[] toCancel = null;
+        synchronized (lock) {
+            if (active) {
+                active = false;
+                //cancel all running jobs
+                toCancel = arraycast!(Job)( running.toArray());
+                //clean up
+                sleeping.clear();
+                waiting.clear();
+                running.clear();
+            }
+        }
+
+        // Give running jobs a chance to finish. Wait 0.1 seconds for up to 3 times.
+        if (toCancel !is null && toCancel.length > 0) {
+            for (int i = 0; i < toCancel.length; i++) {
+                cancel(cast(InternalJob)toCancel[i]); // cancel jobs outside sync block to avoid deadlock
+            }
+
+            for (int waitAttempts = 0; waitAttempts < 3; waitAttempts++) {
+                JThread.yield();
+                synchronized (lock) {
+                    if (running.isEmpty())
+                        break;
+                }
+                if (DEBUG_SHUTDOWN) {
+                    JobManager.debug_(Format("Shutdown - job wait cycle #{}", (waitAttempts + 1))); //$NON-NLS-1$
+                    Job[] stillRunning = null;
+                    synchronized (lock) {
+                        stillRunning = arraycast!(Job)( running.toArray());
+                    }
+                    if (stillRunning !is null) {
+                        for (int j = 0; j < stillRunning.length; j++) {
+                            JobManager.debug_(Format("\tJob: {}", printJobName(stillRunning[j]))); //$NON-NLS-1$
+                        }
+                    }
+                }
+                try {
+                    JThread.sleep(100);
+                } catch (InterruptedException e) {
+                    //ignore
+                }
+                JThread.yield();
+            }
+
+            synchronized (lock) { // retrieve list of the jobs that are still running
+                toCancel = arraycast!(Job)( running.toArray());
+            }
+        }
+
+        if (toCancel !is null) {
+            for (int i = 0; i < toCancel.length; i++) {
+                String jobName = printJobName(toCancel[i]);
+                //this doesn't need to be translated because it's just being logged
+                String msg = "Job found still running after platform shutdown.  Jobs should be canceled by the plugin that scheduled them during shutdown: " ~ jobName; //$NON-NLS-1$
+                RuntimeLog.log(new Status(IStatus.WARNING, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, msg, null));
+
+                // TODO the RuntimeLog.log in its current implementation won't produce a log
+                // during this stage of shutdown. For now add a standard error output.
+                // One the logging story is improved, the System.err output below can be removed:
+                Stderr.formatln("{}", msg);
+            }
+        }
+
+        pool.shutdown_package();
+    }
+
+    /**
+     * Indicates that a job was running, and has now finished.  Note that this method
+     * can be called under OutOfMemoryError conditions and thus must be paranoid
+     * about allocating objects.
+     */
+    protected void endJob(InternalJob job, IStatus result, bool notify) {
+        long rescheduleDelay = InternalJob.T_NONE;
+        synchronized (lock) {
+            //if the job is finishing asynchronously, there is nothing more to do for now
+            if (result is Job.ASYNC_FINISH)
+                return;
+            //if job is not known then it cannot be done
+            if (job.getState_package() is Job.NONE)
+                return;
+            if (JobManager.DEBUG && notify)
+                JobManager.debug_(Format("Ending job: {}", job)); //$NON-NLS-1$
+            job.setResult(result);
+            job.setProgressMonitor(null);
+            job.setThread_package(null);
+            rescheduleDelay = job.getStartTime();
+            changeState(job, Job.NONE);
+        }
+        //notify listeners outside sync block
+        final bool reschedule = active && rescheduleDelay > InternalJob.T_NONE && job.shouldSchedule_package();
+        if (notify)
+            jobListeners.done(cast(Job) job, result, reschedule);
+        //reschedule the job if requested and we are still active
+        if (reschedule)
+            schedule(job, rescheduleDelay, reschedule);
+    }
+    package void endJob_package(InternalJob job, IStatus result, bool notify) {
+        endJob(job, result, notify);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.IJobManager#endRule(org.eclipse.core.runtime.jobs.ISchedulingRule)
+     */
+    public void endRule(ISchedulingRule rule) {
+        implicitJobs.end(rule, false);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.IJobManager#find(java.lang.String)
+     */
+    public Job[] find(Object family) {
+        List members = select(family);
+        return arraycast!(Job)( members.toArray());
+    }
+
+    /**
+     * Returns a running or blocked job whose scheduling rule conflicts with the
+     * scheduling rule of the given waiting job.  Returns null if there are no
+     * conflicting jobs.  A job can only run if there are no running jobs and no blocked
+     * jobs whose scheduling rule conflicts with its rule.
+     */
+    protected InternalJob findBlockingJob(InternalJob waitingJob) {
+        if (waitingJob.getRule() is null)
+            return null;
+        synchronized (lock) {
+            if (running.isEmpty())
+                return null;
+            //check the running jobs
+            bool hasBlockedJobs = false;
+            for (Iterator it = running.iterator(); it.hasNext();) {
+                InternalJob job = cast(InternalJob) it.next();
+                if (waitingJob.isConflicting(job))
+                    return job;
+                if (!hasBlockedJobs)
+                    hasBlockedJobs = job.previous() !is null;
+            }
+            //there are no blocked jobs, so we are done
+            if (!hasBlockedJobs)
+                return null;
+            //check all jobs blocked by running jobs
+            for (Iterator it = running.iterator(); it.hasNext();) {
+                InternalJob job = cast(InternalJob) it.next();
+                while (true) {
+                    job = job.previous();
+                    if (job is null)
+                        break;
+                    if (waitingJob.isConflicting(job))
+                        return job;
+                }
+            }
+        }
+        return null;
+    }
+    package InternalJob findBlockingJob_package(InternalJob waitingJob) {
+        return findBlockingJob(waitingJob);
+    }
+
+    public LockManager getLockManager() {
+        return lockManager;
+    }
+
+    private void initDebugOptions() {
+        DEBUG = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_DEBUG_JOBS, false);
+        DEBUG_BEGIN_END = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_DEBUG_BEGIN_END, false);
+        DEBUG_DEADLOCK = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_DEADLOCK_ERROR, false);
+        DEBUG_LOCKS = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_LOCKS, false);
+        DEBUG_TIMING = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_DEBUG_JOBS_TIMING, false);
+        DEBUG_SHUTDOWN = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_SHUTDOWN, false);
+    }
+
+    /**
+     * Returns whether the job manager is active (has not been shutdown).
+     */
+    protected bool isActive() {
+        return active;
+    }
+    package bool isActive_package() {
+        return isActive();
+    }
+
+    /**
+     * Returns true if the given job is blocking the execution of a non-system
+     * job.
+     */
+    protected bool isBlocking(InternalJob runningJob) {
+        synchronized (lock) {
+            // if this job isn't running, it can't be blocking anyone
+            if (runningJob.getState_package() !is Job.RUNNING)
+                return false;
+            // if any job is queued behind this one, it is blocked by it
+            InternalJob previous = runningJob.previous();
+            while (previous !is null) {
+                // ignore jobs of lower priority (higher priority value means lower priority)
+                if (previous.getPriority() < runningJob.getPriority()) {
+                    if (!previous.isSystem_package())
+                        return true;
+                    // implicit jobs should interrupt unless they act on behalf of system jobs
+                    if (cast(ThreadJob)previous  && (cast(ThreadJob) previous).shouldInterrupt())
+                        return true;
+                }
+                previous = previous.previous();
+            }
+            // none found
+            return false;
+        }
+    }
+    package bool isBlocking_package(InternalJob runningJob) {
+        return isBlocking(runningJob);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.IJobManager#isIdle()
+     */
+    public bool isIdle() {
+        synchronized (lock) {
+            return running.isEmpty() && waiting.isEmpty();
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.IJobManager#isSuspended()
+     */
+    public bool isSuspended() {
+        synchronized (lock) {
+            return suspended;
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.Job#job(org.eclipse.core.runtime.jobs.Job)
+     */
+    protected void join(InternalJob job) {
+        IJobChangeListener listener;
+        Semaphore barrier;
+        synchronized (lock) {
+            int state = job.getState_package();
+            if (state is Job.NONE)
+                return;
+            //don't join a waiting or sleeping job when suspended (deadlock risk)
+            if (suspended && state !is Job.RUNNING)
+                return;
+            //it's an error for a job to join itself
+            if (state is Job.RUNNING && job.getThread_package() is JThread.currentThread())
+                throw new IllegalStateException("Job attempted to join itself"); //$NON-NLS-1$
+            //the semaphore will be released when the job is done
+            barrier = new Semaphore(null);
+            listener = new class(barrier) JobChangeAdapter {
+                Semaphore barrier_;
+                this( Semaphore a ){
+                    barrier_ = a;
+                }
+                public void done(IJobChangeEvent event) {
+                    barrier_.release();
+                }
+            };
+            job.addJobChangeListener_package(listener);
+            //compute set of all jobs that must run before this one
+            //add a listener that removes jobs from the blocking set when they finish
+        }
+        //wait until listener notifies this thread.
+        try {
+            while (true) {
+                //notify hook to service pending syncExecs before falling asleep
+                lockManager.aboutToWait(job.getThread_package());
+                try {
+                    if (barrier.acquire(Long.MAX_VALUE))
+                        break;
+                } catch (InterruptedException e) {
+                    //loop and keep trying
+                }
+            }
+        } finally {
+            lockManager.aboutToRelease();
+            job.removeJobChangeListener_package(listener);
+        }
+    }
+    package void join_package(InternalJob job) {
+        join(job);
+    }
+
+    /* (non-Javadoc)
+     * @see IJobManager#join(String, IProgressMonitor)
+     */
+    public void join(Object family_, IProgressMonitor monitor) {
+        monitor = monitorFor(monitor);
+        IJobChangeListener listener = null;
+        Set jobs_;
+        int jobCount;
+        Job blocking = null;
+        synchronized (lock) {
+            //don't join a waiting or sleeping job when suspended (deadlock risk)
+            int states = suspended ? Job.RUNNING : Job.RUNNING | Job.WAITING | Job.SLEEPING;
+            jobs_ = Collections.synchronizedSet(new HashSet(select(family_, states)));
+            jobCount = jobs_.size();
+            if (jobCount > 0) {
+                //if there is only one blocking job, use it in the blockage callback below
+                if (jobCount is 1)
+                    blocking = cast(Job) jobs_.iterator().next();
+                listener = new class(family_, jobs_ )JobChangeAdapter {
+                    Object family;
+                    Set jobs;
+                    this(Object a, Set b){
+                        family = a;
+                        jobs = b;
+                    }
+                    public void done(IJobChangeEvent event) {
+                        //don't remove from list if job is being rescheduled
+                        if (!(cast(JobChangeEvent) event).reschedule)
+                            jobs.remove(event.getJob());
+                    }
+
+                    //update the list of jobs if new ones are added during the join
+                    public void scheduled(IJobChangeEvent event) {
+                        //don't add to list if job is being rescheduled
+                        if ((cast(JobChangeEvent) event).reschedule)
+                            return;
+                        Job job = event.getJob();
+                        if (job.belongsTo(family))
+                            jobs.add(job);
+                    }
+                };
+                addJobChangeListener(listener);
+            }
+        }
+        if (jobCount is 0) {
+            //use up the monitor outside synchronized block because monitors call untrusted code
+            monitor.beginTask(JobMessages.jobs_blocked0, 1);
+            monitor.done();
+            return;
+        }
+        //spin until all jobs are completed
+        try {
+            monitor.beginTask(JobMessages.jobs_blocked0, jobCount);
+            monitor.subTask(NLS.bind(JobMessages.jobs_waitFamSub, Integer.toString(jobCount)));
+            reportBlocked(monitor, blocking);
+            int jobsLeft;
+            int reportedWorkDone = 0;
+            while ((jobsLeft = jobs_.size()) > 0) {
+                //don't let there be negative work done if new jobs have
+                //been added since the join began
+                int actualWorkDone = Math.max(0, jobCount - jobsLeft);
+                if (reportedWorkDone < actualWorkDone) {
+                    monitor.worked(actualWorkDone - reportedWorkDone);
+                    reportedWorkDone = actualWorkDone;
+                    monitor.subTask(NLS.bind(JobMessages.jobs_waitFamSub, Integer.toString(jobsLeft)));
+                }
+
+                if (JThread.interrupted())
+                    throw new InterruptedException();
+                if (monitor.isCanceled())
+                    throw new OperationCanceledException();
+                //notify hook to service pending syncExecs before falling asleep
+                lockManager.aboutToWait(null);
+                JThread.sleep(100);
+            }
+        } finally {
+            lockManager.aboutToRelease();
+            removeJobChangeListener(listener);
+            reportUnblocked(monitor);
+            monitor.done();
+        }
+    }
+
+    /**
+     * Returns a non-null progress monitor instance.  If the monitor is null,
+     * returns the default monitor supplied by the progress provider, or a
+     * NullProgressMonitor if no default monitor is available.
+     */
+    private IProgressMonitor monitorFor(IProgressMonitor monitor) {
+        if (monitor is null || (cast(NullProgressMonitor)monitor )) {
+            if (progressProvider !is null) {
+                try {
+                    monitor = progressProvider.getDefaultMonitor();
+                } catch (Exception e) {
+                    String msg = NLS.bind(JobMessages.meta_pluginProblems, JobManager.PI_JOBS);
+                    RuntimeLog.log(new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, msg, e));
+                }
+            }
+        }
+
+        if (monitor is null)
+            return new NullProgressMonitor();
+        return monitor;
+    }
+
+    /* (non-Javadoc)
+     * @see IJobManager#newLock(java.lang.String)
+     */
+    public ILock newLock() {
+        return lockManager.newLock();
+    }
+
+    /**
+     * Removes and returns the first waiting job in the queue. Returns null if there
+     * are no items waiting in the queue.  If an item is removed from the queue,
+     * it is moved to the running jobs list.
+     */
+    private Job nextJob() {
+        synchronized (lock) {
+            //do nothing if the job manager is suspended
+            if (suspended)
+                return null;
+            //tickle the sleep queue to see if anyone wakes up
+            long now = System.currentTimeMillis();
+            InternalJob job = sleeping.peek();
+            while (job !is null && job.getStartTime() < now) {
+                job.setStartTime(now + delayFor(job.getPriority()));
+                job.setWaitQueueStamp(waitQueueCounter++);
+                changeState(job, Job.WAITING);
+                job = sleeping.peek();
+            }
+            //process the wait queue until we find a job whose rules are satisfied.
+            while ((job = waiting.peek()) !is null) {
+                InternalJob blocker = findBlockingJob(job);
+                if (blocker is null)
+                    break;
+                //queue this job after the job that's blocking it
+                changeState(job, InternalJob.BLOCKED);
+                //assert job does not already belong to some other data structure
+                Assert.isTrue(job.next() is null);
+                Assert.isTrue(job.previous() is null);
+                blocker.addLast(job);
+            }
+            //the job to run must be in the running list before we exit
+            //the sync block, otherwise two jobs with conflicting rules could start at once
+            if (job !is null) {
+                changeState(job, InternalJob.ABOUT_TO_RUN);
+                if (JobManager.DEBUG)
+                    JobManager.debug_(Format("Starting job: {}", job)); //$NON-NLS-1$
+            }
+            return cast(Job) job;
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.IJobManager#removeJobListener(org.eclipse.core.runtime.jobs.IJobChangeListener)
+     */
+    public void removeJobChangeListener(IJobChangeListener listener) {
+        jobListeners.remove(listener);
+    }
+
+    /**
+     * Report to the progress monitor that this thread is blocked, supplying
+     * an information message, and if possible the job that is causing the blockage.
+     * Important: An invocation of this method MUST be followed eventually be
+     * an invocation of reportUnblocked.
+     * @param monitor The monitor to report blocking to
+     * @param blockingJob The job that is blocking this thread, or <code>null</code>
+     * @see #reportUnblocked
+     */
+    final void reportBlocked(IProgressMonitor monitor, InternalJob blockingJob) {
+        if (!(cast(IProgressMonitorWithBlocking)monitor ))
+            return;
+        IStatus reason;
+        if (blockingJob is null || cast(ThreadJob)blockingJob || blockingJob.isSystem_package()) {
+            reason = new Status(IStatus.INFO, JobManager.PI_JOBS, 1, JobMessages.jobs_blocked0, null);
+        } else {
+            String msg = NLS.bind(JobMessages.jobs_blocked1, blockingJob.getName_package());
+            reason = new JobStatus(IStatus.INFO, cast(Job) blockingJob, msg);
+        }
+        (cast(IProgressMonitorWithBlocking) monitor).setBlocked(reason);
+    }
+
+    /**
+     * Reports that this thread was blocked, but is no longer blocked and is able
+     * to proceed.
+     * @param monitor The monitor to report unblocking to.
+     * @see #reportBlocked
+     */
+    final void reportUnblocked(IProgressMonitor monitor) {
+        if (cast(IProgressMonitorWithBlocking)monitor )
+            (cast(IProgressMonitorWithBlocking) monitor).clearBlocked();
+    }
+
+    /*(non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.IJobManager#resume()
+     */
+    public final void resume() {
+        synchronized (lock) {
+            suspended = false;
+            //poke the job pool
+            pool.jobQueued();
+        }
+    }
+
+    /** (non-Javadoc)
+     * @deprecated this method should not be used
+     * @see org.eclipse.core.runtime.jobs.IJobManager#resume(org.eclipse.core.runtime.jobs.ISchedulingRule)
+     */
+    public final void resume(ISchedulingRule rule) {
+        implicitJobs.resume(rule);
+    }
+
+    /**
+     * Attempts to immediately start a given job.  Returns true if the job was
+     * successfully started, and false if it could not be started immediately
+     * due to a currently running job with a conflicting rule.  Listeners will never
+     * be notified of jobs that are run in this way.
+     */
+    protected bool runNow(InternalJob job) {
+        synchronized (lock) {
+            //cannot start if there is a conflicting job
+            if (findBlockingJob(job) !is null)
+                return false;
+            changeState(job, Job.RUNNING);
+            job.setProgressMonitor(new NullProgressMonitor());
+            job.run_package(null);
+        }
+        return true;
+    }
+    package bool runNow_package(InternalJob job) {
+        return runNow(job);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.Job#schedule(long)
+     */
+    protected void schedule(InternalJob job, long delay, bool reschedule) {
+        if (!active)
+            throw new IllegalStateException("Job manager has been shut down."); //$NON-NLS-1$
+        Assert.isNotNull(job, "Job is null"); //$NON-NLS-1$
+        Assert.isLegal(delay >= 0, "Scheduling delay is negative"); //$NON-NLS-1$
+        synchronized (lock) {
+            //if the job is already running, set it to be rescheduled when done
+            if (job.getState_package() is Job.RUNNING) {
+                job.setStartTime(delay);
+                return;
+            }
+            //can't schedule a job that is waiting or sleeping
+            if (job.internalGetState() !is Job.NONE)
+                return;
+            if (JobManager.DEBUG)
+                JobManager.debug_(Format("Scheduling job: {}", job)); //$NON-NLS-1$
+            //remember that we are about to schedule the job
+            //to prevent multiple schedule attempts from succeeding (bug 68452)
+            changeState(job, InternalJob.ABOUT_TO_SCHEDULE);
+        }
+        //notify listeners outside sync block
+        jobListeners.scheduled(cast(Job) job, delay, reschedule);
+        //schedule the job
+        doSchedule(job, delay);
+        //call the pool outside sync block to avoid deadlock
+        pool.jobQueued();
+    }
+    package void schedule_package(InternalJob job, long delay, bool reschedule) {
+        schedule(job, delay, reschedule);
+    }
+
+    /**
+     * Adds all family members in the list of jobs to the collection
+     */
+    private void select(List members, Object family, InternalJob firstJob, int stateMask) {
+        if (firstJob is null)
+            return;
+        InternalJob job = firstJob;
+        do {
+            //note that job state cannot be NONE at this point
+            if ((family is null || job.belongsTo_package(family)) && ((job.getState_package() & stateMask) !is 0))
+                members.add(job);
+            job = job.previous();
+        } while (job !is null && job !is firstJob);
+    }
+
+    /**
+     * Returns a list of all jobs known to the job manager that belong to the given family.
+     */
+    private List select(Object family) {
+        return select(family, Job.WAITING | Job.SLEEPING | Job.RUNNING);
+    }
+
+    /**
+     * Returns a list of all jobs known to the job manager that belong to the given
+     * family and are in one of the provided states.
+     */
+    private List select(Object family, int stateMask) {
+        List members = new ArrayList();
+        synchronized (lock) {
+            if ((stateMask & Job.RUNNING) !is 0) {
+                for (Iterator it = running.iterator(); it.hasNext();) {
+                    select(members, family, cast(InternalJob) it.next(), stateMask);
+                }
+            }
+            if ((stateMask & Job.WAITING) !is 0)
+                select(members, family, waiting.peek(), stateMask);
+            if ((stateMask & Job.SLEEPING) !is 0)
+                select(members, family, sleeping.peek(), stateMask);
+        }
+        return members;
+    }
+
+    /* (non-Javadoc)
+     * @see IJobManager#setLockListener(LockListener)
+     */
+    public void setLockListener(LockListener listener) {
+        lockManager.setLockListener(listener);
+    }
+
+    /**
+     * Changes a job priority.
+     */
+    protected void setPriority(InternalJob job, int newPriority) {
+        synchronized (lock) {
+            int oldPriority = job.getPriority();
+            if (oldPriority is newPriority)
+                return;
+            job.internalSetPriority(newPriority);
+            //if the job is waiting to run, re-shuffle the queue
+            if (job.getState_package() is Job.WAITING) {
+                long oldStart = job.getStartTime();
+                job.setStartTime(oldStart + (delayFor(newPriority) - delayFor(oldPriority)));
+                waiting.resort(job);
+            }
+        }
+    }
+    package void setPriority_package(InternalJob job, int newPriority) {
+        setPriority(job, newPriority);
+    }
+
+    /* (non-Javadoc)
+     * @see IJobManager#setProgressProvider(IProgressProvider)
+     */
+    public void setProgressProvider(ProgressProvider provider) {
+        progressProvider = provider;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#setRule
+     */
+    public void setRule(InternalJob job, ISchedulingRule rule) {
+        synchronized (lock) {
+            //cannot change the rule of a job that is already running
+            Assert.isLegal(job.getState_package() is Job.NONE);
+            validateRule(rule);
+            job.internalSetRule(rule);
+        }
+    }
+
+    /**
+     * Puts a job to sleep. Returns true if the job was successfully put to sleep.
+     */
+    protected bool sleep(InternalJob job) {
+        synchronized (lock) {
+            switch (job.getState_package()) {
+                case Job.RUNNING :
+                    //cannot be paused if it is already running (as opposed to ABOUT_TO_RUN)
+                    if (job.internalGetState() is Job.RUNNING)
+                        return false;
+                    //job hasn't started running yet (aboutToRun listener)
+                    break;
+                case Job.SLEEPING :
+                    //update the job wake time
+                    job.setStartTime(InternalJob.T_INFINITE);
+                    //change state again to re-shuffle the sleep queue
+                    changeState(job, Job.SLEEPING);
+                    return true;
+                case Job.NONE :
+                    return true;
+                case Job.WAITING :
+                    //put the job to sleep
+                    break;
+                default:
+            }
+            job.setStartTime(InternalJob.T_INFINITE);
+            changeState(job, Job.SLEEPING);
+        }
+        jobListeners.sleeping(cast(Job) job);
+        return true;
+    }
+    package bool sleep_package(InternalJob job) {
+        return sleep(job);
+    }
+
+    /* (non-Javadoc)
+     * @see IJobManager#sleep(String)
+     */
+    public void sleep(Object family) {
+        //don't synchronize because sleep calls listeners
+        for (Iterator it = select(family).iterator(); it.hasNext();) {
+            sleep(cast(InternalJob) it.next());
+        }
+    }
+
+    /**
+     * Returns the estimated time in milliseconds before the next job is scheduled
+     * to wake up. The result may be negative.  Returns InternalJob.T_INFINITE if
+     * there are no sleeping or waiting jobs.
+     */
+    protected long sleepHint() {
+        synchronized (lock) {
+            //wait forever if job manager is suspended
+            if (suspended)
+                return InternalJob.T_INFINITE;
+            if (!waiting.isEmpty())
+                return 0L;
+            //return the anticipated time that the next sleeping job will wake
+            InternalJob next = sleeping.peek();
+            if (next is null)
+                return InternalJob.T_INFINITE;
+            return next.getStartTime() - System.currentTimeMillis();
+        }
+    }
+    package long sleepHint_package() {
+        return sleepHint();
+    }
+    /**
+     * Returns the next job to be run, or null if no jobs are waiting to run.
+     * The worker must call endJob when the job is finished running.
+     */
+    protected Job startJob() {
+        Job job = null;
+        while (true) {
+            job = nextJob();
+            if (job is null)
+                return null;
+            //must perform this outside sync block because it is third party code
+            if (job.shouldRun()) {
+                //check for listener veto
+                jobListeners.aboutToRun(job);
+                //listeners may have canceled or put the job to sleep
+                synchronized (lock) {
+                    if (job.getState() is Job.RUNNING) {
+                        InternalJob internal = job;
+                        if (internal.isAboutToRunCanceled()) {
+                            internal.setAboutToRunCanceled(false);
+                            //fall through and end the job below
+                        } else {
+                            internal.setProgressMonitor(createMonitor(job));
+                            //change from ABOUT_TO_RUN to RUNNING
+                            internal.internalSetState(Job.RUNNING);
+                            break;
+                        }
+                    }
+                }
+            }
+            if (job.getState() !is Job.SLEEPING) {
+                //job has been vetoed or canceled, so mark it as done
+                endJob(job, Status.CANCEL_STATUS, true);
+                continue;
+            }
+        }
+        jobListeners.running(job);
+        return job;
+
+    }
+    package Job startJob_package() {
+        return startJob();
+    }
+
+    /* non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.IJobManager#suspend()
+     */
+    public final void suspend() {
+        synchronized (lock) {
+            suspended = true;
+        }
+    }
+
+    /** (non-Javadoc)
+     * @deprecated this method should not be used
+     * @see org.eclipse.core.runtime.jobs.IJobManager#suspend(org.eclipse.core.runtime.jobs.ISchedulingRule, org.eclipse.core.runtime.IProgressMonitor)
+     */
+    public final void suspend(ISchedulingRule rule, IProgressMonitor monitor) {
+        Assert.isNotNull(cast(Object)rule);
+        implicitJobs.suspend(rule, monitorFor(monitor));
+    }
+
+    /* non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.IJobManager#transferRule()
+     */
+    public void transferRule(ISchedulingRule rule, JThread destinationThread) {
+        implicitJobs.transfer(rule, destinationThread);
+    }
+
+    /**
+     * Validates that the given scheduling rule obeys the constraints of
+     * scheduling rules as described in the <code>ISchedulingRule</code>
+     * javadoc specification.
+     */
+    private void validateRule(ISchedulingRule rule) {
+        //null rule always valid
+        if (rule is null)
+            return;
+        initNullRule();
+        //contains method must be reflexive
+        Assert.isLegal(rule.contains(rule));
+        //contains method must return false when given an unknown rule
+        Assert.isLegal(!rule.contains(nullRule));
+        //isConflicting method must be reflexive
+        Assert.isLegal(rule.isConflicting(rule));
+        //isConflicting method must return false when given an unknown rule
+        Assert.isLegal(!rule.isConflicting(nullRule));
+    }
+
+    /* (non-Javadoc)
+     * @see Job#wakeUp(long)
+     */
+    protected void wakeUp(InternalJob job, long delay) {
+        Assert.isLegal(delay >= 0, "Scheduling delay is negative"); //$NON-NLS-1$
+        synchronized (lock) {
+            //cannot wake up if it is not sleeping
+            if (job.getState_package() !is Job.SLEEPING)
+                return;
+            doSchedule(job, delay);
+        }
+        //call the pool outside sync block to avoid deadlock
+        pool.jobQueued();
+
+        //only notify of wake up if immediate
+        if (delay is 0)
+            jobListeners.awake(cast(Job) job);
+    }
+    package void wakeUp_package(InternalJob job, long delay) {
+        wakeUp(job, delay);
+    }
+
+    /* (non-Javadoc)
+     * @see IJobFamily#wakeUp(String)
+     */
+    public void wakeUp(Object family) {
+        //don't synchronize because wakeUp calls listeners
+        for (Iterator it = select(family).iterator(); it.hasNext();) {
+            wakeUp(cast(InternalJob) it.next(), 0L);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobMessages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,62 @@
+/**********************************************************************
+ * 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ **********************************************************************/
+module org.eclipse.core.internal.jobs.JobMessages;
+
+import java.lang.JThread;
+import tango.io.Stdout;
+import tango.time.WallClock;
+import tango.text.convert.TimeStamp;
+
+import java.lang.all;
+
+import org.osgi.util.NLS;
+
+/**
+ * Job plugin message catalog
+ */
+public class JobMessages : NLS {
+    private static const String BUNDLE_NAME = "org.eclipse.core.internal.jobs.messages"; //$NON-NLS-1$
+
+    // Job Manager and Locks
+    public static String jobs_blocked0 = "The user operation is waiting for background work to complete.";
+    public static String jobs_blocked1 = "The user operation is waiting for \"{0}\" to complete.";
+    public static String jobs_internalError = "An internal error occured during: \"{0}\".";
+    public static String jobs_waitFamSub =  "{0} work item(s) left.";
+
+    // metadata
+    public static String meta_pluginProblems = "Problems occured when invoking code from plug-in: \"{0}\".";
+
+//     static this() {
+//         // load message values from bundle file
+//         reloadMessages();
+//     }
+
+    public static void reloadMessages() {
+        implMissing(__FILE__,__LINE__);
+//         NLS.initializeMessages(BUNDLE_NAME, import(BUNDLE_NAME~".properties"));
+    }
+
+    /**
+     * Print a debug message to the console.
+     * Pre-pend the message with the current date and the name of the current thread.
+     */
+    public static void message(String message) {
+        StringBuffer buffer = new StringBuffer();
+        char[30] buf;
+        buffer.append(tango.text.convert.TimeStamp.format( buf, WallClock.now()));
+        buffer.append(" - ["); //$NON-NLS-1$
+        buffer.append(JThread.currentThread().getName());
+        buffer.append("] "); //$NON-NLS-1$
+        buffer.append(message);
+        Stdout.formatln(buffer.toString());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobOSGiUtils.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.core.internal.jobs.JobOSGiUtils;
+
+import java.lang.all;
+import java.util.Set;
+
+// import org.osgi.framework.Bundle;
+// import org.osgi.framework.BundleContext;
+// import org.osgi.service.packageadmin.PackageAdmin;
+// import org.osgi.util.tracker.ServiceTracker;
+
+import org.eclipse.core.runtime.jobs.IJobManager;
+// import org.eclipse.osgi.service.debug.DebugOptions;
+
+/**
+ * The class contains a set of helper methods for the runtime Jobs plugin.
+ * The following utility methods are supplied:
+ * - provides access to debug options
+ * - provides some bundle discovery functionality
+ *
+ * The closeServices() method should be called before the plugin is stopped.
+ *
+ * @since org.eclipse.core.jobs 3.2
+ */
+class JobOSGiUtils {
+//     private ServiceTracker debugTracker = null;
+//     private ServiceTracker bundleTracker = null;
+
+    private static /+final+/ JobOSGiUtils singleton;
+
+    /**
+     * Accessor for the singleton instance
+     * @return The JobOSGiUtils instance
+     */
+    public static synchronized JobOSGiUtils getDefault() {
+        if( singleton is null ){
+            singleton = new JobOSGiUtils();
+        }
+        return singleton;
+    }
+
+    /**
+     * Private constructor to block instance creation.
+     */
+    private this() {
+//         super();
+    }
+
+    void openServices() {
+        implMissing(__FILE__,__LINE__);
+//         BundleContext context = JobActivator.getContext();
+//         if (context is null) {
+//             if (JobManager.DEBUG)
+//                 JobMessages.message("JobsOSGiUtils called before plugin started"); //$NON-NLS-1$
+//             return;
+//         }
+//
+//         debugTracker = new ServiceTracker(context, DebugOptions.class.getName(), null);
+//         debugTracker.open();
+//
+//         bundleTracker = new ServiceTracker(context, PackageAdmin.class.getName(), null);
+//         bundleTracker.open();
+    }
+
+    void closeServices() {
+        implMissing(__FILE__,__LINE__);
+//         if (debugTracker !is null) {
+//             debugTracker.close();
+//             debugTracker = null;
+//         }
+//         if (bundleTracker !is null) {
+//             bundleTracker.close();
+//             bundleTracker = null;
+//         }
+    }
+
+    public bool getBooleanDebugOption(String option, bool defaultValue) {
+        implMissing(__FILE__,__LINE__);
+        return false;
+//         if (debugTracker is null) {
+//             if (JobManager.DEBUG)
+//                 JobMessages.message("Debug tracker is not set"); //$NON-NLS-1$
+//             return defaultValue;
+//         }
+//         DebugOptions options = (DebugOptions) debugTracker.getService();
+//         if (options !is null) {
+//             String value = options.getOption(option);
+//             if (value !is null)
+//                 return value.equalsIgnoreCase("true"); //$NON-NLS-1$
+//         }
+//         return defaultValue;
+    }
+
+    /**
+     * Returns the bundle id of the bundle that contains the provided object, or
+     * <code>null</code> if the bundle could not be determined.
+     */
+    public String getBundleId(Object object) {
+        implMissing(__FILE__,__LINE__);
+//         if (bundleTracker is null) {
+//             if (JobManager.DEBUG)
+//                 JobMessages.message("Bundle tracker is not set"); //$NON-NLS-1$
+//             return null;
+//         }
+//         PackageAdmin packageAdmin = (PackageAdmin) bundleTracker.getService();
+//         if (object is null)
+//             return null;
+//         if (packageAdmin is null)
+//             return null;
+//         Bundle source = packageAdmin.getBundle(object.getClass());
+//         if (source !is null && source.getSymbolicName() !is null)
+//             return source.getSymbolicName();
+        return null;
+    }
+
+    /**
+     * Calculates whether the job plugin should set worker threads to be daemon
+     * threads.  When workers are daemon threads, the job plugin does not need
+     * to be explicitly shut down because the VM can exit while workers are still
+     * alive.
+     * @return <code>true</code> if all worker threads should be daemon threads,
+     * and <code>false</code> otherwise.
+     */
+    bool useDaemonThreads() {
+        implMissing(__FILE__,__LINE__);
+        return false;
+//         BundleContext context = JobActivator.getContext();
+//         if (context is null) {
+//             //we are running stand-alone, so consult global system property
+//             String value = System.getProperty(IJobManager.PROP_USE_DAEMON_THREADS);
+//             //default to use daemon threads if property is absent
+//             if (value is null)
+//                 return true;
+//             return "true".equalsIgnoreCase(value); //$NON-NLS-1$
+//         }
+//         //only use daemon threads if the property is defined
+//         final String value = context.getProperty(IJobManager.PROP_USE_DAEMON_THREADS);
+//         //if value is absent, don't use daemon threads to maintain legacy behaviour
+//         if (value is null)
+//             return false;
+//         return "true".equalsIgnoreCase(value); //$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobQueue.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.internal.jobs.JobQueue;
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.core.internal.jobs.InternalJob;
+
+/**
+ * A linked list based priority queue. Either the elements in the queue must
+ * implement Comparable, or a Comparator must be provided.
+ */
+public class JobQueue {
+    /**
+     * The dummy entry sits between the head and the tail of the queue.
+     * dummy.previous() is the head, and dummy.next() is the tail.
+     */
+    private final InternalJob dummy;
+
+    /**
+     * If true, conflicting jobs will be allowed to overtake others in the
+     * queue that have lower priority. If false, higher priority jumps can only
+     * move up the queue by overtaking jobs that they don't conflict with.
+     */
+    private bool allowConflictOvertaking;
+
+    /**
+     * Create a new job queue.
+     */
+    public this(bool allowConflictOvertaking) {
+        //compareTo on dummy is never called
+        dummy = new class("Queue-Head") InternalJob {//$NON-NLS-1$
+            this( String name ){
+                super(name);
+            }
+            public IStatus run(IProgressMonitor m) {
+                return Status.OK_STATUS;
+            }
+        };
+        dummy.setNext(dummy);
+        dummy.setPrevious(dummy);
+        this.allowConflictOvertaking = allowConflictOvertaking;
+    }
+
+    /**
+     * remove all elements
+     */
+    public void clear() {
+        dummy.setNext(dummy);
+        dummy.setPrevious(dummy);
+    }
+
+    /**
+     * Return and remove the element with highest priority, or null if empty.
+     */
+    public InternalJob dequeue() {
+        InternalJob toRemove = dummy.previous();
+        if (toRemove is dummy)
+            return null;
+        return toRemove.remove();
+    }
+
+    /**
+     * Adds an item to the queue
+     */
+    public void enqueue(InternalJob newEntry) {
+        //assert new entry is does not already belong to some other data structure
+        Assert.isTrue(newEntry.next() is null);
+        Assert.isTrue(newEntry.previous() is null);
+        InternalJob tail = dummy.next();
+        //overtake lower priority jobs. Only overtake conflicting jobs if allowed to
+        while (canOvertake(newEntry, tail))
+            tail = tail.next();
+        //new entry is smaller than tail
+        final InternalJob tailPrevious = tail.previous();
+        newEntry.setNext(tail);
+        newEntry.setPrevious(tailPrevious);
+        tailPrevious.setNext(newEntry);
+        tail.setPrevious(newEntry);
+    }
+
+    /**
+     * Returns whether the new entry to overtake the existing queue entry.
+     * @param newEntry The entry to be added to the queue
+     * @param queueEntry The existing queue entry
+     */
+    private bool canOvertake(InternalJob newEntry, InternalJob queueEntry) {
+        //can never go past the end of the queue
+        if (queueEntry is dummy)
+            return false;
+        //if the new entry was already in the wait queue, ensure it is re-inserted in correct position (bug 211799)
+        if (newEntry.getWaitQueueStamp() > 0 && newEntry.getWaitQueueStamp() < queueEntry.getWaitQueueStamp())
+            return true;
+        //if the new entry has lower priority, there is no need to overtake the existing entry
+        if (queueEntry.compareTo(newEntry) >= 0)
+            return false;
+        //the new entry has higher priority, but only overtake the existing entry if the queue allows it
+        return allowConflictOvertaking || !newEntry.isConflicting(queueEntry);
+    }
+
+    /**
+     * Removes the given element from the queue.
+     */
+    public void remove(InternalJob toRemove) {
+        toRemove.remove();
+        //previous of toRemove might now bubble up
+    }
+
+    /**
+     * The given object has changed priority. Reshuffle the heap until it is
+     * valid.
+     */
+    public void resort(InternalJob entry) {
+        remove(entry);
+        enqueue(entry);
+    }
+
+    /**
+     * Returns true if the queue is empty, and false otherwise.
+     */
+    public bool isEmpty() {
+        return dummy.next() is dummy;
+    }
+
+    /**
+     * Return greatest element without removing it, or null if empty
+     */
+    public InternalJob peek() {
+        return dummy.previous() is dummy ? null : dummy.previous();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobStatus.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.internal.jobs.JobStatus;
+
+import java.lang.all;
+
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobStatus;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.internal.jobs.JobManager;
+
+/**
+ * Standard implementation of the IJobStatus interface.
+ */
+public class JobStatus : Status, IJobStatus {
+    private Job job;
+
+    /**
+     * Creates a new job status with no interesting error code or exception.
+     * @param severity
+     * @param job
+     * @param message
+     */
+    public this(int severity, Job job, String message) {
+        super(severity, JobManager.PI_JOBS, 1, message, null);
+        this.job = job;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.IJobStatus#getJob()
+     */
+    public Job getJob() {
+        return job;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/LockManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,303 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.internal.jobs.LockManager;
+
+import java.lang.JThread;
+import java.lang.all;
+import java.util.Stack;
+import java.util.HashMap;
+import java.util.Set;
+
+import org.eclipse.core.internal.runtime.RuntimeLog;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.LockListener;
+
+import org.eclipse.core.internal.jobs.OrderedLock;
+import org.eclipse.core.internal.jobs.DeadlockDetector;
+import org.eclipse.core.internal.jobs.Deadlock;
+import org.eclipse.core.internal.jobs.JobManager;
+import org.eclipse.core.internal.jobs.Worker;
+
+/**
+ * Stores the only reference to the graph that contains all the known
+ * relationships between locks, rules, and the threads that own them.
+ * Synchronizes all access to the graph on the only instance that exists in this class.
+ *
+ * Also stores the state of suspended locks so that they can be re-acquired with
+ * the proper lock depth.
+ */
+public class LockManager {
+    /**
+     * This class captures the state of suspended locks.
+     * Locks are suspended if deadlock is detected.
+     */
+    private static class LockState {
+        private int depth;
+        private OrderedLock lock;
+
+        /**
+         * Suspends ownership of the given lock, and returns the saved state.
+         */
+        protected static LockState suspend(OrderedLock lock) {
+            LockState state = new LockState();
+            state.lock = lock;
+            state.depth = lock.forceRelease_package();
+            return state;
+        }
+
+        /**
+         * Re-acquires a suspended lock and reverts to the correct lock depth.
+         */
+        public void resume() {
+            //spin until the lock is successfully acquired
+            //NOTE: spinning here allows the UI thread to service pending syncExecs
+            //if the UI thread is waiting to acquire a lock.
+            while (true) {
+                try {
+                    if (lock.acquire(Long.MAX_VALUE))
+                        break;
+                } catch (InterruptedException e) {
+                    //ignore and loop
+                }
+            }
+            lock.setDepth_package(depth);
+        }
+    }
+
+    //the lock listener for this lock manager
+    protected LockListener lockListener;
+    /*
+     * The internal data structure that stores all the relationships
+     * between the locks (or rules) and the threads that own them.
+     */
+    private DeadlockDetector locks;
+    /*
+     * Stores thread - stack pairs where every entry in the stack is an array
+     * of locks that were suspended while the thread was acquiring more locks
+     * (a stack is needed because when a thread tries to re-aquire suspended locks,
+     * it can cause deadlock, and some locks it owns can be suspended again)
+     */
+    private HashMap suspendedLocks;
+
+    public this() {
+//         super();
+        locks = new DeadlockDetector();
+        suspendedLocks = new HashMap();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on LockListener
+     */
+    public void aboutToRelease() {
+        if (lockListener is null)
+            return;
+        try {
+            lockListener.aboutToRelease();
+        } catch (Exception e) {
+            handleException(e);
+//         } catch (LinkageError e) {
+//             handleException(e);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on LockListener
+     */
+    public bool aboutToWait(JThread lockOwner) {
+        if (lockListener is null)
+            return false;
+        try {
+            return lockListener.aboutToWait(lockOwner);
+        } catch (Exception e) {
+            handleException(e);
+//         } catch (LinkageError e) {
+//             handleException(e);
+        }
+        return false;
+    }
+
+    /**
+     * This thread has just acquired a lock.  Update graph.
+     */
+    void addLockThread(JThread thread, ISchedulingRule lock) {
+        if (locks is null)
+            return;
+        try {
+            synchronized (locks) {
+                locks.lockAcquired(thread, lock);
+            }
+        } catch (Exception e) {
+            handleInternalError(e);
+        }
+    }
+
+    /**
+     * This thread has just been refused a lock.  Update graph and check for deadlock.
+     */
+    void addLockWaitThread(JThread thread, ISchedulingRule lock) {
+        if (locks is null)
+            return;
+        try {
+            Deadlock found = null;
+            synchronized (locks) {
+                found = locks.lockWaitStart(thread, lock);
+            }
+            if (found is null)
+                return;
+            // if deadlock was detected, the found variable will contain all the information about it,
+            // including which locks to suspend for which thread to resolve the deadlock.
+            ISchedulingRule[] toSuspend = found.getLocks();
+            LockState[] suspended = new LockState[toSuspend.length];
+            for (int i = 0; i < toSuspend.length; i++)
+                suspended[i] = LockState.suspend(cast(OrderedLock) toSuspend[i]);
+            synchronized (suspendedLocks) {
+                Stack prevLocks = cast(Stack) suspendedLocks.get(found.getCandidate());
+                if (prevLocks is null)
+                    prevLocks = new Stack();
+                prevLocks.push(new ArrayWrapperObject(suspended));
+                suspendedLocks.put(found.getCandidate(), prevLocks);
+            }
+        } catch (Exception e) {
+            handleInternalError(e);
+        }
+    }
+
+    /**
+     * Handles exceptions that occur while calling third party code from within the
+     * LockManager. This is essentially an in-lined version of Platform.run(ISafeRunnable)
+     */
+    private static void handleException(Exception e) {
+        IStatus status;
+        if (cast(CoreException)e ) {
+            //logged message should not be translated
+            status = new MultiStatus(JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, "LockManager.handleException", e); //$NON-NLS-1$
+            (cast(MultiStatus) status).merge((cast(CoreException) e).getStatus());
+        } else {
+            status = new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, "LockManager.handleException", e); //$NON-NLS-1$
+        }
+        RuntimeLog.log(status);
+    }
+
+    /**
+     * There was an internal error in the deadlock detection code.  Shut the entire
+     * thing down to prevent further errors.  Recovery is too complex as it
+     * requires freezing all threads and inferring the present lock state.
+     */
+    private void handleInternalError(Exception t) {
+        try {
+            handleException(t);
+            handleException(new Exception(locks.toDebugString()));
+        } catch (Exception e2) {
+            //ignore failure to log or to create the debug string
+        }
+        //discard the deadlock detector for good
+        locks = null;
+    }
+
+    /**
+     * Returns true IFF the underlying graph is empty.
+     * For debugging purposes only.
+     */
+    public bool isEmpty() {
+        return locks.isEmpty();
+    }
+
+    /**
+     * Returns true IFF this thread either owns, or is waiting for, any locks or rules.
+     */
+    public bool isLockOwner() {
+        //all job threads have to be treated as lock owners because UI thread
+        //may try to join a job
+        JThread current = JThread.currentThread();
+        if (cast(Worker)current )
+            return true;
+        if (locks is null)
+            return false;
+        synchronized (locks) {
+            return locks.contains(JThread.currentThread());
+        }
+    }
+
+    /**
+     * Creates and returns a new lock.
+     */
+    public synchronized OrderedLock newLock() {
+        return new OrderedLock(this);
+    }
+
+    /**
+     * Releases all the acquires that were called on the given rule. Needs to be called only once.
+     */
+    void removeLockCompletely(JThread thread, ISchedulingRule rule) {
+        if (locks is null)
+            return;
+        try {
+            synchronized (locks) {
+                locks.lockReleasedCompletely(thread, rule);
+            }
+        } catch (Exception e) {
+            handleInternalError(e);
+        }
+    }
+
+    /**
+     * This thread has just released a lock.  Update graph.
+     */
+    void removeLockThread(JThread thread, ISchedulingRule lock) {
+        try {
+            synchronized (locks) {
+                locks.lockReleased(thread, lock);
+            }
+        } catch (Exception e) {
+            handleInternalError(e);
+        }
+    }
+
+    /**
+     * This thread has just stopped waiting for a lock. Update graph.
+     */
+    void removeLockWaitThread(JThread thread, ISchedulingRule lock) {
+        try {
+            synchronized (locks) {
+                locks.lockWaitStop(thread, lock);
+            }
+        } catch (Exception e) {
+            handleInternalError(e);
+        }
+    }
+
+    /**
+     * Resumes all the locks that were suspended while this thread was waiting to acquire another lock.
+     */
+    void resumeSuspendedLocks(JThread owner) {
+        LockState[] toResume;
+        synchronized (suspendedLocks) {
+            Stack prevLocks = cast(Stack) suspendedLocks.get(owner);
+            if (prevLocks is null)
+                return;
+            toResume = arrayFromObject!(LockState)( prevLocks.pop() );
+            if (prevLocks.empty())
+                suspendedLocks.remove(owner);
+        }
+        for (int i = 0; i < toResume.length; i++)
+            toResume[i].resume();
+    }
+
+    public void setLockListener(LockListener listener) {
+        this.lockListener = listener;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/ObjectMap.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,360 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.internal.jobs.ObjectMap;
+
+import java.lang.all;
+import java.util.Collections;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+import org.eclipse.core.internal.jobs.StringPool;
+
+/**
+ * A specialized map implementation that is optimized for a small set of object
+ * keys.
+ *
+ * Implemented as a single array that alternates keys and values.
+ *
+ * Note: This class is copied from org.eclipse.core.resources
+ */
+public class ObjectMap : Map {
+    // 8 attribute keys, 8 attribute values
+    protected static const int DEFAULT_SIZE = 16;
+    protected static const int GROW_SIZE = 10;
+    protected int count = 0;
+    protected Object[] elements = null;
+
+    /**
+     * Creates a new object map.
+     *
+     * @param initialCapacity
+     *                  The initial number of elements that will fit in the map.
+     */
+    public this(int initialCapacity) {
+        if (initialCapacity > 0)
+            elements = new Object[Math.max(initialCapacity * 2, 0)];
+    }
+
+    /**
+     * Creates a new object map of the same size as the given map and populate
+     * it with the key/attribute pairs found in the map.
+     *
+     * @param map
+     *                  The entries in the given map will be added to the new map.
+     */
+    public this(Map map) {
+        this(map.size());
+        putAll(map);
+    }
+
+    /**
+     * @see Map#clear()
+     */
+    public void clear() {
+        elements = null;
+        count = 0;
+    }
+
+    /**
+     * @see java.lang.Object#clone()
+     */
+    public Object clone() {
+        return new ObjectMap(this);
+    }
+
+    /**
+     * @see Map#containsKey(java.lang.Object)
+     */
+    public bool containsKey(Object key) {
+        if (elements is null || count is 0)
+            return false;
+        for (int i = 0; i < elements.length; i = i + 2)
+            if (elements[i] !is null && elements[i].opEquals(key))
+                return true;
+        return false;
+    }
+    public bool containsKey(String key) {
+        return containsKey(stringcast(key));
+    }
+    /**
+     * @see Map#containsValue(java.lang.Object)
+     */
+    public bool containsValue(Object value) {
+        if (elements is null || count is 0)
+            return false;
+        for (int i = 1; i < elements.length; i = i + 2)
+            if (elements[i] !is null && elements[i].opEquals(value))
+                return true;
+        return false;
+    }
+
+    /**
+     * @see Map#entrySet()
+     *
+     * Note: This implementation does not conform properly to the
+     * specification in the Map interface. The returned collection will not
+     * be bound to this map and will not remain in sync with this map.
+     */
+    public Set entrySet() {
+        return count is 0 ? Collections.EMPTY_SET : toHashMap().entrySet();
+    }
+
+    /**
+     * @see Object#equals(java.lang.Object)
+     */
+    public override int opEquals(Object o) {
+        if (!(cast(Map)o ))
+            return false;
+        Map other = cast(Map) o;
+        //must be same size
+        if (count !is other.size())
+            return false;
+        //keysets must be equal
+        if (!(cast(Object)keySet()).opEquals(cast(Object)other.keySet()))
+            return false;
+        //values for each key must be equal
+        for (int i = 0; i < elements.length; i = i + 2) {
+            if (elements[i] !is null && (!elements[i + 1].opEquals(other.get(elements[i]))))
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * @see Map#get(java.lang.Object)
+     */
+    public Object get(Object key) {
+        if (elements is null || count is 0)
+            return null;
+        for (int i = 0; i < elements.length; i = i + 2)
+            if (elements[i] !is null && elements[i].opEquals(key))
+                return elements[i + 1];
+        return null;
+    }
+    public Object get(String key) {
+        return get(stringcast(key));
+    }
+
+    /**
+     * The capacity of the map has been exceeded, grow the array by GROW_SIZE to
+     * accomodate more entries.
+     */
+    protected void grow() {
+        Object[] expanded = new Object[elements.length + GROW_SIZE];
+        System.arraycopy(elements, 0, expanded, 0, elements.length);
+        elements = expanded;
+    }
+
+    /**
+     * @see Object#hashCode()
+     */
+    public override hash_t toHash() {
+        int hash = 0;
+        for (int i = 0; i < elements.length; i = i + 2) {
+            if (elements[i] !is null) {
+                hash += elements[i].toHash();
+            }
+        }
+        return hash;
+    }
+
+    /**
+     * @see Map#isEmpty()
+     */
+    public bool isEmpty() {
+        return count is 0;
+    }
+
+    /**
+     * Returns all keys in this table as an array.
+     */
+    public String[] keys() {
+        String[] result = new String[count];
+        int next = 0;
+        for (int i = 0; i < elements.length; i = i + 2)
+            if (elements[i] !is null)
+                result[next++] = stringcast( elements[i] );
+        return result;
+    }
+
+    /**
+     * @see Map#keySet()
+     *
+     * Note: This implementation does not conform properly to the
+     * specification in the Map interface. The returned collection will not
+     * be bound to this map and will not remain in sync with this map.
+     */
+    public Set keySet() {
+        Set result = new HashSet(size());
+        for (int i = 0; i < elements.length; i = i + 2) {
+            if (elements[i] !is null) {
+                result.add(elements[i]);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @see Map#put(java.lang.Object, java.lang.Object)
+     */
+    public Object put(Object key, Object value) {
+        if (key is null)
+            throw new NullPointerException();
+        if (value is null)
+            return remove(key);
+
+        // handle the case where we don't have any attributes yet
+        if (elements is null)
+            elements = new Object[DEFAULT_SIZE];
+        if (count is 0) {
+            elements[0] = key;
+            elements[1] = value;
+            count++;
+            return null;
+        }
+
+        int emptyIndex = -1;
+        // replace existing value if it exists
+        for (int i = 0; i < elements.length; i += 2) {
+            if (elements[i] !is null) {
+                if (elements[i].opEquals(key)) {
+                    Object oldValue = elements[i + 1];
+                    elements[i + 1] = value;
+                    return oldValue;
+                }
+            } else if (emptyIndex is -1) {
+                // keep track of the first empty index
+                emptyIndex = i;
+            }
+        }
+        // this will put the emptyIndex greater than the size but
+        // that's ok because we will grow first.
+        if (emptyIndex is -1)
+            emptyIndex = count * 2;
+
+        // otherwise add it to the list of elements.
+        // grow if necessary
+        if (elements.length <= (count * 2))
+            grow();
+        elements[emptyIndex] = key;
+        elements[emptyIndex + 1] = value;
+        count++;
+        return null;
+    }
+    public Object put(String key, Object value) {
+        return put( stringcast(key), value );
+    }
+    public Object put(Object key, String value) {
+        return put( key, stringcast(value) );
+    }
+    public Object put(String key, String value) {
+        return put( stringcast(key), stringcast(value) );
+    }
+
+    /**
+     * @see Map#putAll(java.util.Map)
+     */
+    public void putAll(Map map) {
+        for (Iterator i = map.keySet().iterator(); i.hasNext();) {
+            Object key = i.next();
+            Object value = map.get(key);
+            put(key, value);
+        }
+    }
+
+    /**
+     * @see Map#remove(java.lang.Object)
+     */
+    public Object remove(Object key) {
+        if (elements is null || count is 0)
+            return null;
+        for (int i = 0; i < elements.length; i = i + 2) {
+            if (elements[i] !is null && elements[i].opEquals(key)) {
+                elements[i] = null;
+                Object result = elements[i + 1];
+                elements[i + 1] = null;
+                count--;
+                return result;
+            }
+        }
+        return null;
+    }
+    public Object remove(String key) {
+        return remove( stringcast(key));
+    }
+
+    /* (non-Javadoc
+     * Method declared on IStringPoolParticipant
+     */
+    public void shareStrings(StringPool set) {
+        //copy elements for thread safety
+        Object[] array = elements;
+        if (array is null)
+            return;
+        for (int i = 0; i < array.length; i++) {
+            Object o = array[i];
+            if (cast(ArrayWrapperString)o )
+                array[i] = stringcast(set.add(stringcast( o)));
+        }
+    }
+
+    /**
+     * @see Map#size()
+     */
+    public int size() {
+        return count;
+    }
+
+    /**
+     * Creates a new hash map with the same contents as this map.
+     */
+    private HashMap toHashMap() {
+        HashMap result = new HashMap(size());
+        for (int i = 0; i < elements.length; i = i + 2) {
+            if (elements[i] !is null) {
+                result.put(elements[i], elements[i + 1]);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @see Map#values()
+     *
+     * Note: This implementation does not conform properly to the
+     * specification in the Map interface. The returned collection will not
+     * be bound to this map and will not remain in sync with this map.
+     */
+    public Collection values() {
+        Set result = new HashSet(size());
+        for (int i = 1; i < elements.length; i = i + 2) {
+            if (elements[i] !is null) {
+                result.add(elements[i]);
+            }
+        }
+        return result;
+    }
+
+    public int opApply (int delegate(ref Object value) dg){
+        implMissing(__FILE__, __LINE__ );
+        return 0;
+    }
+    public int opApply (int delegate(ref Object key, ref Object value) dg){
+        implMissing(__FILE__, __LINE__ );
+        return 0;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/OrderedLock.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,319 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.internal.jobs.OrderedLock;
+
+import tango.text.convert.Format;
+import java.lang.JThread;
+import tango.io.Stdout;
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.jobs.ILock;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+import org.eclipse.core.internal.jobs.LockManager;
+import org.eclipse.core.internal.jobs.Queue;
+import org.eclipse.core.internal.jobs.Semaphore;
+
+/**
+ * A lock used to control write access to an exclusive resource.
+ *
+ * The lock avoids circular waiting deadlocks by detecting the deadlocks
+ * and resolving them through the suspension of all locks owned by one
+ * of the threads involved in the deadlock. This makes it impossible for n such
+ * locks to deadlock while waiting for each other.  The downside is that this means
+ * that during an interval when a process owns a lock, it can be forced
+ * to give the lock up and wait until all locks it requires become
+ * available.  This removes the feature of exclusive access to the
+ * resource in contention for the duration between acquire() and
+ * release() calls.
+ *
+ * The lock implementation prevents starvation by granting the
+ * lock in the same order in which acquire() requests arrive. In
+ * this scheme, starvation is only possible if a thread retains
+ * a lock indefinitely.
+ */
+public class OrderedLock : ILock, ISchedulingRule {
+
+    private static const bool DEBUG = false;
+    /**
+     * Locks are sequentially ordered for debugging purposes.
+     */
+    private static int nextLockNumber = 0;
+    /**
+     * The thread of the operation that currently owns the lock.
+     */
+    private /+volatile+/ JThread currentOperationThread;
+    /**
+     * Records the number of successive acquires in the same
+     * thread. The lock is released only when the depth
+     * reaches zero.
+     */
+    private int depth;
+    /**
+     * The manager that implements the deadlock detection and resolution protocol.
+     */
+    private const LockManager manager;
+    private const int number;
+
+    /**
+     * Queue of semaphores for threads currently waiting
+     * on the lock. This queue is not thread-safe, so access
+     * to this queue must be synchronized on the lock instance.
+     */
+    private const Queue operations;
+
+    /**
+     * Creates a new workspace lock.
+     */
+    this(LockManager manager) {
+
+        operations = new Queue();
+
+        this.manager = manager;
+        this.number = nextLockNumber++;
+    }
+
+    /* (non-Javadoc)
+     * @see Locks.ILock#acquire()
+     */
+    public void acquire() {
+        //spin until the lock is successfully acquired
+        //NOTE: spinning here allows the UI thread to service pending syncExecs
+        //if the UI thread is waiting to acquire a lock.
+        while (true) {
+            try {
+                if (acquire(Long.MAX_VALUE))
+                    return;
+            } catch (InterruptedException e) {
+                //ignore and loop
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see Locks.ILock#acquire(long)
+     */
+    public bool acquire(long delay) {
+        implMissing(__FILE__, __LINE__ );
+//         if (Thread.interrupted())
+//             throw new InterruptedException();
+
+        bool success = false;
+        if (delay <= 0)
+            return attempt();
+        Semaphore semaphore = createSemaphore();
+        if (semaphore is null)
+            return true;
+        if (DEBUG)
+            Stdout.formatln("[{}] Operation waiting to be executed... ", JThread.currentThread(), this); //$NON-NLS-1$ //$NON-NLS-2$
+        success = doAcquire(semaphore, delay);
+        manager.resumeSuspendedLocks(JThread.currentThread());
+        if (DEBUG && success)
+            Stdout.formatln("[{}] Operation started... ", JThread.currentThread(), this); //$NON-NLS-1$ //$NON-NLS-2$
+        else if (DEBUG)
+            Stdout.formatln("[{}] Operation timed out... ", JThread.currentThread(), this); //$NON-NLS-1$ //$NON-NLS-2$
+        return success;
+    }
+
+    /**
+     * Attempts to acquire the lock.  Returns false if the lock is not available and
+     * true if the lock has been successfully acquired.
+     */
+    private synchronized bool attempt() {
+        //return true if we already own the lock
+        //also, if nobody is waiting, grant the lock immediately
+        if ((currentOperationThread is JThread.currentThread()) || (currentOperationThread is null && operations.isEmpty())) {
+            depth++;
+            setCurrentOperationThread(JThread.currentThread());
+            return true;
+        }
+        return false;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
+     */
+    public bool contains(ISchedulingRule rule) {
+        return false;
+    }
+
+    /**
+     * Returns null if acquired and a Semaphore object otherwise. If a
+     * waiting semaphore already exists for this thread, it will be returned,
+     * otherwise a new semaphore will be created, enqueued, and returned.
+     */
+    private synchronized Semaphore createSemaphore() {
+        return attempt() ? null : enqueue(new Semaphore(JThread.currentThread()));
+    }
+
+    /**
+     * Attempts to acquire this lock.  Callers will block  until this lock comes available to
+     * them, or until the specified delay has elapsed.
+     */
+    private bool doAcquire(Semaphore semaphore, long delay) {
+        bool success = false;
+        //notify hook to service pending syncExecs before falling asleep
+        if (manager.aboutToWait(this.currentOperationThread)) {
+            //hook granted immediate access
+            //remove semaphore for the lock request from the queue
+            //do not log in graph because this thread did not really get the lock
+            removeFromQueue(semaphore);
+            depth++;
+            manager.addLockThread(currentOperationThread, this);
+            return true;
+        }
+        //Make sure the semaphore is in the queue before we start waiting
+        //It might have been removed from the queue while servicing syncExecs
+        //This is will return our existing semaphore if it is still in the queue
+        semaphore = createSemaphore();
+        if (semaphore is null)
+            return true;
+        manager.addLockWaitThread(JThread.currentThread(), this);
+        try {
+            success = semaphore.acquire(delay);
+        } catch (InterruptedException e) {
+            if (DEBUG)
+                Stdout.formatln(Format("[{}] Operation interrupted while waiting... :-|", JThread.currentThread())); //$NON-NLS-1$ //$NON-NLS-2$
+            throw e;
+        }
+        if (success) {
+            depth++;
+            updateCurrentOperation();
+        } else {
+            removeFromQueue(semaphore);
+            manager.removeLockWaitThread(JThread.currentThread(), this);
+        }
+        return success;
+    }
+
+    /**
+     * Releases this lock from the thread that used to own it.
+     * Grants this lock to the next thread in the queue.
+     */
+    private synchronized void doRelease() {
+        //notify hook
+        manager.aboutToRelease();
+        depth = 0;
+        Semaphore next = cast(Semaphore) operations.peek();
+        setCurrentOperationThread(null);
+        if (next !is null)
+            next.release();
+    }
+
+    /**
+     * If there is another semaphore with the same runnable in the
+     * queue, the other is returned and the new one is not added.
+     */
+    private synchronized Semaphore enqueue(Semaphore newSemaphore) {
+        Semaphore semaphore = cast(Semaphore) operations.get(newSemaphore);
+        if (semaphore is null) {
+            operations.enqueue(newSemaphore);
+            return newSemaphore;
+        }
+        return semaphore;
+    }
+
+    /**
+     * Suspend this lock by granting the lock to the next lock in the queue.
+     * Return the depth of the suspended lock.
+     */
+    protected int forceRelease() {
+        int oldDepth = depth;
+        doRelease();
+        return oldDepth;
+    }
+    package int forceRelease_package() {
+        return forceRelease();
+    }
+
+    /* (non-Javadoc)
+     * @see Locks.ILock#getDepth()
+     */
+    public int getDepth() {
+        return depth;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule)
+     */
+    public bool isConflicting(ISchedulingRule rule) {
+        return rule is this;
+    }
+
+    /* (non-Javadoc)
+     * @see Locks.ILock#release()
+     */
+    public void release() {
+        if (depth is 0)
+            return;
+        //only release the lock when the depth reaches zero
+        Assert.isTrue(depth >= 0, "Lock released too many times"); //$NON-NLS-1$
+        if (--depth is 0)
+            doRelease();
+        else
+            manager.removeLockThread(currentOperationThread, this);
+    }
+
+    /**
+     * Removes a semaphore from the queue of waiting operations.
+     *
+     * @param semaphore The semaphore to remove
+     */
+    private synchronized void removeFromQueue(Semaphore semaphore) {
+        operations.remove(semaphore);
+    }
+
+    /**
+     * If newThread is null, release this lock from its previous owner.
+     * If newThread is not null, grant this lock to newThread.
+     */
+    private void setCurrentOperationThread(JThread newThread) {
+        if ((currentOperationThread !is null) && (newThread is null))
+            manager.removeLockThread(currentOperationThread, this);
+        this.currentOperationThread = newThread;
+        if (currentOperationThread !is null)
+            manager.addLockThread(currentOperationThread, this);
+    }
+
+    /**
+     * Forces the lock to be at the given depth.
+     * Used when re-acquiring a suspended lock.
+     */
+    protected void setDepth(int newDepth) {
+        for (int i = depth; i < newDepth; i++) {
+            manager.addLockThread(currentOperationThread, this);
+        }
+        this.depth = newDepth;
+    }
+    package void setDepth_package(int newDepth) {
+        return setDepth(newDepth);
+    }
+
+    /**
+     * For debugging purposes only.
+     */
+    public String toString() {
+        return Format("OrderedLock ({})", number ); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    /**
+     * This lock has just been granted to a new thread (the thread waited for it).
+     * Remove the request from the queue and update both the graph and the lock.
+     */
+    private synchronized void updateCurrentOperation() {
+        operations.dequeue();
+        setCurrentOperationThread(JThread.currentThread());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Queue.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.internal.jobs.Queue;
+
+import tango.text.convert.Format;
+import java.lang.all;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A Queue of objects.
+ */
+public class Queue {
+    protected Object[] elements_;
+    protected int head;
+    protected bool reuse;
+    protected int tail;
+
+    public this() {
+        this(20, false);
+    }
+
+    /**
+     * The parameter reuse indicates what do you want to happen with
+     * the object reference when you remove it from the queue. If
+     * reuse is false the queue no longer holds a reference to the
+     * object when it is removed. If reuse is true you can use the
+     * method getNextAvailableObject to get an used object, set its
+     * new values and add it again to the queue.
+     */
+    public this(int size, bool reuse) {
+        elements_ = new Object[size];
+        head = tail = 0;
+        this.reuse = reuse;
+    }
+
+    /**
+     * Adds an object to the tail of the queue.
+     */
+    public void enqueue(Object element) {
+        int newTail = increment(tail);
+        if (newTail is head) {
+            grow();
+            newTail = tail + 1;
+        }
+        elements_[tail] = element;
+        tail = newTail;
+    }
+
+    /**
+     * This method does not affect the queue itself. It is only a
+     * helper to decrement an index in the queue.
+     */
+    public int decrement(int index) {
+        return (index is 0) ? (elements_.length - 1) : index - 1;
+    }
+
+    public Iterator elements() {
+        /**/
+        if (isEmpty())
+            return (new ArrayList(0)).iterator();
+
+        /* if head < tail we can use the same array */
+        if (head <= tail)
+            return Arrays.asList(elements_).iterator();
+
+        /* otherwise we need to create a new array */
+        Object[] newElements = new Object[size()];
+        int end = (elements_.length - head);
+        System.arraycopy(elements_, head, newElements, 0, end);
+        System.arraycopy(elements_, 0, newElements, end, tail);
+        return Arrays.asList(newElements).iterator();
+    }
+
+    public Object get(Object o) {
+        int index = head;
+        while (index !is tail) {
+            if (elements_[index].opEquals(o))
+                return elements_[index];
+            index = increment(index);
+        }
+        return null;
+    }
+
+    /**
+     * Removes the given object from the queue. Shifts the underlying array.
+     */
+    public bool remove(Object o) {
+        int index = head;
+        //find the object to remove
+        while (index !is tail) {
+            if (elements_[index].opEquals(o))
+                break;
+            index = increment(index);
+        }
+        //if element wasn't found, return
+        if (index is tail)
+            return false;
+        //store a reference to it (needed for reuse of objects)
+        Object toRemove = elements_[index];
+        int nextIndex = -1;
+        while (index !is tail) {
+            nextIndex = increment(index);
+            if (nextIndex !is tail)
+                elements_[index] = elements_[nextIndex];
+
+            index = nextIndex;
+        }
+        //decrement tail
+        tail = decrement(tail);
+
+        //if objects are reused, transfer the reference that is removed to the end of the queue
+        //otherwise set the element after the last one to null (to avoid duplicate references)
+        elements_[tail] = reuse ? toRemove : null;
+        return true;
+    }
+
+    protected void grow() {
+        int newSize = cast(int) (elements_.length * 1.5);
+        Object[] newElements = new Object[newSize];
+        if (tail >= head)
+            System.arraycopy(elements_, head, newElements, head, size());
+        else {
+            int newHead = newSize - (elements_.length - head);
+            System.arraycopy(elements_, 0, newElements, 0, tail + 1);
+            System.arraycopy(elements_, head, newElements, newHead, (newSize - newHead));
+            head = newHead;
+        }
+        elements_ = newElements;
+    }
+
+    /**
+     * This method does not affect the queue itself. It is only a
+     * helper to increment an index in the queue.
+     */
+    public int increment(int index) {
+        return (index is (elements_.length - 1)) ? 0 : index + 1;
+    }
+
+    public bool isEmpty() {
+        return tail is head;
+    }
+
+    public Object peek() {
+        return elements_[head];
+    }
+
+    /**
+     * Removes an returns the item at the head of the queue.
+     */
+    public Object dequeue() {
+        if (isEmpty())
+            return null;
+        Object result = peek();
+        if (!reuse)
+            elements_[head] = null;
+        head = increment(head);
+        return result;
+    }
+
+    public int size() {
+        return tail > head ? (tail - head) : ((elements_.length - head) + tail);
+    }
+
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("["); //$NON-NLS-1$
+        if (!isEmpty()) {
+            Iterator it = elements();
+            while (true) {
+                sb.append(Format("{}",it.next()));
+                if (it.hasNext())
+                    sb.append(", "); //$NON-NLS-1$
+                else
+                    break;
+            }
+        }
+        sb.append("]"); //$NON-NLS-1$
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Semaphore.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 org.eclipse.core.internal.jobs.Semaphore;
+
+import java.lang.JThread;
+import tango.core.sync.Mutex;
+import tango.core.sync.Condition;
+import java.lang.all;
+import tango.text.convert.Format;
+
+public class Semaphore {
+    protected long notifications;
+    protected JThread runnable;
+
+    private Mutex mutex;
+    private Condition condition;
+
+    public this(JThread runnable) {
+        mutex = new Mutex;
+        condition = new Condition(mutex);
+        this.runnable = runnable;
+        notifications = 0;
+    }
+
+    /**
+     * Attempts to acquire this semaphore.  Returns true if it was successfully acquired,
+     * and false otherwise.
+     */
+    public bool acquire(long delay) {
+        synchronized(mutex){
+            implMissing( __FILE__, __LINE__ );
+// SWT
+//         if (Thread.interrupted())
+//             throw new InterruptedException();
+            long start = System.currentTimeMillis();
+            long timeLeft = delay;
+            while (true) {
+                if (notifications > 0) {
+                    notifications--;
+                    return true;
+                }
+                if (timeLeft <= 0)
+                    return false;
+                condition.wait(timeLeft/1000.0f);
+                timeLeft = start + delay - System.currentTimeMillis();
+            }
+        }
+    }
+
+    public override int opEquals(Object obj) {
+        return (runnable is (cast(Semaphore) obj).runnable);
+    }
+
+    public override hash_t toHash() {
+        return runnable is null ? 0 : (cast(Object)runnable).toHash();
+    }
+
+    public void release() {
+        synchronized( mutex ){
+            notifications++;
+            condition.notifyAll();
+        }
+    }
+
+    // for debug only
+    public String toString() {
+        return Format("Semaphore({})", cast(Object) runnable ); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/StringPool.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.internal.jobs.StringPool;
+
+import java.lang.all;
+import java.util.HashMap;
+
+/**
+ * A string pool is used for sharing strings in a way that eliminates duplicate
+ * equal strings.  A string pool instance can be maintained over a long period
+ * of time, or used as a temporary structure during a string sharing pass over
+ * a data structure.
+ * <p>
+ * This class is not intended to be subclassed by clients.
+ * </p>
+ *
+ * Note: This class is copied from org.eclipse.core.resources
+ *
+ * @since 3.1
+ */
+public final class StringPool {
+    private int savings;
+    private const HashMap map;
+
+    public this(){
+        map = new HashMap();
+    }
+
+    /**
+     * Adds a <code>String</code> to the pool.  Returns a <code>String</code>
+     * that is equal to the argument but that is unique within this pool.
+     * @param string The string to add to the pool
+     * @return A string that is equal to the argument.
+     */
+    public String add(String string) {
+        if (string is null)
+            return string;
+        Object result = map.get(string);
+        if (result !is null) {
+            if (stringcast(result) !is string)
+                savings += 44 + 2 * string.length;
+            return stringcast( result );
+        }
+        map.put(string, string);
+        return string;
+    }
+
+    /**
+     * Returns an estimate of the size in bytes that was saved by sharing strings in
+     * the pool.  In particular, this returns the size of all strings that were added to the
+     * pool after an equal string had already been added.  This value can be used
+     * to estimate the effectiveness of a string sharing operation, in order to
+     * determine if or when it should be performed again.
+     *
+     * In some cases this does not precisely represent the number of bytes that
+     * were saved.  For example, say the pool already contains string S1.  Now
+     * string S2, which is equal to S1 but not identical, is added to the pool five
+     * times. This method will return the size of string S2 multiplied by the
+     * number of times it was added, even though the actual savings in this case
+     * is only the size of a single copy of S2.
+     */
+    public int getSavedStringCount() {
+        return savings;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/ThreadJob.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,348 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.internal.jobs.ThreadJob;
+
+import java.lang.all;
+import java.util.Set;
+import tango.text.convert.Format;
+import tango.io.Stdout;
+import java.lang.JThread;
+import tango.core.sync.Mutex;
+import tango.core.sync.Condition;
+
+import org.eclipse.core.internal.runtime.RuntimeLog;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.internal.jobs.JobManager;
+import org.eclipse.core.internal.jobs.InternalJob;
+import org.eclipse.core.internal.jobs.LockManager;
+
+/**
+ * Captures the implicit job state for a given thread.
+ */
+class ThreadJob : Job {
+    /**
+     * The notifier is a shared object used to wake up waiting thread jobs
+     * when another job completes that is releasing a scheduling rule.
+     */
+
+    static const Mutex mutex;
+    static const Condition condition;
+    static this(){
+        mutex = new Mutex();
+        condition = new Condition(mutex);
+    }
+
+    private const JobManager manager;
+    /**
+     * Set to true if this thread job is running in a thread that did
+     * not own a rule already.  This means it needs to acquire the
+     * rule during beginRule, and must release the rule during endRule.
+     */
+    /+protected+/ bool acquireRule = false;
+
+    /**
+     * Indicates that this thread job did report to the progress manager
+     * that it will be blocked, and therefore when it begins it must
+     * be reported to the job manager when it is no longer blocked.
+     */
+    bool isBlocked = false;
+
+    /**
+     * True if this ThreadJob has begun execution
+     */
+    /+protected+/ bool isRunning_ = false;
+
+    /**
+     * Used for diagnosing mismatched begin/end pairs. This field
+     * is only used when in debug mode, to capture the stack trace
+     * of the last call to beginRule.
+     */
+    private RuntimeException lastPush = null;
+    /**
+     * The actual job that is running in the thread that this
+     * ThreadJob represents.  This will be null if this thread
+     * job is capturing a rule acquired outside of a job.
+     */
+    protected package Job realJob;
+    /**
+     * The stack of rules that have been begun in this thread, but not yet ended.
+     */
+    private ISchedulingRule[] ruleStack;
+    /**
+     * Rule stack pointer.
+     */
+    private int top;
+
+    this(JobManager manager, ISchedulingRule rule) {
+        super("Implicit Job"); //$NON-NLS-1$
+        this.manager = manager;
+        setSystem(true);
+        setPriority(Job.INTERACTIVE);
+        ruleStack = new ISchedulingRule[2];
+        top = -1;
+        setRule(rule);
+    }
+
+    /**
+     * An endRule was called that did not match the last beginRule in
+     * the stack.  Report and log a detailed informational message.
+     * @param rule The rule that was popped
+     */
+    private void illegalPop(ISchedulingRule rule) {
+        StringBuffer buf = new StringBuffer("Attempted to endRule: "); //$NON-NLS-1$
+        buf.append(Format("{}",rule));
+        if (top >= 0 && top < ruleStack.length) {
+            buf.append(", does not match most recent begin: "); //$NON-NLS-1$
+            buf.append(Format("{}",ruleStack[top]));
+        } else {
+            if (top < 0)
+                buf.append(", but there was no matching beginRule"); //$NON-NLS-1$
+            else
+                buf.append( Format(", but the rule stack was out of bounds: {}", top)); //$NON-NLS-1$
+        }
+        buf.append(".  See log for trace information if rule tracing is enabled."); //$NON-NLS-1$
+        String msg = buf.toString();
+        if (JobManager.DEBUG || JobManager.DEBUG_BEGIN_END) {
+            Stdout.formatln("{}",msg);
+            Exception t = lastPush is null ? cast(Exception)new IllegalArgumentException("") : cast(Exception)lastPush;
+            IStatus error = new Status(IStatus.ERROR, JobManager.PI_JOBS, 1, msg, t);
+            RuntimeLog.log(error);
+        }
+        Assert.isLegal(false, msg);
+    }
+
+    /**
+     * Client has attempted to begin a rule that is not contained within
+     * the outer rule.
+     */
+    private void illegalPush(ISchedulingRule pushRule, ISchedulingRule baseRule) {
+        StringBuffer buf = new StringBuffer("Attempted to beginRule: "); //$NON-NLS-1$
+        buf.append(Format("{}",pushRule));
+        buf.append(", does not match outer scope rule: "); //$NON-NLS-1$
+        buf.append(Format("{}",baseRule));
+        String msg = buf.toString();
+        if (JobManager.DEBUG) {
+            Stdout.formatln("{}",msg);
+            IStatus error = new Status(IStatus.ERROR, JobManager.PI_JOBS, 1, msg, new IllegalArgumentException(""));
+            RuntimeLog.log(error);
+        }
+        Assert.isLegal(false, msg);
+
+    }
+
+    /**
+     * Returns true if the monitor is canceled, and false otherwise.
+     * Protects the caller from exception in the monitor implementation.
+     */
+    private bool isCanceled(IProgressMonitor monitor) {
+        try {
+            return monitor.isCanceled();
+        } catch (RuntimeException e) {
+            //logged message should not be translated
+            IStatus status = new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, "ThreadJob.isCanceled", e); //$NON-NLS-1$
+            RuntimeLog.log(status);
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if this thread job was scheduled and actually started running.
+     */
+    bool isRunning() {
+        synchronized(mutex){
+            return isRunning_;
+        }
+    }
+
+    /**
+     * Schedule the job and block the calling thread until the job starts running.
+     * Returns the ThreadJob instance that was started.
+     */
+    ThreadJob joinRun(IProgressMonitor monitor) {
+        if (isCanceled(monitor))
+            throw new OperationCanceledException();
+        //check if there is a blocking thread before waiting
+        InternalJob blockingJob = manager.findBlockingJob_package(this);
+        JThread blocker = blockingJob is null ? null : blockingJob.getThread_package();
+        ThreadJob result = this;
+        try {
+            //just return if lock listener decided to grant immediate access
+            if (manager.getLockManager().aboutToWait(blocker))
+                return this;
+            try {
+                waitStart(monitor, blockingJob);
+                final JThread getThis = JThread.currentThread();
+                while (true) {
+                    if (isCanceled(monitor))
+                        throw new OperationCanceledException();
+                    //try to run the job
+                    if (manager.runNow_package(this))
+                        return this;
+                    //update blocking job
+                    blockingJob = manager.findBlockingJob_package(this);
+                    //the rule could have been transferred to this thread while we were waiting
+                    blocker = blockingJob is null ? null : blockingJob.getThread_package();
+                    if (blocker is getThis && cast(ThreadJob)blockingJob ) {
+                        //now we are just the nested acquire case
+                        result = cast(ThreadJob) blockingJob;
+                        result.push(getRule());
+                        result.isBlocked = this.isBlocked;
+                        return result;
+                    }
+                    //just return if lock listener decided to grant immediate access
+                    if (manager.getLockManager().aboutToWait(blocker))
+                        return this;
+                    //must lock instance before calling wait
+                    synchronized (mutex) {
+                        try {
+                            condition.wait(0.250);
+                        } catch (InterruptedException e) {
+                            //ignore
+                        }
+                    }
+                }
+            } finally {
+                if (this is result)
+                    waitEnd(monitor);
+            }
+        } finally {
+            manager.getLockManager().aboutToRelease();
+        }
+    }
+
+    /**
+     * Pops a rule. Returns true if it was the last rule for this thread
+     * job, and false otherwise.
+     */
+    bool pop(ISchedulingRule rule) {
+        if (top < 0 || ruleStack[top] !is rule)
+            illegalPop(rule);
+        ruleStack[top--] = null;
+        return top < 0;
+    }
+
+    /**
+     * Adds a new scheduling rule to the stack of rules for this thread. Throws
+     * a runtime exception if the new rule is not compatible with the base
+     * scheduling rule for this thread.
+     */
+    void push(ISchedulingRule rule) {
+        ISchedulingRule baseRule = getRule();
+        if (++top >= ruleStack.length) {
+            ISchedulingRule[] newStack = new ISchedulingRule[ruleStack.length * 2];
+            SimpleType!(ISchedulingRule).arraycopy(ruleStack, 0, newStack, 0, ruleStack.length);
+            ruleStack = newStack;
+        }
+        ruleStack[top] = rule;
+        if (JobManager.DEBUG_BEGIN_END)
+            lastPush = new RuntimeException()/+).fillInStackTrace()+/;
+        //check for containment last because we don't want to fail again on endRule
+        if (baseRule !is null && rule !is null && !baseRule.contains(rule))
+            illegalPush(rule, baseRule);
+    }
+
+    /**
+     * Reset all of this job's fields so it can be reused.  Returns false if
+     * reuse is not possible
+     */
+    bool recycle() {
+        //don't recycle if still running for any reason
+        if (getState() !is Job.NONE)
+            return false;
+        //clear and reset all fields
+        acquireRule = isRunning_ = isBlocked = false;
+        realJob = null;
+        setRule(null);
+        setThread(null);
+        if (ruleStack.length !is 2)
+            ruleStack = new ISchedulingRule[2];
+        else
+            ruleStack[0] = ruleStack[1] = null;
+        top = -1;
+        return true;
+    }
+
+    /** (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
+     */
+    public IStatus run(IProgressMonitor monitor) {
+        synchronized (this) {
+            isRunning_ = true;
+        }
+        return ASYNC_FINISH;
+    }
+
+    /**
+     * Records the job that is actually running in this thread, if any
+     * @param realJob The running job
+     */
+    void setRealJob(Job realJob) {
+        this.realJob = realJob;
+    }
+
+    /**
+     * Returns true if this job should cause a self-canceling job
+     * to cancel itself, and false otherwise.
+     */
+    bool shouldInterrupt() {
+        return realJob is null ? true : !realJob.isSystem();
+    }
+
+    /* (non-javadoc)
+     * For debugging purposes only
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer("ThreadJob"); //$NON-NLS-1$
+        buf.append('(').append(Format("{}",realJob)).append(',').append('[');
+        for (int i = 0; i <= top && i < ruleStack.length; i++)
+            buf.append(Format("{}",ruleStack[i])).append(',');
+        buf.append(']').append(')');
+        return buf.toString();
+    }
+
+    /**
+     * Reports that this thread was blocked, but is no longer blocked and is able
+     * to proceed.
+     * @param monitor The monitor to report unblocking to.
+     */
+    private void waitEnd(IProgressMonitor monitor) {
+        final LockManager lockManager = manager.getLockManager();
+        final JThread getThis = JThread.currentThread();
+        if (isRunning()) {
+            lockManager.addLockThread(getThis, getRule());
+            //need to re-acquire any locks that were suspended while this thread was blocked on the rule
+            lockManager.resumeSuspendedLocks(getThis);
+        } else {
+            //tell lock manager that this thread gave up waiting
+            lockManager.removeLockWaitThread(getThis, getRule());
+        }
+    }
+
+    /**
+     * Indicates the start of a wait on a scheduling rule. Report the
+     * blockage to the progress manager and update the lock manager.
+     * @param monitor The monitor to report blocking to
+     * @param blockingJob The job that is blocking this thread, or <code>null</code>
+     */
+    private void waitStart(IProgressMonitor monitor, InternalJob blockingJob) {
+        manager.getLockManager().addLockWaitThread(JThread.currentThread(), getRule());
+        isBlocked = true;
+        manager.reportBlocked(monitor, blockingJob);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Worker.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 org.eclipse.core.internal.jobs.Worker;
+
+import java.lang.JThread;
+import tango.text.convert.Format;
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.internal.runtime.RuntimeLog;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.osgi.util.NLS;
+
+import org.eclipse.core.internal.jobs.InternalJob;
+import org.eclipse.core.internal.jobs.WorkerPool;
+import org.eclipse.core.internal.jobs.JobMessages;
+import org.eclipse.core.internal.jobs.JobManager;
+
+/**
+ * A worker thread processes jobs supplied to it by the worker pool.  When
+ * the worker pool gives it a null job, the worker dies.
+ */
+public class Worker : JThread {
+    //worker number used for debugging purposes only
+    private static int nextWorkerNumber = 0;
+    private /+volatile+/ InternalJob currentJob_;
+    private final WorkerPool pool;
+
+    public this(WorkerPool pool) {
+        super();
+        this.setName( Format("Worker-{}", nextWorkerNumber++)); //$NON-NLS-1$
+        this.pool = pool;
+        //set the context loader to avoid leaking the current context loader
+        //for the thread that spawns this worker (bug 98376)
+// SWT
+//         setContextClassLoader(pool.defaultContextLoader);
+    }
+
+    /**
+     * Returns the currently running job, or null if none.
+     */
+    public Job currentJob() {
+        return cast(Job) currentJob_;
+    }
+
+    private IStatus handleException(InternalJob job, Exception t) {
+        String message = NLS.bind(JobMessages.jobs_internalError, job.getName_package());
+        return new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, message, t);
+    }
+
+    public override void run() {
+        this.setPriority(NORM_PRIORITY);
+        try {
+            while ((currentJob_ = pool.startJob_package(this)) !is null) {
+                currentJob_.setThread_package(this);
+                IStatus result = Status.OK_STATUS;
+                try {
+                    result = currentJob_.run_package(currentJob_.getProgressMonitor());
+                } catch (OperationCanceledException e) {
+                    result = Status.CANCEL_STATUS;
+                } catch (Exception e) {
+                    result = handleException(currentJob_, e);
+//                 } catch (ThreadDeath e) {
+//                     //must not consume thread death
+//                     result = handleException(currentJob_, e);
+//                     throw e;
+//                 } catch (Error e) {
+//                     result = handleException(currentJob_, e);
+                } finally {
+                    //clear interrupted state for this thread
+                    JThread.interrupted();
+
+                    //result must not be null
+                    if (result is null)
+                        result = handleException(currentJob_, new NullPointerException());
+                    pool.endJob_package(currentJob_, result);
+                    if ((result.getSeverity() & (IStatus.ERROR | IStatus.WARNING)) !is 0)
+                        RuntimeLog.log(result);
+                    currentJob_ = null;
+                }
+            }
+        } catch (Exception t) {
+            ExceptionPrintStackTrace(t);
+        } finally {
+            currentJob_ = null;
+            pool.endWorker_package(this);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/WorkerPool.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,297 @@
+/*******************************************************************************
+ * 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.internal.jobs.WorkerPool;
+
+import java.lang.JThread;
+import tango.core.sync.Mutex;
+import tango.core.sync.Condition;
+import tango.text.convert.Format;
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.internal.jobs.JobManager;
+import org.eclipse.core.internal.jobs.Worker;
+
+import org.eclipse.core.internal.jobs.InternalJob;
+import org.eclipse.core.internal.jobs.ThreadJob;
+
+/**
+ * Maintains a pool of worker threads. Threads are constructed lazily as
+ * required, and are eventually discarded if not in use for awhile. This class
+ * maintains the thread creation/destruction policies for the job manager.
+ *
+ * Implementation note: all the data structures of this class are protected
+ * by the instance's object monitor.  To avoid deadlock with third party code,
+ * this lock is never held when calling methods outside this class that may in
+ * turn use locks.
+ */
+class WorkerPool {
+
+    protected Mutex mutex;
+    protected Condition condition;
+
+
+    /**
+     * Threads not used by their best before timestamp are destroyed.
+     */
+    private static const int BEST_BEFORE = 60000;
+    /**
+     * There will always be at least MIN_THREADS workers in the pool.
+     */
+    private static const int MIN_THREADS = 1;
+    /**
+     * Use the busy thread count to avoid starting new threads when a living
+     * thread is just doing house cleaning (notifying listeners, etc).
+     */
+    private int busyThreads = 0;
+
+    /**
+     * The default context class loader to use when creating worker threads.
+     */
+//     protected const ClassLoader defaultContextLoader;
+
+    /**
+     * Records whether new worker threads should be daemon threads.
+     */
+    private bool isDaemon = false;
+
+    private JobManager manager;
+    /**
+     * The number of workers in the threads array
+     */
+    private int numThreads = 0;
+    /**
+     * The number of threads that are currently sleeping
+     */
+    private int sleepingThreads = 0;
+    /**
+     * The living set of workers in this pool.
+     */
+    private Worker[] threads;
+
+    protected package this(JobManager manager) {
+        threads = new Worker[10];
+        this.manager = manager;
+        mutex = new Mutex;
+        condition = new Condition(mutex);
+//         this.defaultContextLoader = Thread.currentThread().getContextClassLoader();
+    }
+
+    /**
+     * Adds a worker to the list of workers.
+     */
+    private void add(Worker worker) {
+        synchronized(mutex){
+            int size = threads.length;
+            if (numThreads + 1 > size) {
+                Worker[] newThreads = new Worker[2 * size];
+                System.arraycopy(threads, 0, newThreads, 0, size);
+                threads = newThreads;
+            }
+            threads[numThreads++] = worker;
+        }
+    }
+
+    private void decrementBusyThreads() {
+        synchronized(mutex){
+            //impossible to have less than zero busy threads
+            if (--busyThreads < 0) {
+                if (JobManager.DEBUG)
+                    Assert.isTrue(false, Integer.toString(busyThreads));
+                busyThreads = 0;
+            }
+        }
+    }
+
+    /**
+     * Signals the end of a job.  Note that this method can be called under
+     * OutOfMemoryError conditions and thus must be paranoid about allocating objects.
+     */
+    protected void endJob(InternalJob job, IStatus result) {
+        decrementBusyThreads();
+        //need to end rule in graph before ending job so that 2 threads
+        //do not become the owners of the same rule in the graph
+        if ((job.getRule_package() !is null) && !(cast(ThreadJob)job )) {
+            //remove any locks this thread may be owning on that rule
+            manager.getLockManager().removeLockCompletely(JThread.currentThread(), job.getRule_package());
+        }
+        manager.endJob_package(job, result, true);
+        //ensure this thread no longer owns any scheduling rules
+        manager.implicitJobs.endJob(job);
+    }
+    package void endJob_package(InternalJob job, IStatus result) {
+        endJob(job, result);
+    }
+
+    /**
+     * Signals the death of a worker thread.  Note that this method can be called under
+     * OutOfMemoryError conditions and thus must be paranoid about allocating objects.
+     */
+    protected void endWorker(Worker worker) {
+        synchronized(mutex){
+            if (remove(worker) && JobManager.DEBUG)
+                JobManager.debug_(Format("worker removed from pool: {}", worker)); //$NON-NLS-1$
+        }
+    }
+    package void endWorker_package(Worker worker) {
+        endWorker(worker);
+    }
+
+    private void incrementBusyThreads() {
+        synchronized(mutex){
+            //impossible to have more busy threads than there are threads
+            if (++busyThreads > numThreads) {
+                if (JobManager.DEBUG)
+                    Assert.isTrue(false, Format( "{},{}", busyThreads, numThreads));
+                busyThreads = numThreads;
+            }
+        }
+    }
+
+    /**
+     * Notification that a job has been added to the queue. Wake a worker,
+     * creating a new worker if necessary. The provided job may be null.
+     */
+    protected package void jobQueued() {
+        synchronized(mutex){
+            //if there is a sleeping thread, wake it up
+            if (sleepingThreads > 0) {
+                condition.notify();
+                return;
+            }
+            //create a thread if all threads are busy
+            if (busyThreads >= numThreads) {
+                Worker worker = new Worker(this);
+                worker.setDaemon(isDaemon);
+                add(worker);
+                if (JobManager.DEBUG)
+                    JobManager.debug_(Format("worker added to pool: {}", worker)); //$NON-NLS-1$
+                worker.start();
+                return;
+            }
+        }
+    }
+
+    /**
+     * Remove a worker thread from our list.
+     * @return true if a worker was removed, and false otherwise.
+     */
+    private bool remove(Worker worker) {
+        synchronized(mutex){
+            for (int i = 0; i < threads.length; i++) {
+                if (threads[i] is worker) {
+                    System.arraycopy(threads, i + 1, threads, i, numThreads - i - 1);
+                    threads[--numThreads] = null;
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Sets whether threads created in the worker pool should be daemon threads.
+     */
+    void setDaemon(bool value) {
+        this.isDaemon = value;
+    }
+
+    protected void shutdown() {
+        synchronized(mutex){
+            condition.notifyAll();
+        }
+    }
+    package void shutdown_package() {
+        shutdown();
+    }
+
+    /**
+     * Sleep for the given duration or until woken.
+     */
+    private void sleep(long duration) {
+        synchronized(mutex){
+            sleepingThreads++;
+            busyThreads--;
+            if (JobManager.DEBUG)
+                JobManager.debug_(Format("worker sleeping for: {}ms", duration)); //$NON-NLS-1$ //$NON-NLS-2$
+            try {
+                condition.wait(duration/1000.0f);
+            } catch (InterruptedException e) {
+                if (JobManager.DEBUG)
+                    JobManager.debug_("worker interrupted while waiting... :-|"); //$NON-NLS-1$
+            } finally {
+                sleepingThreads--;
+                busyThreads++;
+            }
+        }
+    }
+
+    /**
+     * Returns a new job to run. Returns null if the thread should die.
+     */
+    protected InternalJob startJob(Worker worker) {
+        //if we're above capacity, kill the thread
+        synchronized (mutex) {
+            if (!manager.isActive_package()) {
+                //must remove the worker immediately to prevent all threads from expiring
+                endWorker(worker);
+                return null;
+            }
+            //set the thread to be busy now in case of reentrant scheduling
+            incrementBusyThreads();
+        }
+        Job job = null;
+        try {
+            job = manager.startJob_package();
+            //spin until a job is found or until we have been idle for too long
+            long idleStart = System.currentTimeMillis();
+            while (manager.isActive_package() && job is null) {
+                long hint = manager.sleepHint_package();
+                if (hint > 0)
+                    sleep(Math.min(hint, BEST_BEFORE));
+                job = manager.startJob_package();
+                //if we were already idle, and there are still no new jobs, then
+                // the thread can expire
+                synchronized (mutex) {
+                    if (job is null && (System.currentTimeMillis() - idleStart > BEST_BEFORE) && (numThreads - busyThreads) > MIN_THREADS) {
+                        //must remove the worker immediately to prevent all threads from expiring
+                        endWorker(worker);
+                        return null;
+                    }
+                }
+            }
+            if (job !is null) {
+                //if this job has a rule, then we are essentially acquiring a lock
+                if ((job.getRule() !is null) && !(cast(ThreadJob)job )) {
+                    //don't need to re-aquire locks because it was not recorded in the graph
+                    //that this thread waited to get this rule
+                    manager.getLockManager().addLockThread(JThread.currentThread(), job.getRule());
+                }
+                //see if we need to wake another worker
+                if (manager.sleepHint_package() <= 0)
+                    jobQueued();
+            }
+        } finally {
+            //decrement busy thread count if we're not running a job
+            if (job is null)
+                decrementBusyThreads();
+        }
+        return job;
+    }
+    package InternalJob startJob_package(Worker worker) {
+        return startJob(worker);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/IJobChangeEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.jobs.IJobChangeEvent;
+
+import java.lang.all;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.Job;
+
+/**
+ * An event describing a change to the state of a job.
+ *
+ * @see IJobChangeListener
+ * @since 3.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IJobChangeEvent {
+    /**
+     * The amount of time in milliseconds to wait after scheduling the job before it
+     * should be run, or <code>-1</code> if not applicable for this type of event.
+     * This value is only applicable for the <code>scheduled</code> event.
+     *
+     * @return the delay time for this event
+     */
+    public long getDelay();
+
+    /**
+     * The job on which this event occurred.
+     *
+     * @return the job for this event
+     */
+    public Job getJob();
+
+    /**
+     * The result returned by the job's run method, or <code>null</code> if
+     * not applicable.  This value is only applicable for the <code>done</code> event.
+     *
+     * @return the status for this event
+     */
+    public IStatus getResult();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/IJobChangeListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 org.eclipse.core.runtime.jobs.IJobChangeListener;
+
+import java.lang.all;
+import java.util.Set;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+
+/**
+ * Callback interface for clients interested in being notified when jobs change state.
+ * <p>
+ * A single job listener instance can be added either to the job manager, for notification
+ * of all scheduled jobs, or to any set of individual jobs.  A single listener instance should
+ * not be added to both the job manager, and to individual jobs (such a listener may
+ * receive duplicate notifications).
+ * </p><p>
+ * Clients should not rely on the result of the <code>Job#getState()</code>
+ * method on jobs for which notification is occurring. Listeners are notified of
+ * all job state changes, but whether the state change occurs before, during, or
+ * after listeners are notified is unspecified.
+ * </p><p>
+ * Clients may implement this interface.
+ * </p>
+ * @see JobChangeAdapter
+ * @see IJobManager#addJobChangeListener(IJobChangeListener)
+ * @see IJobManager#removeJobChangeListener(IJobChangeListener)
+ * @see Job#addJobChangeListener(IJobChangeListener)
+ * @see Job#getState()
+ * @see Job#removeJobChangeListener(IJobChangeListener)
+ * @since 3.0
+ */
+public interface IJobChangeListener {
+    /**
+     * Notification that a job is about to be run. Listeners are allowed to sleep, cancel,
+     * or change the priority of the job before it is started (and as a result may prevent
+     * the run from actually occurring).
+     *
+     * @param event the event details
+     */
+    public void aboutToRun(IJobChangeEvent event);
+
+    /**
+     * Notification that a job was previously sleeping and has now been rescheduled
+     * to run.
+     *
+     * @param event the event details
+     */
+    public void awake(IJobChangeEvent event);
+
+    /**
+     * Notification that a job has completed execution, either due to cancelation, successful
+     * completion, or failure.  The event status object indicates how the job finished,
+     * and the reason for failure, if applicable.
+     *
+     * @param event the event details
+     */
+    public void done(IJobChangeEvent event);
+
+    /**
+     * Notification that a job has started running.
+     *
+     * @param event the event details
+     */
+    public void running(IJobChangeEvent event);
+
+    /**
+     * Notification that a job is being added to the queue of scheduled jobs.
+     * The event details includes the scheduling delay before the job should start
+     * running.
+     *
+     * @param event the event details, including the job instance and the scheduling
+     * delay
+     */
+    public void scheduled(IJobChangeEvent event);
+
+    /**
+     * Notification that a job was waiting to run and has now been put in the
+     * sleeping state.
+     *
+     * @param event the event details
+     */
+    public void sleeping(IJobChangeEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/IJobManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,421 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.jobs.IJobManager;
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.jobs.ILock;
+import org.eclipse.core.runtime.jobs.LockListener;
+
+import org.eclipse.core.runtime.jobs.ProgressProvider;
+import java.lang.JThread;
+
+/**
+ * The job manager provides facilities for scheduling, querying, and maintaining jobs
+ * and locks.  In particular, the job manager provides the following services:
+ * <ul>
+ * <li>Maintains a queue of jobs that are waiting to be run.  Items can be added to
+ * the queue using the <code>schedule</code> method.</li>
+ * <li>Allows manipulation of groups of jobs called job families.  Job families can
+ * be canceled, put to sleep, or woken up atomically.  There is also a mechanism
+ * for querying the set of known jobs in a given family.</li>
+ * <li>Allows listeners to find out about progress on running jobs, and to find out
+ * when jobs have changed states.</li>
+ * <li>Provides a factory for creating lock objects.  Lock objects are smart monitors
+ * that have strategies for avoiding deadlock.</li>
+ * <li>Provide feedback to a client that is waiting for a given job or family of jobs
+ * to complete.</li>
+ * </ul>
+ *
+ * @see Job
+ * @see ILock
+ * @since 3.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IJobManager {
+    /**
+     * A system property key indicating whether the job manager should create
+     * job threads as daemon threads.  Set to <code>true</code> to force all worker
+     * threads to be created as daemon threads. Set to <code>false</code> to force
+     * all worker threads to be created as non-daemon threads.
+     * @since 3.3
+     */
+    public static final String PROP_USE_DAEMON_THREADS = "eclipse.jobs.daemon"; //$NON-NLS-1$
+
+    /**
+     * Registers a job listener with the job manager.
+     * Has no effect if an identical listener is already registered.
+     *
+     * @param listener the listener to be added
+     * @see #removeJobChangeListener(IJobChangeListener)
+     * @see IJobChangeListener
+     */
+    public void addJobChangeListener(IJobChangeListener listener);
+
+    /**
+     * Begins applying this rule in the calling thread.  If the rule conflicts with another
+     * rule currently running in another thread, this method blocks until there are
+     * no conflicting rules.  Calls to <tt>beginRule</tt> must eventually be followed
+     * by a matching call to <tt>endRule</tt> in the same thread and with the identical
+     * rule instance.
+     * <p>
+     * Rules can be nested only if the rule for the inner <tt>beginRule</tt>
+     * is contained within the rule for the outer <tt>beginRule</tt>.  Rule containment
+     * is tested with the API method <tt>ISchedulingRule.contains</tt>.  Also, begin/end
+     * pairs must be strictly nested.  Only the rule that has most recently begun
+     * can be ended at any given time.
+     * <p>
+     * A rule of <code>null</code> can be used, but will be ignored for scheduling
+     * purposes.  The outermost non-null rule in the thread will be used for scheduling. A
+     * <code>null</code> rule that is begun must still be ended.
+     * <p>
+     * If this method is called from within a job that has a scheduling rule, the
+     * given rule must also be contained within the rule for the running job.
+     * <p>
+     * Note that <tt>endRule</tt> must be called even if <tt>beginRule</tt> fails.
+     * The recommended usage is:
+     * <pre>
+     * final ISchedulingRule rule = ...;
+     * try {
+     *  manager.beginRule(rule, monitor);
+     * } finally {
+     *  manager.endRule(rule);
+     * }
+     * </pre>
+     *
+     * @param rule the rule to begin applying in this thread, or <code>null</code>
+     * @param monitor a progress monitor, or <code>null</code> if progress
+     *    reporting and cancellation are not desired
+     * @throws IllegalArgumentException if the rule is not strictly nested within
+     *  all other rules currently active for this thread
+     * @throws OperationCanceledException if the supplied monitor reports cancelation
+     *  before the rule becomes available
+     * @see ISchedulingRule#contains(ISchedulingRule)
+     */
+    public void beginRule(ISchedulingRule rule, IProgressMonitor monitor);
+
+    /**
+     * Cancels all jobs in the given job family.  Jobs in the family that are currently waiting
+     * will be removed from the queue.  Sleeping jobs will be discarded without having
+     * a chance to wake up.  Currently executing jobs will be asked to cancel but there
+     * is no guarantee that they will do so.
+     *
+     * @param family the job family to cancel, or <code>null</code> to cancel all jobs
+     * @see Job#belongsTo(Object)
+     */
+    public void cancel(Object family);
+
+    /**
+     * Returns a progress monitor that can be used to provide
+     * aggregated progress feedback on a set of running jobs. A user
+     * interface will typically group all jobs in a progress group together,
+     * providing progress feedback for individual jobs as well as aggregated
+     * progress for the entire group.  Jobs in the group may be run sequentially,
+     * in parallel, or some combination of the two.
+     * <p>
+     * Recommended usage (this snippet runs two jobs in sequence in a
+     * single progress group):
+     * <pre>
+     *    Job parseJob, compileJob;
+     *    IProgressMonitor pm = Platform.getJobManager().createProgressGroup();
+     *    try {
+     *       pm.beginTask("Building", 10);
+     *       parseJob.setProgressGroup(pm, 5);
+     *       parseJob.schedule();
+     *       compileJob.setProgressGroup(pm, 5);
+     *       compileJob.schedule();
+     *       parseJob.join();
+     *       compileJob.join();
+     *    } finally {
+     *       pm.done();
+     *    }
+     * </pre>
+     *
+     * @see Job#setProgressGroup(IProgressMonitor, int)
+     * @see IProgressMonitor
+     * @return a progress monitor
+     */
+    public IProgressMonitor createProgressGroup();
+
+    /**
+     * Returns the job that is currently running in this thread, or <code>null</code> if there
+     * is no currently running job.
+     *
+     * @return the job or <code>null</code>
+     */
+    public Job currentJob();
+
+    /**
+     * Ends the application of a rule to the calling thread.  Calls to <tt>endRule</tt>
+     * must be preceded by a matching call to <tt>beginRule</tt> in the same thread
+     * with an identical rule instance.
+     * <p>
+     * Rules can be nested only if the rule for the inner <tt>beginRule</tt>
+     * is contained within the rule for the outer <tt>beginRule</tt>.  Also, begin/end
+     * pairs must be strictly nested.  Only the rule that has most recently begun
+     * can be ended at any given time.
+     *
+     * @param rule the rule to end applying in this thread
+     * @throws IllegalArgumentException if this method is called on a rule for which
+     * there is no matching begin, or that does not match the most recent begin.
+     * @see ISchedulingRule#contains(ISchedulingRule)
+     */
+    public void endRule(ISchedulingRule rule);
+
+    /**
+     * Returns all waiting, executing and sleeping jobs belonging
+     * to the given family. If no jobs are found, an empty array is returned.
+     *
+     * @param family the job family to find, or <code>null</code> to find all jobs
+     * @return the job array
+     * @see Job#belongsTo(Object)
+     */
+    public Job[] find(Object family);
+
+    /**
+     * Returns whether the job manager is currently idle.  The job manager is
+     * idle if no jobs are currently running or waiting to run.
+     *
+     * @return <code>true</code> if the job manager is idle, and
+     * <code>false</code> otherwise
+     * @since 3.1
+     */
+    public bool isIdle();
+
+    /**
+     * Returns whether the job manager is currently suspended.
+     *
+     * @return <code>true</code> if the job manager is suspended, and
+     * <code>false</code> otherwise
+     * @since 3.4
+     * @see #suspend()
+     * @see #resume()
+     */
+    public bool isSuspended();
+
+    /**
+     * Waits until all jobs of the given family are finished.  This method will block the
+     * calling thread until all such jobs have finished executing, or until this thread is
+     * interrupted.   If there are no jobs in
+     * the family that are currently waiting, running, or sleeping, this method returns
+     * immediately.  Feedback on how the join is progressing is provided to a  progress
+     * monitor.
+     * <p>
+     * If this method is called while the job manager is suspended, only jobs
+     * that are currently running will be joined; Once there are no jobs
+     * in the family in the {@link Job#RUNNING} state, this method returns.
+     * </p>
+     * <p>
+     * Note that there is a deadlock risk when using join.  If the calling thread owns
+     * a lock or object monitor that the joined thread is waiting for, deadlock
+     * will occur. This method can also result in starvation of the current thread if
+     * another thread continues to add jobs of the given family, or if a
+     * job in the given family reschedules itself in an infinite loop.
+     * </p>
+     *
+     * @param family the job family to join, or <code>null</code> to join all jobs.
+     * @param monitor Progress monitor for reporting progress on how the
+     * wait is progressing, or <code>null</code> if no progress monitoring is required.
+     * @exception InterruptedException if this thread is interrupted while waiting
+     * @exception OperationCanceledException if the progress monitor is canceled while waiting
+     * @see Job#belongsTo(Object)
+     * @see #suspend()
+     */
+    public void join(Object family, IProgressMonitor monitor);
+
+    /**
+     * Creates a new lock object.  All lock objects supplied by the job manager
+     * know about each other and will always avoid circular deadlock amongst
+     * themselves.
+     *
+     * @return the new lock object
+     */
+    public ILock newLock();
+
+    /**
+     * Removes a job listener from the job manager.
+     * Has no effect if an identical listener is not already registered.
+     *
+     * @param listener the listener to be removed
+     * @see #addJobChangeListener(IJobChangeListener)
+     * @see IJobChangeListener
+     */
+    public void removeJobChangeListener(IJobChangeListener listener);
+
+    /**
+     * Resumes execution of jobs after a previous <code>suspend</code>.  All
+     * jobs that were sleeping or waiting prior to the suspension, or that were
+     * scheduled while the job manager was suspended, will now be eligible
+     * for execution.
+     * <p>
+     * Calling this method on a rule that is not suspended  has no effect.  If another
+     * thread also owns the rule at the time this method is called, then the rule will
+     * not be resumed until all threads have released the rule.
+     *
+     * @deprecated This method is not safe and should not be used.
+     * Suspending a scheduling rule violates the thread safety
+     * of clients that use scheduling rules as a mutual exclusion mechanism,
+     * and can result in concurrency problems in all clients that use the suspended rule.
+     * @see #suspend(ISchedulingRule, IProgressMonitor)
+     */
+    public void resume(ISchedulingRule rule);
+
+    /**
+     * Resumes execution of jobs after a previous <code>suspend</code>.  All
+     * jobs that were sleeping or waiting prior to the suspension, or that were
+     * scheduled while the job manager was suspended, will now be eligible
+     * for execution.
+     * <p>
+     * Calling <code>resume</code> when the job manager is not suspended
+     * has no effect.
+     *
+     * @see #suspend()
+     * @see #isSuspended()
+     */
+    public void resume();
+
+    /**
+     * Provides a hook that is notified whenever a thread is about to wait on a lock,
+     * or when a thread is about to release a lock.  This hook must only be set once.
+     * <p>
+     * This method is for internal use by the platform-related plug-ins.
+     * Clients should not call this method.
+     * </p>
+     * @see LockListener
+     */
+    public void setLockListener(LockListener listener);
+
+    /**
+     * Registers a progress provider with the job manager.  If there was a
+     * provider already registered, it is replaced.
+     * <p>
+     * This method is intended for use by the currently executing Eclipse application.
+     * Plug-ins outside the currently running application should not call this method.
+     * </p>
+     *
+     * @param provider the new provider, or <code>null</code> if no progress
+     * is needed
+     */
+    public void setProgressProvider(ProgressProvider provider);
+
+    /**
+     * Suspends execution of all jobs.  Jobs that are already running
+     * when this method is invoked will complete as usual, but all sleeping and
+     * waiting jobs will not be executed until the job manager is resumed.
+     * <p>
+     * The job manager will remain suspended until a subsequent call to
+     * <code>resume</code>.  Further calls to <code>suspend</code>
+     * when the job manager is already suspended are ignored.
+     * <p>
+     * All attempts to join sleeping and waiting jobs while the job manager is
+     * suspended will return immediately.
+     * <p>
+     * Note that this very powerful function should be used with extreme caution.
+     * Suspending the job manager will prevent all jobs in the system from executing,
+     * which may have adverse affects on components that are relying on
+     * execution of jobs. The job manager should never be suspended without intent
+     * to resume execution soon afterwards.
+     *
+     * @see #resume()
+     * @see #join(Object, IProgressMonitor)
+     * @see #isSuspended()
+     */
+    public void suspend();
+
+    /**
+     * Defers execution of all jobs with scheduling rules that conflict with the
+     * given rule. The caller will be blocked until all currently executing jobs with
+     * conflicting rules are completed.  Conflicting jobs that are sleeping or waiting at
+     * the time this method is called will not be executed until the rule is resumed.
+     * <p>
+     * While a rule is suspended, all calls to <code>beginRule</code> and
+     * <code>endRule</code> on a suspended rule will not block the caller.
+     * The rule remains suspended until a subsequent call to
+     * <code>resume(ISchedulingRule)</code> with the identical rule instance.
+     * Further calls to <code>suspend</code> with an identical rule prior to calling
+     * <code>resume</code> are ignored.
+     * </p>
+     * <p>
+     * This method is long-running; progress and cancelation are provided by
+     * the given progress monitor. In the case of cancelation, the rule will
+     * not be suspended.
+     * </p>
+     * Note: this very powerful function should be used with extreme caution.
+     * Suspending rules will prevent jobs in the system from executing, which may
+     * have adverse effects on components that are relying on execution of jobs.
+     * The job manager should never be suspended without intent to resume
+     * execution soon afterwards. Deadlock will result if the thread responsible
+     * for resuming the rule attempts to join a suspended job.
+     *
+     * @deprecated This method is not safe and should not be used.
+     * Suspending a scheduling rule violates the thread safety
+     * of clients that use scheduling rules as a mutual exclusion mechanism,
+     * and can result in concurrency problems in all clients that use the suspended rule.
+     * @param rule The scheduling rule to suspend. Must not be <code>null</code>.
+     * @param monitor a progress monitor, or <code>null</code> if progress
+     * reporting is not desired
+     * @exception OperationCanceledException if the operation is canceled.
+     * Cancelation can occur even if no progress monitor is provided.
+     * @see #resume(ISchedulingRule)
+     */
+    public void suspend(ISchedulingRule rule, IProgressMonitor monitor);
+
+    /**
+     * Requests that all jobs in the given job family be suspended.  Jobs currently
+     * waiting to be run will be removed from the queue and moved into the
+     * <code>SLEEPING</code> state.  Jobs that have been put to sleep
+     * will remain in that state until either resumed or canceled.  This method has
+     * no effect on jobs that are not currently waiting to be run.
+     * <p>
+     * Sleeping jobs can be resumed using <code>wakeUp</code>.
+     *
+     * @param family the job family to sleep, or <code>null</code> to sleep all jobs.
+     * @see Job#belongsTo(Object)
+     */
+    public void sleep(Object family);
+
+    /**
+     * Transfers ownership of a scheduling rule to another thread.  The identical
+     * scheduling rule must currently be owned by the calling thread as a result of
+     * a previous call to <code>beginRule</code>.  The destination thread must
+     * not already own a scheduling rule.
+     * <p>
+     * Calling this method is equivalent to atomically calling <code>endRule</code>
+     * in the calling thread followed by an immediate <code>beginRule</code> in
+     * the destination thread.  The destination thread is responsible for subsequently
+     * calling <code>endRule</code> when it is finished using the rule.
+     * <p>
+     * This method has no effect when the destination thread is the same as the
+     * calling thread.
+     *
+     * @param rule The scheduling rule to transfer
+     * @param destinationThread The new owner for the transferred rule.
+     * @since 3.1
+     */
+    public void transferRule(ISchedulingRule rule, JThread destinationThread);
+
+    /**
+     * Resumes scheduling of all sleeping jobs in the given family.  This method
+     * has no effect on jobs in the family that are not currently sleeping.
+     *
+     * @param family the job family to wake up, or <code>null</code> to wake up all jobs
+     * @see Job#belongsTo(Object)
+     */
+    public void wakeUp(Object family);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/IJobStatus.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.jobs.IJobStatus;
+
+import java.lang.all;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.Job;
+
+/**
+ * Represents status relating to the execution of jobs.
+ *
+ * @see org.eclipse.core.runtime.IStatus
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IJobStatus : IStatus {
+    /**
+     * Returns the job associated with this status.
+     *
+     * @return the job associated with this status
+     */
+    public Job getJob();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/ILock.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.jobs.ILock;
+
+import java.lang.all;
+
+/**
+ * A lock is used to control access to an exclusive resource.
+ * <p>
+ * Locks are reentrant.  That is, they can be acquired multiple times by the same thread
+ * without releasing.  Locks are only released when the number of successful acquires
+ * equals the number of successful releases.
+ * </p><p>
+ * Locks are capable of detecting and recovering from programming errors that cause
+ * circular waiting deadlocks. When a deadlock between two or more <tt>ILock</tt>
+ * instances is detected, detailed debugging information is printed to the log file.  The
+ * locks will then automatically recover from the deadlock by employing a release
+ * and wait strategy. One thread will lose control of the locks it owns, thus breaking
+ * the deadlock and  allowing other threads to proceed.  Once that thread's locks are
+ * all available, it will be given exclusive access to all its locks and allowed to proceed.
+ * A thread can only lose locks while it is waiting on an <tt>acquire()</tt> call.
+ *
+ * </p><p>
+ * Successive acquire attempts by different threads are queued and serviced on
+ * a first come, first served basis.
+ * </p><p>
+ * It is very important that acquired locks eventually get released.  Calls to release
+ * should be done in a finally block to ensure they execute.  For example:
+ * <pre>
+ * try {
+ *  lock.acquire();
+ *  // ... do work here ...
+ * } finally {
+ *  lock.release();
+ * }
+ * </pre>
+ * Note: although <tt>lock.acquire</tt> should never fail, it is good practice to place
+ * it inside the try block anyway.  Releasing without acquiring is far less catastrophic
+ * than acquiring without releasing.
+ * </p>
+ *
+ * @see IJobManager#newLock()
+ * @since 3.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ILock {
+    /**
+     * Attempts to acquire this lock.  If the lock is in use and the specified delay is
+     * greater than zero, the calling thread will block until one of the following happens:
+     * <ul>
+     * <li>This lock is available</li>
+     * <li>The thread is interrupted</li>
+     * <li>The specified delay has elapsed</li>
+     * </ul>
+     * <p>
+     * While a thread is waiting,  locks it already owns may be granted to other threads
+     * if necessary to break a deadlock.  In this situation, the calling thread may be blocked
+     * for longer than the specified delay.  On returning from this call, the calling thread
+     * will once again have exclusive access to any other locks it owned upon entering
+     * the acquire method.
+     *
+     * @param delay the number of milliseconds to delay
+     * @return <code>true</code> if the lock was successfully acquired, and
+     * <code>false</code> otherwise.
+     * @exception InterruptedException if the thread was interrupted
+     */
+    public bool acquire(long delay);
+
+    /**
+     * Acquires this lock.  If the lock is in use, the calling thread will block until the lock
+     * becomes available.  If the calling thread owns several locks, it will be blocked
+     * until all threads it requires become available, or until the thread is interrupted.
+     * While a thread is waiting, its locks may be granted to other threads if necessary
+     * to break a deadlock.  On returning from this call, the calling thread will
+     * have exclusive access to this lock, and any other locks it owned upon
+     * entering the acquire method.
+     * <p>
+     * This implementation ignores attempts to interrupt the thread.  If response to
+     * interruption is needed, use the method <code>acquire(long)</code>
+     */
+    public void acquire();
+
+    /**
+     * Returns the number of nested acquires on this lock that have not been released.
+     * This is the number of times that release() must be called before the lock is
+     * freed.
+     *
+     * @return the number of nested acquires that have not been released
+     */
+    public int getDepth();
+
+    /**
+     * Releases this lock. Locks must only be released by the thread that currently
+     * owns the lock.
+     */
+    public void release();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/ISchedulingRule.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Scheduling rules are used by jobs to indicate when they need exclusive access
+ * to a resource.  Scheduling rules can also be applied synchronously to a thread
+ * using <tt>IJobManager.beginRule(ISchedulingRule)</tt> and 
+ * <tt>IJobManager.endRule(ISchedulingRule)</tt>.  The job manager guarantees that 
+ * no two jobs with conflicting scheduling rules will run concurrently. 
+ * Multiple rules can be applied to a given thread only if the outer rule explicitly 
+ * allows the nesting as specified by the <code>contains</code> method.  
+ * <p>
+ * Clients may implement this interface.
+ * 
+ * @see Job#getRule()
+ * @see Job#setRule(ISchedulingRule)
+ * @see Job#schedule(long)
+ * @see IJobManager#beginRule(ISchedulingRule, org.eclipse.core.runtime.IProgressMonitor)
+ * @see IJobManager#endRule(ISchedulingRule)
+ * @since 3.0
+ */
+public interface ISchedulingRule {
+    /**
+     * Returns whether this scheduling rule completely contains another scheduling
+     * rule.  Rules can only be nested within a thread if the inner rule is completely 
+     * contained within the outer rule.
+     * <p>
+     * Implementations of this method must obey the rules of a partial order relation
+     * on the set of all scheduling rules.  In particular, implementations must be reflexive
+     * (a.contains(a) is always true), antisymmetric (a.contains(b) and b.contains(a) iff a.equals(b), 
+     * and transitive (if a.contains(b) and b.contains(c), then a.contains(c)).  Implementations
+     * of this method must return <code>false</code> when compared to a rule they
+     * know nothing about.
+     * 
+     * @param rule the rule to check for containment
+     * @return <code>true</code> if this rule contains the given rule, and 
+     * <code>false</code> otherwise.
+     */
+    public bool contains(ISchedulingRule rule);
+
+    /**
+     * Returns whether this scheduling rule is compatible with another scheduling rule.
+     * If <code>true</code> is returned, then no job with this rule will be run at the 
+     * same time as a job with the conflicting rule.  If <code>false</code> is returned, 
+     * then the job manager is free to run jobs with these rules at the same time.
+     * <p>
+     * Implementations of this method must be reflexive, symmetric, and consistent,
+     * and must return <code>false</code> when compared  to a rule they know 
+     * nothing about.
+     *
+     * @param rule the rule to check for conflicts
+     * @return <code>true</code> if the rule is conflicting, and <code>false</code>
+     *  otherwise.
+     */
+    public bool isConflicting(ISchedulingRule rule);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/Job.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,700 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.jobs.Job;
+
+import java.lang.JThread;
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.internal.jobs.InternalJob;
+import org.eclipse.core.internal.jobs.JobManager;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobManager;
+import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * Jobs are units of runnable work that can be scheduled to be run with the job
+ * manager.  Once a job has completed, it can be scheduled to run again (jobs are
+ * reusable).
+ * <p>
+ * Jobs have a state that indicates what they are currently doing.  When constructed,
+ * jobs start with a state value of <code>NONE</code>.  When a job is scheduled
+ * to be run, it moves into the <code>WAITING</code> state.  When a job starts
+ * running, it moves into the <code>RUNNING</code> state.  When execution finishes
+ * (either normally or through cancelation), the state changes back to
+ * <code>NONE</code>.
+ * </p><p>
+ * A job can also be in the <code>SLEEPING</code> state.  This happens if a user
+ * calls Job.sleep() on a waiting job, or if a job is scheduled to run after a specified
+ * delay.  Only jobs in the <code>WAITING</code> state can be put to sleep.
+ * Sleeping jobs can be woken at any time using Job.wakeUp(), which will put the
+ * job back into the <code>WAITING</code> state.
+ * </p><p>
+ * Jobs can be assigned a priority that is used as a hint about how the job should
+ * be scheduled.  There is no guarantee that jobs of one priority will be run before
+ * all jobs of lower priority.  The javadoc for the various priority constants provide
+ * more detail about what each priority means.  By default, jobs start in the
+ * <code>LONG</code> priority class.
+ *
+ * @see IJobManager
+ * @since 3.0
+ */
+public abstract class Job : InternalJob, IAdaptable {
+    // SWT from IAdaptable
+    public Object getAdapter(ClassInfo adapter){
+        return super.getAdapter(adapter);
+    }
+
+    /**
+     * Job status return value that is used to indicate asynchronous job completion.
+     * @see Job#run(IProgressMonitor)
+     * @see Job#done(IStatus)
+     */
+    private static IStatus ASYNC_FINISH_;
+    public static IStatus ASYNC_FINISH(){
+        if( ASYNC_FINISH_ is null ){
+            synchronized( Job.classinfo ) {
+                if( ASYNC_FINISH_ is null ){
+                    ASYNC_FINISH_ = new Status(IStatus.OK, JobManager.PI_JOBS, 1, "", null);//$NON-NLS-1$
+                }
+            }
+        }
+        return ASYNC_FINISH_;
+    }
+
+    /* Job priorities */
+    /**
+     * Job priority constant (value 10) for interactive jobs.
+     * Interactive jobs generally have priority over all other jobs.
+     * Interactive jobs should be either fast running or very low on CPU
+     * usage to avoid blocking other interactive jobs from running.
+     *
+     * @see #getPriority()
+     * @see #setPriority(int)
+     * @see #run(IProgressMonitor)
+     */
+    public static const int INTERACTIVE = 10;
+    /**
+     * Job priority constant (value 20) for short background jobs.
+     * Short background jobs are jobs that typically complete within a second,
+     * but may take longer in some cases.  Short jobs are given priority
+     * over all other jobs except interactive jobs.
+     *
+     * @see #getPriority()
+     * @see #setPriority(int)
+     * @see #run(IProgressMonitor)
+     */
+    public static const int SHORT = 20;
+    /**
+     * Job priority constant (value 30) for long-running background jobs.
+     *
+     * @see #getPriority()
+     * @see #setPriority(int)
+     * @see #run(IProgressMonitor)
+     */
+    public static const int LONG = 30;
+    /**
+     * Job priority constant (value 40) for build jobs.  Build jobs are
+     * generally run after all other background jobs complete.
+     *
+     * @see #getPriority()
+     * @see #setPriority(int)
+     * @see #run(IProgressMonitor)
+     */
+    public static const int BUILD = 40;
+
+    /**
+     * Job priority constant (value 50) for decoration jobs.
+     * Decoration jobs have lowest priority.  Decoration jobs generally
+     * compute extra information that the user may be interested in seeing
+     * but is generally not waiting for.
+     *
+     * @see #getPriority()
+     * @see #setPriority(int)
+     * @see #run(IProgressMonitor)
+     */
+    public static const int DECORATE = 50;
+    /**
+     * Job state code (value 0) indicating that a job is not
+     * currently sleeping, waiting, or running (i.e., the job manager doesn't know
+     * anything about the job).
+     *
+     * @see #getState()
+     */
+    public static const int NONE = 0;
+    /**
+     * Job state code (value 1) indicating that a job is sleeping.
+     *
+     * @see #run(IProgressMonitor)
+     * @see #getState()
+     */
+    public static const int SLEEPING = 0x01;
+    /**
+     * Job state code (value 2) indicating that a job is waiting to be run.
+     *
+     * @see #getState()
+     */
+    public static const int WAITING = 0x02;
+    /**
+     * Job state code (value 4) indicating that a job is currently running
+     *
+     * @see #getState()
+     */
+    public static const int RUNNING = 0x04;
+
+    /**
+     * Returns the job manager.
+     *
+     * @return the job manager
+     * @since org.eclipse.core.jobs 3.2
+     */
+    public static final IJobManager getJobManager() {
+        return manager;
+    }
+
+    /**
+     * Creates a new job with the specified name.  The job name is a human-readable
+     * value that is displayed to users.  The name does not need to be unique, but it
+     * must not be <code>null</code>.
+     *
+     * @param name the name of the job.
+     */
+    public this(String name) {
+        super(name);
+    }
+
+    /**
+     * Registers a job listener with this job
+     * Has no effect if an identical listener is already registered.
+     *
+     * @param listener the listener to be added.
+     */
+    public final void addJobChangeListener(IJobChangeListener listener) {
+        super.addJobChangeListener(listener);
+    }
+
+    /**
+     * Returns whether this job belongs to the given family.  Job families are
+     * represented as objects that are not interpreted or specified in any way
+     * by the job manager.  Thus, a job can choose to belong to any number of
+     * families.
+     * <p>
+     * Clients may override this method.  This default implementation always returns
+     * <code>false</code>.  Overriding implementations must return <code>false</code>
+     * for families they do not recognize.
+     * </p>
+     *
+     * @param family the job family identifier
+     * @return <code>true</code> if this job belongs to the given family, and
+     * <code>false</code> otherwise.
+     */
+    public bool belongsTo(Object family) {
+        return false;
+    }
+
+    /**
+     * Stops the job.  If the job is currently waiting,
+     * it will be removed from the queue.  If the job is sleeping,
+     * it will be discarded without having a chance to resume and its sleeping state
+     * will be cleared.  If the job is currently executing, it will be asked to
+     * stop but there is no guarantee that it will do so.
+     *
+     * @return <code>false</code> if the job is currently running (and thus may not
+     * respond to cancelation), and <code>true</code> in all other cases.
+     */
+    public final bool cancel() {
+        return super.cancel();
+    }
+
+    /**
+     * A hook method indicating that this job is running and {@link #cancel()}
+     * is being called for the first time.
+     * <p>
+     * Subclasses may override this method to perform additional work when
+     * a cancelation request is made.  This default implementation does nothing.
+     * @since 3.3
+     */
+    protected void canceling() {
+        //default implementation does nothing
+    }
+
+    /**
+     * Jobs that complete their execution asynchronously must indicate when they
+     * are finished by calling this method.  This method must not be called by
+     * a job that has not indicated that it is executing asynchronously.
+     * <p>
+     * This method must not be called from within the scope of a job's <code>run</code>
+     * method.  Jobs should normally indicate completion by returning an appropriate
+     * status from the <code>run</code> method.  Jobs that return a status of
+     * <code>ASYNC_FINISH</code> from their run method must later call
+     * <code>done</code> to indicate completion.
+     *
+     * @param result a status object indicating the result of the job's execution.
+     * @see #ASYNC_FINISH
+     * @see #run(IProgressMonitor)
+     */
+    public final void done(IStatus result) {
+        super.done(result);
+    }
+
+    /**
+     * Returns the human readable name of this job.  The name is never
+     * <code>null</code>.
+     *
+     * @return the name of this job
+     */
+    public final String getName() {
+        return super.getName();
+    }
+
+    /**
+     * Returns the priority of this job.  The priority is used as a hint when the job
+     * is scheduled to be run.
+     *
+     * @return the priority of the job.  One of INTERACTIVE, SHORT, LONG, BUILD,
+     *  or DECORATE.
+     */
+    public final int getPriority() {
+        return super.getPriority();
+    }
+
+    /**
+     * Returns the value of the property of this job identified by the given key,
+     * or <code>null</code> if this job has no such property.
+     *
+     * @param key the name of the property
+     * @return the value of the property,
+     *     or <code>null</code> if this job has no such property
+     * @see #setProperty(QualifiedName, Object)
+     */
+    public final Object getProperty(QualifiedName key) {
+        return super.getProperty(key);
+    }
+
+    /**
+     * Returns the result of this job's last run.
+     *
+     * @return the result of this job's last run, or <code>null</code> if this
+     * job has never finished running.
+     */
+    public final IStatus getResult() {
+        return super.getResult();
+    }
+
+    /**
+     * Returns the scheduling rule for this job.  Returns <code>null</code> if this job has no
+     * scheduling rule.
+     *
+     * @return the scheduling rule for this job, or <code>null</code>.
+     * @see ISchedulingRule
+     * @see #setRule(ISchedulingRule)
+     */
+    public final ISchedulingRule getRule() {
+        return super.getRule();
+    }
+
+    /**
+     * Returns the state of the job. Result will be one of:
+     * <ul>
+     * <li><code>Job.RUNNING</code> - if the job is currently running.</li>
+     * <li><code>Job.WAITING</code> - if the job is waiting to be run.</li>
+     * <li><code>Job.SLEEPING</code> - if the job is sleeping.</li>
+     * <li><code>Job.NONE</code> - in all other cases.</li>
+     * </ul>
+     * <p>
+     * Note that job state is inherently volatile, and in most cases clients
+     * cannot rely on the result of this method being valid by the time the
+     * result is obtained.  For example, if <tt>getState</tt> returns
+     * <tt>RUNNING</tt>,  the job may have actually completed by the
+     * time the <tt>getState</tt> method returns.  All clients can infer from
+     * invoking this method is that  the job was recently in the returned state.
+     *
+     * @return the job state
+     */
+    public final int getState() {
+        return super.getState();
+    }
+
+    /**
+     * Returns the thread that this job is currently running in.
+     *
+     * @return the thread this job is running in, or <code>null</code>
+     * if this job is not running or the thread is unknown.
+     */
+    public final JThread getThread() {
+        return super.getThread();
+    }
+
+    /**
+     * Returns whether this job is blocking a higher priority non-system job from
+     * starting due to a conflicting scheduling rule.  Returns <code>false</code>
+     * if this job is not running, or is not blocking a higher priority non-system job.
+     *
+     * @return <code>true</code> if this job is blocking a higher priority non-system
+     * job, and <code>false</code> otherwise.
+     * @see #getRule()
+     * @see #isSystem()
+     */
+    public final bool isBlocking() {
+        return super.isBlocking();
+    }
+
+    /**
+     * Returns whether this job is a system job.  System jobs are typically not
+     * revealed to users in any UI presentation of jobs.  Other than their UI presentation,
+     * system jobs act exactly like other jobs.  If this value is not explicitly set, jobs
+     * are treated as non-system jobs.  The default value is <code>false</code>.
+     *
+     * @return <code>true</code> if this job is a system job, and
+     * <code>false</code> otherwise.
+     * @see #setSystem(bool)
+     */
+    public final bool isSystem() {
+        return super.isSystem();
+    }
+
+    /**
+     * Returns whether this job has been directly initiated by a UI end user.
+     * These jobs may be presented differently in the UI.  The default value
+     * is <code>false</code>.
+     *
+     * @return <code>true</code> if this job is a user-initiated job, and
+     * <code>false</code> otherwise.
+     * @see #setUser(bool)
+     */
+    public final bool isUser() {
+        return super.isUser();
+    }
+
+    /**
+     * Waits until this job is finished.  This method will block the calling thread until the
+     * job has finished executing, or until this thread has been interrupted.  If the job
+     * has not been scheduled, this method returns immediately.  A job must not
+     * be joined from within the scope of its run method.
+     * <p>
+     * If this method is called on a job that reschedules itself from within the
+     * <tt>run</tt> method, the join will return at the end of the first execution.
+     * In other words, join will return the first time this job exits the
+     * {@link #RUNNING} state, or as soon as this job enters the {@link #NONE} state.
+     * </p>
+     * <p>
+     * If this method is called while the job manager is suspended, this job
+     * will only be joined if it is already running; if this job is waiting or sleeping,
+     * this method returns immediately.
+     * </p>
+     * <p>
+     * Note that there is a deadlock risk when using join.  If the calling thread owns
+     * a lock or object monitor that the joined thread is waiting for, deadlock
+     * will occur.
+     * </p>
+     *
+     * @exception InterruptedException if this thread is interrupted while waiting
+     * @see ILock
+     * @see IJobManager#suspend()
+     */
+    public final void join() {
+        super.join();
+    }
+
+    /**
+     * Removes a job listener from this job.
+     * Has no effect if an identical listener is not already registered.
+     *
+     * @param listener the listener to be removed
+     */
+    public final void removeJobChangeListener(IJobChangeListener listener) {
+        super.removeJobChangeListener(listener);
+    }
+
+    /**
+     * Executes this job.  Returns the result of the execution.
+     * <p>
+     * The provided monitor can be used to report progress and respond to
+     * cancellation.  If the progress monitor has been canceled, the job
+     * should finish its execution at the earliest convenience and return a result
+     * status of severity {@link IStatus#CANCEL}.  The singleton
+     * cancel status {@link Status#CANCEL_STATUS} can be used for
+     * this purpose.  The monitor is only valid for the duration of the invocation
+     * of this method.
+     * <p>
+     * This method must not be called directly by clients.  Clients should call
+     * <code>schedule</code>, which will in turn cause this method to be called.
+     * <p>
+     * Jobs can optionally finish their execution asynchronously (in another thread) by
+     * returning a result status of {@link #ASYNC_FINISH}.  Jobs that finish
+     * asynchronously <b>must</b> specify the execution thread by calling
+     * <code>setThread</code>, and must indicate when they are finished by calling
+     * the method <code>done</code>.
+     *
+     * @param monitor the monitor to be used for reporting progress and
+     * responding to cancelation. The monitor is never <code>null</code>
+     * @return resulting status of the run. The result must not be <code>null</code>
+     * @see #ASYNC_FINISH
+     * @see #done(IStatus)
+     */
+    protected abstract IStatus run(IProgressMonitor monitor);
+
+    /**
+     * Schedules this job to be run.  The job is added to a queue of waiting
+     * jobs, and will be run when it arrives at the beginning of the queue.
+     * <p>
+     * This is a convenience method, fully equivalent to
+     * <code>schedule(0L)</code>.
+     * </p>
+     * @see #schedule(long)
+     */
+    public final void schedule() {
+        super.schedule(0L);
+    }
+
+    /**
+     * Schedules this job to be run after a specified delay.  The job is put in the
+     * {@link #SLEEPING} state until the specified delay has elapsed, after which
+     * the job is added to a queue of {@link #WAITING} jobs. Once the job arrives
+     * at the beginning of the queue, it will be run at the first available opportunity.
+     * </p><p>
+     * Jobs of equal priority and <code>delay</code> with conflicting scheduling
+     * rules are guaranteed to run in the order they are scheduled. No guarantees
+     * are made about the relative execution order of jobs with unrelated or
+     * <code>null</code> scheduling rules, or different priorities.
+     * <p>
+     * If this job is currently running, it will be rescheduled with the specified
+     * delay as soon as it finishes.  If this method is called multiple times
+     * while the job is running, the job will still only be rescheduled once,
+     * with the most recent delay value that was provided.
+     * </p><p>
+     * Scheduling a job that is waiting or sleeping has no effect.
+     * </p>
+     *
+     * @param delay a time delay in milliseconds before the job should run
+     * @see ISchedulingRule
+     */
+    public final void schedule(long delay) {
+        super.schedule(delay);
+    }
+
+    /**
+     * Changes the name of this job.  If the job is currently running, waiting,
+     * or sleeping, the new job name may not take effect until the next time the
+     * job is scheduled.
+     * <p>
+     * The job name is a human-readable value that is displayed to users.  The name
+     * does not need to be unique, but it must not be <code>null</code>.
+     *
+     * @param name the name of the job.
+     */
+    public final void setName(String name) {
+        super.setName(name);
+    }
+
+    /**
+     * Sets the priority of the job.  This will not affect the execution of
+     * a running job, but it will affect how the job is scheduled while
+     * it is waiting to be run.
+     *
+     * @param priority the new job priority.  One of
+     * INTERACTIVE, SHORT, LONG, BUILD, or DECORATE.
+     */
+    public final void setPriority(int priority) {
+        super.setPriority(priority);
+    }
+
+    /**
+     * Associates this job with a progress group.  Progress feedback
+     * on this job's next execution will be displayed together with other
+     * jobs in that group. The provided monitor must be a monitor
+     * created by the method <tt>IJobManager.createProgressGroup</tt>
+     * and must have at least <code>ticks</code> units of available work.
+     * <p>
+     * The progress group must be set before the job is scheduled.
+     * The group will be used only for a single invocation of the job's
+     * <tt>run</tt> method, after which any association of this job to the
+     * group will be lost.
+     *
+     * @see IJobManager#createProgressGroup()
+     * @param group The progress group to use for this job
+     * @param ticks the number of work ticks allocated from the
+     *    parent monitor, or {@link IProgressMonitor#UNKNOWN}
+     */
+    public final void setProgressGroup(IProgressMonitor group, int ticks) {
+        super.setProgressGroup(group, ticks);
+    }
+
+    /**
+     * Sets the value of the property of this job identified
+     * by the given key. If the supplied value is <code>null</code>,
+     * the property is removed from this resource.
+     * <p>
+     * Properties are intended to be used as a caching mechanism
+     * by ISV plug-ins. They allow key-object associations to be stored with
+     * a job instance.  These key-value associations are maintained in
+     * memory (at all times), and the information is never discarded automatically.
+     * </p><p>
+     * The qualifier part of the property name must be the unique identifier
+     * of the declaring plug-in (e.g. <code>"com.example.plugin"</code>).
+     * </p>
+     *
+     * @param key the qualified name of the property
+     * @param value the value of the property,
+     *     or <code>null</code> if the property is to be removed
+     * @see #getProperty(QualifiedName)
+     */
+    public void setProperty(QualifiedName key, Object value) {
+        super.setProperty(key, value);
+    }
+
+    /**
+     * Sets the scheduling rule to be used when scheduling this job.  This method
+     * must be called before the job is scheduled.
+     *
+     * @param rule the new scheduling rule, or <code>null</code> if the job
+     * should have no scheduling rule
+     * @see #getRule()
+     */
+    public final void setRule(ISchedulingRule rule) {
+        super.setRule(rule);
+    }
+
+    /**
+     * Sets whether or not this job is a system job.  System jobs are typically not
+     * revealed to users in any UI presentation of jobs.  Other than their UI presentation,
+     * system jobs act exactly like other jobs.  If this value is not explicitly set, jobs
+     * are treated as non-system jobs. This method must be called before the job
+     * is scheduled.
+     *
+     * @param value <code>true</code> if this job should be a system job, and
+     * <code>false</code> otherwise.
+     * @see #isSystem()
+     */
+    public final void setSystem(bool value) {
+        super.setSystem(value);
+    }
+
+    /**
+     * Sets whether or not this job has been directly initiated by a UI end user.
+     * These jobs may be presented differently in the UI. This method must be
+     * called before the job is scheduled.
+     *
+     * @param value <code>true</code> if this job is a user-initiated job, and
+     * <code>false</code> otherwise.
+     * @see #isUser()
+     */
+    public final void setUser(bool value) {
+        super.setUser(value);
+    }
+
+    /**
+     * Sets the thread that this job is currently running in, or <code>null</code>
+     * if this job is not running or the thread is unknown.
+     * <p>
+     * Jobs that use the {@link #ASYNC_FINISH} return code should tell
+     * the job what thread it is running in.  This is used to prevent deadlocks.
+     *
+     * @param thread the thread that this job is running in.
+     *
+     * @see #ASYNC_FINISH
+     * @see #run(IProgressMonitor)
+     */
+    public final void setThread(JThread thread) {
+        super.setThread(thread);
+    }
+
+    /**
+     * Returns whether this job should be run.
+     * If <code>false</code> is returned, this job will be discarded by the job manager
+     * without running.
+     * <p>
+     * This method is called immediately prior to calling the job's
+     * run method, so it can be used for last minute pre-condition checking before
+     * a job is run. This method must not attempt to schedule or change the
+     * state of any other job.
+     * </p><p>
+     * Clients may override this method.  This default implementation always returns
+     * <code>true</code>.
+     * </p>
+     *
+     * @return <code>true</code> if this job should be run
+     *   and <code>false</code> otherwise
+     */
+    public bool shouldRun() {
+        return true;
+    }
+
+    /**
+     * Returns whether this job should be scheduled.
+     * If <code>false</code> is returned, this job will be discarded by the job manager
+     * without being added to the queue.
+     * <p>
+     * This method is called immediately prior to adding the job to the waiting job
+     * queue.,so it can be used for last minute pre-condition checking before
+     * a job is scheduled.
+     * </p><p>
+     * Clients may override this method.  This default implementation always returns
+     * <code>true</code>.
+     * </p>
+     *
+     * @return <code>true</code> if the job manager should schedule this job
+     *   and <code>false</code> otherwise
+     */
+    public bool shouldSchedule() {
+        return true;
+    }
+
+    /**
+     * Requests that this job be suspended.  If the job is currently waiting to be run, it
+     * will be removed from the queue move into the {@link #SLEEPING} state.
+     * The job will remain asleep until either resumed or canceled.  If this job is not
+     * currently waiting to be run, this method has no effect.
+     * <p>
+     * Sleeping jobs can be resumed using <code>wakeUp</code>.
+     *
+     * @return <code>false</code> if the job is currently running (and thus cannot
+     * be put to sleep), and <code>true</code> in all other cases
+     * @see #wakeUp()
+     */
+    public final bool sleep() {
+        return super.sleep();
+    }
+
+    /**
+     * Puts this job immediately into the {@link #WAITING} state so that it is
+     * eligible for immediate execution. If this job is not currently sleeping,
+     * the request is ignored.
+     * <p>
+     * This is a convenience method, fully equivalent to
+     * <code>wakeUp(0L)</code>.
+     * </p>
+     * @see #sleep()
+     */
+    public final void wakeUp() {
+        super.wakeUp(0L);
+    }
+
+    /**
+     * Puts this job back into the {@link #WAITING} state after
+     * the specified delay. This is equivalent to canceling the sleeping job and
+     * rescheduling with the given delay.  If this job is not currently sleeping,
+     * the request  is ignored.
+     *
+     * @param delay the number of milliseconds to delay
+     * @see #sleep()
+     */
+    public final void wakeUp(long delay) {
+        super.wakeUp(delay);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/JobChangeAdapter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.jobs.JobChangeAdapter;
+
+import java.lang.all;
+import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+
+/**
+ * This adapter class provides default implementations for the
+ * methods described by the <code>IJobChangeListener</code> interface.
+ * <p>
+ * Classes that wish to listen to the progress of scheduled jobs can
+ * extend this class and override only the methods which they are
+ * interested in.
+ * </p>
+ *
+ * @see IJobChangeListener
+ * @since 3.0
+ */
+public class JobChangeAdapter : IJobChangeListener {
+    /* (non-Javadoc)
+     * @see IJobChangeListener#aboutToRun(IJobChangeEvent)
+     * This default implementation does nothing
+     */
+    public void aboutToRun(IJobChangeEvent event) {
+        // do nothing
+    }
+
+    /* (non-Javadoc)
+     * @see IJobChangeListener#awake(IJobChangeEvent)
+     * This default implementation does nothing
+     */
+    public void awake(IJobChangeEvent event) {
+        // do nothing
+    }
+
+    /* (non-Javadoc)
+     * @see IJobChangeListener#done(IJobChangeEvent)
+     * This default implementation does nothing
+     */
+    public void done(IJobChangeEvent event) {
+        // do nothing
+    }
+
+    /* (non-Javadoc)
+     * @see IJobChangeListener#running(IJobChangeEvent)
+     * This default implementation does nothing
+     */
+    public void running(IJobChangeEvent event) {
+        // do nothing
+    }
+
+    /* (non-Javadoc)
+     * @see IJobChangeListener#scheduled(IJobChangeEvent)
+     * This default implementation does nothing
+     */
+    public void scheduled(IJobChangeEvent event) {
+        // do nothing
+    }
+
+    /* (non-Javadoc)
+     * @see IJobChangeListener#sleeping(IJobChangeEvent)
+     * This default implementation does nothing
+     */
+    public void sleeping(IJobChangeEvent event) {
+        // do nothing
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/LockListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.jobs.LockListener;
+
+import java.lang.JThread;
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.internal.jobs.JobManager;
+import org.eclipse.core.internal.jobs.LockManager;
+import org.eclipse.core.runtime.jobs.Job;
+
+/**
+ * A lock listener is notified whenever a thread is about to wait
+ * on a lock, and when a thread is about to release a lock.
+ * <p>
+ * This class is for internal use by the platform-related plug-ins.
+ * Clients outside of the base platform should not reference or subclass this class.
+ * </p>
+ *
+ * @see IJobManager#setLockListener(LockListener)
+ * @since 3.0
+ */
+public class LockListener {
+    private const LockManager manager;
+
+    public this(){
+        manager = (cast(JobManager)Job.getJobManager()).getLockManager();
+    }
+
+    /**
+     * Notification that a thread is about to block on an attempt to acquire a lock.
+     * Returns whether the thread should be granted immediate access to the lock.
+     * <p>
+     * This default implementation always returns <code>false</code>.
+     * Subclasses may override.
+     *
+     * @param lockOwner the thread that currently owns the lock this thread is
+     * waiting for, or <code>null</code> if unknown.
+     * @return <code>true</code> if the thread should be granted immediate access,
+     * and <code>false</code> if it should wait for the lock to be available
+     */
+    public bool aboutToWait(JThread lockOwner) {
+        return false;
+    }
+
+    /**
+     * Notification that a thread is about to release a lock.
+     * <p>
+     * This default implementation does nothing. Subclasses may override.
+     */
+    public void aboutToRelease() {
+        //do nothing
+    }
+
+    /**
+     * Returns whether this thread currently owns any locks
+     * @return <code>true</code> if this thread owns any locks, and
+     * <code>false</code> otherwise.
+     */
+    protected final bool isLockOwnerThread() {
+        return manager.isLockOwner();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/MultiRule.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.jobs.MultiRule;
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Set;
+
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * A MultiRule is a compound scheduling rule that represents a fixed group of child
+ * scheduling rules.  A MultiRule conflicts with another rule if any of its children conflict
+ * with that rule.  More formally, a compound rule represents a logical intersection
+ * of its child rules with respect to the <code>isConflicting</code> equivalence
+ * relation.
+ * <p>
+ * A MultiRule will never contain other MultiRules as children.  If a MultiRule is provided
+ * as a child, its children will be added instead.
+ * </p>
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class MultiRule : ISchedulingRule {
+    private ISchedulingRule[] rules;
+
+    /**
+     * Returns a scheduling rule that encompasses all provided rules.  The resulting
+     * rule may or may not be an instance of <code>MultiRule</code>.  If all
+     * provided rules are <code>null</code> then the result will be
+     * <code>null</code>.
+     *
+     * @param ruleArray An array of scheduling rules, some of which may be <code>null</code>
+     * @return a combined scheduling rule, or <code>null</code>
+     * @since 3.1
+     */
+    public static ISchedulingRule combine(ISchedulingRule[] ruleArray) {
+        ISchedulingRule result = null;
+        for (int i = 0; i < ruleArray.length; i++) {
+            if (ruleArray[i] is null)
+                continue;
+            if (result is null) {
+                result = ruleArray[i];
+                continue;
+            }
+            result = combine(result, ruleArray[i]);
+        }
+        return result;
+    }
+
+    /**
+     * Returns a scheduling rule that encompasses both provided rules.  The resulting
+     * rule may or may not be an instance of <code>MultiRule</code>.  If both
+     * provided rules are <code>null</code> then the result will be
+     * <code>null</code>.
+     *
+     * @param rule1 a scheduling rule, or <code>null</code>
+     * @param rule2 another scheduling rule, or <code>null</code>
+     * @return a combined scheduling rule, or <code>null</code>
+     */
+    public static ISchedulingRule combine(ISchedulingRule rule1, ISchedulingRule rule2) {
+        if (rule1 is rule2)
+            return rule1;
+        if (rule1 is null)
+            return rule2;
+        if (rule2 is null)
+            return rule1;
+        if (rule1.contains(rule2))
+            return rule1;
+        if (rule2.contains(rule1))
+            return rule2;
+        MultiRule result = new MultiRule();
+        result.rules = [rule1, rule2];
+        //make sure we don't end up with nested multi-rules
+        if (cast(MultiRule)rule1  || cast(MultiRule)rule2 )
+            result.rules = flatten(result.rules);
+        return result;
+    }
+
+    /*
+     * Collapses an array of rules that may contain MultiRules into an
+     * array in which no rules are MultiRules.
+     */
+    private static ISchedulingRule[] flatten(ISchedulingRule[] nestedRules) {
+        ArrayList myRules = new ArrayList(nestedRules.length);
+        for (int i = 0; i < nestedRules.length; i++) {
+            if (cast(MultiRule)nestedRules[i] ) {
+                ISchedulingRule[] children = (cast(MultiRule) nestedRules[i]).getChildren();
+                for (int j = 0; j < children.length; j++)
+                    myRules.add(cast(Object)children[j]);
+            } else {
+                myRules.add(cast(Object)nestedRules[i]);
+            }
+        }
+        return arraycast!(ISchedulingRule)( myRules.toArray() );
+    }
+
+    /**
+     * Creates a new scheduling rule that composes a set of nested rules.
+     *
+     * @param nestedRules the nested rules for this compound rule.
+     */
+    public this(ISchedulingRule[] nestedRules) {
+        this.rules = flatten(nestedRules);
+    }
+
+    /**
+     * Creates a new scheduling rule with no nested rules. For
+     * internal use only.
+     */
+    private this() {
+        //to be invoked only by factory methods
+    }
+
+    /**
+     * Returns the child rules within this rule.
+     * @return the child rules
+     */
+    public ISchedulingRule[] getChildren() {
+        return rules.dup;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
+     */
+    public bool contains(ISchedulingRule rule) {
+        if (this is rule)
+            return true;
+        if (cast(MultiRule)rule ) {
+            ISchedulingRule[] otherRules = (cast(MultiRule) rule).getChildren();
+            //for each child of the target, there must be some child in this rule that contains it.
+            for (int other = 0; other < otherRules.length; other++) {
+                bool found = false;
+                for (int mine = 0; !found && mine < rules.length; mine++)
+                    found = rules[mine].contains(otherRules[other]);
+                if (!found)
+                    return false;
+            }
+            return true;
+        }
+        for (int i = 0; i < rules.length; i++)
+            if (rules[i].contains(rule))
+                return true;
+        return false;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule)
+     */
+    public bool isConflicting(ISchedulingRule rule) {
+        if (this is rule)
+            return true;
+        if (cast(MultiRule)rule ) {
+            ISchedulingRule[] otherRules = (cast(MultiRule) rule).getChildren();
+            for (int j = 0; j < otherRules.length; j++)
+                for (int i = 0; i < rules.length; i++)
+                    if (rules[i].isConflicting(otherRules[j]))
+                        return true;
+        } else {
+            for (int i = 0; i < rules.length; i++)
+                if (rules[i].isConflicting(rule))
+                    return true;
+        }
+        return false;
+    }
+
+    /*
+     * For debugging purposes only.
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("MultiRule["); //$NON-NLS-1$
+        int last = rules.length - 1;
+        for (int i = 0; i < rules.length; i++) {
+            buffer.append(rules[i] ? (cast(Object)rules[i]).toString() : "null" );
+            if (i !is last)
+                buffer.append(',');
+        }
+        buffer.append(']');
+        return buffer.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/ProgressProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 org.eclipse.core.runtime.jobs.ProgressProvider;
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+
+import org.eclipse.core.runtime.jobs.Job;
+
+/**
+ * The progress provider supplies the job manager with progress monitors for
+ * running jobs.  There can only be one progress provider at any given time.
+ * <p>
+ * This class is intended for use by the currently executing Eclipse application.
+ * Plug-ins outside the currently running application should not reference or
+ * subclass this class.
+ * </p>
+ *
+ * @see IJobManager#setProgressProvider(ProgressProvider)
+ * @since 3.0
+ */
+public abstract class ProgressProvider {
+    /**
+     * Provides a new progress monitor instance to be used by the given job.
+     * This method is called prior to running any job that does not belong to a
+     * progress group. The returned monitor will be supplied to the job's
+     * <code>run</code> method.
+     *
+     * @see #createProgressGroup()
+     * @see Job#setProgressGroup(IProgressMonitor, int)
+     * @param job the job to create a progress monitor for
+     * @return a progress monitor, or <code>null</code> if no progress monitoring
+     * is needed.
+     */
+    public abstract IProgressMonitor createMonitor(Job job);
+
+    /**
+     * Returns a progress monitor that can be used to provide
+     * aggregated progress feedback on a set of running jobs.
+     * This method implements <code>IJobManager.createProgressGroup</code>,
+     * and must obey all rules specified in that contract.
+     * <p>
+     * This default implementation returns a new
+     * <code>NullProgressMonitor</code>  Subclasses may override.
+     *
+     * @see IJobManager#createProgressGroup()
+     * @return a progress monitor
+     */
+    public IProgressMonitor createProgressGroup() {
+        return new NullProgressMonitor();
+    }
+
+    /**
+     * Returns a progress monitor that can be used by a running job
+     * to report progress in the context of a progress group. This method
+     * implements <code>Job.setProgressGroup</code>.  One of the
+     * two <code>createMonitor</code> methods will be invoked
+     * prior to each execution of a job, depending on whether a progress
+     * group was specified for the job.
+     * <p>
+     * The provided monitor must be a monitor returned by the method
+     * <code>createProgressGroup</code>.  This method is responsible
+     * for asserting this and throwing an appropriate runtime exception
+     * if an invalid monitor is provided.
+     * <p>
+     * This default implementation returns a new
+     * <code>SubProgressMonitor</code>.  Subclasses may override.
+     *
+     * @see IJobManager#createProgressGroup()
+     * @see Job#setProgressGroup(IProgressMonitor, int)
+     * @param job the job to create a progress monitor for
+     * @param group the progress monitor group that this job belongs to
+     * @param ticks the number of ticks of work for the progress monitor
+     * @return a progress monitor, or <code>null</code> if no progress monitoring
+     * is needed.
+     */
+    public IProgressMonitor createMonitor(Job job, IProgressMonitor group, int ticks) {
+        return new SubProgressMonitor(group, ticks);
+    }
+
+    /**
+     * Returns a progress monitor to use when none has been provided
+     * by the client running the job.
+     * <p>
+     * This default implementation returns a new
+     * <code>NullProgressMonitor</code>  Subclasses may override.
+     *
+     * @return a progress monitor
+     */
+    public IProgressMonitor getDefaultMonitor() {
+        return new NullProgressMonitor();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/messages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,79 @@
+###############################################################################
+# Copyright (c) 2000, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+### Runtime plugin messages
+
+### Authorization
+auth_notAvailable = Authorization infrastructure (org.eclipse.core.runtime.compatibility.auth) not installed.
+
+### metadata
+meta_appNotInit = The application has not been initialized.
+meta_exceptionParsingLog = An exception occurred while parsing the log file: {0}
+
+### plugins
+plugin_deactivatedLoad = Attempt to load class \"{0}\" from de-activated plug-in \"{1}\".
+plugin_shutdownProblems = Problems encountered shutting down plug-in: \"{0}\".
+plugin_startupProblems = Problems encountered starting up plug-in: \"{0}\".
+
+### Preferences
+preferences_saveProblems = Problems saving preferences.
+
+### Line Separator Platforms
+line_separator_platform_mac_os_9 = Mac OS 9
+line_separator_platform_unix = Unix
+line_separator_platform_windows = Windows
+
+### Product
+provider_invalid_general = Errors while processing the product providers.
+provider_invalid = Problem creating the provider registered by {0}.
+product_notFound = Product {0} could not be found.
+
+### Compatibility - parsing/resolving
+parse_badPrereqOnFrag = Fragment \"{0}\" requires non-existent plug-in \"{1}\".  Fragment ignored.
+parse_duplicateFragment = Duplicate fragment found with id \"{0}\" and version \"{1}\".
+parse_duplicateLib = Fragment \"{0}\" for plug-in \"{1}\", has added duplicate library entry \"{2}\" .
+parse_duplicatePlugin = Two plug-ins found with the same id: \"{0}\". Ignoring duplicate at \"{1}\".
+parse_error = Parsing error: \"{0}\".
+parse_errorNameLineColumn = Parsing error in \"{0}\" [line {1}, column {2}]: \"{3}\".
+parse_errorProcessing = Error while processing \"{0}\".
+parse_extPointDisabled = Extension point \"{0}\" specified in plug-in \"{1}\" is disabled.
+parse_extPointUnknown = Unknown extension point \"{0}\" specified in plug-in \"{1}\".
+parse_fragmentMissingAttr = Fragment \"{0}\" ignored due to missing attributes.
+parse_fragmentMissingIdName = Fragment ignored due to missing attributes (including name and id).
+parse_internalStack = Element/end element mismatch for element \"{0}\".
+parse_missingFPName = Plug-in name attribute missing from fragment at \"{0}\".
+parse_missingFPVersion = Plug-in version attribute missing from fragment at \"{0}\".
+parse_missingFragmentPd = Plug-in descriptor \"{0}\" not found for fragment \"{1}\".  Fragment ignored.
+parse_missingPluginId = Id attribute missing from plug-in or fragment at \"{0}\".
+parse_missingPluginName = Name attribute missing from plug-in or fragment at \"{0}\".
+parse_missingPluginVersion = Version attribute missing from plug-in or fragment at \"{0}\".
+parse_nullFragmentIdentifier = Fragment not loaded due to missing id or version number: \"{0}\".
+parse_nullPluginIdentifier = Plug-in not loaded due to missing id or version number: \"{0}\".
+parse_pluginMissingAttr = Plug-in \"{0}\" disabled due to missing attributes.
+parse_pluginMissingIdName = Plug-in disabled due to missing attributes (including name and id).
+parse_prereqDisabled = Plug-in \"{0}\" was disabled due to missing or disabled prerequisite plug-in \"{1}\".
+parse_prereqLoop = Detected prerequisite loop from \"{0}\" to \"{1}\".
+parse_prereqOptLoop = Optional prerequisite from \"{0}\" to \"{1}\" produced loop.  Prerequisite ignored.
+parse_unknownAttribute = Unknown attribute \"{1}\" for element \"{0}\" ignored.
+parse_unknownElement = Unknown element \"{1}\", found within a \"{0}\", ignored.
+parse_unknownEntry = Unknown element parsed by plug-in registry: \"{0}\".
+parse_unknownLibraryType = Unknown library type \"{0}\" for library \"{1}\".
+parse_unknownTopElement = Unknown element \"{0}\", found at the top level, ignored.
+parse_unsatisfiedOptPrereq = Optional prerequisite constraint from \"{0}\" to\" {1}\" ignored.
+parse_unsatisfiedPrereq = Unable to satisfy prerequisite constraint from \"{0}\" to \"{1}\".
+parse_validExport = \"{0}\" is not a valid value for the attribute \"export\".   Use \"true\" or \"false\".
+parse_validMatch = \"{0}\" is not a valid value for the attribute \"match\".   Use \"perfect\", \"equivalent\", \"compatible\" or \"greaterOrEqual\".
+
+### Compatibility - plugins
+plugin_notPluginClass = Supplied runtime class \"{0}\" does not extend class Plugin.
+plugin_unableToResolve = Unable to resolve plug-in registry.
+plugin_pluginDisabled = Attempt to activate a disabled plug-in: \"{0}\".
+plugin_instantiateClassError = Plug-in \"{0}\" was unable to instantiate class \"{1}\".
+plugin_loadClassError = Plug-in {0} was unable to load class {1}.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.runtime/src/org/eclipse/core/runtime/ILog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.ILog;
+
+import java.lang.all;
+import org.eclipse.core.runtime.ILogListener;
+import org.eclipse.core.runtime.IStatus;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * A log to which status events can be written.  Logs appear on individual
+ * plug-ins and on the platform itself.  Clients can register log listeners which
+ * will receive notification of all log events as they come in.
+ * <p>
+ * XXX Need to create a new log interface on common plugin. That interface should be a super interface of this ILog.
+ * getBundle() would stay here. In the super interface we would have getName()
+ * </p>
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ILog {
+    /**
+     * Adds the given log listener to this log.  Subsequently the log listener will
+     * receive notification of all log events passing through this log.
+     * This method has no affect if the identical listener is already registered on this log.
+     *
+     * @param listener the listener to add to this log
+     * @see Platform#addLogListener(ILogListener)
+     */
+    public void addLogListener(ILogListener listener);
+
+    /**
+     * Returns the plug-in with which this log is associated.
+     *
+     * @return the plug-in with which this log is associated
+     * @since 3.0
+     */
+    public Bundle getBundle();
+
+    /**
+     * Logs the given status.  The status is distributed to the log listeners
+     * installed on this log and then to the log listeners installed on the platform.
+     *
+     * @param status the status to log
+     */
+    public void log(IStatus status);
+
+    /**
+     * Removes the given log listener to this log.  Subsequently the log listener will
+     * no longer receive notification of log events passing through this log.
+     * This method has no affect if the identical listener is not registered on this log.
+     *
+     * @param listener the listener to remove
+     * @see Platform#removeLogListener(ILogListener)
+     */
+    public void removeLogListener(ILogListener listener);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.runtime/src/org/eclipse/core/runtime/Platform.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1578 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.Platform;
+
+import java.lang.all;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.ILog;
+// import java.io.IOException;
+// import java.lang.reflect.Method;
+// import java.net.URL;
+// import java.util.HashMap;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+//
+import org.eclipse.osgi.framework.Bundle;
+// import org.osgi.service.packageadmin.PackageAdmin;
+//
+// import org.eclipse.core.internal.runtime.CompatibilityHelper;
+// import org.eclipse.core.internal.runtime.InternalPlatform;
+// import org.eclipse.core.internal.runtime.Messages;
+// import org.eclipse.core.internal.runtime.PlatformActivator;
+// import org.eclipse.core.internal.runtime.auth.AuthorizationHandler;
+// import org.eclipse.core.runtime.content.IContentTypeManager;
+// import org.eclipse.core.runtime.jobs.IJobManager;
+// import org.eclipse.core.runtime.jobs.Job;
+// import org.eclipse.core.runtime.preferences.IPreferencesService;
+// import org.eclipse.osgi.service.datalocation.Location;
+// import org.eclipse.osgi.service.debug.DebugOptions;
+// import org.eclipse.osgi.service.environment.EnvironmentInfo;
+// import org.eclipse.osgi.service.resolver.PlatformAdmin;
+
+/**
+ * The central class of the Eclipse Platform Runtime. This class cannot
+ * be instantiated or subclassed by clients; all functionality is provided
+ * by static methods.  Features include:
+ * <ul>
+ * <li>the platform registry of installed plug-ins</li>
+ * <li>the platform adapter manager</li>
+ * <li>the platform log</li>
+ * <li>the authorization info management</li>
+ * </ul>
+ * <p>
+ * Most users don't have to worry about Platform's lifecycle. However, if your
+ * code can call methods of this class when Platform is not running, it becomes
+ * necessary to check {@link #isRunning()} before making the call. A runtime
+ * exception might be thrown or incorrect result might be returned if a method
+ * from this class is called while Platform is not running.
+ * </p>
+ */
+public final class Platform {
+
+//     /**
+//      * The unique identifier constant (value "<code>org.eclipse.core.runtime</code>")
+//      * of the Core Runtime (pseudo-) plug-in.
+//      */
+//     public static final String PI_RUNTIME = "org.eclipse.core.runtime"; //$NON-NLS-1$
+//
+//     /**
+//      * The simple identifier constant (value "<code>applications</code>") of
+//      * the extension point of the Core Runtime plug-in where plug-ins declare
+//      * the existence of runnable applications. A plug-in may define any
+//      * number of applications; however, the platform is only capable
+//      * of running one application at a time.
+//      *
+//      */
+//     public static final String PT_APPLICATIONS = "applications"; //$NON-NLS-1$
+//
+//     /**
+//      * The simple identifier constant (value "<code>adapters</code>") of
+//      * the extension point of the Core Runtime plug-in where plug-ins declare
+//      * the existence of adapter factories. A plug-in may define any
+//      * number of adapters.
+//      *
+//      * @see IAdapterManager#hasAdapter(Object, String)
+//      * @since 3.0
+//      */
+//     public static final String PT_ADAPTERS = "adapters"; //$NON-NLS-1$
+//
+//     /**
+//      * The simple identifier constant (value "<code>preferences</code>") of
+//      * the extension point of the Core Runtime plug-in where plug-ins declare
+//      * extensions to the preference facility. A plug-in may define any number
+//      * of preference extensions.
+//      *
+//      * @see #getPreferencesService()
+//      * @since 3.0
+//      */
+//     public static final String PT_PREFERENCES = Preferences.PT_PREFERENCES;
+//
+//     /**
+//      * The simple identifier constant (value "<code>products</code>") of
+//      * the extension point of the Core Runtime plug-in where plug-ins declare
+//      * the existence of a product. A plug-in may define any
+//      * number of products; however, the platform is only capable
+//      * of running one product at a time.
+//      *
+//      * @see #getProduct()
+//      * @since 3.0
+//      */
+//     public static final String PT_PRODUCT = "products"; //$NON-NLS-1$
+//
+//     /**
+//      * Debug option value denoting the time at which the platform runtime
+//      * was started.  This constant can be used in conjunction with
+//      * <code>getDebugOption</code> to find the string value of
+//      * <code>System.currentTimeMillis()</code> when the platform was started.
+//      */
+//     public static final String OPTION_STARTTIME = PI_RUNTIME + "/starttime"; //$NON-NLS-1$
+//
+//     /**
+//      * Name of a preference for configuring the performance level for this system.
+//      *
+//      * <p>
+//      * This value can be used by all components to customize features to suit the
+//      * speed of the user's machine.  The platform job manager uses this value to make
+//      * scheduling decisions about background jobs.
+//      * </p>
+//      * <p>
+//      * The preference value must be an integer between the constant values
+//      * MIN_PERFORMANCE and MAX_PERFORMANCE
+//      * </p>
+//      * @see #MIN_PERFORMANCE
+//      * @see #MAX_PERFORMANCE
+//      * @since 3.0
+//      */
+//     public static final String PREF_PLATFORM_PERFORMANCE = "runtime.performance"; //$NON-NLS-1$
+//
+//     /**
+//      * Constant (value "line.separator") name of the preference used for storing
+//      * the line separator.
+//      *
+//      * @see #knownPlatformLineSeparators
+//      * @since 3.1
+//      */
+//     public static final String PREF_LINE_SEPARATOR = "line.separator"; //$NON-NLS-1$
+//
+//     /**
+//      * Constant (value 1) indicating the minimum allowed value for the
+//      * <code>PREF_PLATFORM_PERFORMANCE</code> preference setting.
+//      * @since 3.0
+//      */
+//     public static final int MIN_PERFORMANCE = 1;
+//
+//     /**
+//      * Constant (value 5) indicating the maximum allowed value for the
+//      * <code>PREF_PLATFORM_PERFORMANCE</code> preference setting.
+//      * @since 3.0
+//      */
+//     public static final int MAX_PERFORMANCE = 5;
+//
+//     /**
+//      * Status code constant (value 1) indicating a problem in a plug-in
+//      * manifest (<code>plugin.xml</code>) file.
+//      */
+//     public static final int PARSE_PROBLEM = 1;
+//
+//     /**
+//      * Status code constant (value 2) indicating an error occurred while running a plug-in.
+//      */
+//     public static final int PLUGIN_ERROR = 2;
+//
+//     /**
+//      * Status code constant (value 3) indicating an error internal to the
+//      * platform has occurred.
+//      */
+//     public static final int INTERNAL_ERROR = 3;
+//
+//     /**
+//      * Status code constant (value 4) indicating the platform could not read
+//      * some of its metadata.
+//      */
+//     public static final int FAILED_READ_METADATA = 4;
+//
+//     /**
+//      * Status code constant (value 5) indicating the platform could not write
+//      * some of its metadata.
+//      */
+//     public static final int FAILED_WRITE_METADATA = 5;
+//
+//     /**
+//      * Status code constant (value 6) indicating the platform could not delete
+//      * some of its metadata.
+//      */
+//     public static final int FAILED_DELETE_METADATA = 6;
+//
+//     /**
+//      * Constant string (value "win32") indicating the platform is running on a
+//      * Window 32-bit operating system (e.g., Windows 98, NT, 2000).
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String OS_WIN32 = "win32";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "linux") indicating the platform is running on a
+//      * Linux-based operating system.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String OS_LINUX = "linux";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "aix") indicating the platform is running on an
+//      * AIX-based operating system.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String OS_AIX = "aix";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "solaris") indicating the platform is running on a
+//      * Solaris-based operating system.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String OS_SOLARIS = "solaris";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "hpux") indicating the platform is running on an
+//      * HP/UX-based operating system.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String OS_HPUX = "hpux";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "qnx") indicating the platform is running on a
+//      * QNX-based operating system.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String OS_QNX = "qnx";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "macosx") indicating the platform is running on a
+//      * Mac OS X operating system.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String OS_MACOSX = "macosx";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "unknown") indicating the platform is running on a
+//      * machine running an unknown operating system.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String OS_UNKNOWN = "unknown";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "x86") indicating the platform is running on an
+//      * x86-based architecture.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String ARCH_X86 = "x86";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "PA_RISC") indicating the platform is running on an
+//      * PA_RISC-based architecture.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String ARCH_PA_RISC = "PA_RISC";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "ppc") indicating the platform is running on an
+//      * PowerPC-based architecture.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String ARCH_PPC = "ppc";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "sparc") indicating the platform is running on an
+//      * Sparc-based architecture.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String ARCH_SPARC = "sparc";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "x86_64") indicating the platform is running on an
+//      * x86 64bit-based architecture.
+//      *
+//      * @since 3.1
+//      */
+//     public static final String ARCH_X86_64 = "x86_64";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "amd64") indicating the platform is running on an
+//      * AMD64-based architecture.
+//      *
+//      * @since 3.0
+//      * @deprecated use <code>ARCH_X86_64</code> instead. Note the values
+//      * has been changed to be the value of the <code>ARCH_X86_64</code> constant.
+//      */
+//     public static final String ARCH_AMD64 = ARCH_X86_64;
+//
+//     /**
+//      * Constant string (value "ia64") indicating the platform is running on an
+//      * IA64-based architecture.
+//      *
+//      * @since 3.0
+//      */
+//     public static final String ARCH_IA64 = "ia64"; //$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "ia64_32") indicating the platform is running on an
+//      * IA64 32bit-based architecture.
+//      *
+//      * @since 3.1
+//      */
+//     public static final String ARCH_IA64_32 = "ia64_32";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "win32") indicating the platform is running on a
+//      * machine using the Windows windowing system.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String WS_WIN32 = "win32";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "motif") indicating the platform is running on a
+//      * machine using the Motif windowing system.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String WS_MOTIF = "motif";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "gtk") indicating the platform is running on a
+//      * machine using the GTK windowing system.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String WS_GTK = "gtk";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "photon") indicating the platform is running on a
+//      * machine using the Photon windowing system.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String WS_PHOTON = "photon";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "carbon") indicating the platform is running on a
+//      * machine using the Carbon windowing system (Mac OS X).
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String WS_CARBON = "carbon";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "wpf") indicating the platform is running on a
+//      * machine using the WPF windowing system.
+//      * @since 3.3
+//      */
+//     public static final String WS_WPF = "wpf";//$NON-NLS-1$
+//
+//     /**
+//      * Constant string (value "unknown") indicating the platform is running on a
+//      * machine running an unknown windowing system.
+//      * <p>
+//      * Note this constant has been moved from the deprecated
+//      * org.eclipse.core.boot.BootLoader class and its value has not changed.
+//      * </p>
+//      * @since 3.0
+//      */
+//     public static final String WS_UNKNOWN = "unknown";//$NON-NLS-1$
+//
+//     // private constants for platform line separators and their associated platform names
+//     private static final String LINE_SEPARATOR_KEY_MAC_OS_9 = Messages.line_separator_platform_mac_os_9;
+//     private static final String LINE_SEPARATOR_KEY_UNIX = Messages.line_separator_platform_unix;
+//     private static final String LINE_SEPARATOR_KEY_WINDOWS = Messages.line_separator_platform_windows;
+//
+//     private static final String LINE_SEPARATOR_VALUE_CR = "\r"; //$NON-NLS-1$
+//     private static final String LINE_SEPARATOR_VALUE_LF = "\n"; //$NON-NLS-1$
+//     private static final String LINE_SEPARATOR_VALUE_CRLF = "\r\n"; //$NON-NLS-1$
+//
+//     /**
+//      * Private constructor to block instance creation.
+//      */
+//     private Platform() {
+//         super();
+//     }
+//
+//     /**
+//      * Adds the given authorization information to the key ring. The
+//      * information is relevant for the specified protection space and the
+//      * given authorization scheme. The protection space is defined by the
+//      * combination of the given server URL and realm. The authorization
+//      * scheme determines what the authorization information contains and how
+//      * it should be used. The authorization information is a <code>Map</code>
+//      * of <code>String</code> to <code>String</code> and typically
+//      * contains information such as user names and passwords.
+//      *
+//      * @param serverUrl the URL identifying the server for this authorization
+//      *      information. For example, "http://www.example.com/".
+//      * @param realm the subsection of the given server to which this
+//      *      authorization information applies.  For example,
+//      *      "realm1@example.com" or "" for no realm.
+//      * @param authScheme the scheme for which this authorization information
+//      *      applies. For example, "Basic" or "" for no authorization scheme
+//      * @param info a <code>Map</code> containing authorization information
+//      *      such as user names and passwords (key type : <code>String</code>,
+//      *      value type : <code>String</code>)
+//      * @exception CoreException if there are problems setting the
+//      *      authorization information. Reasons include:
+//      * <ul>
+//      * <li>The keyring could not be saved.</li>
+//      * </ul>
+//      * @deprecated Authorization database is superseded by the Equinox secure storage.
+//      * Use <code>org.eclipse.equinox.security.storage.SecurePreferencesFactory</code>
+//      * to obtain secure preferences and <code>org.eclipse.equinox.security.storage.ISecurePreferences</code>
+//      * for data access and modifications.
+//      * Consider using <code>ISecurePreferences#put(String, String, bool)</code> as a replacement of this method.
+//      */
+//     public static void addAuthorizationInfo(URL serverUrl, String realm, String authScheme, Map info) throws CoreException {
+//         try {
+//             AuthorizationHandler.addAuthorizationInfo(serverUrl, realm, authScheme, info);
+//         } catch (NoClassDefFoundError e) {
+//             // The authorization code is not available so just log and continue
+//             logAuthNotAvailable(e);
+//         }
+//     }
+//
+//     /**
+//      * Adds the given log listener to the notification list of the platform.
+//      * <p>
+//      * Once registered, a listener starts receiving notification as entries
+//      * are added to plug-in logs via <code>ILog.log()</code>. The listener continues to
+//      * receive notifications until it is replaced or removed.
+//      * </p>
+//      *
+//      * @param listener the listener to register
+//      * @see ILog#addLogListener(ILogListener)
+//      * @see #removeLogListener(ILogListener)
+//      * XXX Use the LogMgr service.
+//      */
+//     public static void addLogListener(ILogListener listener) {
+//         InternalPlatform.getDefault().addLogListener(listener);
+//     }
+//
+//     /**
+//      * Adds the specified resource to the protection space specified by the
+//      * given realm. All targets at or deeper than the depth of the last
+//      * symbolic element in the path of the given resource URL are assumed to
+//      * be in the same protection space.
+//      *
+//      * @param resourceUrl the URL identifying the resources to be added to
+//      *      the specified protection space. For example,
+//      *      "http://www.example.com/folder/".
+//      * @param realm the name of the protection space. For example,
+//      *      "realm1@example.com"
+//      * @exception CoreException if there are problems setting the
+//      *      authorization information. Reasons include:
+//      * <ul>
+//      * <li>The key ring could not be saved.</li>
+//      * </ul>
+//      * @deprecated Authorization database is superseded by the Equinox secure storage.
+//      * Use <code>org.eclipse.equinox.security.storage.SecurePreferencesFactory</code>
+//      * to obtain secure preferences and <code>org.eclipse.equinox.security.storage.ISecurePreferences</code>
+//      * for data access and modifications.
+//      */
+//     public static void addProtectionSpace(URL resourceUrl, String realm) throws CoreException {
+//         try {
+//             AuthorizationHandler.addProtectionSpace(resourceUrl, realm);
+//         } catch (NoClassDefFoundError e) {
+//             // The authorization code is not available so just log and continue
+//             logAuthNotAvailable(e);
+//         }
+//     }
+//
+//     /**
+//      * Returns a URL that is the local equivalent of the
+//      * supplied URL. This method is expected to be used with the
+//      * plug-in-relative URLs returned by IPluginDescriptor, Bundle.getEntry()
+//      * and Platform.find().
+//      * If the specified URL is not a plug-in-relative URL, it
+//      * is returned as is. If the specified URL is a plug-in-relative
+//      * URL of a file (including .jar archive), it is returned as
+//      * a locally accessible URL using "file:" protocol
+//      * (extracting/caching the file locally, if required). If the specified URL
+//      * is a plug-in-relative URL of a directory, the directory and any files and directories
+//      * under it are made locally accessible likewise.
+//      *
+//      * @param url original plug-in-relative URL.
+//      * @return the resolved URL
+//      * @exception IOException if unable to resolve URL
+//      * @see #resolve(URL)
+//      * @see #find(Bundle, IPath)
+//      * @see Bundle#getEntry(String)
+//      * @deprecated use {@link FileLocator#toFileURL(URL)} instead
+//      */
+//     public static URL asLocalURL(URL url) throws IOException {
+//         return FileLocator.toFileURL(url);
+//     }
+//
+//     /**
+//      * Takes down the splash screen if one was put up.
+//      * XXX this is application life cycle. Need to have the appropriate method on IApplication.
+//      */
+//     public static void endSplash() {
+//         InternalPlatform.getDefault().endSplash();
+//     }
+//
+//     /**
+//      * Removes the authorization information for the specified protection
+//      * space and given authorization scheme. The protection space is defined
+//      * by the given server URL and realm.
+//      *
+//      * @param serverUrl the URL identifying the server to remove the
+//      *      authorization information for. For example,
+//      *      "http://www.example.com/".
+//      * @param realm the subsection of the given server to remove the
+//      *      authorization information for. For example,
+//      *      "realm1@example.com" or "" for no realm.
+//      * @param authScheme the scheme for which the authorization information
+//      *      to remove applies. For example, "Basic" or "" for no
+//      *      authorization scheme.
+//      * @exception CoreException if there are problems removing the
+//      *      authorization information. Reasons include:
+//      * <ul>
+//      * <li>The keyring could not be saved.</li>
+//      * </ul>
+//      * @deprecated Authorization database is superseded by the Equinox secure storage.
+//      * Use <code>org.eclipse.equinox.security.storage.SecurePreferencesFactory</code>
+//      * to obtain secure preferences and <code>org.eclipse.equinox.security.storage.ISecurePreferences</code>
+//      * for data access and modifications.
+//      * Consider using <code>ISecurePreferences#clear()</code> as a replacement of this method.
+//      */
+//     public static void flushAuthorizationInfo(URL serverUrl, String realm, String authScheme) throws CoreException {
+//         try {
+//             AuthorizationHandler.flushAuthorizationInfo(serverUrl, realm, authScheme);
+//         } catch (NoClassDefFoundError e) {
+//             // The authorization code is not available so just log and continue
+//             logAuthNotAvailable(e);
+//         }
+//     }
+//
+//     private static void logAuthNotAvailable(Throwable e) {
+//         InternalPlatform.getDefault().log(new Status(IStatus.WARNING, Platform.PI_RUNTIME, 0, Messages.auth_notAvailable, null));
+//     }
+//
+//     /**
+//      * Returns the adapter manager used for extending
+//      * <code>IAdaptable</code> objects.
+//      *
+//      * @return the adapter manager for this platform
+//      * @see IAdapterManager
+//      * XXX register as a service (same pattern than Jobs)
+//      * Do we want to make it available as a singleton?
+//      */
+//     public static IAdapterManager getAdapterManager() {
+//         return InternalPlatform.getDefault().getAdapterManager();
+//     }
+//
+//     /**
+//      * Returns the authorization information for the specified protection
+//      * space and given authorization scheme. The protection space is defined
+//      * by the given server URL and realm. Returns <code>null</code> if no
+//      * such information exists.
+//      *
+//      * @param serverUrl the URL identifying the server for the authorization
+//      *      information. For example, "http://www.example.com/".
+//      * @param realm the subsection of the given server to which the
+//      *      authorization information applies.  For example,
+//      *      "realm1@example.com" or "" for no realm.
+//      * @param authScheme the scheme for which the authorization information
+//      *      applies. For example, "Basic" or "" for no authorization scheme
+//      * @return the authorization information for the specified protection
+//      *      space and given authorization scheme, or <code>null</code> if no
+//      *      such information exists
+//      * @deprecated Authorization database is superseded by the Equinox secure storage.
+//      * Use <code>org.eclipse.equinox.security.storage.SecurePreferencesFactory</code>
+//      * to obtain secure preferences and <code>org.eclipse.equinox.security.storage.ISecurePreferences</code>
+//      * for data access and modifications.
+//      * Consider using <code>ISecurePreferences#get(String, String)</code> as a replacement of this method.
+//      */
+//     public static Map getAuthorizationInfo(URL serverUrl, String realm, String authScheme) {
+//         try {
+//             return AuthorizationHandler.getAuthorizationInfo(serverUrl, realm, authScheme);
+//         } catch (NoClassDefFoundError e) {
+//             // The authorization code is not available so just log and continue
+//             logAuthNotAvailable(e);
+//         }
+//         return null;
+//     }
+//
+//     /**
+//      * Returns the command line args provided to the Eclipse runtime layer when it was first run.
+//      * The returned value does not include arguments consumed by the lower levels of Eclipse
+//      * (e.g., OSGi or the launcher).
+//      * Note that individual platform runnables may be provided with different arguments
+//      * if they are being run individually rather than with <code>Platform.run()</code>.
+//      * <p>
+//      * Clients are also able to acquire the {@link EnvironmentInfo} service and query it for
+//      * the command-line arguments.
+//      * </p>
+//      * @return the command line used to start the platform
+//      */
+//     public static String[] getCommandLineArgs() {
+//         return InternalPlatform.getDefault().getCommandLineArgs();
+//     }
+//
+//     /**
+//      * Returns the content type manager.
+//      * <p>
+//      * Clients are also able to acquire the {@link IContentTypeManager} service.
+//      * </p>
+//      * @return the content type manager
+//      * @since 3.0
+//      */
+//     public static IContentTypeManager getContentTypeManager() {
+//         return InternalPlatform.getDefault().getContentTypeManager();
+//     }
+
+    /**
+     * Returns the identified option.  <code>null</code>
+     * is returned if no such option is found.   Options are specified
+     * in the general form <i>&lt;plug-in id&gt;/&lt;option-path&gt;</i>.
+     * For example, <code>org.eclipse.core.runtime/debug</code>
+     * <p>
+     * Clients are also able to acquire the {@link DebugOptions} service
+     * and query it for debug options.
+     * </p>
+     * @param option the name of the option to lookup
+     * @return the value of the requested debug option or <code>null</code>
+     */
+    public static String getDebugOption(String option) {
+        return null;
+// SWT FIXME:  impl
+//         return InternalPlatform.getDefault().getOption(option);
+    }
+
+//     /**
+//      * Returns the location of the platform working directory.
+//      * <p>
+//      * Callers of this method should consider using <code>getInstanceLocation</code>
+//      * instead.  In various, typically non IDE-related configurations of Eclipse, the platform
+//      * working directory may not be on the local file system.  As such, the more general
+//      * form of this location is as a URL.
+//      * </p><p>
+//      * Alternatively, instead of calling <code>getInstanceLocation</code> clients are
+//      * able to acquire the {@link Location} service (with the type {@link Location#INSTANCE_FILTER})
+//      * and then change the resulting URL to a path. See the javadoc for <code>getInstanceLocation</code>
+//      * for more details.
+//      * </p>
+//      * @return the location of the platform
+//      * @see #getInstanceLocation()
+//      */
+//     public static IPath getLocation() throws IllegalStateException {
+//         return InternalPlatform.getDefault().getLocation();
+//     }
+
+    /**
+     * Returns the location of the platform log file.  This file may contain information
+     * about errors that have previously occurred during this invocation of the Platform.
+     * <p>
+     * It is recommended not to keep this value, as the log location may vary when an instance
+     * location is being set.</p>
+     * <p>
+     * Note: it is very important that users of this method do not leave the log
+     * file open for extended periods of time.  Doing so may prevent others
+     * from writing to the log file, which could result in important error messages
+     * being lost.  It is strongly recommended that clients wanting to read the
+     * log file for extended periods should copy the log file contents elsewhere,
+     * and immediately close the original file.</p>
+     * @return the path of the log file on disk.
+     *
+     * XXX  consider making an ILogger interface that listeners can implements and it allows
+     * us to implement Platform.getLogLocation()
+     */
+    public static IPath getLogFileLocation() {
+        implMissing(__FILE__,__LINE__);
+        return null;
+        //return InternalPlatform.getDefault().getMetaArea().getLogLocation();
+    }
+
+//     /**
+//      * Returns the plug-in runtime object for the identified plug-in
+//      * or <code>null</code> if no such plug-in can be found.  If
+//      * the plug-in is defined but not yet activated, the plug-in will
+//      * be activated before being returned.
+//      * <p>
+//      * <b>Note</b>: This method is only able to find and return plug-in
+//      * objects for plug-ins described using plugin.xml according to the
+//      * traditional Eclipse conventions.  Eclipse 3.0 permits plug-ins to be
+//      * described in manifest.mf files and to define their own bundle
+//      * activators.  Such plug-ins cannot be discovered by this method.</p>
+//      *
+//      * @param id the unique identifier of the desired plug-in
+//      *      (e.g., <code>"com.example.acme"</code>).
+//      * @return the plug-in runtime object, or <code>null</code>
+//      * @deprecated
+//      * This method only works if the compatibility layer is installed and must not be used otherwise.
+//      * See the comments on {@link IPluginDescriptor#getPlugin()} for details.
+//      */
+//     public static Plugin getPlugin(String id) {
+//         try {
+//             IPluginRegistry registry = getPluginRegistry();
+//             if (registry is null)
+//                 throw new IllegalStateException();
+//             IPluginDescriptor pd = registry.getPluginDescriptor(id);
+//             if (pd is null)
+//                 return null;
+//             return pd.getPlugin();
+//         } catch (CoreException e) {
+//             // TODO log the exception
+//         }
+//         return null;
+//     }
+//
+//     /**
+//      * Returns the plug-in registry for this platform.
+//      *
+//      * @return the plug-in registry
+//      * @see IPluginRegistry
+//      * @deprecated <code>IPluginRegistry</code> was refactored in Eclipse 3.0.
+//      * This method only works if the compatibility layer is installed and must not be used otherwise.
+//      * See the comments on {@link IPluginRegistry} and its methods for details.
+//      */
+//     public static IPluginRegistry getPluginRegistry() {
+//         Bundle compatibility = InternalPlatform.getDefault().getBundle(CompatibilityHelper.PI_RUNTIME_COMPATIBILITY);
+//         if (compatibility is null)
+//             throw new IllegalStateException();
+//
+//         Class oldInternalPlatform = null;
+//         try {
+//             oldInternalPlatform = compatibility.loadClass("org.eclipse.core.internal.plugins.InternalPlatform"); //$NON-NLS-1$
+//             Method getPluginRegistry = oldInternalPlatform.getMethod("getPluginRegistry", null); //$NON-NLS-1$
+//             return (IPluginRegistry) getPluginRegistry.invoke(oldInternalPlatform, null);
+//         } catch (Exception e) {
+//             //Ignore the exceptions, return null
+//         }
+//         return null;
+//
+//     }
+//
+//     /**
+//      * Returns the location in the local file system of the plug-in
+//      * state area for the given plug-in.
+//      * The platform must be running.
+//      * <p>
+//      * The plug-in state area is a file directory within the
+//      * platform's metadata area where a plug-in is free to create files.
+//      * The content and structure of this area is defined by the plug-in,
+//      * and the particular plug-in is solely responsible for any files
+//      * it puts there. It is recommended for plug-in preference settings.
+//      * </p>
+//      *
+//      * @param plugin the plug-in whose state location is returned
+//      * @return a local file system path
+//      * @deprecated clients should call <code>getStateLocation</code> instead
+//      */
+//     public static IPath getPluginStateLocation(Plugin plugin) {
+//         return plugin.getStateLocation();
+//     }
+//
+//     /**
+//      * Returns the protection space (realm) for the specified resource, or
+//      * <code>null</code> if the realm is unknown.
+//      *
+//      * @param resourceUrl the URL of the resource whose protection space is
+//      *      returned. For example, "http://www.example.com/folder/".
+//      * @return the protection space (realm) for the specified resource, or
+//      *      <code>null</code> if the realm is unknown
+//      * @deprecated Authorization database is superseded by the Equinox secure storage.
+//      * Use <code>org.eclipse.equinox.security.storage.SecurePreferencesFactory</code>
+//      * to obtain secure preferences and <code>org.eclipse.equinox.security.storage.ISecurePreferences</code>
+//      * for data access and modifications.
+//      */
+//     public static String getProtectionSpace(URL resourceUrl) {
+//         try {
+//             return AuthorizationHandler.getProtectionSpace(resourceUrl);
+//         } catch (NoClassDefFoundError e) {
+//             // The authorization code is not available so just log and continue
+//             logAuthNotAvailable(e);
+//         }
+//         return null;
+//     }
+//
+//     /**
+//      * Removes the indicated (identical) log listener from the notification list
+//      * of the platform.  If no such listener exists, no action is taken.
+//      *
+//      * @param listener the listener to de-register
+//      * @see ILog#removeLogListener(ILogListener)
+//      * @see #addLogListener(ILogListener)
+//      * XXX Use the LogMgr service.
+//      */
+//     public static void removeLogListener(ILogListener listener) {
+//         InternalPlatform.getDefault().removeLogListener(listener);
+//     }
+//
+//     /**
+//      * Returns a URL which is the resolved equivalent of the
+//      * supplied URL. This method is expected to be used with the
+//      * plug-in-relative URLs returned by IPluginDescriptor, Bundle.getEntry()
+//      * and Platform.find().
+//      * <p>
+//      * If the specified URL is not a plug-in-relative URL, it is returned
+//      * as is. If the specified URL is a plug-in-relative URL, this method
+//      * attempts to reduce the given URL to one which is native to the Java
+//      * class library (eg. file, http, etc).
+//      * </p><p>
+//      * Note however that users of this API should not assume too much about the
+//      * results of this method.  While it may consistently return a file: URL in certain
+//      * installation configurations, others may result in jar: or http: URLs.
+//      * </p>
+//      * @param url original plug-in-relative URL.
+//      * @return the resolved URL
+//      * @exception IOException if unable to resolve URL
+//      * @see #asLocalURL(URL)
+//      * @see #find(Bundle, IPath)
+//      * @see Bundle#getEntry(String)
+//      * @deprecated use {@link FileLocator#resolve(URL)} instead
+//      */
+//     public static URL resolve(URL url) throws IOException {
+//         return FileLocator.resolve(url);
+//     }
+//
+//     /**
+//      * Runs the given runnable in a protected mode.   Exceptions
+//      * thrown in the runnable are logged and passed to the runnable's
+//      * exception handler.  Such exceptions are not rethrown by this method.
+//      *
+//      * @param runnable the runnable to run
+//      * @deprecated clients should use <code>SafeRunner#run</code> instead
+//      */
+//     public static void run(ISafeRunnable runnable) {
+//         SafeRunner.run(runnable);
+//     }
+//
+//     /**
+//      * Returns the platform job manager.
+//      *
+//      * @return the platform's job manager
+//      * @since 3.0
+//      * @deprecated The method {@link Job#getJobManager()} should be used instead.
+//      */
+//     public static IJobManager getJobManager() {
+//         return Job.getJobManager();
+//     }
+//
+//     /**
+//      * Returns the extension registry for this platform.
+//      *
+//      * @return the extension registry
+//      * @see IExtensionRegistry
+//      * @since 3.0
+//      */
+//     public static IExtensionRegistry getExtensionRegistry() {
+//         return InternalPlatform.getDefault().getRegistry();
+//     }
+//
+//     /**
+//      * Returns a URL for the given path in the given bundle.  Returns <code>null</code> if the URL
+//      * could not be computed or created.
+//      *
+//      * @param bundle the bundle in which to search
+//      * @param path path relative to plug-in installation location
+//      * @return a URL for the given path or <code>null</code>.  The actual form
+//      * of the returned URL is not specified.
+//      * @see #find(Bundle, IPath, Map)
+//      * @see #resolve(URL)
+//      * @see #asLocalURL(URL)
+//      * @since 3.0
+//      * @deprecated use {@link FileLocator#find(Bundle, IPath, Map)}
+//      */
+//     public static URL find(Bundle bundle, IPath path) {
+//         return FileLocator.find(bundle, path, null);
+//     }
+//
+//     /**
+//      * Returns a URL for the given path in the given bundle.  Returns <code>null</code> if the URL
+//      * could not be computed or created.
+//      * <p>
+//      * find looks for this path in given bundle and any attached fragments.
+//      * <code>null</code> is returned if no such entry is found.  Note that
+//      * there is no specific order to the fragments.
+//      * </p><p>
+//      * The following arguments may also be used
+//      * <pre>
+//      *     $nl$ - for language specific information
+//      *     $os$ - for operating system specific information
+//      *     $ws$ - for windowing system specific information
+//      * </pre>
+//      * </p><p>
+//      * A path of $nl$/about.properties in an environment with a default
+//      * locale of en_CA will return a URL corresponding to the first place
+//      * about.properties is found according to the following order:
+//      * <pre>
+//      *     plugin root/nl/en/CA/about.properties
+//      *     fragment1 root/nl/en/CA/about.properties
+//      *     fragment2 root/nl/en/CA/about.properties
+//      *     ...
+//      *     plugin root/nl/en/about.properties
+//      *     fragment1 root/nl/en/about.properties
+//      *     fragment2 root/nl/en/about.properties
+//      *     ...
+//      *     plugin root/about.properties
+//      *     fragment1 root/about.properties
+//      *     fragment2 root/about.properties
+//      *     ...
+//      * </pre>
+//      * </p><p>
+//      * The current environment variable values can be overridden using
+//      * the override map argument.
+//      * </p>
+//      *
+//      * @param bundle the bundle in which to search
+//      * @param path file path relative to plug-in installation location
+//      * @param override map of override substitution arguments to be used for
+//      * any $arg$ path elements. The map keys correspond to the substitution
+//      * arguments (eg. "$nl$" or "$os$"). The resulting
+//      * values must be of type java.lang.String. If the map is <code>null</code>,
+//      * or does not contain the required substitution argument, the default
+//      * is used.
+//      * @return a URL for the given path or <code>null</code>.  The actual form
+//      * of the returned URL is not specified.
+//      * @see #resolve(URL)
+//      * @see #asLocalURL(URL)
+//      * @since 3.0
+//      * @deprecated use {@link FileLocator#find(Bundle, IPath, Map)} instead
+//      */
+//     public static URL find(Bundle bundle, IPath path, Map override) {
+//         return FileLocator.find(bundle, path, override);
+//     }
+//
+//     /**
+//      * Returns the location in the local file system of the
+//      * plug-in state area for the given bundle.
+//      * If the plug-in state area did not exist prior to this call,
+//      * it is created.
+//      * <p>
+//      * The plug-in state area is a file directory within the
+//      * platform's metadata area where a plug-in is free to create files.
+//      * The content and structure of this area is defined by the plug-in,
+//      * and the particular plug-in is solely responsible for any files
+//      * it puts there. It is recommended for plug-in preference settings and
+//      * other configuration parameters.
+//      * </p>
+//      *
+//      * @param bundle the bundle whose state location if returned
+//      * @return a local file system path
+//      * @since 3.0
+//      * XXX Investigate the usage of a service factory
+//      */
+//     public static IPath getStateLocation(Bundle bundle) {
+//         return InternalPlatform.getDefault().getStateLocation(bundle);
+//     }
+//
+//     /**
+//      * Returns a number that changes whenever the set of installed plug-ins
+//      * changes. This can be used for invalidating caches that are based on
+//      * the set of currently installed plug-ins. (e.g. extensions)
+//      * <p>
+//      * Clients are also able to acquire the {@link PlatformAdmin} service
+//      * and get the timestamp from its state object.
+//      * </p>
+//      * @return a number related to the set of installed plug-ins
+//      * @since 3.1
+//      */
+//     public static long getStateStamp() {
+//         return InternalPlatform.getDefault().getStateTimeStamp();
+//     }
+
+    /**
+     * Returns the log for the given bundle.  If no such log exists, one is created.
+     *
+     * @param bundle the bundle whose log is returned
+     * @return the log for the given bundle
+     * @since 3.0
+     * XXX change this into a LogMgr service that would keep track of the map. See if it can be a service factory.
+     * It would contain all the methods that are here.
+     * Relate to RuntimeLog if appropriate.
+     * The system log listener needs to be optional: turned on or off. What about a system property? :-)
+     */
+    public static ILog getLog(Bundle bundle) {
+        implMissing(__FILE__,__LINE__);
+        return null;
+        //return InternalPlatform.getDefault().getLog(bundle);
+    }
+
+//     /**
+//      * Returns the given bundle's resource bundle for the current locale.
+//      * <p>
+//      * This resource bundle is typically stored as the plugin.properties file
+//      * in the plug-in itself, and contains any translatable strings used in the
+//      * plug-in manifest file (plugin.xml).
+//      * </p>
+//      * <p>
+//      *  This mechanism is intended only for
+//      * externalizing strings found in the plug-in manifest file. Using this
+//      * method for externalizing strings in your code may result in degraded
+//      * memory performance.
+//      * </p>
+//      *
+//      * @param bundle the bundle whose resource bundle is being queried
+//      * @return the resource bundle
+//      * @exception MissingResourceException if the resource bundle was not found
+//      * @since 3.0
+//      * XXX this is deprecated. use NLS or BundleFinder.find()
+//      */
+//     public static ResourceBundle getResourceBundle(Bundle bundle) throws MissingResourceException {
+//         return InternalPlatform.getDefault().getResourceBundle(bundle);
+//     }
+//
+//     /**
+//      * Returns a resource string corresponding to the given argument value.
+//      * If the argument value specifies a resource key, the string
+//      * is looked up in the default resource bundle for the given runtime bundle. If the argument does not
+//      * specify a valid key, the argument itself is returned as the
+//      * resource string. The key lookup is performed in the
+//      * file referenced in the Bundle-Localization header of the bundle manifest. If a resource string
+//      * corresponding to the key is not found in the resource bundle
+//      * the key value, or any default text following the key in the
+//      * argument value is returned as the resource string.
+//      * A key is identified as a string beginning with the "%" character.
+//      * Note, that the "%" character is stripped off prior to lookup
+//      * in the resource bundle.
+//      * <p>
+//      * Equivalent to <code>getResourceString(bundle, value, getResourceBundle())</code>
+//      * </p>
+//      *
+//      * @param bundle the bundle whose resource bundle is being queried
+//      * @param value the value to look for
+//      * @return the resource string
+//      * @see #getResourceBundle(Bundle)
+//      * @since 3.0
+//      * XXX this is deprecated. use NLS or  BundleFinder.find()
+//      */
+//     public static String getResourceString(Bundle bundle, String value) {
+//         return InternalPlatform.getDefault().getResourceString(bundle, value);
+//     }
+//
+//     /**
+//      * Returns a resource string corresponding to the given argument
+//      * value and resource bundle in the given runtime bundle.
+//      * If the argument value specifies a resource key, the string
+//      * is looked up in the given resource bundle. If the argument does not
+//      * specify a valid key, the argument itself is returned as the
+//      * resource string. The key lookup is performed against the
+//      * specified resource bundle. If a resource string
+//      * corresponding to the key is not found in the resource bundle
+//      * the key value, or any default text following the key in the
+//      * argument value is returned as the resource string.
+//      * A key is identified as a string beginning with the "%" character.
+//      * Note that the "%" character is stripped off prior to lookup
+//      * in the resource bundle.
+//      * <p>
+//      * For example, assume resource bundle plugin.properties contains
+//      * name = Project Name
+//      * <pre>
+//      *     getResourceString("Hello World") returns "Hello World"</li>
+//      *     getResourceString("%name") returns "Project Name"</li>
+//      *     getResourceString("%name Hello World") returns "Project Name"</li>
+//      *     getResourceString("%abcd Hello World") returns "Hello World"</li>
+//      *     getResourceString("%abcd") returns "%abcd"</li>
+//      *     getResourceString("%%name") returns "%name"</li>
+//      * </pre>
+//      * </p>
+//      *
+//      * @param bundle the bundle whose resource bundle is being queried
+//      * @param value the value
+//      * @param resourceBundle the resource bundle to query
+//      * @return the resource string
+//      * @see #getResourceBundle(Bundle)
+//      * @since 3.0
+//      * XXX this is deprecated. use NLS or  BundleFinder.find()
+//      */
+//     public static String getResourceString(Bundle bundle, String value, ResourceBundle resourceBundle) {
+//         return InternalPlatform.getDefault().getResourceString(bundle, value, resourceBundle);
+//     }
+//
+//     /**
+//      * Returns the string name of the current system architecture.
+//      * The value is a user-defined string if the architecture is
+//      * specified on the command line, otherwise it is the value
+//      * returned by <code>java.lang.System.getProperty("os.arch")</code>.
+//      * <p>
+//      * Clients are also able to acquire the {@link EnvironmentInfo} service and query it for
+//      * the operating-system architecture.
+//      * </p>
+//      * @return the string name of the current system architecture
+//      * @since 3.0
+//      */
+//     public static String getOSArch() {
+//         return InternalPlatform.getDefault().getOSArch();
+//     }
+//
+//     /**
+//      * Returns the string name of the current locale for use in finding files
+//      * whose path starts with <code>$nl$</code>.
+//      * <p>
+//      * Clients are also able to acquire the {@link EnvironmentInfo} service and query it for
+//      * the NL.
+//      * </p>
+//      * @return the string name of the current locale
+//      * @since 3.0
+//      */
+//     public static String getNL() {
+//         return InternalPlatform.getDefault().getNL();
+//     }
+//
+//     /**
+//      * Returns the string name of the current operating system for use in finding
+//      * files whose path starts with <code>$os$</code>.  <code>OS_UNKNOWN</code> is
+//      * returned if the operating system cannot be determined.
+//      * The value may indicate one of the operating systems known to the platform
+//      * (as specified in <code>knownOSValues</code>) or a user-defined string if
+//      * the operating system name is specified on the command line.
+//      * <p>
+//      * Clients are also able to acquire the {@link EnvironmentInfo} service and query it for
+//      * the operating-system.
+//      * </p>
+//      * @return the string name of the current operating system
+//      * @since 3.0
+//      */
+//     public static String getOS() {
+//         return InternalPlatform.getDefault().getOS();
+//     }
+//
+//     /**
+//      * Returns the string name of the current window system for use in finding files
+//      * whose path starts with <code>$ws$</code>.  <code>null</code> is returned
+//      * if the window system cannot be determined.
+//      * <p>
+//      * Clients are also able to acquire the {@link EnvironmentInfo} service and query it for
+//      * the windowing system.
+//      * </p>
+//      * @return the string name of the current window system or <code>null</code>
+//      * @since 3.0
+//      */
+//     public static String getWS() {
+//         return InternalPlatform.getDefault().getWS();
+//     }
+//
+//     /**
+//      * Returns the arguments not consumed by the framework implementation itself.  Which
+//      * arguments are consumed is implementation specific. These arguments are available
+//      * for use by the application.
+//      *
+//      * @return the array of command line arguments not consumed by the framework.
+//      * @since 3.0
+//      * XXX Use the Environment info service. Need to see how to set the value of the app args.
+//      */
+//     public static String[] getApplicationArgs() {
+//         return InternalPlatform.getDefault().getApplicationArgs();
+//     }
+//
+//     /**
+//      * Returns the platform administrator for this running Eclipse.
+//      * <p>
+//      * Note: This is an internal method and <em>must not</em>
+//      * be used by clients which are not part of the Eclipse Platform.
+//      * This method allows access to classes which are not Eclipse
+//      * Platform API but are part of the OSGi runtime that the Eclipse
+//      * Platform is built on. Even as the Eclipse Platform evolves
+//      * in compatible ways from release to release, the details of
+//      * the OSGi implementation might not.
+//      * </p><p>
+//      * Clients can also acquire the {@link PlatformAdmin} service
+//      * to retrieve this object.
+//      * </p>
+//      * @return the platform admin for this instance of Eclipse
+//      * @since 3.0
+//      */
+//     public static PlatformAdmin getPlatformAdmin() {
+//         return InternalPlatform.getDefault().getPlatformAdmin();
+//     }
+//
+//     /**
+//      * Returns the location of the platform's working directory (also known as the instance data area).
+//      * <code>null</code> is returned if the platform is running without an instance location.
+//      * <p>
+//      * This method is equivalent to acquiring the <code>org.eclipse.osgi.service.datalocation.Location</code>
+//      * service with the property "type" equal to {@link Location#INSTANCE_FILTER}.
+//      *</p>
+//      * @return the location of the platform's instance data area or <code>null</code> if none
+//      * @since 3.0
+//      * @see Location#INSTANCE_FILTER
+//      */
+//     public static Location getInstanceLocation() {
+//         return InternalPlatform.getDefault().getInstanceLocation();
+//     }
+//
+//     /**
+//      * Returns the currently registered bundle group providers.
+//      * <p>
+//      * Clients are also able to acquire the {@link IBundleGroupProvider} service and query it for
+//      * the registered bundle group providers.
+//      * </p>
+//      * @return the currently registered bundle group providers
+//      * @since 3.0
+//      */
+//     public static IBundleGroupProvider[] getBundleGroupProviders() {
+//         return InternalPlatform.getDefault().getBundleGroupProviders();
+//     }
+//
+//     /**
+//      * Return the interface into the preference mechanism. The returned
+//      * object can be used for such operations as searching for preference
+//      * values across multiple scopes and preference import/export.
+//      * <p>
+//      * Clients are also able to acquire the {@link IPreferencesService} service via
+//      * OSGi mechanisms and use it for preference functions.
+//      * </p>
+//      * @return an object to interface into the preference mechanism
+//      * @since 3.0
+//      */
+//     public static IPreferencesService getPreferencesService() {
+//         return InternalPlatform.getDefault().getPreferencesService();
+//     }
+//
+//     /**
+//      * Returns the product which was selected when running this Eclipse instance
+//      * or <code>null</code> if none
+//      * @return the current product or <code>null</code> if none
+//      * @since 3.0
+//      * XXX move this into the app model.
+//      */
+//     public static IProduct getProduct() {
+//         return InternalPlatform.getDefault().getProduct();
+//     }
+//
+//     /**
+//      * Registers the given bundle group provider with the platform.
+//      * <p>
+//      * Clients are also able to use the {@link IBundleGroupProvider} service to
+//      * register themselves as a bundle group provider.
+//      * </p>
+//      * @param provider a provider to register
+//      * @since 3.0
+//      */
+//     public static void registerBundleGroupProvider(IBundleGroupProvider provider) {
+//         InternalPlatform.getDefault().registerBundleGroupProvider(provider);
+//     }
+//
+//     /**
+//      * De-registers the given bundle group provider with the platform.
+//      * <p>
+//      * Clients are also able to use the {@link IBundleGroupProvider} service mechanism
+//      * for unregistering themselves.
+//      * </p>
+//      * @param provider a provider to de-register
+//      * @since 3.0
+//      * @see #registerBundleGroupProvider(IBundleGroupProvider)
+//      */
+//     public static void unregisterBundleGroupProvider(IBundleGroupProvider provider) {
+//         InternalPlatform.getDefault().unregisterBundleGroupProvider(provider);
+//     }
+//
+//     /**
+//      * Returns the location of the configuration information
+//      * used to run this instance of Eclipse.  The configuration area typically
+//      * contains the list of plug-ins available for use, various settings
+//      * (those shared across different instances of the same configuration)
+//      * and any other such data needed by plug-ins.
+//      * <code>null</code> is returned if the platform is running without a configuration location.
+//      * <p>
+//      * This method is equivalent to acquiring the <code>org.eclipse.osgi.service.datalocation.Location</code>
+//      * service with the property "type" equal to {@link Location#CONFIGURATION_FILTER}.
+//      *</p>
+//      * @return the location of the platform's configuration data area or <code>null</code> if none
+//      * @since 3.0
+//      * @see Location#CONFIGURATION_FILTER
+//      */
+//     public static Location getConfigurationLocation() {
+//         return InternalPlatform.getDefault().getConfigurationLocation();
+//     }
+//
+//     /**
+//      * Returns the location of the platform's user data area.  The user data area is a location on the system
+//      * which is specific to the system's current user.  By default it is located relative to the
+//      * location given by the System property "user.home".
+//      * <code>null</code> is returned if the platform is running without an user location.
+//      * <p>
+//      * This method is equivalent to acquiring the <code>org.eclipse.osgi.service.datalocation.Location</code>
+//      * service with the property "type" equal to {@link Location#USER_FILTER}.
+//      *</p>
+//      * @return the location of the platform's user data area or <code>null</code> if none
+//      * @since 3.0
+//      * @see Location#USER_FILTER
+//      */
+//     public static Location getUserLocation() {
+//         return InternalPlatform.getDefault().getUserLocation();
+//     }
+//
+//     /**
+//      * Returns the location of the base installation for the running platform
+//      * <code>null</code> is returned if the platform is running without a configuration location.
+//      * <p>
+//      * This method is equivalent to acquiring the <code>org.eclipse.osgi.service.datalocation.Location</code>
+//      * service with the property "type" equal to {@link Location#INSTALL_FILTER}.
+//      *</p>
+//      * @return the location of the platform's installation area or <code>null</code> if none
+//      * @since 3.0
+//      * @see Location#INSTALL_FILTER
+//      */
+//     public static Location getInstallLocation() {
+//         return InternalPlatform.getDefault().getInstallLocation();
+//     }
+//
+//     /**
+//      * Checks if the specified bundle is a fragment bundle.
+//      * <p>
+//      * Clients are also able to acquire the {@link PackageAdmin} service
+//      * to query if the given bundle is a fragment by asking for the bundle type
+//      * and checking against constants on the service interface.
+//      * </p>
+//      * @param bundle the bundle to query
+//      * @return true if the specified bundle is a fragment bundle; otherwise false is returned.
+//      * @since 3.0
+//      */
+//     public static bool isFragment(Bundle bundle) {
+//         return InternalPlatform.getDefault().isFragment(bundle);
+//     }
+//
+//     /**
+//      * Returns an array of attached fragment bundles for the specified bundle.  If the
+//      * specified bundle is a fragment then <tt>null</tt> is returned.  If no fragments are
+//      * attached to the specified bundle then <tt>null</tt> is returned.
+//      * <p>
+//      * Clients are also able to acquire the {@link PackageAdmin} service and query
+//      * it for the fragments of the given bundle.
+//      * </p>
+//      * @param bundle the bundle to get the attached fragment bundles for.
+//      * @return an array of fragment bundles or <tt>null</tt> if the bundle does not
+//      * have any attached fragment bundles.
+//      * @since 3.0
+//      */
+//     public static Bundle[] getFragments(Bundle bundle) {
+//         return InternalPlatform.getDefault().getFragments(bundle);
+//     }
+//
+    /**
+     * Returns the resolved bundle with the specified symbolic name that has the
+     * highest version.  If no resolved bundles are installed that have the
+     * specified symbolic name then null is returned.
+     * <p>
+     * Clients are also able to acquire the {@link PackageAdmin} service and query
+     * it for the bundle with the specified symbolic name. Clients can ask the
+     * service for all bundles with that particular name and then determine the
+     * one with the highest version. Note that clients may want to filter
+     * the results based on the state of the bundles.
+     * </p>
+     * @param symbolicName the symbolic name of the bundle to be returned.
+     * @return the bundle that has the specified symbolic name with the
+     * highest version, or <tt>null</tt> if no bundle is found.
+     * @since 3.0
+     */
+    public static Bundle getBundle(String symbolicName) {
+        implMissing(__FILE__, __LINE__ );
+        return null;
+        //return InternalPlatform.getDefault().getBundle(symbolicName);
+    }
+
+//     /**
+//      * Returns all bundles with the specified symbolic name.  If no resolved bundles
+//      * with the specified symbolic name can be found, <tt>null</tt> is returned.
+//      * If the version argument is not null then only the Bundles that have
+//      * the specified symbolic name and a version greater than or equal to the
+//      * specified version are returned. The returned bundles are ordered in
+//      * descending bundle version order.
+//      * <p>
+//      * Clients are also able to acquire the {@link PackageAdmin} service and query
+//      * it for all bundle versions with the given symbolic name, after turning the
+//      * specific version into a version range. Note that clients may want to filter
+//      * the results based on the state of the bundles.
+//      * </p>
+//      * @param symbolicName the symbolic name of the bundles that are to be returned.
+//      * @param version the version that the return bundle versions must match,
+//      * or <tt>null</tt> if no version matching is to be done.
+//      * @return the array of Bundles with the specified name that match the
+//      * specified version and match rule, or <tt>null</tt> if no bundles are found.
+//      */
+//     public static Bundle[] getBundles(String symbolicName, String version) {
+//         return InternalPlatform.getDefault().getBundles(symbolicName, version);
+//     }
+//
+//     /**
+//      * Returns an array of host bundles that the specified fragment bundle is
+//      * attached to or <tt>null</tt> if the specified bundle is not attached to a host.
+//      * If the bundle is not a fragment bundle then <tt>null</tt> is returned.
+//      * <p>
+//      * Clients are also able to acquire the {@link PackageAdmin} service and query
+//      * it for the hosts for the given bundle.
+//      * </p>
+//      * @param bundle the bundle to get the host bundles for.
+//      * @return an array of host bundles or null if the bundle does not have any
+//      * host bundles.
+//      * @since 3.0
+//      */
+//     public static Bundle[] getHosts(Bundle bundle) {
+//         return InternalPlatform.getDefault().getHosts(bundle);
+//     }
+//
+//     /**
+//      * Returns whether the platform is running.
+//      *
+//      * @return <code>true</code> if the platform is running,
+//      *      and <code>false</code> otherwise
+//      *@since 3.0
+//      *XXX do what you want to do. track osgi, track runtime, or whatever.
+//      */
+//     public static bool isRunning() {
+//         return InternalPlatform.getDefault().isRunning();
+//     }
+//
+//     /**
+//      * Returns a list of known system architectures.
+//      * <p>
+//      * Note that this list is not authoritative; there may be legal values
+//      * not included in this list. Indeed, the value returned by
+//      * <code>getOSArch</code> may not be in this list. Also, this list may
+//      * change over time as Eclipse comes to run on more operating environments.
+//      * </p>
+//      *
+//      * @return the list of system architectures known to the system
+//      * @see #getOSArch()
+//      * @since 3.0
+//      * XXX This is useless
+//      */
+//     public static String[] knownOSArchValues() {
+//         return InternalPlatform.getDefault().knownOSArchValues();
+//     }
+//
+//     /**
+//      * Returns a list of known operating system names.
+//      * <p>
+//      * Note that this list is not authoritative; there may be legal values
+//      * not included in this list. Indeed, the value returned by
+//      * <code>getOS</code> may not be in this list. Also, this list may
+//      * change over time as Eclipse comes to run on more operating environments.
+//      * </p>
+//      *
+//      * @return the list of operating systems known to the system
+//      * @see #getOS()
+//      * @since 3.0
+//      * XXX This is useless
+//      */
+//     public static String[] knownOSValues() {
+//         return InternalPlatform.getDefault().knownOSValues();
+//     }
+//
+//     /**
+//      * Returns a map of known platform line separators. The keys are
+//      * translated names of platforms and the values are their associated
+//      * line separator strings.
+//      *
+//      * @return a map of platform to their line separator string
+//      * @since 3.1
+//      */
+//     public static Map knownPlatformLineSeparators() {
+//         Map result = new HashMap();
+//         result.put(LINE_SEPARATOR_KEY_MAC_OS_9, LINE_SEPARATOR_VALUE_CR);
+//         result.put(LINE_SEPARATOR_KEY_UNIX, LINE_SEPARATOR_VALUE_LF);
+//         result.put(LINE_SEPARATOR_KEY_WINDOWS, LINE_SEPARATOR_VALUE_CRLF);
+//         return result;
+//     }
+//
+//     /**
+//      * Returns a list of known windowing system names.
+//      * <p>
+//      * Note that this list is not authoritative; there may be legal values
+//      * not included in this list. Indeed, the value returned by
+//      * <code>getWS</code> may not be in this list. Also, this list may
+//      * change over time as Eclipse comes to run on more operating environments.
+//      * </p>
+//      *
+//      * @return the list of window systems known to the system
+//      * @see #getWS()
+//      * @since 3.0
+//      * XXX This is useless
+//      */
+//     public static String[] knownWSValues() {
+//         return InternalPlatform.getDefault().knownWSValues();
+//     }
+//
+//     /**
+//      * Returns <code>true</code> if the platform is currently running in
+//      * debug mode.  The platform is typically put in debug mode using the
+//      * "-debug" command line argument.
+//      * <p>
+//      * Clients are also able to acquire the {@link EnvironmentInfo} service and query it
+//      * to see if they are in debug mode.
+//      * </p>
+//      * @return whether or not the platform is running in debug mode
+//      * @since 3.0
+//      */
+//     public static bool inDebugMode() {
+//         return PlatformActivator.getContext().getProperty("osgi.debug") !is null; //$NON-NLS-1$
+//     }
+//
+//     /**
+//      * Returns <code>true</code> if the platform is currently running in
+//      * development mode.  That is, if special procedures are to be
+//      * taken when defining plug-in class paths.  The platform is typically put in
+//      * development mode using the "-dev" command line argument.
+//      * <p>
+//      * Clients are also able to acquire the {@link EnvironmentInfo} service and query it
+//      * to see if they are in development mode.
+//      * </p>
+//      * @return whether or not the platform is running in development mode
+//      * @since 3.0
+//      */
+//     public static bool inDevelopmentMode() {
+//         return PlatformActivator.getContext().getProperty("osgi.dev") !is null; //$NON-NLS-1$
+//     }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/AbsoluteBendpoint.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.AbsoluteBendpoint;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.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 org.eclipse.draw2d.Bendpoint#getLocation()
+ */
+public Point getLocation() {
+    return this;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractBackground.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.AbstractBackground;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.AbstractBorder;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.AbstractBorder;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.Border;
+import org.eclipse.draw2d.IFigure;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+
+/**
+ * Provides generic support for borders.
+ * @author hudsonr
+ */
+public abstract class AbstractBorder
+    : Border
+{
+
+private static Dimension EMPTY_;
+private static Dimension EMPTY(){
+    if( EMPTY_ is null ){
+        synchronized( AbstractBorder.classinfo ){
+            if( EMPTY_ is null ){
+                EMPTY_ = new Dimension();
+            }
+        }
+    }
+    return EMPTY_;
+}
+
+/** A temporary Rectangle*/
+private static Rectangle tempRect_;
+protected static Rectangle tempRect(){
+    if( tempRect_ is null ){
+        synchronized( AbstractBorder.classinfo ){
+            if( tempRect_ is null ){
+                tempRect_ = new Rectangle();
+            }
+        }
+    }
+    return tempRect_;
+}
+
+/**
+ * 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 org.eclipse.draw2d.Border#getPreferredSize(IFigure)
+ */
+public Dimension getPreferredSize(IFigure f) {
+    return EMPTY;
+}
+
+/**
+ * @see org.eclipse.draw2d.Border#isOpaque()
+ */
+public bool isOpaque() {
+    return false;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractConnectionAnchor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.AbstractConnectionAnchor;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.ConnectionAnchorBase;
+import org.eclipse.draw2d.AncestorListener;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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 org.eclipse.draw2d.AncestorListener#ancestorMoved(IFigure)
+ */
+public void ancestorMoved(IFigure figure) {
+    fireAnchorMoved();
+}
+
+/**
+ * @see org.eclipse.draw2d.AncestorListener#ancestorAdded(IFigure)
+ */
+public void ancestorAdded(IFigure ancestor) { }
+
+/**
+ * @see org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractHintLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.AbstractHintLayout;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.AbstractLayout;
+import org.eclipse.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 org.eclipse.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 org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractLabeledBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.AbstractLabeledBorder;
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.AbstractBorder;
+import org.eclipse.draw2d.LabeledBorder;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.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 org.eclipse.draw2d.LabeledBorder#getLabel()
+ */
+public String getLabel() {
+    return label;
+}
+
+/**
+ * @see org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.AbstractLayout;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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 org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractLocator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.AbstractLocator;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Locator;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/AbstractRouter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.AbstractRouter;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.Connection;
+import org.eclipse.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 Point START_;
+private static Point END_;
+
+private static Point START(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    assert(START_);
+    return START_;
+}
+private static Point END(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    assert(END_);
+    return END_;
+}
+
+private static bool initStaticCtor_done = false;
+private static void initStaticCtor(){
+    synchronized( AbstractRouter.classinfo ){
+        if( !initStaticCtor_done ){
+            START_ = new Point();
+            END_ = new Point();
+            initStaticCtor_done = true;
+        }
+    }
+}
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/AccessibleBase.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.AccessibleBase;
+
+import java.lang.all;
+
+/**
+ * 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 SWT'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/org.eclipse.draw2d/src/org/eclipse/draw2d/ActionEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ActionEvent;
+
+import java.lang.all;
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/ActionListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ActionListener;
+
+import java.lang.all;
+import tango.core.Traits;
+import tango.core.Tuple;
+
+import org.eclipse.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);
+
+}
+// SWT 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/org.eclipse.draw2d/src/org/eclipse/draw2d/AncestorHelper.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.AncestorHelper;
+
+import java.lang.all;
+import org.eclipse.dwtxhelper.Bean;
+
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.FigureListener;
+import org.eclipse.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 org.eclipse.draw2d.FigureListener#figureMoved(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/AncestorListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.AncestorListener;
+
+import java.lang.all;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/AnchorListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.AnchorListener;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Animation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,277 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.Animation;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.Animator;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Animator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.Animator;
+
+import java.lang.all;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ArrowButton.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ArrowButton;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.Button;
+import org.eclipse.draw2d.Orientable;
+import org.eclipse.draw2d.Triangle;
+import org.eclipse.draw2d.MarginBorder;
+import org.eclipse.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 org.eclipse.draw2d.Orientable#setDirection(int)
+ */
+public void setDirection(int value) {
+    setChildrenDirection(value);
+}
+
+/**
+ * @see org.eclipse.draw2d.Orientable#setOrientation(int)
+ */
+public void setOrientation(int value) {
+    setChildrenOrientation(value);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/ArrowLocator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ArrowLocator;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.ConnectionLocator;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Connection;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/AutomaticRouter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,222 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.AutomaticRouter;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.internal.MultiValueMap;
+import org.eclipse.draw2d.AbstractRouter;
+import org.eclipse.draw2d.ConnectionRouter;
+import org.eclipse.draw2d.ConnectionAnchor;
+import org.eclipse.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 org.eclipse.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 org.eclipse.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 org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Bendpoint.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.Bendpoint;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+
+/**
+ * A helper used to calculate the point at which a {@link org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/BendpointConnectionRouter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.BendpointConnectionRouter;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.PrecisionPoint;
+import org.eclipse.draw2d.AbstractRouter;
+import org.eclipse.draw2d.Connection;
+import org.eclipse.draw2d.Bendpoint;
+
+/**
+ * Routes {@link Connection}s through a <code>List</code> of {@link Bendpoint Bendpoints}.
+ */
+public class BendpointConnectionRouter
+    : AbstractRouter
+{
+
+private Map constraints;
+
+private static PrecisionPoint A_POINT_;
+private static PrecisionPoint A_POINT(){
+    if( A_POINT_ is null ){
+        synchronized( BendpointConnectionRouter.classinfo ){
+            if( A_POINT_ is null ){
+                A_POINT_ = new PrecisionPoint();
+            }
+        }
+    }
+    return A_POINT_;
+}
+
+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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/BendpointLocator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.BendpointLocator;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.ConnectionLocator;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Border.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.Border;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/BorderLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,353 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.BorderLayout;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.AbstractHintLayout;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.PositionConstants;
+
+/**
+ * @author Pratik Shah
+ */
+public class BorderLayout
+    : AbstractHintLayout
+{
+
+/**
+ * Constant to be used as a constraint for child figures
+ */
+private static Integer CENTER_;
+public static Integer CENTER(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return CENTER_;
+}
+/**
+ * Constant to be used as a constraint for child figures
+ */
+private static Integer TOP_;
+public static Integer TOP(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return TOP_;
+}
+/**
+ * Constant to be used as a constraint for child figures
+ */
+private static Integer BOTTOM_;
+public static Integer BOTTOM(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return BOTTOM_;
+}
+/**
+ * Constant to be used as a constraint for child figures
+ */
+private static Integer LEFT_;
+public static Integer LEFT(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return LEFT_;
+}
+/**
+ * Constant to be used as a constraint for child figures
+ */
+private static Integer RIGHT_;
+public static Integer RIGHT(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return RIGHT_;
+}
+
+private static bool initStaticCtor_done = false;
+private static void initStaticCtor(){
+    synchronized( BorderLayout.classinfo ){
+        if( !initStaticCtor_done ){
+            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);
+            initStaticCtor_done = true;
+        }
+    }
+}
+
+private IFigure center, left, top, bottom, right;
+private int vGap = 0, hGap = 0;
+
+/**
+ * @see org.eclipse.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 org.eclipse.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 org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/BufferedGraphicsSource.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.BufferedGraphicsSource;
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+static import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Caret;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.GraphicsSource;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.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 org.eclipse.draw2d.GraphicsSource#flushGraphics(org.eclipse.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 org.eclipse.draw2d.GraphicsSource#getGraphics(org.eclipse.draw2d.geometry.Rectangle)
+ */
+public Graphics getGraphics(Rectangle region) {
+    if (control is null || control.isDisposed())
+        return null;
+
+    org.eclipse.swt.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 (SWTError noMoreHandles) {
+        imageBuffer = null;
+    } catch (IllegalArgumentException tooBig) {
+        imageBuffer = null;
+    }
+
+    controlGC = new GC(control,
+            control.getStyle() & (SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT));
+    Graphics graphics;
+    if (imageBuffer !is null) {
+        imageGC = new GC(imageBuffer,
+                control.getStyle() & (SWT.RIGHT_TO_LEFT | SWT.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Button.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.Button;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.draw2d.Clickable;
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ButtonBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,347 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.ButtonBorder;
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.SchemeBorder;
+import org.eclipse.draw2d.Border;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.Clickable;
+import org.eclipse.draw2d.ButtonModel;
+import org.eclipse.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
+ */
+private static Border BUTTON_;
+public static Border BUTTON(){
+    if( BUTTON_ is null ){
+        synchronized( ButtonScheme.classinfo ){
+            if( BUTTON_ is null ){
+                BUTTON_ = new ButtonBorder(SCHEMES.BUTTON);
+            }
+        }
+    }
+    return BUTTON_;
+}
+/**
+ * Inverted hightlight colors from BUTTON.
+ * @see SCHEMES#BUTTON_CONTRAST
+ */
+private static Border BUTTON_CONTRAST_;
+public static Border BUTTON_CONTRAST(){
+    if( BUTTON_CONTRAST_ is null ){
+        synchronized( ButtonScheme.classinfo ){
+            if( BUTTON_CONTRAST_ is null ){
+                BUTTON_CONTRAST_ = new ButtonBorder(SCHEMES.BUTTON_CONTRAST);
+            }
+        }
+    }
+    return BUTTON_CONTRAST_;
+}
+/**
+ * Used for scrollbar buttons.
+ * @see SCHEMES#BUTTON_SCROLLBAR
+ */
+private static Border BUTTON_SCROLLBAR_;
+public static Border BUTTON_SCROLLBAR(){
+    if( BUTTON_SCROLLBAR_ is null ){
+        synchronized( ButtonScheme.classinfo ){
+            if( BUTTON_SCROLLBAR_ is null ){
+                BUTTON_SCROLLBAR_ =  new ButtonBorder(SCHEMES.BUTTON_SCROLLBAR);
+            }
+        }
+    }
+    return BUTTON_SCROLLBAR_;
+}
+/**
+ * Used for toolbar buttons.
+ * @see SCHEMES#TOOLBAR
+ */
+private static Border TOOLBAR_;
+public static Border TOOLBAR(){
+    if( TOOLBAR_ is null ){
+        synchronized( ButtonScheme.classinfo ){
+            if( TOOLBAR_ is null ){
+                TOOLBAR_ = new ButtonBorder(SCHEMES.TOOLBAR);
+            }
+        }
+    }
+    return 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
+     */
+    private static ButtonScheme BUTTON_CONTRAST_;
+    static ButtonScheme BUTTON_CONTRAST(){
+        if( BUTTON_CONTRAST_ is null ){
+            synchronized( ButtonScheme.classinfo ){
+                if( BUTTON_CONTRAST_ is null ){
+                    BUTTON_CONTRAST_ = new ButtonScheme(
+                        [ColorConstants.button, ColorConstants.buttonLightest],
+                        DARKEST_DARKER
+                    );
+                }
+            }
+        }
+        return BUTTON_CONTRAST_;
+    }
+    /**
+     * Regular button scheme
+     */
+    private static ButtonScheme BUTTON_;
+    static ButtonScheme BUTTON(){
+        if( BUTTON_ is null ){
+            synchronized( ButtonScheme.classinfo ){
+                if( BUTTON_ is null ){
+                    BUTTON_ = new ButtonScheme(
+                        [ColorConstants.buttonLightest],
+                        DARKEST_DARKER
+                    );
+                }
+            }
+        }
+        return BUTTON_;
+    }
+    /**
+     * Toolbar button scheme
+     */
+    private static ButtonScheme TOOLBAR_;
+    static ButtonScheme TOOLBAR(){
+        if( TOOLBAR_ is null ){
+            synchronized( ButtonScheme.classinfo ){
+                if( TOOLBAR_ is null ){
+                    TOOLBAR_ = new ButtonScheme(
+                        [ColorConstants.buttonLightest],
+                        [ColorConstants.buttonDarker]
+                    );
+                }
+            }
+        }
+        return TOOLBAR_;
+    }
+    /**
+     * Scrollbar button scheme
+     */
+    private static ButtonScheme BUTTON_SCROLLBAR_;
+    static ButtonScheme BUTTON_SCROLLBAR(){
+        if( BUTTON_SCROLLBAR_ is null ){
+            synchronized( ButtonScheme.classinfo ){
+                if( BUTTON_SCROLLBAR_ is null ){
+                    BUTTON_SCROLLBAR_ = new ButtonScheme(
+                        [ColorConstants.button, ColorConstants.buttonLightest],
+                        DARKEST_DARKER,
+                        [ColorConstants.buttonDarker],
+                        [ColorConstants.buttonDarker]
+                    );
+                }
+            }
+        }
+        return BUTTON_SCROLLBAR_;
+    }
+}
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/ButtonGroup.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,230 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.ButtonGroup;
+
+import java.lang.all;
+import org.eclipse.dwtxhelper.Bean;
+
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ButtonModel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,571 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.ButtonModel;
+
+import java.lang.all;
+import org.eclipse.swt.dwthelper.Runnable;
+import org.eclipse.dwtxhelper.Timer;
+import org.eclipse.dwtxhelper.TimerTask;
+static import org.eclipse.swt.widgets.Display;
+
+import org.eclipse.draw2d.ButtonGroup;
+import org.eclipse.draw2d.ActionListener;
+import org.eclipse.draw2d.ChangeListener;
+import org.eclipse.draw2d.ButtonStateTransitionListener;
+import org.eclipse.draw2d.EventListenerList;
+import org.eclipse.draw2d.ActionEvent;
+import org.eclipse.draw2d.ChangeEvent;
+
+
+//import org.eclipse.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() {
+        org.eclipse.swt.widgets.Display.Display.getDefault().syncExec(dgRunnable( {
+                if (!isEnabled())
+                    timer.cancel();
+                fireActionPerformed();
+        }));
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/ButtonStateTransitionListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ButtonStateTransitionListener;
+
+import java.lang.all;
+
+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/org.eclipse.draw2d/src/org/eclipse/draw2d/ChangeEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ChangeEvent;
+
+import java.lang.all;
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/ChangeListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ChangeListener;
+
+import java.lang.all;
+import org.eclipse.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);
+
+}
+
+// SWT 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/org.eclipse.draw2d/src/org/eclipse/draw2d/CheckBox.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.CheckBox;
+
+import java.lang.all;
+import org.eclipse.swt.dwthelper.ByteArrayInputStream;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.draw2d.Toggle;
+import org.eclipse.draw2d.Label;
+import org.eclipse.draw2d.ChangeListener;
+import org.eclipse.draw2d.ChangeEvent;
+import org.eclipse.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;
+
+private static Image
+    UNCHECKED_,
+    CHECKED_;
+
+package static Image UNCHECKED(){
+    if( UNCHECKED_ is null ){
+        synchronized( CheckBox.classinfo ){
+            if( UNCHECKED_ is null ){
+                UNCHECKED_ = createImage( getImportData!("org.eclipse.draw2d.checkboxenabledoff.gif"));
+            }
+        }
+    }
+    assert( UNCHECKED_ );
+    return UNCHECKED_;
+}
+package static Image CHECKED(){
+    if( CHECKED_ is null ){
+        synchronized( CheckBox.classinfo ){
+            if( CHECKED_ is null ){
+                CHECKED_ = createImage( getImportData!("org.eclipse.draw2d.checkboxenabledon.gif"));
+            }
+        }
+    }
+    assert( CHECKED_ );
+    return CHECKED_;
+}
+
+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/org.eclipse.draw2d/src/org/eclipse/draw2d/ChopboxAnchor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ChopboxAnchor;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Clickable.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,507 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.Clickable;
+
+import java.lang.all;
+
+
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.ActionListener;
+import org.eclipse.draw2d.ActionEvent;
+import org.eclipse.draw2d.ChangeListener;
+import org.eclipse.draw2d.ClickableEventHandler;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.ButtonModel;
+import org.eclipse.draw2d.ChangeEvent;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.ToggleModel;
+import org.eclipse.draw2d.StackLayout;
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ClickableEventHandler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ClickableEventHandler;
+
+import java.lang.all;
+import org.eclipse.draw2d.MouseMotionListener;
+import org.eclipse.draw2d.MouseEvent;
+import org.eclipse.draw2d.KeyEvent;
+import org.eclipse.draw2d.FocusEvent;
+import org.eclipse.draw2d.KeyListener;
+import org.eclipse.draw2d.FocusListener;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.ChangeEvent;
+import org.eclipse.draw2d.ChangeListener;
+import org.eclipse.draw2d.MouseListener;
+import org.eclipse.draw2d.FigureListener;
+import org.eclipse.draw2d.Clickable;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ColorConstants.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,488 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.ColorConstants;
+
+import java.lang.all;
+import org.eclipse.swt.dwthelper.Runnable;
+import tango.core.sync.Mutex;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * A collection of color-related constants.
+ */
+public struct ColorConstants {
+
+    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 SWT#COLOR_WIDGET_HIGHLIGHT_SHADOW
+ */
+private static Color buttonLightest_;
+public static Color buttonLightest(){
+    if( buttonLightest_ is null ){
+        synchronized( Display.classinfo ){
+            if( buttonLightest_ is null ){
+                buttonLightest_ = getColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
+            }
+        }
+    }
+    return buttonLightest_;
+}
+/**
+ * @see SWT#COLOR_WIDGET_BACKGROUND
+ */
+private static Color button_;
+public static Color button(){
+    if( button_ is null ){
+        synchronized( Display.classinfo ){
+            if( button_ is null ){
+                button_ = getColor(SWT.COLOR_WIDGET_BACKGROUND);
+            }
+        }
+    }
+    return button_;
+}
+/**
+ * @see SWT#COLOR_WIDGET_NORMAL_SHADOW
+ */
+private static Color buttonDarker_;
+public static Color buttonDarker(){
+    if( buttonDarker_ is null ){
+        synchronized( Display.classinfo ){
+            if( buttonDarker_ is null ){
+                buttonDarker_ = getColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
+            }
+        }
+    }
+    return buttonDarker_;
+}
+/**
+ * @see SWT#COLOR_WIDGET_DARK_SHADOW
+ */
+private static Color buttonDarkest_;
+public static Color buttonDarkest(){
+    if( buttonDarkest_ is null ){
+        synchronized( Display.classinfo ){
+            if( buttonDarkest_ is null ){
+                buttonDarkest_ = getColor(SWT.COLOR_WIDGET_DARK_SHADOW);
+            }
+        }
+    }
+    return buttonDarkest_;
+}
+
+/**
+ * @see SWT#COLOR_LIST_BACKGROUND
+ */
+private static Color listBackground_;
+public static Color listBackground(){
+    if( listBackground_ is null ){
+        synchronized( Display.classinfo ){
+            if( listBackground_ is null ){
+                listBackground_ = getColor(SWT.COLOR_LIST_BACKGROUND);
+            }
+        }
+    }
+    return listBackground_;
+}
+/**
+ * @see SWT#COLOR_LIST_FOREGROUND
+ */
+private static Color listForeground_;
+public static Color listForeground(){
+    if( listForeground_ is null ){
+        synchronized( Display.classinfo ){
+            if( listForeground_ is null ){
+                listForeground_ = getColor(SWT.COLOR_LIST_FOREGROUND);
+            }
+        }
+    }
+    return listForeground_;
+}
+
+/**
+ * @see SWT#COLOR_WIDGET_BACKGROUND
+ */
+private static Color menuBackground_;
+public static Color menuBackground(){
+    if( menuBackground_ is null ){
+        synchronized( Display.classinfo ){
+            if( menuBackground_ is null ){
+                menuBackground_ = getColor(SWT.COLOR_WIDGET_BACKGROUND);
+            }
+        }
+    }
+    return menuBackground_;
+}
+/**
+ * @see SWT#COLOR_WIDGET_FOREGROUND
+ */
+private static Color menuForeground_;
+public static Color menuForeground(){
+    if( menuForeground_ is null ){
+        synchronized( Display.classinfo ){
+            if( menuForeground_ is null ){
+                menuForeground_ = getColor(SWT.COLOR_WIDGET_FOREGROUND);
+            }
+        }
+    }
+    return menuForeground_;
+}
+/**
+ * @see SWT#COLOR_LIST_SELECTION
+ */
+private static Color menuBackgroundSelected_;
+public static Color menuBackgroundSelected(){
+    if( menuBackgroundSelected_ is null ){
+        synchronized( Display.classinfo ){
+            if( menuBackgroundSelected_ is null ){
+                menuBackgroundSelected_ = getColor(SWT.COLOR_LIST_SELECTION);
+            }
+        }
+    }
+    return menuBackgroundSelected_;
+}
+/**
+ * @see SWT#COLOR_LIST_SELECTION_TEXT
+ */
+private static Color menuForegroundSelected_;
+public static Color menuForegroundSelected(){
+    if( menuForegroundSelected_ is null ){
+        synchronized( Display.classinfo ){
+            if( menuForegroundSelected_ is null ){
+                menuForegroundSelected_ = getColor(SWT.COLOR_LIST_SELECTION_TEXT);
+            }
+        }
+    }
+    return menuForegroundSelected_;
+}
+
+/**
+ * @see SWT#COLOR_TITLE_BACKGROUND
+ */
+private static Color titleBackground_;
+public static Color titleBackground(){
+    if( titleBackground_ is null ){
+        synchronized( Display.classinfo ){
+            if( titleBackground_ is null ){
+                titleBackground_ = getColor(SWT.COLOR_TITLE_BACKGROUND);
+            }
+        }
+    }
+    return titleBackground_;
+}
+/**
+ * @see SWT#COLOR_TITLE_BACKGROUND_GRADIENT
+ */
+private static Color titleGradient_;
+public static Color titleGradient(){
+    if( titleGradient_ is null ){
+        synchronized( Display.classinfo ){
+            if( titleGradient_ is null ){
+                titleGradient_ = getColor(SWT.COLOR_TITLE_BACKGROUND_GRADIENT);
+            }
+        }
+    }
+    return titleGradient_;
+}
+/**
+ * @see SWT#COLOR_TITLE_FOREGROUND
+ */
+private static Color titleForeground_;
+public static Color titleForeground(){
+    if( titleForeground_ is null ){
+        synchronized( Display.classinfo ){
+            if( titleForeground_ is null ){
+                titleForeground_ = getColor(SWT.COLOR_TITLE_FOREGROUND);
+            }
+        }
+    }
+    return titleForeground_;
+}
+/**
+ * @see SWT#COLOR_TITLE_INACTIVE_FOREGROUND
+ */
+private static Color titleInactiveForeground_;
+public static Color titleInactiveForeground(){
+    if( titleInactiveForeground_ is null ){
+        synchronized( Display.classinfo ){
+            if( titleInactiveForeground_ is null ){
+                titleInactiveForeground_ = getColor(SWT.COLOR_TITLE_INACTIVE_FOREGROUND);
+            }
+        }
+    }
+    return titleInactiveForeground_;
+}
+/**
+ * @see SWT#COLOR_TITLE_INACTIVE_BACKGROUND
+ */
+private static Color titleInactiveBackground_;
+public static Color titleInactiveBackground(){
+    if( titleInactiveBackground_ is null ){
+        synchronized( Display.classinfo ){
+            if( titleInactiveBackground_ is null ){
+                titleInactiveBackground_ = getColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND);
+            }
+        }
+    }
+    return titleInactiveBackground_;
+}
+/**
+ * @see SWT#COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT
+ */
+private static Color titleInactiveGradient_;
+public static Color titleInactiveGradient(){
+    if( titleInactiveGradient_ is null ){
+        synchronized( Display.classinfo ){
+            if( titleInactiveGradient_ is null ){
+                titleInactiveGradient_ = getColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT);
+            }
+        }
+    }
+    return titleInactiveGradient_;
+}
+
+/**
+ * @see SWT#COLOR_INFO_FOREGROUND
+ */
+private static Color tooltipForeground_;
+public static Color tooltipForeground(){
+    if( tooltipForeground_ is null ){
+        synchronized( Display.classinfo ){
+            if( tooltipForeground_ is null ){
+                tooltipForeground_ = getColor(SWT.COLOR_INFO_FOREGROUND);
+            }
+        }
+    }
+    return tooltipForeground_;
+}
+/**
+ * @see SWT#COLOR_INFO_BACKGROUND
+ */
+private static Color tooltipBackground_;
+public static Color tooltipBackground(){
+    if( tooltipBackground_ is null ){
+        synchronized( Display.classinfo ){
+            if( tooltipBackground_ is null ){
+                tooltipBackground_ = getColor(SWT.COLOR_INFO_BACKGROUND);
+            }
+        }
+    }
+    return tooltipBackground_;
+}
+
+/*
+ * Misc. colors
+ */
+/** One of the pre-defined colors */
+private static Color white_;
+public static Color white(){
+    if( white_ is null ){
+        synchronized( Display.classinfo ){
+            if( white_ is null ){
+                white_ = new Color(null, 255, 255, 255);
+            }
+        }
+    }
+    return white_;
+}
+/** One of the pre-defined colors */
+private static Color lightGray_;
+public static Color lightGray(){
+    if( lightGray_ is null ){
+        synchronized( Display.classinfo ){
+            if( lightGray_ is null ){
+                lightGray_ = new Color(null, 192, 192, 192);
+            }
+        }
+    }
+    return lightGray_;
+}
+/** One of the pre-defined colors */
+private static Color gray_;
+public static Color gray(){
+    if( gray_ is null ){
+        synchronized( Display.classinfo ){
+            if( gray_ is null ){
+                gray_ = new Color(null, 128, 128, 128);
+            }
+        }
+    }
+    return gray_;
+}
+/** One of the pre-defined colors */
+private static Color darkGray_;
+public static Color darkGray(){
+    if( darkGray_ is null ){
+        synchronized( Display.classinfo ){
+            if( darkGray_ is null ){
+                darkGray_ = new Color(null,  64,  64,  64);
+            }
+        }
+    }
+    return darkGray_;
+}
+/** One of the pre-defined colors */
+private static Color black_;
+public static Color black(){
+    if( black_ is null ){
+        synchronized( Display.classinfo ){
+            if( black_ is null ){
+                black_ = new Color(null,   0,   0,   0);
+            }
+        }
+    }
+    return black_;
+}
+/** One of the pre-defined colors */
+private static Color red_;
+public static Color red(){
+    if( red_ is null ){
+        synchronized( Display.classinfo ){
+            if( red_ is null ){
+                red_ = new Color(null, 255,   0,   0);
+            }
+        }
+    }
+    return red_;
+}
+/** One of the pre-defined colors */
+private static Color orange_;
+public static Color orange(){
+    if( orange_ is null ){
+        synchronized( Display.classinfo ){
+            if( orange_ is null ){
+                orange_ = new Color(null, 255, 196,   0);
+            }
+        }
+    }
+    return orange_;
+}
+/** One of the pre-defined colors */
+private static Color yellow_;
+public static Color yellow(){
+    if( yellow_ is null ){
+        synchronized( Display.classinfo ){
+            if( yellow_ is null ){
+                yellow_ = new Color(null, 255, 255,   0);
+            }
+        }
+    }
+    return yellow_;
+}
+/** One of the pre-defined colors */
+private static Color green_;
+public static Color green(){
+    if( green_ is null ){
+        synchronized( Display.classinfo ){
+            if( green_ is null ){
+                green_ = new Color(null,   0, 255,   0);
+            }
+        }
+    }
+    return green_;
+}
+/** One of the pre-defined colors */
+private static Color lightGreen_;
+public static Color lightGreen(){
+    if( lightGreen_ is null ){
+        synchronized( Display.classinfo ){
+            if( lightGreen_ is null ){
+                lightGreen_ = new Color(null,  96, 255,  96);
+            }
+        }
+    }
+    return lightGreen_;
+}
+/** One of the pre-defined colors */
+private static Color darkGreen_;
+public static Color darkGreen(){
+    if( darkGreen_ is null ){
+        synchronized( Display.classinfo ){
+            if( darkGreen_ is null ){
+                darkGreen_ = new Color(null,   0, 127,   0);
+            }
+        }
+    }
+    return darkGreen_;
+}
+/** One of the pre-defined colors */
+private static Color cyan_;
+public static Color cyan(){
+    if( cyan_ is null ){
+        synchronized( Display.classinfo ){
+            if( cyan_ is null ){
+                cyan_ = new Color(null,   0, 255, 255);
+            }
+        }
+    }
+    return cyan_;
+}
+/** One of the pre-defined colors */
+private static Color lightBlue_;
+public static Color lightBlue(){
+    if( lightBlue_ is null ){
+        synchronized( Display.classinfo ){
+            if( lightBlue_ is null ){
+                lightBlue_ = new Color(null, 127, 127, 255);
+            }
+        }
+    }
+    return lightBlue_;
+}
+/** One of the pre-defined colors */
+private static Color blue_;
+public static Color blue(){
+    if( blue_ is null ){
+        synchronized( Display.classinfo ){
+            if( blue_ is null ){
+                blue_ = new Color(null,   0,   0, 255);
+            }
+        }
+    }
+    return blue_;
+}
+/** One of the pre-defined colors */
+private static Color darkBlue_;
+public static Color darkBlue(){
+    if( darkBlue_ is null ){
+        synchronized( Display.classinfo ){
+            if( darkBlue_ is null ){
+                darkBlue_ = new Color(null,   0,   0, 127);
+            }
+        }
+    }
+    return darkBlue_;
+}
+
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/CompoundBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.CompoundBorder;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+
+import org.eclipse.draw2d.AbstractBorder;
+import org.eclipse.draw2d.Border;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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 org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Connection.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.Connection;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.ConnectionRouter;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ConnectionAnchor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ConnectionAnchor;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.AnchorListener;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ConnectionAnchorBase.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ConnectionAnchorBase;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.ConnectionAnchor;
+import org.eclipse.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 org.eclipse.draw2d.ConnectionAnchor#addAnchorListener(AnchorListener)
+ */
+public void addAnchorListener(AnchorListener listener) {
+    listeners.add(cast(Object)listener);
+}
+
+/**
+ * @see org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ConnectionEndpointLocator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ConnectionEndpointLocator;
+
+import java.lang.all;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.geometry.Transposer;
+import org.eclipse.draw2d.Locator;
+import org.eclipse.draw2d.Connection;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ConnectionLayer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ConnectionLayer;
+
+import java.lang.all;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.draw2d.FreeformLayer;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.ConnectionRouter;
+import org.eclipse.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 = SWT.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 SWT.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 SWT#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/org.eclipse.draw2d/src/org/eclipse/draw2d/ConnectionLocator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ConnectionLocator;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.AbstractLocator;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ConnectionRouter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ConnectionRouter;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.AnchorListener;
+import org.eclipse.draw2d.AbstractRouter;
+import org.eclipse.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.
+ */
+private static NullConnectionRouter ConnectionRouter_NULL_;
+public static NullConnectionRouter ConnectionRouter_NULL(){
+    if( ConnectionRouter_NULL_ is null ){
+        synchronized( NullConnectionRouter.classinfo ){
+            if( ConnectionRouter_NULL_ is null ){
+                ConnectionRouter_NULL_ = new NullConnectionRouter();
+            }
+        }
+    }
+    return ConnectionRouter_NULL_;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/CoordinateListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.CoordinateListener;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Cursors.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,314 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.Cursors;
+
+import java.lang.all;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.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 SWT#CURSOR_ARROW
+ */
+private static Cursor ARROW_;
+public static Cursor ARROW(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return ARROW_;
+}
+
+/**
+ * @see SWT#CURSOR_SIZEN
+ */
+private static Cursor SIZEN_;
+public static Cursor SIZEN(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return SIZEN_;
+}
+
+/**
+ * @see SWT#CURSOR_SIZENE
+ */
+private static Cursor SIZENE_;
+public static Cursor SIZENE(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return SIZENE_;
+}
+
+/**
+ * @see SWT#CURSOR_SIZEE
+ */
+private static Cursor SIZEE_;
+public static Cursor SIZEE(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return SIZEE_;
+}
+
+/**
+ * @see SWT#CURSOR_SIZESE
+ */
+private static Cursor SIZESE_;
+public static Cursor SIZESE(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return SIZESE_;
+}
+
+/**
+ * @see SWT#CURSOR_SIZES
+ */
+private static Cursor SIZES_;
+public static Cursor SIZES(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return SIZES_;
+}
+
+/**
+ * @see SWT#CURSOR_SIZESW
+ */
+private static Cursor SIZESW_;
+public static Cursor SIZESW(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return SIZESW_;
+}
+
+/**
+ * @see SWT#CURSOR_SIZEW
+ */
+private static Cursor SIZEW_;
+public static Cursor SIZEW(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return SIZEW_;
+}
+
+/**
+ * @see SWT#CURSOR_SIZENW
+ */
+private static Cursor SIZENW_;
+public static Cursor SIZENW(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return SIZENW_;
+}
+
+/**
+ * @see SWT#CURSOR_APPSTARTING
+ */
+private static Cursor APPSTARTING_;
+public static Cursor APPSTARTING(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return APPSTARTING_;
+}
+
+/**
+ * @see SWT#CURSOR_CROSS
+ */
+private static Cursor CROSS_;
+public static Cursor CROSS(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return CROSS_;
+}
+
+/**
+ * @see SWT#CURSOR_HAND
+ */
+private static Cursor HAND_;
+public static Cursor HAND(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return HAND_;
+}
+
+/**
+ * @see SWT#CURSOR_HELP
+ */
+private static Cursor HELP_;
+public static Cursor HELP(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return HELP_;
+}
+
+/**
+ * @see SWT#CURSOR_IBEAM
+ */
+private static Cursor IBEAM_;
+public static Cursor IBEAM(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return IBEAM_;
+}
+
+/**
+ * @see SWT#CURSOR_NO
+ */
+private static Cursor NO_;
+public static Cursor NO(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return NO_;
+}
+
+/**
+ * @see SWT#CURSOR_SIZEALL
+ */
+private static Cursor SIZEALL_;
+public static Cursor SIZEALL(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return SIZEALL_;
+}
+
+/**
+ * @see SWT#CURSOR_SIZENESW
+ */
+private static Cursor SIZENESW_;
+public static Cursor SIZENESW(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return SIZENESW_;
+}
+
+/**
+ * @see SWT#CURSOR_SIZENWSE
+ */
+private static Cursor SIZENWSE_;
+public static Cursor SIZENWSE(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return SIZENWSE_;
+}
+
+/**
+ * @see SWT#CURSOR_SIZEWE
+ */
+private static Cursor SIZEWE_;
+public static Cursor SIZEWE(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return SIZEWE_;
+}
+
+/**
+ * @see SWT#CURSOR_SIZENS
+ */
+private static Cursor SIZENS_;
+public static Cursor SIZENS(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return SIZENS_;
+}
+
+/**
+ * @see SWT#CURSOR_UPARROW
+ */
+private static Cursor UPARROW_;
+public static Cursor UPARROW(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return UPARROW_;
+}
+
+/**
+ * @see SWT#CURSOR_WAIT
+ */
+private static Cursor WAIT_;
+public static Cursor WAIT(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return WAIT_;
+}
+
+private static bool initStaticCtor_done = false;
+private static void initStaticCtor() {
+    synchronized(Cursor.classinfo){
+        if(!initStaticCtor_done){
+            ARROW_        = new Cursor(null, SWT.CURSOR_ARROW);
+            SIZEN_        = new Cursor(null, SWT.CURSOR_SIZEN);
+            SIZENE_       = new Cursor(null, SWT.CURSOR_SIZENE);
+            SIZEE_        = new Cursor(null, SWT.CURSOR_SIZEE);
+            SIZESE_       = new Cursor(null, SWT.CURSOR_SIZESE);
+            SIZES_        = new Cursor(null, SWT.CURSOR_SIZES);
+            SIZESW_       = new Cursor(null, SWT.CURSOR_SIZESW);
+            SIZEW_        = new Cursor(null, SWT.CURSOR_SIZEW);
+            SIZENW_       = new Cursor(null, SWT.CURSOR_SIZENW);
+            SIZENS_       = new Cursor(null, SWT.CURSOR_SIZENS);
+            SIZEWE_       = new Cursor(null, SWT.CURSOR_SIZEWE);
+            APPSTARTING_  = new Cursor(null, SWT.CURSOR_APPSTARTING);
+            CROSS_        = new Cursor(null, SWT.CURSOR_CROSS);
+            HAND_         = new Cursor(null, SWT.CURSOR_HAND);
+            HELP_         = new Cursor(null, SWT.CURSOR_HELP);
+            IBEAM_        = new Cursor(null, SWT.CURSOR_IBEAM);
+            NO_           = new Cursor(null, SWT.CURSOR_NO);
+            SIZEALL_      = new Cursor(null, SWT.CURSOR_SIZEALL);
+            SIZENESW_     = new Cursor(null, SWT.CURSOR_SIZENESW);
+            SIZENWSE_     = new Cursor(null, SWT.CURSOR_SIZENWSE);
+            UPARROW_      = new Cursor(null, SWT.CURSOR_UPARROW);
+            WAIT_         = new Cursor(null, SWT.CURSOR_WAIT);
+            initStaticCtor_done = true;
+        }
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/DefaultRangeModel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.DefaultRangeModel;
+
+import java.lang.all;
+import org.eclipse.dwtxhelper.Bean;
+import tango.text.convert.Format;
+
+import org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/DeferredUpdateManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,338 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.DeferredUpdateManager;
+
+import java.lang.all;
+import org.eclipse.swt.dwthelper.Runnable;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.UpdateManager;
+import org.eclipse.draw2d.GraphicsSource;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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 SWTException(SWT.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/org.eclipse.draw2d/src/org/eclipse/draw2d/DelegatingLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.DelegatingLayout;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.AbstractLayout;
+import org.eclipse.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 org.eclipse.draw2d.LayoutManager#getConstraint(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Ellipse.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.Ellipse;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Shape;
+import org.eclipse.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 org.eclipse.draw2d.Shape#fillShape(org.eclipse.draw2d.Graphics)
+ */
+protected void fillShape(Graphics graphics) {
+    graphics.fillOval(getBounds());
+}
+
+/**
+ * Outlines the ellipse.
+ * @see org.eclipse.draw2d.Shape#outlineShape(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/EllipseAnchor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.EllipseAnchor;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.AbstractConnectionAnchor;
+import org.eclipse.draw2d.IFigure;
+
+/**
+ * Similar to a {@link org.eclipse.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 org.eclipse.draw2d.AbstractConnectionAnchor#AbstractConnectionAnchor()
+ */
+public this() { }
+
+/**
+ * @see org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/EventDispatcher.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.EventDispatcher;
+
+import java.lang.all;
+
+import org.eclipse.swt.accessibility.AccessibleControlEvent;
+import org.eclipse.swt.accessibility.AccessibleControlListener;
+import org.eclipse.swt.accessibility.AccessibleListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+
+import org.eclipse.draw2d.IFigure;
+
+/**
+ * Listens to various SWT 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 SWT 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/org.eclipse.draw2d/src/org/eclipse/draw2d/EventListenerList.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.EventListenerList;
+
+import java.lang.all;
+
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/ExclusionSearch.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.ExclusionSearch;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.TreeSearch;
+import org.eclipse.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 org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/FanRouter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.FanRouter;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.Ray;
+import org.eclipse.draw2d.AutomaticRouter;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Figure.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1941 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.Figure;
+
+import java.lang.all;
+import org.eclipse.dwtxhelper.Bean;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.geometry.Translatable;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.LayoutManager;
+import org.eclipse.draw2d.EventListenerList;
+import org.eclipse.draw2d.Border;
+import org.eclipse.draw2d.AncestorHelper;
+import org.eclipse.draw2d.CoordinateListener;
+import org.eclipse.draw2d.FigureListener;
+import org.eclipse.draw2d.FocusListener;
+import org.eclipse.draw2d.KeyListener;
+import org.eclipse.draw2d.MouseListener;
+import org.eclipse.draw2d.MouseMotionListener;
+import org.eclipse.draw2d.TreeSearch;
+import org.eclipse.draw2d.LayoutListener;
+import org.eclipse.draw2d.UpdateManager;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.AncestorListener;
+import org.eclipse.draw2d.MouseEvent;
+import org.eclipse.draw2d.EventDispatcher;
+import org.eclipse.draw2d.FocusEvent;
+import org.eclipse.draw2d.KeyEvent;
+import org.eclipse.draw2d.AncestorHelper;
+import org.eclipse.draw2d.ExclusionSearch;
+import org.eclipse.draw2d.AbstractBackground;
+import org.eclipse.draw2d.Orientable;
+import org.eclipse.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);
+    assert(children);
+    for (int i = 0; i < children.size(); i++){
+        assert(children.get(i));
+        assert(cast(IFigure)children.get(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 org.eclipse.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.
+     */
+    private static IdentitySearch INSTANCE_;
+    public static IdentitySearch INSTANCE(){
+        if( INSTANCE_ is null ){
+            synchronized( IdentitySearch.classinfo ){
+                if( INSTANCE_ is null ){
+                    INSTANCE_ = new IdentitySearch();
+                }
+            }
+        }
+        return INSTANCE_;
+    }
+
+    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.
+ */
+private static UpdateManager NO_MANAGER_;
+protected static UpdateManager NO_MANAGER(){
+    if( NO_MANAGER_ is null ){
+        synchronized( Figure.classinfo ){
+            if( NO_MANAGER_ is null ){
+                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) { }
+                };
+            }
+        }
+    }
+    return NO_MANAGER_;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/FigureCanvas.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,559 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.FigureCanvas;
+
+import java.lang.all;
+import org.eclipse.dwtxhelper.Bean;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Viewport;
+import org.eclipse.draw2d.LightweightSystem;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Border;
+import org.eclipse.draw2d.RangeModel;
+static import org.eclipse.swt.graphics.Point;
+static import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.draw2d.UpdateListener;
+import org.eclipse.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 =
+        SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT
+        | SWT.V_SCROLL | SWT.H_SCROLL
+        | SWT.NO_BACKGROUND
+        | SWT.NO_REDRAW_RESIZE
+        | SWT.DOUBLE_BUFFERED
+        | SWT.BORDER;
+
+/**
+ * The default styles are mixed in when certain constructors are used. This
+ * constant is a bitwise OR of the following SWT style constants:
+ * <UL>
+ *   <LI>{@link SWT#NO_REDRAW_RESIZE}</LI>
+ *   <LI>{@link SWT#NO_BACKGROUND}</LI>
+ *   <LI>{@link SWT#V_SCROLL}</LI>
+ *   <LI>{@link SWT#H_SCROLL}</LI>
+ * </UL>
+ */
+static const int DEFAULT_STYLES =
+        SWT.NO_REDRAW_RESIZE
+        | SWT.NO_BACKGROUND
+        | SWT.V_SCROLL
+        | SWT.H_SCROLL;
+
+private static const int REQUIRED_STYLES =
+        SWT.NO_REDRAW_RESIZE
+        | SWT.V_SCROLL
+        | SWT.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, SWT.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, SWT.DOUBLE_BUFFERED, lws);
+}
+
+/**
+ * Constructor taking a lightweight system and SWT 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 org.eclipse.swt.widgets.Composite#computeSize(int, int, bool)
+ */
+public org.eclipse.swt.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 org.eclipse.swt.graphics.Point.Point(size.width, size.height);
+}
+
+/**
+ * @return the contents of the {@link Viewport}.
+ */
+public IFigure getContents() {
+    return getViewport().getContents();
+}
+
+/**
+ * @see org.eclipse.swt.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, org.eclipse.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++) {
+        org.eclipse.swt.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;
+        org.eclipse.swt.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++) {
+        org.eclipse.swt.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;
+        org.eclipse.swt.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 org.eclipse.swt.widgets.Control#setFont(org.eclipse.swt.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/org.eclipse.draw2d/src/org/eclipse/draw2d/FigureListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.FigureListener;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/FigureUtilities.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,405 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.FigureUtilities;
+
+import java.lang.all;
+
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Shape;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Graphics;
+static import org.eclipse.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_;
+private static Color ghostFillColor(){
+    if( ghostFillColor_ is null ){
+        synchronized( FigureUtilities.classinfo ){
+            if( ghostFillColor_ is null ){
+                ghostFillColor_ = new Color(null, 31, 31, 31);
+            }
+        }
+    }
+    return ghostFillColor_;
+}
+
+/**
+ * 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 org.eclipse.swt.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 org.eclipse.swt.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) {
+    org.eclipse.swt.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,
+        org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/FlowLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,467 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.FlowLayout;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.geometry.Transposer;
+import org.eclipse.draw2d.AbstractHintLayout;
+import org.eclipse.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 org.eclipse.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 org.eclipse.draw2d.AbstractHintLayout#isSensitiveHorizontally(IFigure)
+ */
+protected bool isSensitiveHorizontally(IFigure parent) {
+    return isHorizontal();
+}
+
+/**
+ * @see org.eclipse.draw2d.AbstractHintLayout#isSensitiveVertically(IFigure)
+ */
+protected bool isSensitiveVertically(IFigure parent) {
+    return !isHorizontal();
+}
+
+/**
+ * @see org.eclipse.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;
+        default:
+    }
+
+    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;
+                default:
+            }
+            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/org.eclipse.draw2d/src/org/eclipse/draw2d/FocusBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.FocusBorder;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.AbstractBorder;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.ColorConstants;
+
+/**
+ * A Border that looks like the system's focus rectangle.
+ */
+public class FocusBorder
+    : AbstractBorder
+{
+
+/**
+ * Constructs a new FocusBorder.
+ */
+public this() { }
+
+/**
+ * @see org.eclipse.draw2d.Border#getInsets(IFigure)
+ */
+public Insets getInsets(IFigure figure) {
+    return new Insets(1);
+}
+
+/**
+ * @see org.eclipse.draw2d.Border#isOpaque()
+ */
+public bool isOpaque() {
+    return true;
+}
+
+/**
+ * Paints a focus rectangle.
+ * @see org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/FocusEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.FocusEvent;
+
+import java.lang.all;
+import org.eclipse.draw2d.IFigure;
+
+/**
+ * An event that occurs when an {@link org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/FocusListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.FocusListener;
+
+import java.lang.all;
+import org.eclipse.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 org.eclipse.draw2d.FocusListener#focusGained(FocusEvent)
+     */
+    public void focusGained(FocusEvent fe) { }
+    /**
+     * @see org.eclipse.draw2d.FocusListener#focusLost(FocusEvent)
+     */
+    public void focusLost(FocusEvent fe) { }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/FocusTraverseManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.FocusTraverseManager;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/FrameBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.FrameBorder;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.draw2d.CompoundBorder;
+import org.eclipse.draw2d.LabeledBorder;
+import org.eclipse.draw2d.SchemeBorder;
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.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).
+ */
+private static SchemeBorder.Scheme SCHEME_FRAME_;
+protected static SchemeBorder.Scheme SCHEME_FRAME(){
+    if( SCHEME_FRAME_ is null ){
+        synchronized( FrameBorder.classinfo ){
+            if( SCHEME_FRAME_ is null ){
+                SCHEME_FRAME_ = new SchemeBorder.Scheme(
+                    [
+                        ColorConstants.button,
+                        ColorConstants.buttonLightest,
+                        ColorConstants.button
+                    ],
+                    [
+                        ColorConstants.buttonDarkest,
+                        ColorConstants.buttonDarker,
+                        ColorConstants.button
+                    ]
+                );
+            }
+        }
+    }
+    return SCHEME_FRAME_;
+}
+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/org.eclipse.draw2d/src/org/eclipse/draw2d/FreeformFigure.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.FreeformFigure;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.FreeformListener;
+
+
+/**
+ * A figure that allows its children to extend into negative coordinates.  This figure
+ * must be placed in a {@link org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/FreeformHelper.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.FreeformHelper;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.FreeformListener;
+import org.eclipse.draw2d.FreeformFigure;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/FreeformLayer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.FreeformLayer;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+
+import org.eclipse.draw2d.Layer;
+import org.eclipse.draw2d.FreeformFigure;
+import org.eclipse.draw2d.FreeformListener;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/FreeformLayeredPane.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.FreeformLayeredPane;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.LayeredPane;
+import org.eclipse.draw2d.FreeformFigure;
+import org.eclipse.draw2d.FreeformHelper;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.FreeformListener;
+
+/**
+ * A LayeredPane that contains {@link org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/FreeformLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.FreeformLayout;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.XYLayout;
+import org.eclipse.draw2d.IFigure;
+
+/**
+ * A layout for {@link org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/FreeformListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.FreeformListener;
+
+import java.lang.all;
+
+/**
+ * Listens for extent changes in {@link org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/FreeformViewport.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.FreeformViewport;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Viewport;
+import org.eclipse.draw2d.ViewportLayout;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.FreeformFigure;
+
+/**
+ * A viewport for {@link org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Graphics.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.Graphics;
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Path;
+import org.eclipse.swt.graphics.Pattern;
+import org.eclipse.swt.graphics.TextLayout;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.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 SWT#LINE_CUSTOM
+ */
+public static final int LINE_CUSTOM = SWT.LINE_CUSTOM;
+
+/**
+ * @see SWT#LINE_DASH
+ */
+public static final int LINE_DASH = SWT.LINE_DASH;
+
+/**
+ * @see SWT#LINE_DASHDOT
+ */
+public static final int LINE_DASHDOT = SWT.LINE_DASHDOT;
+
+/**
+ * @see SWT#LINE_DASHDOTDOT
+ */
+public static final int LINE_DASHDOTDOT = SWT.LINE_DASHDOTDOT;
+
+/**
+ * @see SWT#LINE_DOT
+ */
+public static final int LINE_DOT = SWT.LINE_DOT;
+
+/**
+ * @see SWT#LINE_SOLID
+ */
+public static final int LINE_SOLID = SWT.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>SWT.DEFAULT</code>,
+ * <code>SWT.OFF</code> or <code>SWT.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 SWT.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>SWT.FILL_EVEN_ODD</code> or
+ * <code>SWT.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>SWT.DEFAULT</code>,
+ * <code>SWT.NONE</code>, <code>SWT.LOW</code> or <code>SWT.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>SWT.CAP_FLAT</code>, <code>SWT.CAP_ROUND</code>, or <code>SWT.CAP_SQUARE</code>.
+ *
+ * @return the cap style used for drawing lines
+ * @since 3.1
+ */
+public int getLineCap() {
+    subclassFunctionMission();
+    return SWT.CAP_FLAT;
+}
+
+
+/**
+ * Returns the line join style, which will be one of the constants
+ * <code>SWT.JOIN_MITER</code>, <code>SWT.JOIN_ROUND</code>, or
+ * <code>SWT.JOIN_BEVEL</code>.
+ *
+ * @since 3.1
+ * @return the join style used for drawing lines
+ */
+public int getLineJoin() {
+    subclassFunctionMission();
+    return SWT.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>SWT.DEFAULT</code>, <code>SWT.OFF</code> or <code>SWT.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 SWT.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>SWT.DEFAULT</code>, <code>SWT.OFF</code> or <code>SWT.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>SWT.FILL_EVEN_ODD</code> or <code>SWT.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>SWT.DEFAULT</code>, <code>SWT.NONE</code>,  <code>SWT.LOW</code> or
+ * <code>SWT.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>SWT.CAP_FLAT</code>, <code>SWT.CAP_ROUND</code>, or <code>SWT.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>SWT.JOIN_MITER</code>, <code>SWT.JOIN_ROUND</code>, or
+ * <code>SWT.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>SWT.LINE_SOLID</code>, <code>SWT.LINE_DASH</code>, <code>SWT.LINE_DOT</code>,
+ * <code>SWT.LINE_DASHDOT</code> or <code>SWT.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>SWT.DEFAULT</code>, <code>SWT.OFF</code> or <code>SWT.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 SWT. 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/org.eclipse.draw2d/src/org/eclipse/draw2d/GraphicsSource.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.GraphicsSource;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Graphics;
+
+/**
+ * Provides a {@link org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/GridData.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.GridData;
+
+import java.lang.all;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.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:
+     *
+     * SWT.BEGINNING (or SWT.TOP): Position the figure at the top of the cell
+     * SWT.CENTER: Position the figure in the vertical center of the cell
+     * SWT.END (or SWT.BOTTOM): Position the figure at the bottom of the cell
+     * SWT.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:
+     *
+     * SWT.BEGINNING (or SWT.LEFT): Position the figure at the left of the cell
+     * SWT.CENTER: Position the figure in the horizontal center of the cell
+     * SWT.END (or SWT.RIGHT): Position the figure at the right of the cell
+     * SWT.FILL: Resize the figure to fill the cell horizontally
+     */
+    public int horizontalAlignment = BEGINNING;
+
+    /**
+     * widthHint specifies a minimum width for the column. A value of
+     * SWT.DEFAULT indicates that no minimum width is specified.
+     *
+     * The default value is SWT.DEFAULT.
+     */
+    public int widthHint = SWT.DEFAULT;
+
+    /**
+     * heightHint specifies a minimum height for the row. A value of SWT.DEFAULT
+     * indicates that no minimum height is specified.
+     *
+     * The default value is SWT.DEFAULT.
+     */
+    public int heightHint = SWT.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 SWT.BEGINNING,
+     * SWT.TOP or SWT.LEFT instead.
+     */
+    public static const int BEGINNING = SWT.BEGINNING;
+
+    /**
+     * Value for horizontalAlignment or verticalAlignment. Position the figure
+     * in the vertical or horizontal center of the cell Not recommended. Use
+     * SWT.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 SWT.END,
+     * SWT.BOTTOM or SWT.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 SWT.FILL
+     * instead.
+     */
+    public static const int FILL = SWT.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, SWT.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, SWT.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, SWT.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, SWT.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(SWT.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(SWT.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(SWT.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(SWT.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, SWT.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(SWT.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(SWT.FILL, SWT.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 SWT.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 SWT.FILL:
+            hAlign = "SWT.FILL"; //$NON-NLS-1$
+            break;
+        case SWT.BEGINNING:
+            hAlign = "SWT.BEGINNING"; //$NON-NLS-1$
+            break;
+        case SWT.LEFT:
+            hAlign = "SWT.LEFT"; //$NON-NLS-1$
+            break;
+        case SWT.END:
+            hAlign = "SWT.END"; //$NON-NLS-1$
+            break;
+        case END:
+            hAlign = "GridData.END"; //$NON-NLS-1$
+            break;
+        case SWT.RIGHT:
+            hAlign = "SWT.RIGHT"; //$NON-NLS-1$
+            break;
+        case SWT.CENTER:
+            hAlign = "SWT.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 SWT.FILL:
+            vAlign = "SWT.FILL"; //$NON-NLS-1$
+            break;
+        case SWT.BEGINNING:
+            vAlign = "SWT.BEGINNING"; //$NON-NLS-1$
+            break;
+        case SWT.TOP:
+            vAlign = "SWT.TOP"; //$NON-NLS-1$
+            break;
+        case SWT.END:
+            vAlign = "SWT.END"; //$NON-NLS-1$
+            break;
+        case END:
+            vAlign = "GridData.END"; //$NON-NLS-1$
+            break;
+        case SWT.BOTTOM:
+            vAlign = "SWT.BOTTOM"; //$NON-NLS-1$
+            break;
+        case SWT.CENTER:
+            vAlign = "SWT.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 SWT.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 SWT.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/org.eclipse.draw2d/src/org/eclipse/draw2d/GridLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,800 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.GridLayout;
+
+import java.lang.all;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.AbstractLayout;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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 org.eclipse.draw2d.AbstractLayout#calculatePreferredSize(org.eclipse.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 SWT.DEFAULT)
+            size.width = wHint;
+        if (hHint !is SWT.DEFAULT)
+            size.height = hHint;
+
+        return size;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.draw2d.LayoutManager#layout(org.eclipse.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 SWT.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 SWT.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 SWT.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 SWT.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 SWT.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 SWT.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 SWT.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 SWT.FILL)
+                                    || (data.cacheWidth > currentWidth)) {
+                                int trim = 0;
+                                /*
+                                 * // *Note*: Left this in place from SWT //
+                                 * 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 = SWT.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 SWT.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 SWT.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 SWT.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 SWT.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 SWT.CENTER:
+                        case GridData.CENTER:
+                            childX = gridX
+                                    + Math.max(0, (cellWidth - childWidth) / 2);
+                            break;
+                        case SWT.RIGHT:
+                        case SWT.END:
+                        case GridData.END:
+                            childX = gridX
+                                    + Math.max(0, cellWidth - childWidth);
+                            break;
+                        case SWT.FILL:
+                            childWidth = cellWidth - data.horizontalIndent;
+                            break;
+                        default:
+                        }
+                        cellHeight += verticalSpacing * (vSpan - 1);
+                        int childY = gridY; // + data.verticalIndent;
+                        int childHeight = Math
+                                .min(data.cacheHeight, cellHeight);
+                        switch (data.verticalAlignment) {
+                        case SWT.CENTER:
+                        case GridData.CENTER:
+                            childY = gridY
+                                    + Math.max(0,
+                                            (cellHeight - childHeight) / 2);
+                            break;
+                        case SWT.BOTTOM:
+                        case SWT.END:
+                        case GridData.END:
+                            childY = gridY
+                                    + Math.max(0, cellHeight - childHeight);
+                            break;
+                        case SWT.FILL:
+                            childHeight = cellHeight; // -
+                            // data.verticalIndent;
+                            break;
+                        default:
+                        }
+                        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 org.eclipse.draw2d.LayoutManager#getConstraint(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/GroupBoxBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.GroupBoxBorder;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.AbstractLabeledBorder;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/IFigure.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,929 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.IFigure;
+
+import java.lang.all;
+
+import org.eclipse.dwtxhelper.Bean;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.geometry.Translatable;
+
+import org.eclipse.draw2d.AncestorListener;
+import org.eclipse.draw2d.CoordinateListener;
+import org.eclipse.draw2d.FigureListener;
+import org.eclipse.draw2d.FocusListener;
+import org.eclipse.draw2d.KeyListener;
+import org.eclipse.draw2d.KeyEvent;
+import org.eclipse.draw2d.LayoutListener;
+import org.eclipse.draw2d.MouseListener;
+import org.eclipse.draw2d.MouseEvent;
+import org.eclipse.draw2d.MouseMotionListener;
+import org.eclipse.draw2d.TreeSearch;
+import org.eclipse.draw2d.Border;
+import org.eclipse.draw2d.LayoutManager;
+import org.eclipse.draw2d.FocusEvent;
+import org.eclipse.draw2d.UpdateManager;
+import org.eclipse.draw2d.EventDispatcher;
+import org.eclipse.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;
+    }
+}
+
+private static bool initStaticCtor_done = false;
+private static void initStaticCtor (){
+    // synchronizing on IFigure gave a segmentation fault
+    synchronized( Object.classinfo ){
+        if( !initStaticCtor_done ){
+            IFigure_MAX_DIMENSION_ = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+            IFigure_MIN_DIMENSION_ = new Dimension(5, 5);
+            IFigure_NO_INSETS_ = new NoInsets();
+            initStaticCtor_done = true;
+        }
+    }
+}
+/**
+ * The maximum allowable dimension. ({@link Integer#MAX_VALUE},{@link Integer#MAX_VALUE})
+ */
+private static Dimension IFigure_MAX_DIMENSION_;
+static Dimension IFigure_MAX_DIMENSION(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return IFigure_MAX_DIMENSION_;
+}
+
+/**
+ * The minimum allowable dimension. (5,5)
+ */
+private static Dimension IFigure_MIN_DIMENSION_;
+static Dimension IFigure_MIN_DIMENSION(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return IFigure_MIN_DIMENSION_;
+}
+
+/**
+ * Empty Insets.
+ */
+private static Insets IFigure_NO_INSETS_;
+static Insets IFigure_NO_INSETS(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    return 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 org.eclipse.swt.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 org.eclipse.swt.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ImageFigure.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ImageFigure;
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.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 org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ImageUtilities.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ImageUtilities;
+
+import java.lang.all;
+
+
+static import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.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)
+        SWT.error(SWT.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)
+        SWT.error(SWT.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) {
+    org.eclipse.swt.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/org.eclipse.draw2d/src/org/eclipse/draw2d/InputEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.InputEvent;
+
+import java.lang.all;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.draw2d.EventDispatcher;
+import org.eclipse.draw2d.IFigure;
+
+/**
+ * The base class for Draw2d events.
+ */
+public abstract class InputEvent
+    : /+java.util.+/EventObject
+{
+
+private int state;
+
+private bool consumed = false;
+
+/** @see SWT#ALT */
+public static const int ALT = SWT.ALT;
+/** @see SWT#CONTROL */
+public static const int CONTROL = SWT.CONTROL;
+/** @see SWT#SHIFT */
+public static const int SHIFT = SWT.SHIFT;
+/** @see SWT#BUTTON1 */
+public static const int BUTTON1 = SWT.BUTTON1;
+/** @see SWT#BUTTON2 */
+public static const int BUTTON2 = SWT.BUTTON2;
+/** @see SWT#BUTTON3 */
+public static const int BUTTON3 = SWT.BUTTON3;
+/** @see SWT#BUTTON4 */
+public static const int BUTTON4 = SWT.BUTTON4;
+/** @see SWT#BUTTON5 */
+public static const int BUTTON5 = SWT.BUTTON5;
+/** A bitwise OR'ing of {@link #BUTTON1}, {@link #BUTTON2}, {@link #BUTTON3},
+ * {@link #BUTTON4} and {@link #BUTTON5} */
+public static const int ANY_BUTTON = SWT.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/org.eclipse.draw2d/src/org/eclipse/draw2d/KeyEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.KeyEvent;
+
+import java.lang.all;
+import org.eclipse.draw2d.InputEvent;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.EventDispatcher;
+static import org.eclipse.swt.events.KeyEvent;
+
+/**
+ * An event caused by the user interacting with the keyboard.
+ */
+public class KeyEvent
+    : InputEvent
+{
+
+/**
+ * The character that was pressed.
+ * @see org.eclipse.swt.events.KeyEvent#character
+ */
+public char character;
+
+/**
+ * The keycode.
+ * @see org.eclipse.swt.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 SWT key event used to supply the statemask, keycode and character
+ */
+public this(EventDispatcher dispatcher, IFigure source,
+                    org.eclipse.swt.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/org.eclipse.draw2d/src/org/eclipse/draw2d/KeyListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.KeyListener;
+
+import java.lang.all;
+import org.eclipse.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 org.eclipse.draw2d.KeyListener#keyPressed(KeyEvent)
+     */
+    public void keyPressed(KeyEvent ke) { }
+    /**
+     * @see org.eclipse.draw2d.KeyListener#keyReleased(KeyEvent)
+     */
+    public void keyReleased(KeyEvent ke) { }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/Label.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,709 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.Label;
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.PositionConstants;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.TextUtilities;
+import org.eclipse.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;
+        default:
+    }
+}
+
+/**
+ * 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;
+        default:
+    }
+
+    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;
+    default:
+    }
+}
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/LabelAnchor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.LabelAnchor;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.ChopboxAnchor;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/LabeledBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.LabeledBorder;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/LabeledContainer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.LabeledContainer;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.Border;
+import org.eclipse.draw2d.LabeledBorder;
+import org.eclipse.draw2d.GroupBoxBorder;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Layer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.Layer;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/LayeredPane.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.LayeredPane;
+
+import java.lang.all;
+import org.eclipse.draw2d.Layer;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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 org.eclipse.draw2d.IFigure#remove(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/LayoutAnimator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.LayoutAnimator;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Animator;
+import org.eclipse.draw2d.LayoutListener;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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 {
+
+private static LayoutAnimator INSTANCE_;
+static LayoutAnimator INSTANCE(){
+    if( INSTANCE_ is null ){
+        synchronized( LayoutAnimator.classinfo ){
+            if( INSTANCE_ is null ){
+                INSTANCE_ = new LayoutAnimator();
+            }
+        }
+    }
+    assert(INSTANCE_);
+    return INSTANCE_;
+}
+
+/**
+ * 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 org.eclipse.draw2d.LayoutListener#layout(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/LayoutListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.LayoutListener;
+
+import java.lang.all;
+
+import org.eclipse.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) { }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/LayoutManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.LayoutManager;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/LightweightSystem.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.LightweightSystem;
+
+import java.lang.all;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.accessibility.AccessibleControlEvent;
+import org.eclipse.swt.accessibility.AccessibleControlListener;
+import org.eclipse.swt.accessibility.AccessibleEvent;
+import org.eclipse.swt.accessibility.AccessibleListener;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.EventDispatcher;
+import org.eclipse.draw2d.UpdateManager;
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.DeferredUpdateManager;
+import org.eclipse.draw2d.SWTEventDispatcher;
+import org.eclipse.draw2d.StackLayout;
+import org.eclipse.draw2d.BufferedGraphicsSource;
+import org.eclipse.draw2d.NativeGraphicsSource;
+
+/**
+ * The LightweightSystem is the link between SWT and Draw2d. It is the component that
+ * provides the ability for {@link Figure Figures} to be hosted on an SWT Canvas.
+ * <p>
+ * Normal procedure for using a LightweightSystem:
+ * <ol>
+ *      <li>Create an SWT 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 SWT listeners to the LightWeightSystem's Canvas. This allows for SWT 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(SWT.MouseWheel, handler);
+
+    canvas.addControlListener(new class() ControlAdapter {
+        public void controlResized(ControlEvent e) {
+            this.outer.controlResized();
+        }
+    });
+    canvas.addListener(SWT.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() & SWT.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() & SWT.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(org.eclipse.swt.widgets.Event)
+     * @since 3.1
+     */
+    public void handleEvent(Event event) {
+        // Mouse wheel events
+        if (event.type is SWT.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/org.eclipse.draw2d/src/org/eclipse/draw2d/LineBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.LineBorder;
+
+import java.lang.all;
+
+
+import org.eclipse.draw2d.AbstractBorder;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Graphics;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Locator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.Locator;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ManhattanConnectionRouter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,420 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.ManhattanConnectionRouter;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.Ray;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.AbstractRouter;
+import org.eclipse.draw2d.Connection;
+import org.eclipse.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_;
+private static Ray UP(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    assert(UP_);
+    return UP_;
+}
+private static Ray DOWN(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    assert(DOWN_);
+    return DOWN_;
+}
+private static Ray LEFT(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    assert(LEFT_);
+    return LEFT_;
+}
+private static Ray RIGHT(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    assert(RIGHT_);
+    return RIGHT_;
+}
+
+private static bool initStaticCtor_done = false;
+private static void initStaticCtor(){
+    synchronized( ManhattanConnectionRouter.classinfo ){
+        if( !initStaticCtor_done ){
+            UP_      = new Ray(0, -1);
+            DOWN_    = new Ray(0, 1);
+            LEFT_    = new Ray(-1, 0);
+            RIGHT_   = new Ray(1, 0);
+            initStaticCtor_done = true;
+        }
+    }
+}
+
+
+
+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/org.eclipse.draw2d/src/org/eclipse/draw2d/MarginBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.MarginBorder;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.AbstractBorder;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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 org.eclipse.draw2d.Border#getInsets(IFigure)
+ */
+public Insets getInsets(IFigure figure) {
+    return insets;
+}
+
+/**
+ * This method does nothing, since this border is just for spacing.
+ * @see org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/MidpointLocator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.MidpointLocator;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.ConnectionLocator;
+import org.eclipse.draw2d.Connection;
+
+/**
+ * A ConnectionLocator that is used to place figures at the midpoint between two points on
+ * a {@link org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/MouseEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.MouseEvent;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import tango.text.convert.Format;
+import org.eclipse.draw2d.InputEvent;
+import org.eclipse.draw2d.EventDispatcher;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/MouseListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.MouseListener;
+
+import java.lang.all;
+import org.eclipse.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 org.eclipse.draw2d.MouseListener#mousePressed(MouseEvent)
+     */
+    public void mousePressed(MouseEvent me) { }
+    /**
+     * @see org.eclipse.draw2d.MouseListener#mouseReleased(MouseEvent)
+     */
+    public void mouseReleased(MouseEvent me) { }
+    /**
+     * @see org.eclipse.draw2d.MouseListener#mouseDoubleClicked(MouseEvent)
+     */
+    public void mouseDoubleClicked(MouseEvent me) { }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/MouseMotionListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.MouseMotionListener;
+
+import java.lang.all;
+import org.eclipse.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 org.eclipse.draw2d.MouseMotionListener#mouseDragged(MouseEvent)
+     */
+    public void mouseDragged(MouseEvent me) { }
+    /**
+     * @see org.eclipse.draw2d.MouseMotionListener#mouseEntered(MouseEvent)
+     */
+    public void mouseEntered(MouseEvent me) { }
+    /**
+     * @see org.eclipse.draw2d.MouseMotionListener#mouseExited(MouseEvent)
+     */
+    public void mouseExited(MouseEvent me) { }
+    /**
+     * @see org.eclipse.draw2d.MouseMotionListener#mouseMoved(MouseEvent)
+     */
+    public void mouseMoved(MouseEvent me) { }
+    /**
+     * @see org.eclipse.draw2d.MouseMotionListener#mouseHover(MouseEvent)
+     */
+    public void mouseHover(MouseEvent me) { }
+}
+
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/NativeGraphicsSource.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.NativeGraphicsSource;
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.GraphicsSource;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Orientable.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.Orientable;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Panel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.Panel;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Polygon.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.Polygon;
+
+import java.lang.all;
+import org.eclipse.draw2d.Polyline;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.IFigure;
+
+/**
+ * Renders a {@link org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/PolygonDecoration.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,175 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.PolygonDecoration;
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.Transform;
+import org.eclipse.draw2d.RotatableDecoration;
+import org.eclipse.draw2d.Polygon;
+
+/**
+ * A rotatable, polygon shaped decoration most commonly used for decorating the ends of
+ * {@link org.eclipse.draw2d.Polyline polylines}.
+ */
+public class PolygonDecoration
+    : Polygon
+    , RotatableDecoration
+{
+
+/** Template for a triangle that points to the right when the rotation angle is 0 */
+private static bool initStaticConsts_done = false;
+private static PointList TRIANGLE_TIP_;
+/** Template for a triangle that points to the left when the rotation angle is 0 */
+private static PointList INVERTED_TRIANGLE_TIP_;
+
+public static PointList TRIANGLE_TIP(){
+    if( !initStaticConsts_done ) initStaticConsts();
+    return TRIANGLE_TIP_;
+}
+public static PointList INVERTED_TRIANGLE_TIP(){
+    if( !initStaticConsts_done ) initStaticConsts();
+    return INVERTED_TRIANGLE_TIP_;
+}
+
+private static void initStaticConsts() {
+    synchronized( PolygonDecoration.classinfo ){
+        if( !initStaticConsts_done ){
+            TRIANGLE_TIP_ = new PointList();
+            TRIANGLE_TIP_.addPoint(0, 0);
+            TRIANGLE_TIP_.addPoint(-1, 1);
+            TRIANGLE_TIP_.addPoint(-1, -1);
+
+            INVERTED_TRIANGLE_TIP_ = new PointList();
+            INVERTED_TRIANGLE_TIP_.addPoint(0, 1);
+            INVERTED_TRIANGLE_TIP_.addPoint(0, -1);
+            INVERTED_TRIANGLE_TIP_.addPoint(-1, 0);
+
+            initStaticConsts_done = true;
+        }
+    }
+}
+
+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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Polyline.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,323 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.Polyline;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Shape;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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 Rectangle LINEBOUNDS_;
+private static Rectangle LINEBOUNDS(){
+    if( LINEBOUNDS_ is null ){
+        synchronized( Polyline.classinfo ){
+            if( LINEBOUNDS_ is null ){
+                LINEBOUNDS_ = Rectangle.SINGLETON;
+                assert( LINEBOUNDS !is null );
+            }
+        }
+    }
+    return LINEBOUNDS_;
+}
+
+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 org.eclipse.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 org.eclipse.draw2d.Shape#fillShape(Graphics)
+ */
+protected void fillShape(Graphics g) { }
+
+/**
+ * @see org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/PolylineConnection.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.PolylineConnection;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.Polyline;
+import org.eclipse.draw2d.Connection;
+import org.eclipse.draw2d.ConnectionAnchor;
+import org.eclipse.draw2d.AnchorListener;
+import org.eclipse.draw2d.ConnectionRouter;
+import org.eclipse.draw2d.ConnectionLocator;
+import org.eclipse.draw2d.RotatableDecoration;
+import org.eclipse.draw2d.RoutingListener;
+import org.eclipse.draw2d.DelegatingLayout;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/PolylineDecoration.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.PolylineDecoration;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.Transform;
+import org.eclipse.draw2d.Polyline;
+import org.eclipse.draw2d.RotatableDecoration;
+import org.eclipse.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_ */
+private static PointList TRIANGLE_TIP_;
+public static PointList TRIANGLE_TIP(){
+    if( TRIANGLE_TIP_ is null ){
+        synchronized( PolylineDecoration.classinfo ){
+            if( TRIANGLE_TIP_ is null ){
+                PointList tmp = new PointList();
+                tmp.addPoint(-1, 1);
+                tmp.addPoint(0, 0);
+                tmp.addPoint(-1, -1);
+                TRIANGLE_TIP_ = tmp;
+            }
+        }
+    }
+    return TRIANGLE_TIP_;
+}
+
+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/org.eclipse.draw2d/src/org/eclipse/draw2d/PopUpHelper.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.PopUpHelper;
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.LightweightSystem;
+
+/**
+ * Provides abstract support for classes that manage popups. Popups in Draw2d consist of a
+ * LightweightSystem object with an SWT 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, SWT.ON_TOP | SWT.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 SWT style bits for the shell
+ * @since 3.1
+ */
+protected this(Control c, int shellStyle) {
+    control = c;
+    this.shellStyle = shellStyle | SWT.NO_BACKGROUND | SWT.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/org.eclipse.draw2d/src/org/eclipse/draw2d/PositionConstants.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.PositionConstants;
+
+import java.lang.all;
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/PrintFigureOperation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.PrintFigureOperation;
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.printing.Printer;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.PrintOperation;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.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 org.eclipse.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 SWT.RIGHT_TO_LEFT if the print source is mirrored; SWT.LEFT_TO_RIGHT otherwise
+ * @see org.eclipse.draw2d.PrintOperation#getGraphicsOrientation()
+ */
+int getGraphicsOrientation() {
+    return getPrintSource().isMirrored() ? SWT.RIGHT_TO_LEFT : SWT.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 org.eclipse.draw2d.PrintOperation#preparePrintSource()
+ */
+protected void preparePrintSource() {
+    oldBGColor = getPrintSource().getLocalBackgroundColor();
+    getPrintSource().setBackgroundColor(ColorConstants.white);
+}
+
+/**
+ * Prints the pages based on the current print mode.
+ * @see org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/PrintOperation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.PrintOperation;
+
+import java.lang.all;
+
+static import org.eclipse.swt.graphics.Rectangle;
+static import org.eclipse.swt.graphics.Point;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.printing.Printer;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.PrinterGraphics;
+import org.eclipse.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
+ * SWT.LEFT_TO_RIGHT.
+ * @return SWT.LEFT_TO_RIGHT or SWT.RIGHT_TO_LEFT
+ * @since 3.1
+ * @TODO Make protected post-3.1
+ */
+int getGraphicsOrientation() {
+    return SWT.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() {
+    org.eclipse.swt.graphics.Rectangle.Rectangle trim = printer.computeTrim(0, 0, 0, 0);
+    org.eclipse.swt.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/org.eclipse.draw2d/src/org/eclipse/draw2d/PrinterGraphics.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.PrinterGraphics;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.printing.Printer;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.draw2d.ScaledGraphics;
+import org.eclipse.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 org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/RangeModel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.RangeModel;
+
+import java.lang.all;
+
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/RectangleFigure.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.RectangleFigure;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Shape;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/RelativeBendpoint.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.RelativeBendpoint;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PrecisionPoint;
+import org.eclipse.draw2d.Bendpoint;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/RelativeLocator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.RelativeLocator;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.PrecisionRectangle;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Locator;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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 org.eclipse.draw2d.Locator#relocate(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/RotatableDecoration.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.RotatableDecoration;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/RoundedRectangle.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.RoundedRectangle;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Shape;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/RoutingAnimator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,224 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.RoutingAnimator;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.Animator;
+import org.eclipse.draw2d.RoutingListener;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Connection;
+import org.eclipse.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 {
+
+private static RoutingAnimator INSTANCE_;
+static RoutingAnimator INSTANCE(){
+    if( INSTANCE_ is null ){
+        synchronized( RoutingAnimator.classinfo ){
+            if( INSTANCE_ is null ){
+                INSTANCE_ = new RoutingAnimator();
+            }
+        }
+    }
+    return INSTANCE_;
+}
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/RoutingListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.RoutingListener;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/SWTEventDispatcher.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.SWTEventDispatcher;
+
+import java.lang.all;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.accessibility.AccessibleControlEvent;
+import org.eclipse.swt.accessibility.AccessibleControlListener;
+import org.eclipse.swt.accessibility.AccessibleEvent;
+import org.eclipse.swt.accessibility.AccessibleListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.draw2d.EventDispatcher;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.MouseEvent;
+import org.eclipse.draw2d.FocusEvent;
+import org.eclipse.draw2d.KeyEvent;
+import org.eclipse.draw2d.ToolTipHelper;
+static import org.eclipse.swt.widgets.Control;
+import org.eclipse.draw2d.FocusTraverseManager;
+static import org.eclipse.swt.events.FocusEvent;
+static import org.eclipse.swt.events.KeyEvent;
+static import org.eclipse.swt.events.MouseEvent;
+
+/**
+ * The SWTEventDispatcher provides draw2d with the ability to dispatch SWT Events. The
+ * {@link org.eclipse.draw2d.LightweightSystem} adds SWT event listeners on its Canvas.
+ * When the Canvas receives an SWT 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 SWT#BUTTON_MASK} instead.
+ */
+protected static const int ANY_BUTTON = SWT.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 org.eclipse.swt.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(org.eclipse.swt.events.FocusEvent)
+ */
+public void dispatchFocusGained(org.eclipse.swt.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(org.eclipse.swt.events.FocusEvent)
+ */
+public void dispatchFocusLost(org.eclipse.swt.events.FocusEvent.FocusEvent e) {
+    setFocus(null);
+}
+
+/**
+ * @see EventDispatcher#dispatchKeyPressed(org.eclipse.swt.events.KeyEvent)
+ */
+public void dispatchKeyPressed(org.eclipse.swt.events.KeyEvent.KeyEvent e) {
+    if (focusOwner !is null) {
+        KeyEvent event = new KeyEvent(this, focusOwner, e);
+        focusOwner.handleKeyPressed(event);
+    }
+}
+
+/**
+ * @see EventDispatcher#dispatchKeyReleased(org.eclipse.swt.events.KeyEvent)
+ */
+public void dispatchKeyReleased(org.eclipse.swt.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 SWT.TRAVERSE_TAB_NEXT)
+        nextFigure = getFocusTraverseManager().getNextFocusableFigure(root, focusOwner);
+    else if (e.detail is SWT.TRAVERSE_TAB_PREVIOUS)
+        nextFigure =
+                getFocusTraverseManager().getPreviousFocusableFigure(root, focusOwner);
+
+    if (nextFigure !is null) {
+        e.doit = false;
+        setFocus(nextFigure);
+    }
+}
+
+/**
+ * @see EventDispatcher#dispatchMouseHover(org.eclipse.swt.events.MouseEvent)
+ */
+public void dispatchMouseHover(org.eclipse.swt.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();
+        org.eclipse.swt.graphics.Point.Point absolute;
+        absolute = control.toDisplay(new org.eclipse.swt.graphics.Point.Point(me.x, me.y));
+        toolTipHelper.displayToolTipNear(hoverSource, tip, absolute.x, absolute.y);
+    }
+}
+
+/**
+ * @see EventDispatcher#dispatchMouseDoubleClicked(org.eclipse.swt.events.MouseEvent)
+ */
+public void dispatchMouseDoubleClicked(org.eclipse.swt.events.MouseEvent.MouseEvent me) {
+    receive(me);
+    if (mouseTarget !is null)
+        mouseTarget.handleMouseDoubleClicked(currentEvent);
+}
+
+/**
+ * @see EventDispatcher#dispatchMouseEntered(org.eclipse.swt.events.MouseEvent)
+ */
+public void dispatchMouseEntered(org.eclipse.swt.events.MouseEvent.MouseEvent me) {
+    receive(me);
+}
+
+/**
+ * @see EventDispatcher#dispatchMouseExited(org.eclipse.swt.events.MouseEvent)
+ */
+public void dispatchMouseExited(org.eclipse.swt.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(org.eclipse.swt.events.MouseEvent)
+ */
+public void dispatchMousePressed(org.eclipse.swt.events.MouseEvent.MouseEvent me) {
+    receive(me);
+    if (mouseTarget !is null) {
+        mouseTarget.handleMousePressed(currentEvent);
+        if (currentEvent.isConsumed())
+            setCapture(mouseTarget);
+    }
+}
+
+/**
+ * @see EventDispatcher#dispatchMouseMoved(org.eclipse.swt.events.MouseEvent)
+ */
+public void dispatchMouseMoved(org.eclipse.swt.events.MouseEvent.MouseEvent me) {
+    receive(me);
+    if (mouseTarget !is null) {
+        if ((me.stateMask & SWT.BUTTON_MASK) !is 0)
+            mouseTarget.handleMouseDragged(currentEvent);
+        else
+            mouseTarget.handleMouseMoved(currentEvent);
+    }
+}
+
+/**
+ * @see EventDispatcher#dispatchMouseReleased(org.eclipse.swt.events.MouseEvent)
+ */
+public void dispatchMouseReleased(org.eclipse.swt.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(org.eclipse.swt.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() org.eclipse.swt.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, org.eclipse.swt.events.MouseEvent.MouseEvent me) {
+    hoverSource = figure;
+    if (figure !is null) {
+        Control control = cast(Control)me.getSource();
+        org.eclipse.swt.graphics.Point.Point absolute;
+        absolute = control.toDisplay(new org.eclipse.swt.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(org.eclipse.swt.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(org.eclipse.swt.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/org.eclipse.draw2d/src/org/eclipse/draw2d/SWTGraphics.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1208 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.SWTGraphics;
+
+import java.lang.all;
+static import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Path;
+import org.eclipse.swt.graphics.Pattern;
+import org.eclipse.swt.graphics.TextLayout;
+import org.eclipse.swt.graphics.Transform;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Graphics;
+
+/**
+ * A concrete implementation of <code>Graphics</code> using an SWT
+ * <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;
+
+    this(){
+    }
+    this(LazyState other){
+        bgColor      = other.bgColor;
+        fgColor      = other.fgColor;
+        font         = other.font;
+        graphicHints = other.graphicHints;
+        lineWidth    = other.lineWidth;
+        relativeClip = other.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(org.eclipse.swt.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(){
+    }
+
+    this(State other){
+        super(other);
+        affineMatrix = other.affineMatrix.dup;
+        alpha        = other.alpha;
+        bgPattern    = other.bgPattern;
+        dx           = other.dx;
+        dy           = other.dy;
+        fgPattern    = other.fgPattern;
+        lineDash     = other.lineDash.dup;
+    }
+    public Object clone() {
+        return new State(this);
+    }
+
+    /**
+     * 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 = 3 << AA_SHIFT;
+static const int AA_SHIFT = 8;
+static const int AA_WHOLE_NUMBER = 1;
+static const int ADVANCED_GRAPHICS_MASK = 1 << ADVANCED_SHIFT;
+static const int ADVANCED_HINTS_DEFAULTS = ((SWT.DEFAULT + AA_WHOLE_NUMBER) << TEXT_AA_SHIFT)
+            | ((SWT.DEFAULT + AA_WHOLE_NUMBER) << AA_SHIFT)
+            | ((SWT.DEFAULT + INTERPOLATION_WHOLE_NUMBER) << INTERPOLATION_SHIFT);
+static const int ADVANCED_HINTS_MASK = TEXT_AA_MASK | AA_MASK | INTERPOLATION_MASK;
+static const int ADVANCED_SHIFT = 15;
+static const int CAP_MASK = 3 << CAP_SHIFT;
+static const int CAP_SHIFT = 4;
+static const int FILL_RULE_MASK = 1 << FILL_RULE_SHIFT;
+static const int FILL_RULE_SHIFT = 14;
+static const int FILL_RULE_WHOLE_NUMBER = -1;
+static const int INTERPOLATION_MASK = 3 << INTERPOLATION_SHIFT;
+static const int INTERPOLATION_SHIFT = 12;
+static const int INTERPOLATION_WHOLE_NUMBER = 1;
+static const int JOIN_MASK = 3 << JOIN_SHIFT;
+static const int JOIN_SHIFT = 6;
+static const int LINE_STYLE_MASK = 7;
+
+static const int TEXT_AA_MASK = 3 << TEXT_AA_SHIFT;
+static const int TEXT_AA_SHIFT = 10;
+static const int XOR_MASK = 1 << XOR_SHIFT;
+static const int XOR_SHIFT = 3;
+
+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 cast(Object)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 org.eclipse.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)
+                SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+            copy[i] = dash;
+        }
+        currentState.lineDash = copy;
+        setLineStyle(SWT.LINE_CUSTOM);
+        gc.setLineDash(copy);
+    } else {
+        currentState.lineDash = null;
+        setLineStyle(SWT.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ScalableFigure.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ScalableFigure;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ScalableFreeformLayeredPane.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ScalableFreeformLayeredPane;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.geometry.Translatable;
+import org.eclipse.draw2d.FreeformLayeredPane;
+import org.eclipse.draw2d.ScalableFigure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.ScaledGraphics;
+
+/**
+ * @author hudsonr
+ * @since 2.1
+ */
+public class ScalableFreeformLayeredPane
+    : FreeformLayeredPane
+    , ScalableFigure
+{
+
+private double scale = 1.0;
+
+/**
+ * @see org.eclipse.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 org.eclipse.draw2d.IFigure#isCoordinateSystem()
+ */
+public bool isCoordinateSystem() {
+    return true;
+}
+
+/**
+ * @see org.eclipse.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 org.eclipse.draw2d.Figure#translateToParent(Translatable)
+ */
+public void translateToParent(Translatable t) {
+    t.performScale(scale);
+}
+
+/**
+ * @see org.eclipse.draw2d.Figure#translateFromParent(Translatable)
+ */
+public void translateFromParent(Translatable t) {
+    t.performScale(1 / scale);
+}
+
+/**
+ * @see org.eclipse.draw2d.Figure#useLocalCoordinates()
+ */
+protected final bool useLocalCoordinates() {
+    return false;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/ScalableLayeredPane.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ScalableLayeredPane;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.geometry.Translatable;
+import org.eclipse.draw2d.LayeredPane;
+import org.eclipse.draw2d.ScalableFigure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.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 org.eclipse.draw2d.IFigure#isCoordinateSystem()
+ */
+public bool isCoordinateSystem() {
+    return true;
+}
+
+/**
+ * @see org.eclipse.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 org.eclipse.draw2d.Figure#translateFromParent(Translatable)
+ */
+public void translateFromParent(Translatable t) {
+    t.performScale(1 / scale);
+}
+
+/**
+ * @see org.eclipse.draw2d.Figure#translateToParent(Translatable)
+ */
+public void translateToParent(Translatable t) {
+    t.performScale(scale);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/ScaledGraphics.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,891 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.ScaledGraphics;
+
+import java.lang.all;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Path;
+import org.eclipse.swt.graphics.PathData;
+import org.eclipse.swt.graphics.TextLayout;
+import org.eclipse.swt.graphics.TextStyle;
+import org.eclipse.swt.widgets.Display;
+static import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.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 static int[][] intArrayCache(){
+    if( intArrayCache_ is null ){
+        synchronized( ScaledGraphics.classinfo ){
+            if( intArrayCache_ is null ){
+                intArrayCache_ = new int[][](8);
+                for (int i = 0; i < intArrayCache_.length; i++)
+                    intArrayCache_[i] = new int[i + 1];
+            }
+        }
+    }
+    return intArrayCache_;
+}
+private const Rectangle tempRECT;
+
+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) {
+    // instance init
+    tempRECT = new Rectangle();
+    localCache = new FontHeightCache();
+    targetCache = new FontHeightCache();
+    stack = new ArrayList();
+    fontKey = new FontKey();
+    fontDataCache = new HashMap();
+    fontCache = new HashMap();
+
+    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 SWT.PATH_MOVE_TO:
+                scaledPath.moveTo(p.points[index], p.points[index + 1]);
+                index += 2;
+                break;
+            case SWT.PATH_LINE_TO:
+                scaledPath.lineTo(p.points[index], p.points[index + 1]);
+                index += 2;
+                break;
+            case SWT.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 SWT.PATH_QUAD_TO:
+                scaledPath.quadTo(p.points[index], p.points[index + 1],
+                        p.points[index + 2], p.points[index + 3]);
+                index += 4;
+                break;
+            case SWT.PATH_CLOSE:
+                scaledPath.close();
+                break;
+            default:
+        }
+    }
+    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) {
+    org.eclipse.swt.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/SchemeBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,393 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.SchemeBorder;
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Border;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.AbstractBorder;
+import org.eclipse.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 **/
+private static bool initStaticConsts_done = false;
+private static Color[]
+    DARKEST_DARKER_,
+    LIGHTER_DARKER_,
+    DARKER_LIGHTER_;
+
+protected static Color[] DARKEST_DARKER(){
+    if( !initStaticConsts_done ) initStaticConsts();
+    return DARKEST_DARKER_;
+}
+protected static Color[] LIGHTER_DARKER(){
+    if( !initStaticConsts_done ) initStaticConsts();
+    return LIGHTER_DARKER_;
+}
+protected static Color[] DARKER_LIGHTER(){
+    if( !initStaticConsts_done ) initStaticConsts();
+    return DARKER_LIGHTER_;
+}
+
+
+static void initStaticConsts(){
+    synchronized( SchemeBorder.classinfo ){
+        if( !initStaticConsts_done ) {
+            DARKEST_DARKER_   = [ColorConstants.buttonDarkest,  ColorConstants.buttonDarker];
+            LIGHTER_DARKER_   = [ColorConstants.buttonLightest, ColorConstants.buttonDarker];
+            DARKER_LIGHTER_   = [ColorConstants.buttonDarker, ColorConstants.buttonLightest];
+            initStaticConsts_done = true;
+        }
+    }
+}
+
+/**
+ * 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 **/
+    private static bool initStaticConsts_done = false;
+    private static Scheme
+        BUTTON_CONTRAST_,
+        BUTTON_RAISED_,
+        BUTTON_PRESSED_,
+        RAISED_,
+        LOWERED_,
+        RIDGED_,
+        ETCHED_;
+
+    public static Scheme BUTTON_CONTRAST(){
+        if( !initStaticConsts_done ) initStaticConsts();
+        return BUTTON_CONTRAST_;
+    }
+    public static Scheme BUTTON_RAISED(){
+        if( !initStaticConsts_done ) initStaticConsts();
+        return BUTTON_RAISED_;
+    }
+    public static Scheme BUTTON_PRESSED(){
+        if( !initStaticConsts_done ) initStaticConsts();
+        return BUTTON_PRESSED_;
+    }
+    public static Scheme RAISED(){
+        if( !initStaticConsts_done ) initStaticConsts();
+        return RAISED_;
+    }
+    public static Scheme LOWERED(){
+        if( !initStaticConsts_done ) initStaticConsts();
+        return LOWERED_;
+    }
+    public static Scheme RIDGED(){
+        if( !initStaticConsts_done ) initStaticConsts();
+        return RIDGED_;
+    }
+    public static Scheme ETCHED(){
+        if( !initStaticConsts_done ) initStaticConsts();
+        return ETCHED_;
+    }
+
+
+    static void initStaticConsts(){
+        synchronized( SchemeBorder.classinfo ){
+            if( !initStaticConsts_done ) {
+                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);
+                initStaticConsts_done = true;
+            }
+        }
+    }
+
+}
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/ScrollBar.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,674 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.ScrollBar;
+
+import java.lang.all;
+
+import org.eclipse.dwtxhelper.Bean;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.geometry.Transposer;
+
+import org.eclipse.draw2d.Orientable;
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.RangeModel;
+import org.eclipse.draw2d.Clickable;
+import org.eclipse.draw2d.MouseMotionListener;
+import org.eclipse.draw2d.MouseListener;
+import org.eclipse.draw2d.MouseEvent;
+import org.eclipse.draw2d.FigureUtilities;
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.draw2d.Button;
+import org.eclipse.draw2d.RangeModel;
+import org.eclipse.draw2d.DefaultRangeModel;
+import org.eclipse.draw2d.ArrowButton;
+import org.eclipse.draw2d.ButtonBorder;
+import org.eclipse.draw2d.ChangeListener;
+import org.eclipse.draw2d.ChangeEvent;
+import org.eclipse.draw2d.Panel;
+import org.eclipse.draw2d.ScrollBarLayout;
+import org.eclipse.draw2d.SchemeBorder;
+import org.eclipse.draw2d.ActionListener;
+import org.eclipse.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 Color COLOR_TRACK_;
+private static Color COLOR_TRACK(){
+    if( COLOR_TRACK_ is null ){
+        synchronized( ScrollBar.classinfo ){
+            if( COLOR_TRACK_ is null ){
+                COLOR_TRACK_ = FigureUtilities.mixColors(
+                    ColorConstants.white,
+                    ColorConstants.button);
+            }
+        }
+    }
+    return COLOR_TRACK_;
+}
+
+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/org.eclipse.draw2d/src/org/eclipse/draw2d/ScrollBarLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ScrollBarLayout;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.geometry.Transposer;
+
+import org.eclipse.draw2d.AbstractLayout;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ScrollPane.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ScrollPane;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.ScrollBar;
+import org.eclipse.draw2d.Viewport;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ScrollPaneLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ScrollPaneLayout;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+
+import org.eclipse.draw2d.AbstractHintLayout;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.ScrollPane;
+import org.eclipse.draw2d.ScrollBar;
+import org.eclipse.draw2d.Viewport;
+import org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ScrollPaneSolver.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ScrollPaneSolver;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Viewport;
+
+/**
+ * This class handles the calculation of solving for the area of a
+ * {@link org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Shape.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.Shape;
+
+import java.lang.all;
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ShortestPathConnectionRouter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,316 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.ShortestPathConnectionRouter;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.PrecisionPoint;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.graph.Path;
+import org.eclipse.draw2d.graph.ShortestPathRouter;
+import org.eclipse.draw2d.AbstractRouter;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.LayoutListener;
+import org.eclipse.draw2d.FigureListener;
+import org.eclipse.draw2d.Connection;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/SimpleEtchedBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.SimpleEtchedBorder;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.SchemeBorder;
+import org.eclipse.draw2d.Border;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.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 */
+private static Border singleton_;
+
+/** The insets */
+private static Insets INSETS_;
+
+public static Border singleton(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    assert(singleton_);
+    return singleton_;
+}
+protected static Insets INSETS(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    assert(INSETS_);
+    return INSETS_;
+}
+
+private static bool initStaticCtor_done = false;
+private static void initStaticCtor(){
+    synchronized( SimpleEtchedBorder.classinfo ){
+        if( !initStaticCtor_done ){
+            singleton_ = new SimpleEtchedBorder();
+            INSETS_ = new Insets(2);
+            initStaticCtor_done = true;
+        }
+    }
+}
+
+
+
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/SimpleLoweredBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.SimpleLoweredBorder;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.draw2d.SchemeBorder;
+import org.eclipse.draw2d.ColorConstants;
+
+/**
+ * Provides a lowered border.
+ */
+public final class SimpleLoweredBorder
+    : SchemeBorder
+{
+
+private static Scheme DOUBLE_;
+private static Scheme DOUBLE(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    assert(DOUBLE_);
+    return DOUBLE_;
+}
+
+private static bool initStaticCtor_done = false;
+private static void initStaticCtor(){
+    synchronized( SimpleLoweredBorder.classinfo ){
+        if( !initStaticCtor_done ){
+            DOUBLE_ = new Scheme(
+                [ColorConstants.buttonDarkest,  ColorConstants.buttonDarker],
+                [ColorConstants.buttonLightest, ColorConstants.button] );
+            initStaticCtor_done = true;
+        }
+    }
+}
+
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/SimpleRaisedBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.SimpleRaisedBorder;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.draw2d.SchemeBorder;
+import org.eclipse.draw2d.ColorConstants;
+
+/**
+ * Provides a raised border.
+ */
+public class SimpleRaisedBorder
+    : SchemeBorder
+{
+
+private static Scheme DOUBLE_;
+private static Scheme DOUBLE(){
+    if( !initStaticCtor_done ) initStaticCtor();
+    assert(DOUBLE_);
+    return DOUBLE_;
+}
+
+private static bool initStaticCtor_done = false;
+private static void initStaticCtor(){
+    synchronized( SimpleRaisedBorder.classinfo ){
+        if( !initStaticCtor_done ){
+            DOUBLE_ = new Scheme(
+                [ColorConstants.buttonLightest, ColorConstants.button],
+                [ColorConstants.buttonDarkest,  ColorConstants.buttonDarker]
+            );
+            initStaticCtor_done = true;
+        }
+    }
+}
+
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/StackLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.StackLayout;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.AbstractHintLayout;
+import org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/SubordinateUpdateManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.SubordinateUpdateManager;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.UpdateManager;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/TextUtilities.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.TextUtilities;
+
+import java.lang.all;
+
+
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.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
+     */
+    private static TextUtilities INSTANCE_;
+    public static TextUtilities INSTANCE(){
+        if( INSTANCE_ is null ){
+            synchronized( TextUtilities.classinfo ){
+                if( INSTANCE_ is null ){
+                    INSTANCE_ = new TextUtilities();
+                }
+            }
+        }
+        return INSTANCE_;
+    }
+    /**
+     * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/TitleBarBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,204 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.TitleBarBorder;
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.AbstractLabeledBorder;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.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 static Color defaultColor(){
+    if( defaultColor_ is null ){
+        synchronized( TitleBarBorder.classinfo ){
+            if( defaultColor_ is null ){
+                defaultColor_ = ColorConstants.menuBackgroundSelected;
+            }
+        }
+    }
+    return defaultColor_;
+}
+
+private int textAlignment;
+private Insets padding;
+private Color fillColor;
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/Toggle.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.Toggle;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.draw2d.Clickable;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Label;
+
+/**
+ * Basic Rule for Toggle: Whoever creates the toggle is reponsible for response changes
+ * for it (selection, rollover, etc). Only {@link org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ToggleButton.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ToggleButton;
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Toggle;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.Label;
+import org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ToggleModel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ToggleModel;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ToolTipHelper.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ToolTipHelper;
+
+import java.lang.all;
+import org.eclipse.swt.dwthelper.Runnable;
+import org.eclipse.dwtxhelper.Timer;
+import org.eclipse.dwtxhelper.TimerTask;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseTrackAdapter;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Display;
+static import org.eclipse.swt.widgets.Control;
+static import org.eclipse.swt.graphics.Rectangle;
+static import org.eclipse.swt.events.MouseEvent;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.PopUpHelper;
+import org.eclipse.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 SWT 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(org.eclipse.swt.widgets.Control.Control c) {
+    super(c, SWT.TOOL | SWT.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) {
+    org.eclipse.swt.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(org.eclipse.swt.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ToolbarLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ToolbarLayout;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.geometry.Transposer;
+import org.eclipse.draw2d.AbstractHintLayout;
+import org.eclipse.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 org.eclipse.draw2d.AbstractHintLayout#isSensitiveHorizontally(IFigure)
+ */
+protected bool isSensitiveHorizontally(IFigure parent) {
+    return !isHorizontal();
+}
+
+/**
+ * @see org.eclipse.draw2d.AbstractHintLayout#isSensitiveVertically(IFigure)
+ */
+protected bool isSensitiveVertically(IFigure parent) {
+    return isHorizontal();
+}
+
+/**
+ * @see org.eclipse.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;
+        default:
+        }
+        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/org.eclipse.draw2d/src/org/eclipse/draw2d/TreeSearch.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.TreeSearch;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Triangle.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.Triangle;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.Shape;
+import org.eclipse.draw2d.Orientable;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/UpdateListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.UpdateListener;
+
+import java.lang.all;
+
+import org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/UpdateManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.UpdateManager;
+
+import java.lang.all;
+import org.eclipse.swt.dwthelper.Runnable;
+
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.UpdateListener;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/Viewport.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.Viewport;
+
+import java.lang.all;
+import org.eclipse.dwtxhelper.Bean;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.geometry.Translatable;
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.RangeModel;
+import org.eclipse.draw2d.ViewportLayout;
+import org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/ViewportLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.ViewportLayout;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.AbstractHintLayout;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.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 org.eclipse.draw2d.AbstractHintLayout#isSensitiveHorizontally(IFigure)
+ */
+protected bool isSensitiveHorizontally(IFigure parent) {
+    return (cast(Viewport)parent).getContentsTracksWidth();
+}
+
+/**
+ * @see org.eclipse.draw2d.AbstractHintLayout#isSensitiveHorizontally(IFigure)
+ */
+protected bool isSensitiveVertically(IFigure parent) {
+    return (cast(Viewport)parent).getContentsTracksHeight();
+}
+
+/**
+ * @see org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/XYAnchor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.XYAnchor;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.ConnectionAnchorBase;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/XYLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.XYLayout;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+
+import org.eclipse.draw2d.AbstractLayout;
+import org.eclipse.draw2d.IFigure;
+
+/**
+ * This class implements the {@link org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Dimension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,477 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.geometry.Dimension;
+
+import org.eclipse.draw2d.geometry.Point;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Translatable;
+static import org.eclipse.swt.graphics.Point;
+static import org.eclipse.swt.graphics.Image;
+static import org.eclipse.swt.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.*/
+private static Dimension SINGLETON_;
+public static Dimension SINGLETON(){
+    if( SINGLETON_ is null ){
+        synchronized( Dimension.classinfo ){
+            if( SINGLETON_ is null ){
+                SINGLETON_ = new Dimension();
+            }
+        }
+    }
+    return SINGLETON_;
+}
+
+/**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(org.eclipse.swt.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(org.eclipse.swt.graphics.Image.Image image) {
+    org.eclipse.swt.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 org.eclipse.draw2d.geometry.Translatable#performScale(double)
+ */
+public void performScale(double factor) {
+    scale(factor);
+}
+
+/**
+ * @see org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Geometry.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.geometry.Geometry;
+
+import java.lang.all;
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Insets.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.geometry.Insets;
+
+import java.lang.all;
+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/org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Point.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,444 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.geometry.Point;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.PositionConstants;
+import org.eclipse.draw2d.geometry.Translatable;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Rectangle;
+
+static import org.eclipse.swt.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*/
+private static Point SINGLETON_;
+public static Point SINGLETON(){
+    if( SINGLETON_ is null ){
+        synchronized( Point.classinfo ){
+            if( SINGLETON_ is null ){
+                SINGLETON_ = new Point();
+            }
+        }
+    }
+    return SINGLETON_;
+}
+
+/**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 SWT Point.
+ * @param copy  Point from which the initial values are taken.
+ * @since 2.0
+ */
+public this(org.eclipse.swt.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 SWT {@link org.eclipse.swt.graphics.Point Point} from this Point.
+ * @return  A new SWT Point
+ * @since 2.0
+ */
+public org.eclipse.swt.graphics.Point.Point getSWTPoint() {
+    return new org.eclipse.swt.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/org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/PointList.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.geometry.PointList;
+
+import java.lang.all;
+import tango.text.convert.Format;
+
+import org.eclipse.draw2d.geometry.Translatable;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.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}. SWT 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 org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/PrecisionDimension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.geometry.PrecisionDimension;
+
+import java.lang.all;
+import org.eclipse.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 org.eclipse.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 org.eclipse.draw2d.geometry.Dimension#preciseWidth()
+ */
+public double preciseWidth() {
+    return preciseWidth_;
+}
+
+/**
+ * @see org.eclipse.draw2d.geometry.Dimension#preciseHeight()
+ */
+public double preciseHeight() {
+    return preciseHeight_;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/PrecisionPoint.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.geometry.PrecisionPoint;
+
+import java.lang.all;
+import org.eclipse.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 org.eclipse.draw2d.geometry.Point#getCopy()
+ */
+public Point getCopy() {
+    return new PrecisionPoint(preciseX_, preciseY_);
+}
+
+
+/**
+ * @see org.eclipse.draw2d.geometry.Point#performScale(double)
+ */
+public void performScale(double factor) {
+    preciseX_ = preciseX_ * factor;
+    preciseY_ = preciseY_ * factor;
+    updateInts();
+}
+
+/**
+ * @see org.eclipse.draw2d.geometry.Point#performTranslate(int, int)
+ */
+public void performTranslate(int dx, int dy) {
+    preciseX_ += dx;
+    preciseY_ += dy;
+    updateInts();
+}
+
+/**
+ * @see org.eclipse.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 org.eclipse.draw2d.geometry.Point#preciseX()
+ */
+public double preciseX() {
+    return preciseX_;
+}
+
+/**
+ * @see org.eclipse.draw2d.geometry.Point#preciseY()
+ */
+public double preciseY() {
+    return preciseY_;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/PrecisionRectangle.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.geometry.PrecisionRectangle;
+
+import java.lang.all;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.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 org.eclipse.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 org.eclipse.draw2d.geometry.Rectangle#crop(org.eclipse.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 org.eclipse.draw2d.geometry.Rectangle#performScale(double)
+ */
+public void performScale(double factor) {
+    preciseX_ *= factor;
+    preciseY_ *= factor;
+    preciseWidth_ *= factor;
+    preciseHeight_ *= factor;
+    updateInts();
+}
+
+/**
+ * @see org.eclipse.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 org.eclipse.draw2d.geometry.Rectangle#resize(org.eclipse.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 org.eclipse.draw2d.geometry.Rectangle#translate(org.eclipse.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 org.eclipse.draw2d.geometry.Rectangle#union(org.eclipse.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 org.eclipse.draw2d.geometry.Rectangle#union(org.eclipse.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 org.eclipse.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 org.eclipse.draw2d.geometry.Rectangle#setLocation(org.eclipse.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 org.eclipse.draw2d.geometry.Rectangle#shrink(int, int)
+ */
+public Rectangle shrink(int h, int v) {
+    return shrink(cast(double)h, cast(double)v);
+}
+
+/**
+ * @see org.eclipse.draw2d.geometry.Rectangle#contains(org.eclipse.draw2d.geometry.Point)
+ */
+public bool contains(Point p) {
+    return preciseX_ <= p.preciseX() && p.preciseX() <= preciseX_ + preciseWidth_
+    && preciseY_ <= p.preciseY() && p.preciseY() <= preciseY_ + preciseHeight_;
+}
+
+/**
+ * @see org.eclipse.draw2d.geometry.Rectangle#preciseX()
+ */
+public double preciseX() {
+    return preciseX_;
+}
+
+/**
+ * @see org.eclipse.draw2d.geometry.Rectangle#preciseY()
+ */
+public double preciseY() {
+    return preciseY_;
+}
+
+/**
+ * @see org.eclipse.draw2d.geometry.Rectangle#preciseWidth()
+ */
+public double preciseWidth() {
+    return preciseWidth_;
+}
+
+/**
+ * @see org.eclipse.draw2d.geometry.Rectangle#preciseHeight()
+ */
+public double preciseHeight() {
+    return preciseHeight_;
+}
+
+/**
+ * @see org.eclipse.draw2d.geometry.Rectangle#setSize(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Ray.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.geometry.Ray;
+
+import java.lang.all;
+import tango.text.convert.Format;
+
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Rectangle.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,982 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.geometry.Rectangle;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Translatable;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.PositionConstants;
+import tango.text.convert.Format;
+
+static import org.eclipse.swt.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.*/
+private static Rectangle SINGLETON_;
+public static Rectangle SINGLETON(){
+    if( SINGLETON_ is null ){
+        synchronized( Rectangle.classinfo ){
+            if( SINGLETON_ is null ){
+                SINGLETON_ = new Rectangle();
+            }
+        }
+    }
+    return SINGLETON_;
+}
+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 SWT {@link org.eclipse.swt.graphics.Rectangle}.
+ *
+ * @param rect The SWT Rectangle being copied
+ * @since 2.0
+ */
+public this(org.eclipse.swt.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 org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Transform.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.geometry.Transform;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Translatable.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.geometry.Translatable;
+
+import java.lang.all;
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/geometry/Transposer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.geometry.Transposer;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/BreakCycles.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,279 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.BreakCycles;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.GraphVisitor;
+import org.eclipse.draw2d.graph.NodeList;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.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(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Cell.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.Cell;
+
+import java.lang.all;
+
+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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CollapsedEdges.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.CollapsedEdges;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CompoundBreakCycles.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.CompoundBreakCycles;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.GraphVisitor;
+import org.eclipse.draw2d.graph.NodeList;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.Subgraph;
+import org.eclipse.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(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CompoundDirectedGraph.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.CompoundDirectedGraph;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.NodeList;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CompoundDirectedGraphLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.CompoundDirectedGraphLayout;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.DirectedGraphLayout;
+import org.eclipse.draw2d.graph.TransposeMetrics;
+import org.eclipse.draw2d.graph.CompoundBreakCycles;
+import org.eclipse.draw2d.graph.RouteEdges;
+import org.eclipse.draw2d.graph.ConvertCompoundGraph;
+import org.eclipse.draw2d.graph.InitialRankSolver;
+import org.eclipse.draw2d.graph.TightSpanningTreeSolver;
+import org.eclipse.draw2d.graph.RankAssignmentSolver;
+import org.eclipse.draw2d.graph.CompoundPopulateRanks;
+import org.eclipse.draw2d.graph.CompoundVerticalPlacement;
+import org.eclipse.draw2d.graph.MinCross;
+import org.eclipse.draw2d.graph.CompoundRankSorter;
+import org.eclipse.draw2d.graph.SortSubgraphs;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CompoundHorizontalPlacement.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,175 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.CompoundHorizontalPlacement;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.HorizontalPlacement;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.RankList;
+import org.eclipse.draw2d.graph.Rank;
+import org.eclipse.draw2d.graph.Subgraph;
+import org.eclipse.draw2d.graph.SubgraphBoundary;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.NodeList;
+import org.eclipse.draw2d.graph.CompoundDirectedGraph;
+import org.eclipse.draw2d.graph.Edge;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CompoundPopulateRanks.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.CompoundPopulateRanks;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.PopulateRanks;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.Subgraph;
+import org.eclipse.draw2d.graph.CompoundDirectedGraph;
+import org.eclipse.draw2d.graph.Edge;
+import org.eclipse.draw2d.graph.NodeList;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CompoundRankSorter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.CompoundRankSorter;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.RankSorter;
+import org.eclipse.draw2d.graph.Subgraph;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.Rank;
+import org.eclipse.draw2d.graph.NestingTree;
+import org.eclipse.draw2d.graph.CompoundDirectedGraph;
+import org.eclipse.draw2d.graph.Edge;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/CompoundVerticalPlacement.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.CompoundVerticalPlacement;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.VerticalPlacement;
+import org.eclipse.draw2d.graph.CompoundDirectedGraph;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/ConvertCompoundGraph.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.ConvertCompoundGraph;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.graph.GraphVisitor;
+import org.eclipse.draw2d.graph.NodeList;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.Subgraph;
+import org.eclipse.draw2d.graph.SubgraphBoundary;
+import org.eclipse.draw2d.graph.CompoundDirectedGraph;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.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(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/DirectedGraph.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.DirectedGraph;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.PositionConstants;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.graph.EdgeList;
+import org.eclipse.draw2d.graph.NodeList;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.RankList;
+import org.eclipse.draw2d.graph.Edge;
+import org.eclipse.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 org.eclipse.draw2d.PositionConstants#EAST}
+ * <LI>{@link org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/DirectedGraphLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.DirectedGraphLayout;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.TransposeMetrics;
+import org.eclipse.draw2d.graph.BreakCycles;
+import org.eclipse.draw2d.graph.RouteEdges;
+import org.eclipse.draw2d.graph.InitialRankSolver;
+import org.eclipse.draw2d.graph.TightSpanningTreeSolver;
+import org.eclipse.draw2d.graph.RankAssignmentSolver;
+import org.eclipse.draw2d.graph.PopulateRanks;
+import org.eclipse.draw2d.graph.VerticalPlacement;
+import org.eclipse.draw2d.graph.MinCross;
+import org.eclipse.draw2d.graph.LocalOptimizer;
+import org.eclipse.draw2d.graph.HorizontalPlacement;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.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
+ * org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Edge.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.Edge;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.graph.NodeList;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/EdgeList.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.EdgeList;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/GraphUtilities.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.GraphUtilities;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.Subgraph;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.NodeList;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.Rank;
+import org.eclipse.draw2d.graph.Edge;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/GraphVisitor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.GraphVisitor;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/HorizontalPlacement.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,427 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.HorizontalPlacement;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.SpanningTreeVisitor;
+import org.eclipse.draw2d.graph.NodeCluster;
+import org.eclipse.draw2d.graph.NodePair;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.Edge;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.RankList;
+import org.eclipse.draw2d.graph.CollapsedEdges;
+import org.eclipse.draw2d.graph.Rank;
+import org.eclipse.draw2d.graph.EdgeList;
+import org.eclipse.draw2d.graph.InitialRankSolver;
+import org.eclipse.draw2d.graph.TightSpanningTreeSolver;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/InitialRankSolver.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.InitialRankSolver;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.GraphVisitor;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.NodeList;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.EdgeList;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/InvertEdges.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.InvertEdges;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.GraphVisitor;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.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(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/LocalOptimizer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.LocalOptimizer;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.GraphVisitor;
+import org.eclipse.draw2d.graph.GraphUtilities;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.Rank;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.EdgeList;
+import org.eclipse.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(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/MinCross.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.MinCross;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.RankSorter;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.GraphVisitor;
+import org.eclipse.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(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/NestingTree.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.NestingTree;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.Rank;
+import org.eclipse.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(cast(Object)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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Node.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,331 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.Node;
+
+import java.lang.all;
+import tango.text.convert.Format;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.graph.EdgeList;
+import org.eclipse.draw2d.graph.Subgraph;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/NodeCluster.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.NodeCluster;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.NodeList;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/NodeList.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.NodeList;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/NodePair.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.NodePair;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Obstacle.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.Obstacle;
+
+import java.lang.all;
+import tango.text.convert.Format;
+
+import org.eclipse.draw2d.PositionConstants;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.graph.Vertex;
+import org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Path.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,848 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.Path;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.PositionConstants;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.graph.Segment;
+import org.eclipse.draw2d.graph.Vertex;
+import org.eclipse.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 bool initStaticCtor_done = false;
+private static Point CURRENT;
+private static const double EPSILON = 1.04;
+private static Point NEXT;
+private static const double OVAL_CONSTANT = 1.13;
+
+private static void initStaticCtor(){
+    if( !initStaticCtor_done ){
+        synchronized( Path.classinfo ){
+            if( !initStaticCtor_done ){
+                CURRENT = new Point();
+                NEXT = new Point();
+                initStaticCtor_done = true;
+            }
+        }
+    }
+    assert( CURRENT );
+    assert( NEXT );
+}
+
+/**
+ * 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() {
+    initStaticCtor();
+    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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/PopulateRanks.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.PopulateRanks;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.RevertableChange;
+import org.eclipse.draw2d.graph.GraphVisitor;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.RankList;
+import org.eclipse.draw2d.graph.Rank;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.Edge;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Rank.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.Rank;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.SubgraphBoundary;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/RankAssignmentSolver.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,321 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.RankAssignmentSolver;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.SpanningTreeVisitor;
+import org.eclipse.draw2d.graph.EdgeList;
+import org.eclipse.draw2d.graph.Edge;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/RankList.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.RankList;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/RankSorter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.RankSorter;
+
+import java.lang.all;
+import org.eclipse.dwtxhelper.Random;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.Rank;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.EdgeList;
+import org.eclipse.draw2d.graph.Edge;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/RevertableChange.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.RevertableChange;
+
+import java.lang.all;
+
+/**
+ * @since 3.1
+ */
+class RevertableChange {
+
+void revert() { }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/RouteEdges.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.RouteEdges;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.graph.GraphVisitor;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.Edge;
+import org.eclipse.draw2d.graph.SubgraphBoundary;
+import org.eclipse.draw2d.graph.ShortestPathRouter;
+import org.eclipse.draw2d.graph.Path;
+import org.eclipse.draw2d.graph.VirtualNode;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Segment.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.Segment;
+
+import java.lang.all;
+import tango.text.convert.Format;
+
+import org.eclipse.draw2d.geometry.Geometry;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/ShortestPathRouter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,908 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.ShortestPathRouter;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.PositionConstants;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.graph.Path;
+import org.eclipse.draw2d.graph.Vertex;
+import org.eclipse.draw2d.graph.Segment;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/SortSubgraphs.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.SortSubgraphs;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.GraphVisitor;
+import org.eclipse.draw2d.graph.NestingTree;
+import org.eclipse.draw2d.graph.CompoundDirectedGraph;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.NodeList;
+import org.eclipse.draw2d.graph.NodePair;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.RankList;
+import org.eclipse.draw2d.graph.Rank;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/SpanningTreeVisitor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.SpanningTreeVisitor;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.GraphVisitor;
+import org.eclipse.draw2d.graph.Edge;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Subgraph.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.Subgraph;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.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 Insets NO_INSETS_;
+private static Insets NO_INSETS(){
+    if( NO_INSETS_ is null ){
+        synchronized( Subgraph.classinfo ){
+            if( NO_INSETS_ is null ){
+                NO_INSETS_ = new Insets();
+            }
+        }
+    }
+    assert( NO_INSETS_ );
+    return NO_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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/SubgraphBoundary.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.SubgraphBoundary;
+
+import java.lang.all;
+import tango.text.convert.Format;
+
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.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;
+        default:
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/TightSpanningTreeSolver.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.TightSpanningTreeSolver;
+
+import java.lang.all;
+import org.eclipse.draw2d.graph.SpanningTreeVisitor;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.Edge;
+import org.eclipse.draw2d.graph.EdgeList;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/TransposeMetrics.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.TransposeMetrics;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.PositionConstants;
+import org.eclipse.draw2d.geometry.Transposer;
+import org.eclipse.draw2d.graph.GraphVisitor;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.Edge;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/Vertex.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,223 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.graph.Vertex;
+
+import tango.text.convert.Format;
+import java.lang.all;
+
+import org.eclipse.draw2d.PositionConstants;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.graph.Obstacle;
+import org.eclipse.draw2d.graph.Path;
+import org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/VerticalPlacement.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.VerticalPlacement;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.graph.GraphVisitor;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.Rank;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/VirtualNode.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.VirtualNode;
+
+import java.lang.all;
+import tango.text.convert.Format;
+
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.Edge;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/graph/VirtualNodeCreation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.graph.VirtualNodeCreation;
+
+import java.lang.all;
+import tango.text.convert.Format;
+
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.graph.Edge;
+import org.eclipse.draw2d.graph.DirectedGraph;
+import org.eclipse.draw2d.graph.RevertableChange;
+import org.eclipse.draw2d.graph.Node;
+import org.eclipse.draw2d.graph.Subgraph;
+import org.eclipse.draw2d.graph.VirtualNode;
+import org.eclipse.draw2d.graph.GraphUtilities;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/internal/MultiValueMap.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.internal.MultiValueMap;
+
+import java.lang.all;
+
+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/org.eclipse.draw2d/src/org/eclipse/draw2d/parts/ScrollableThumbnail.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.parts.ScrollableThumbnail;
+
+import java.lang.all;
+import org.eclipse.dwtxhelper.Bean;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.FigureListener;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.KeyEvent;
+import org.eclipse.draw2d.KeyListener;
+import org.eclipse.draw2d.MouseEvent;
+import org.eclipse.draw2d.MouseListener;
+import org.eclipse.draw2d.MouseMotionListener;
+import org.eclipse.draw2d.Viewport;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.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 SWT.HOME || (isMirrored() ? ke.keycode is SWT.ARROW_RIGHT
+                    : ke.keycode is SWT.ARROW_LEFT))
+                viewport.setViewLocation(viewport.getViewLocation().translate(-moveX, 0));
+            else if (ke.keycode is SWT.END || (isMirrored() ? ke.keycode is SWT.ARROW_LEFT
+                    : ke.keycode is SWT.ARROW_RIGHT))
+                viewport.setViewLocation(viewport.getViewLocation().translate(moveX, 0));
+            else if (ke.keycode is SWT.ARROW_UP || ke.keycode is SWT.PAGE_UP)
+                viewport.setViewLocation(viewport.getViewLocation().translate(0, -moveY));
+            else if (ke.keycode is SWT.ARROW_DOWN  || ke.keycode is SWT.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/parts/Thumbnail.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,512 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.parts.Thumbnail;
+
+import java.lang.all;
+import org.eclipse.swt.dwthelper.Runnable;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+static import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.SWTGraphics;
+import org.eclipse.draw2d.ScaledGraphics;
+import org.eclipse.draw2d.UpdateListener;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.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);
+        org.eclipse.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() ? SWT.RIGHT_TO_LEFT : SWT.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 org.eclipse.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 org.eclipse.draw2d.UpdateListener#notifyValidating()
+ */
+public void notifyValidating() {
+//  setDirty(true);
+//  revalidate();
+}
+
+/**
+ * @see org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/AbstractFlowBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.AbstractFlowBorder;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.AbstractBorder;
+import org.eclipse.draw2d.Border;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.text.FlowBorder;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/BidiChars.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.BidiChars;
+
+import java.lang.all;
+
+/**
+ * @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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/BidiInfo.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.BidiInfo;
+
+import java.lang.all;
+
+/**
+ * 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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/BidiProcessor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,270 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.text.BidiProcessor;
+
+import java.lang.all;
+
+//import org.eclipse.dwtxhelper.UBidi;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.TextLayout;
+import org.eclipse.draw2d.text.FlowFigure;
+import org.eclipse.draw2d.text.BidiInfo;
+import org.eclipse.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 bool isMacOS(){
+    return SWT.getPlatform().equals("carbon"); //$NON-NLS-1$
+}
+
+/**
+ * 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.
+ */
+private static BidiProcessor INSTANCE_;
+public static BidiProcessor INSTANCE(){
+    if( INSTANCE_ is null ){
+        synchronized( BidiProcessor.classinfo ){
+            if( INSTANCE_ is null ){
+                INSTANCE_ = new BidiProcessor();
+            }
+        }
+    }
+    return INSTANCE_;
+}
+
+private StringBuffer bidiText;
+private List list;
+private int orientation = SWT.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 SWT.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: SWT 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 SWT.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(SWT.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 SWT.LEFT_TO_RIGHT or SWT.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/BlockBox.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.BlockBox;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.text.CompositeBox;
+import org.eclipse.draw2d.text.BlockFlow;
+import org.eclipse.draw2d.text.LineRoot;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/BlockFlow.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,312 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.text.BlockFlow;
+
+import java.lang.all;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.PositionConstants;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.text.FlowFigure;
+import org.eclipse.draw2d.text.BlockBox;
+import org.eclipse.draw2d.text.BidiProcessor;
+import org.eclipse.draw2d.text.FlowFigureLayout;
+import org.eclipse.draw2d.text.BidiChars;
+import org.eclipse.draw2d.text.BlockFlowLayout;
+import org.eclipse.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 = SWT.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 org.eclipse.draw2d.text.FlowFigure#contributeBidi(org.eclipse.draw2d.text.BidiProcessor)
+ */
+protected void contributeBidi(BidiProcessor proc) {
+    proc.addControlChar(BidiChars.P_SEP);
+}
+
+BlockBox createBlockBox() {
+    return new BlockBox(this);
+}
+
+/**
+ * @see org.eclipse.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 (SWT.RIGHT_TO_LEFT if mirrored; SWT.LEFT_TO_RIGHT
+ * otherwise).
+ *
+ * @return SWT.RIGHT_TO_LEFT or SWT.LEFT_TO_RIGHT
+ * @see #setOrientation(int)
+ * @since 3.1
+ */
+public int getOrientation() {
+    if (orientation !is SWT.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() ? SWT.RIGHT_TO_LEFT : SWT.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 org.eclipse.draw2d.Figure#paintBorder(org.eclipse.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, SWT.LEAD | SWT.TRAIL);
+    } else
+        super.paintBorder(graphics);
+    if (selectionStart !is -1) {
+        graphics.restoreState();
+        graphics.setXORMode(true);
+        graphics.setBackgroundColor(ColorConstants.white);
+        graphics.fillRectangle(getBounds());
+    }
+}
+
+/**
+ * @see org.eclipse.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 org.eclipse.draw2d.text.FlowFigure#revalidateBidi(org.eclipse.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 SWT#LEFT_TO_RIGHT}
+ *   <LI>{@link SWT#RIGHT_TO_LEFT}
+ *   <LI>{@link SWT#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 &= SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
+    if (this.orientation is orientation)
+        return;
+    this.orientation = orientation;
+    revalidateBidi(this);
+}
+
+/**
+ * @see org.eclipse.draw2d.Figure#useLocalCoordinates()
+ */
+protected bool useLocalCoordinates() {
+    return true;
+}
+
+/**
+ * Re-evaluate the Bidi state of all the fragments if it has been
+ * invalidated.
+ * @see org.eclipse.draw2d.IFigure#validate()
+ */
+public void validate() {
+    if (!bidiValid) {
+        BidiProcessor.INSTANCE.setOrientation(getOrientation());
+        if (getOrientation() is SWT.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/BlockFlowLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,250 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.text.BlockFlowLayout;
+
+import java.lang.all;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.PositionConstants;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.draw2d.text.FlowContainerLayout;
+import org.eclipse.draw2d.text.BlockBox;
+import org.eclipse.draw2d.text.BlockFlow;
+import org.eclipse.draw2d.text.CompositeBox;
+import org.eclipse.draw2d.text.FlowFigure;
+import org.eclipse.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 SWT.LEFT_TO_RIGHT
+                    ? PositionConstants.ALWAYS_LEFT : PositionConstants.ALWAYS_RIGHT;
+        else
+            alignment = orientation is SWT.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/CaretInfo.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.CaretInfo;
+
+import java.lang.all;
+
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/CompositeBox.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.CompositeBox;
+
+import java.lang.all;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/ContentBox.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.ContentBox;
+
+import java.lang.all;
+import org.eclipse.draw2d.text.FlowBox;
+import org.eclipse.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 org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowAdapter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.text.FlowAdapter;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.text.FlowFigure;
+import org.eclipse.draw2d.text.BidiProcessor;
+import org.eclipse.draw2d.text.FlowFigureLayout;
+import org.eclipse.draw2d.text.FlowContext;
+import org.eclipse.draw2d.text.ContentBox;
+import org.eclipse.draw2d.text.BidiInfo;
+import org.eclipse.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 org.eclipse.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 org.eclipse.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 org.eclipse.draw2d.IFigure#setBounds(org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.FlowBorder;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.Border;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.text.FlowFigure;
+
+/**
+ * Experimental API. This is a special type of border for use with {@link
+ * org.eclipse.draw2d.text.FlowFigure}s. This interface should not be implemented by
+ * clients. Clients should extend {@link org.eclipse.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 org.eclipse.swt.SWT#LEAD}
+ *   <LI>{@link org.eclipse.swt.SWT#TRAIL}
+ *   <LI>{@link org.eclipse.swt.SWT#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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowBox.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.FlowBox;
+
+import java.lang.all;
+import org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowContainerLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.text.FlowContainerLayout;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.text.FlowFigureLayout;
+import org.eclipse.draw2d.text.FlowBox;
+import org.eclipse.draw2d.text.LineBox;
+import org.eclipse.draw2d.text.FlowContext;
+import org.eclipse.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 org.eclipse.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 org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowContext.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.FlowContext;
+
+import java.lang.all;
+import org.eclipse.draw2d.text.FlowBox;
+import org.eclipse.draw2d.text.CompositeBox;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowFigure.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,192 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.text.FlowFigure;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.text.BidiProcessor;
+import org.eclipse.draw2d.text.FlowFigureLayout;
+import org.eclipse.draw2d.text.BidiInfo;
+import org.eclipse.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 org.eclipse.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 org.eclipse.draw2d.IFigure#remove(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowFigureLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.FlowFigureLayout;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.LayoutManager;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.text.FlowContext;
+import org.eclipse.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 org.eclipse.draw2d.LayoutManager#getConstraint(org.eclipse.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 org.eclipse.draw2d.LayoutManager#getMinimumSize(org.eclipse.draw2d.IFigure, int, int)
+ */
+public Dimension getMinimumSize(IFigure container, int wHint, int hHint) {
+    return null;
+}
+
+/**
+ * Not applicable.
+ * @see org.eclipse.draw2d.LayoutManager#getPreferredSize(org.eclipse.draw2d.IFigure, int, int)
+ */
+public Dimension getPreferredSize(IFigure container, int wHint, int hHint) {
+    return null;
+}
+
+/**
+ * Not applicable.
+ * @see org.eclipse.draw2d.LayoutManager#invalidate()
+ */
+public void invalidate() { }
+
+/**
+ * Called during {@link #layout(IFigure)}.
+ */
+protected abstract void layout();
+
+/**
+ * @see org.eclipse.draw2d.LayoutManager#layout(IFigure)
+ */
+public final void layout(IFigure figure) {
+    layout ();
+}
+
+/**
+ * Not applicable.
+ * @see org.eclipse.draw2d.LayoutManager#remove(org.eclipse.draw2d.IFigure)
+ */
+public void remove(IFigure child) { }
+
+/**
+ * Not applicable.
+ * @see org.eclipse.draw2d.LayoutManager#setConstraint(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowPage.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.FlowPage;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.text.BlockFlow;
+import org.eclipse.draw2d.text.FlowFigureLayout;
+import org.eclipse.draw2d.text.PageFlowLayout;
+import org.eclipse.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 org.eclipse.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 org.eclipse.draw2d.Figure#addNotify()
+ */
+public void addNotify() {
+    super.addNotify();
+    setValid(false);
+}
+
+/**
+ * @see org.eclipse.draw2d.text.BlockFlow#createDefaultFlowLayout()
+ */
+protected FlowFigureLayout createDefaultFlowLayout() {
+    return new PageFlowLayout(this);
+}
+
+/**
+ * @see org.eclipse.draw2d.Figure#getMinimumSize(int, int)
+ */
+public Dimension getMinimumSize(int w, int h) {
+    return getPreferredSize(w, h);
+}
+
+/**
+ * @see org.eclipse.draw2d.Figure#invalidate()
+ */
+public void invalidate() {
+    pageSizeCacheValues = new Dimension[3];
+    super.invalidate();
+}
+
+/**
+ * @see org.eclipse.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 org.eclipse.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 org.eclipse.draw2d.Figure#validate()
+ */
+public void validate() {
+    if (isValid())
+        return;
+    super.validate();
+    postValidate();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/text/FlowUtilities.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,440 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.text.FlowUtilities;
+
+import java.lang.all;
+
+import org.eclipse.dwtxhelper.mangoicu.UBreakIterator;
+import org.eclipse.dwtxhelper.mangoicu.ULocale;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.TextLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.draw2d.FigureUtilities;
+import org.eclipse.draw2d.TextUtilities;
+import org.eclipse.draw2d.text.TextFragmentBox;
+import org.eclipse.draw2d.text.InlineFlow;
+import org.eclipse.draw2d.text.FlowContext;
+import org.eclipse.draw2d.text.FlowBorder;
+import org.eclipse.draw2d.text.ParagraphTextLayout;
+import org.eclipse.draw2d.text.TextFlow;
+
+interface LookAhead {
+    int getWidth();
+}
+
+/**
+ * Utility class for FlowFigures.
+ * @author hudsonr
+ * @since 3.4
+ */
+public class FlowUtilities
+{
+
+/**
+ * a singleton default instance
+ */
+private static FlowUtilities INSTANCE_;
+public static FlowUtilities INSTANCE(){
+    if( INSTANCE_ is null ){
+        synchronized( FlowUtilities.classinfo ){
+            if( INSTANCE_ is null ){
+                INSTANCE_ = new FlowUtilities();
+            }
+        }
+    }
+    return INSTANCE_;
+}
+
+
+
+private static bool INTERNAL_LINE_BREAK_initialized = false;
+private static UBreakIterator INTERNAL_LINE_BREAK_;
+private static UBreakIterator INTERNAL_LINE_BREAK(){
+    if( !INTERNAL_LINE_BREAK_initialized ){
+        synchronized( FlowUtilities.classinfo ){
+            if( !INTERNAL_LINE_BREAK_initialized ){
+                INTERNAL_LINE_BREAK_ = UBreakIterator.openLineIterator( ULocale.Default );
+            }
+            INTERNAL_LINE_BREAK_initialized = true;
+        }
+    }
+    return INTERNAL_LINE_BREAK_;
+}
+
+private static TextLayout layout;
+
+private static bool LINE_BREAK_initialized = false;
+private static UBreakIterator LINE_BREAK_;
+static UBreakIterator LINE_BREAK(){
+    if( !LINE_BREAK_initialized ){
+        synchronized( FlowUtilities.classinfo ){
+            if( !LINE_BREAK_initialized ){
+                LINE_BREAK_ = UBreakIterator.openLineIterator( ULocale.Default );
+            }
+            LINE_BREAK_initialized = true;
+        }
+    }
+    return LINE_BREAK_;
+}
+
+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 SWT TextLayout that can be used for Bidi
+ * @since 3.1
+ */
+static TextLayout getTextLayout() {
+    if (layout is null)
+        layout = new TextLayout(Display.getDefault());
+    layout.setOrientation(SWT.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/InlineFlow.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.text.InlineFlow;
+
+import java.lang.all;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.draw2d.Border;
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.text.FlowFigure;
+import org.eclipse.draw2d.text.FlowFigureLayout;
+import org.eclipse.draw2d.text.FlowBox;
+import org.eclipse.draw2d.text.InlineFlowLayout;
+import org.eclipse.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 org.eclipse.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 = SWT.LEAD;
+            if (i is frags.size() - 1)
+                sides |= SWT.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/InlineFlowLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.InlineFlowLayout;
+
+import java.lang.all;
+import org.eclipse.draw2d.text.FlowContainerLayout;
+import org.eclipse.draw2d.text.FlowFigure;
+import org.eclipse.draw2d.text.LineBox;
+import org.eclipse.draw2d.text.CompositeBox;
+import org.eclipse.draw2d.text.NestedLine;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/LineBox.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.LineBox;
+
+import java.lang.all;
+import org.eclipse.draw2d.text.CompositeBox;
+import org.eclipse.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 org.eclipse.draw2d.text.CompositeBox#add(org.eclipse.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 org.eclipse.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 org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/LineRoot.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,274 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.text.LineRoot;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.text.LineBox;
+import org.eclipse.draw2d.text.FlowBox;
+import org.eclipse.draw2d.text.NestedLine;
+import org.eclipse.draw2d.text.ContentBox;
+import org.eclipse.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 org.eclipse.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 org.eclipse.draw2d.text.CompositeBox#add(org.eclipse.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 org.eclipse.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 org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/NestedLine.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.NestedLine;
+
+import java.lang.all;
+import org.eclipse.draw2d.text.LineBox;
+import org.eclipse.draw2d.text.LineRoot;
+import org.eclipse.draw2d.text.InlineFlow;
+import org.eclipse.draw2d.text.FlowUtilities;
+import org.eclipse.draw2d.text.FlowBox;
+
+/**
+ * @since 3.1
+ */
+public class NestedLine : LineBox {
+
+InlineFlow owner;
+private LineRoot root;
+
+this(InlineFlow owner) {
+    this.owner = owner;
+}
+
+/**
+ * @see org.eclipse.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 org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/PageFlowLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.PageFlowLayout;
+
+import java.lang.all;
+import org.eclipse.draw2d.text.BlockFlowLayout;
+import org.eclipse.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 org.eclipse.draw2d.text.BlockFlowLayout#getContextWidth()
+ */
+int getContextWidth() {
+    return (cast(FlowPage)getFlowFigure()).getPageWidth();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.draw2d/src/org/eclipse/draw2d/text/ParagraphTextLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.text.ParagraphTextLayout;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.draw2d.text.TextLayout;
+import org.eclipse.draw2d.text.TextFlow;
+import org.eclipse.draw2d.text.FlowUtilities;
+import org.eclipse.draw2d.text.FlowContext;
+import org.eclipse.draw2d.text.TextFragmentBox;
+import org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/SimpleTextLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.text.SimpleTextLayout;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.draw2d.text.TextLayout;
+import org.eclipse.draw2d.text.TextFlow;
+import org.eclipse.draw2d.text.TextFragmentBox;
+import org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/TextFlow.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,685 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.text.TextFlow;
+
+import java.lang.all;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.TextLayout;
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.TextUtilities;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.text.InlineFlow;
+import org.eclipse.draw2d.text.BidiInfo;
+import org.eclipse.draw2d.text.BidiProcessor;
+import org.eclipse.draw2d.text.FlowFigureLayout;
+import org.eclipse.draw2d.text.TextFragmentBox;
+import org.eclipse.draw2d.text.CaretInfo;
+import org.eclipse.draw2d.text.FlowUtilities;
+import org.eclipse.draw2d.text.ParagraphTextLayout;
+import org.eclipse.draw2d.text.BidiChars;
+import org.eclipse.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 org.eclipse.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 org.eclipse.draw2d.text.FlowFigure#contributeBidi(org.eclipse.draw2d.text.BidiProcessor)
+ */
+protected void contributeBidi(BidiProcessor proc) {
+    bidiInfo = null;
+    proc.add(this, getText());
+}
+
+/**
+ * @see org.eclipse.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 org.eclipse.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(org.eclipse.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(SWT.RIGHT_TO_LEFT);
+        tl.setFont(g.getFont());
+        tl.setText(draw);
+        g.drawTextLayout(tl, x, y);
+    }
+}
+
+/**
+ * @see org.eclipse.draw2d.text.FlowFigure#setBidiInfo(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/TextFragmentBox.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.TextFragmentBox;
+
+import java.lang.all;
+import org.eclipse.draw2d.text.ContentBox;
+import org.eclipse.draw2d.text.TextFlow;
+import org.eclipse.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 org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/text/TextLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.text.TextLayout;
+
+import java.lang.all;
+
+import org.eclipse.draw2d.text.FlowFigureLayout;
+import org.eclipse.draw2d.text.TextFlow;
+import org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/widgets/ImageBorder.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.draw2d.widgets.ImageBorder;
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.draw2d.AbstractBorder;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Insets;
+import org.eclipse.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 org.eclipse.draw2d.AbstractBorder#getPreferredSize(org.eclipse.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/org.eclipse.draw2d/src/org/eclipse/draw2d/widgets/MultiLineLabel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * 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 org.eclipse.draw2d.widgets.MultiLineLabel;
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.accessibility.ACC;
+import org.eclipse.swt.accessibility.AccessibleAdapter;
+import org.eclipse.swt.accessibility.AccessibleControlAdapter;
+import org.eclipse.swt.accessibility.AccessibleControlEvent;
+import org.eclipse.swt.accessibility.AccessibleEvent;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.draw2d.Border;
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.draw2d.FigureCanvas;
+import org.eclipse.draw2d.FocusEvent;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.MarginBorder;
+import org.eclipse.draw2d.Viewport;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.text.FlowPage;
+import org.eclipse.draw2d.text.TextFlow;
+import org.eclipse.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;
+private static Border MARGIN_;
+static Border MARGIN(){
+    if( MARGIN_ is null ){
+        synchronized( MultiLineLabel.classinfo ){
+            if( MARGIN_ is null ){
+                MARGIN_ = new MarginBorder(2);
+            }
+        }
+    }
+    return MARGIN_;
+}
+
+private Image image;
+
+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() & SWT.MIRRORED) !is 0;
+            if (e.keyCode is SWT.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 SWT.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 SWT.ARROW_RIGHT)
+                    || (mirrored && e.keyCode is SWT.ARROW_LEFT)) {
+                scrollToX(p.x + dx);
+                scrollToX(p.x + dx * 2);
+                scrollToX(p.x + dx * 3);
+            } else if ((!mirrored && e.keyCode is SWT.ARROW_LEFT)
+                    || (mirrored && e.keyCode is SWT.ARROW_RIGHT)) {
+                scrollToX(p.x - dx);
+                scrollToX(p.x - dx * 2);
+                scrollToX(p.x - dx * 3);
+            }
+        }
+    });
+}
+
+/**
+ * @see org.eclipse.swt.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 org.eclipse.swt.widgets.Canvas#setFont(org.eclipse.swt.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);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/IRuntimeConstants.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.internal.runtime.IRuntimeConstants;
+
+import java.lang.all;
+
+public interface IRuntimeConstants {
+
+    /**
+     * The unique identifier constant (value "<code>org.eclipse.core.runtime</code>")
+     * of the Core Runtime (pseudo-) plug-in.
+     */
+    public static const String PI_RUNTIME = "org.eclipse.core.runtime"; //$NON-NLS-1$
+
+    /**
+     * Name of this bundle.
+     */
+    public static const String PI_COMMON = "org.eclipse.equinox.common"; //$NON-NLS-1$
+
+    /**
+     * Status code constant (value 2) indicating an error occurred while running a plug-in.
+     */
+    public static const int PLUGIN_ERROR = 2;
+
+    /**
+     * Status code constant (value 5) indicating the platform could not write
+     * some of its metadata.
+     */
+    public static const int FAILED_WRITE_METADATA = 5;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/LocalizationUtils.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.core.internal.runtime.LocalizationUtils;
+
+import java.lang.all;
+
+/**
+ * Helper methods related to string localization.
+ *
+ * @since org.eclipse.equinox.common 3.3
+ */
+public class LocalizationUtils {
+    /**
+     * This method can be used in the absence of NLS class. The method tries to
+     * use the NLS-based translation routine. If it falls, the method returns the original
+     * non-translated key.
+     *
+     * @param key case-sensitive name of the filed in the translation file representing 
+     * the string to be translated
+     * @return The localized message or the non-translated key
+     */
+    static public String safeLocalize(String key) {
+//TODO: LocalizationUtils tries to load module CommonMessages. How to handle this?
+//         try {
+//             Class messageClass = Class.forName("org.eclipse.core.internal.runtime.CommonMessages"); //$NON-NLS-1$
+//             if (messageClass is null)
+//                 return key;
+//             Field field = messageClass.getDeclaredField(key);
+//             if (field is null)
+//                 return key;
+//             Object value = field.get(null);
+//             if (value instanceof String)
+//                 return (String) value;
+//         } catch (ClassNotFoundException e) {
+//             // eat exception and fall through
+//         } catch (NoClassDefFoundError e) {
+//             // eat exception and fall through
+//         } catch (SecurityException e) {
+//             // eat exception and fall through
+//         } catch (NoSuchFieldException e) {
+//             // eat exception and fall through
+//         } catch (IllegalArgumentException e) {
+//             // eat exception and fall through
+//         } catch (IllegalAccessException e) {
+//             // eat exception and fall through
+//         }
+        return key;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/PrintStackUtil.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.core.internal.runtime.PrintStackUtil;
+
+import org.eclipse.core.runtime.IStatus;
+import tango.io.stream.Format;
+import java.lang.all;
+
+public class PrintStackUtil {
+
+    static public void printChildren(IStatus status, FormatOutput!(char) output) {
+        IStatus[] children = status.getChildren();
+        if (children is null || children.length is 0)
+            return;
+        for (int i = 0; i < children.length; i++) {
+            output.formatln("Contains: {}", children[i].getMessage()); //$NON-NLS-1$
+            Exception exception = children[i].getException();
+            if (exception !is null)
+                ExceptionPrintStackTrace(exception);
+            printChildren(children[i], output);
+        }
+    }
+
+//     static public void printChildren(IStatus status, FormatOutput!(char) output) {
+//         IStatus[] children = status.getChildren();
+//         if (children is null || children.length is 0)
+//             return;
+//         for (int i = 0; i < children.length; i++) {
+//             output.formatln( "Contains: {}", children[i].getMessage()); //$NON-NLS-1$
+//             output.flush(); // call to synchronize output
+//             Exception exception = children[i].getException();
+//             if (exception !is null)
+//                 ExceptionPrintStackTrace(exception);
+//             printChildren(children[i], output);
+//         }
+//     }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/internal/runtime/RuntimeLog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Julian Chen - fix for bug #92572, jclRM
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.internal.runtime.RuntimeLog;
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.core.runtime.ILogListener;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.internal.runtime.IRuntimeConstants;
+
+/**
+ * NOT API!!!  This log infrastructure was split from the InternalPlatform.
+ *
+ * @since org.eclipse.equinox.common 3.2
+ */
+// XXX this must be removed and replaced with something more reasonable
+public final class RuntimeLog {
+
+    private static ArrayList logListeners;
+
+    /**
+     * Keep the messages until the first log listener is registered.
+     * Once first log listeners is registred, it is going to receive
+     * all status messages accumulated during the period when no log
+     * listener was available.
+     */
+    private static ArrayList queuedMessages;
+
+    static this(){
+        logListeners = new ArrayList(5);
+        queuedMessages = new ArrayList(5);
+    }
+
+    /**
+     * See org.eclipse.core.runtime.Platform#addLogListener(ILogListener)
+     */
+    public static void addLogListener(ILogListener listener) {
+        synchronized (logListeners) {
+            bool firstListener = (logListeners.size() is 0);
+            // replace if already exists (Set behaviour but we use an array
+            // since we want to retain order)
+            logListeners.remove(cast(Object)listener);
+            logListeners.add(cast(Object)listener);
+            if (firstListener) {
+                for (Iterator i = queuedMessages.iterator(); i.hasNext();) {
+                    try {
+                        IStatus recordedMessage = cast(IStatus) i.next();
+                        listener.logging(recordedMessage, IRuntimeConstants.PI_RUNTIME);
+                    } catch (Exception e) {
+                        handleException(e);
+// SWT Fixme
+//                     } catch (LinkageError e) {
+//                         handleException(e);
+                    }
+                }
+                queuedMessages.clear();
+            }
+        }
+    }
+
+    /**
+     * See org.eclipse.core.runtime.Platform#removeLogListener(ILogListener)
+     */
+    public static void removeLogListener(ILogListener listener) {
+        synchronized (logListeners) {
+            logListeners.remove(cast(Object)listener);
+        }
+    }
+
+    /**
+     * Checks if the given listener is present
+     */
+    public static bool contains(ILogListener listener) {
+        synchronized (logListeners) {
+            return logListeners.contains(cast(Object)listener);
+        }
+    }
+
+    /**
+     * Notifies all listeners of the platform log.
+     */
+    public static void log(IStatus status) {
+        // create array to avoid concurrent access
+        ILogListener[] listeners;
+        synchronized (logListeners) {
+            listeners = arraycast!(ILogListener)( logListeners.toArray());
+            if (listeners.length is 0) {
+                queuedMessages.add(cast(Object)status);
+                return;
+            }
+        }
+        for (int i = 0; i < listeners.length; i++) {
+            try {
+                listeners[i].logging(status, IRuntimeConstants.PI_RUNTIME);
+            } catch (Exception e) {
+                handleException(e);
+// SWT Fixme
+//             } catch (LinkageError e) {
+//                 handleException(e);
+            }
+        }
+    }
+
+    private static void handleException(Exception e) {
+        if (!(cast(OperationCanceledException)e )) {
+            // Got a error while logging. Don't try to log again, just put it into stderr
+            ExceptionPrintStackTrace(e);
+        }
+    }
+
+    /**
+     * Helps determine if any listeners are registered with the logging mechanism.
+     * @return true if no listeners are registered
+     */
+    public static bool isEmpty() {
+        synchronized (logListeners) {
+            return (logListeners.size() is 0);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/Assert.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.Assert;
+
+import org.eclipse.core.runtime.AssertionFailedException;
+
+import java.lang.all;
+
+/**
+ * <code>Assert</code> is useful for for embedding runtime sanity checks
+ * in code. The predicate methods all test a condition and throw some
+ * type of unchecked exception if the condition does not hold.
+ * <p>
+ * Assertion failure exceptions, like most runtime exceptions, are
+ * thrown when something is misbehaving. Assertion failures are invariably
+ * unspecified behavior; consequently, clients should never rely on
+ * these being thrown (and certainly should not being catching them
+ * specifically).
+ * </p><p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * This class is not intended to be instantiated or sub-classed by clients.
+ * </p>
+ * @since org.eclipse.equinox.common 3.2
+ * @noextend This class is not intended to be subclassed by clients.
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public final class Assert {
+    /* This class is not intended to be instantiated. */
+    private this() {
+        // not allowed
+    }
+
+    /** Asserts that an argument is legal. If the given bool is
+     * not <code>true</code>, an <code>IllegalArgumentException</code>
+     * is thrown.
+     *
+     * @param expression the outcode of the check
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     * @exception IllegalArgumentException if the legality test failed
+     */
+    public static bool isLegal(bool expression) {
+        return isLegal(expression, ""); //$NON-NLS-1$
+    }
+
+    /** Asserts that an argument is legal. If the given bool is
+     * not <code>true</code>, an <code>IllegalArgumentException</code>
+     * is thrown.
+     * The given message is included in that exception, to aid debugging.
+     *
+     * @param expression the outcode of the check
+     * @param message the message to include in the exception
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     * @exception IllegalArgumentException if the legality test failed
+     */
+    public static bool isLegal(bool expression, String message) {
+        if (!expression)
+            throw new IllegalArgumentException(message);
+        return expression;
+    }
+
+    /** Asserts that the given object is not <code>null</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     *
+     * @param object the value to test
+     */
+    public static void isNotNull(Object object) {
+        isNotNull(object, ""); //$NON-NLS-1$
+    }
+    public static void isNotNull(String object) {
+        isTrue(object.ptr !is null); //$NON-NLS-1$
+    }
+
+    /** Asserts that the given object is not <code>null</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     * The given message is included in that exception, to aid debugging.
+     *
+     * @param object the value to test
+     * @param message the message to include in the exception
+     */
+    public static void isNotNull(Object object, String message) {
+        if (object is null)
+            throw new AssertionFailedException("null argument:" ~ message); //$NON-NLS-1$
+    }
+
+    /** Asserts that the given bool is <code>true</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     *
+     * @param expression the outcode of the check
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     */
+    public static bool isTrue(bool expression) {
+        return isTrue(expression, ""); //$NON-NLS-1$
+    }
+
+    /** Asserts that the given bool is <code>true</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     * The given message is included in that exception, to aid debugging.
+     *
+     * @param expression the outcode of the check
+     * @param message the message to include in the exception
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     */
+    public static bool isTrue(bool expression, String message) {
+        if (!expression)
+            throw new AssertionFailedException("assertion failed: " ~ message); //$NON-NLS-1$
+        return expression;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/AssertionFailedException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.AssertionFailedException;
+
+import java.lang.all;
+version(JIVE) import jive.stacktrace;
+
+/**
+ * <code>AssertionFailedException</code> is a runtime exception thrown
+ * by some of the methods in <code>Assert</code>.
+ * <p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * This class is not intended to be instantiated or sub-classed by clients.
+ * </p>
+ * @see Assert
+ * @since org.eclipse.equinox.common 3.2
+ * @noextend This class is not intended to be subclassed by clients.
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public class AssertionFailedException : RuntimeException {
+
+    /**
+     * All serializable objects should have a stable serialVersionUID
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Constructs a new exception with the given message.
+     *
+     * @param detail the message
+     */
+    public this(String detail) {
+        super(detail);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/CoreException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.core.runtime.IStatus;
+
+// import java.io.PrintStream;
+// import java.io.PrintWriter;
+
+import java.lang.all;
+import tango.io.Stdout;
+
+/**
+ * A checked exception representing a failure.
+ * <p>
+ * Core exceptions contain a status object describing the
+ * cause of the exception.
+ * </p><p>
+ * This class can be used without OSGi running.
+ * </p>
+ * @see IStatus
+ */
+public class CoreException : Exception {
+
+    /**
+     * All serializable objects should have a stable serialVersionUID
+     */
+    private static const long serialVersionUID = 1L;
+
+    /** Status object. */
+    private IStatus status;
+
+    /**
+     * Creates a new exception with the given status object.  The message
+     * of the given status is used as the exception message.
+     *
+     * @param status the status object to be associated with this exception
+     */
+    public this(IStatus status) {
+        super(status.getMessage());
+        this.status = status;
+    }
+
+    /**
+      * Returns the cause of this exception, or <code>null</code> if none.
+      *
+      * @return the cause for this exception
+      * @since 3.4
+      */
+    public Exception getCause() {
+        return status.getException();
+    }
+
+    /**
+     * Returns the status object for this exception.
+     * <p>
+     *   <b>IMPORTANT:</b><br>
+     *   The result must NOT be used to log a <code>CoreException</code>
+     *   (e.g., using <code>yourPlugin.getLog().log(status);</code>),
+     *   since that code pattern hides the original stacktrace.
+     *   Instead, create a new {@link Status} with your plug-in ID and
+     *   this <code>CoreException</code>, and log that new status.
+     * </p>
+     *
+     * @return a status object
+     */
+    public final IStatus getStatus() {
+        return status;
+    }
+
+    /**
+     * Prints a stack trace out for the exception, and
+     * any nested exception that it may have embedded in
+     * its Status object.
+     */
+    public void printStackTrace() {
+//         printStackTrace(System.err);
+        Stderr.formatln( "Exception in File {}({}): {}", this.file, this.line, this.msg );
+        foreach( msg; this.info ){
+            Stderr.formatln( "    trc: {}", msg );
+        }
+        if (status.getException() !is null) {
+            Stderr.formatln( "{}[{}]: ", this.classinfo.name, status.getCode() ); //$NON-NLS-1$ //$NON-NLS-2$
+//             status.getException().printStackTrace();
+                auto e = status.getException();
+                Stderr.formatln( "Exception in File {}({}): {}", e.file, e.line, e.msg );
+                foreach( msg; e.info ){
+                    Stderr.formatln( "    trc: {}", msg );
+                }
+        }
+    }
+
+//FIXME
+//     /**
+//      * Prints a stack trace out for the exception, and
+//      * any nested exception that it may have embedded in
+//      * its Status object.
+//      *
+//      * @param output the stream to write to
+//      */
+//     public void printStackTrace(PrintStream output) {
+//         synchronized (output) {
+//             super.printStackTrace(output);
+//             if (status.getException() !is null) {
+//                 output.print(getClass().getName() + "[" + status.getCode() + "]: "); //$NON-NLS-1$ //$NON-NLS-2$
+//                 status.getException().printStackTrace(output);
+//             }
+//         }
+//     }
+//
+//     /**
+//      * Prints a stack trace out for the exception, and
+//      * any nested exception that it may have embedded in
+//      * its Status object.
+//      *
+//      * @param output the stream to write to
+//      */
+//     public void printStackTrace(PrintWriter output) {
+//         synchronized (output) {
+//             super.printStackTrace(output);
+//             if (status.getException() !is null) {
+//                 output.print(getClass().getName() + "[" + status.getCode() + "]: "); //$NON-NLS-1$ //$NON-NLS-2$
+//                 status.getException().printStackTrace(output);
+//             }
+//         }
+//     }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/IAdaptable.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.IAdaptable;
+
+import java.lang.all;
+
+/**
+ * An interface for an adaptable object.
+ * <p>
+ * Adaptable objects can be dynamically extended to provide different
+ * interfaces (or "adapters").  Adapters are created by adapter
+ * factories, which are in turn managed by type by adapter managers.
+ * </p>
+ * For example,
+ * <pre>
+ *     IAdaptable a = [some adaptable];
+ *     IFoo x = (IFoo)a.getAdapter(IFoo.class);
+ *     if (x !is null)
+ *         [do IFoo things with x]
+ * </pre>
+ * <p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * Clients may implement this interface, or obtain a default implementation
+ * of this interface by subclassing <code>PlatformObject</code>.
+ * </p>
+ * @see IAdapterFactory
+ * @see IAdapterManager
+ * @see PlatformObject
+ */
+public interface IAdaptable {
+    /**
+     * Returns an object which is an instance of the given class
+     * associated with this object. Returns <code>null</code> if
+     * no such object can be found.
+     *
+     * @param adapter the adapter class to look up
+     * @return a object castable to the given class,
+     *    or <code>null</code> if this object does not
+     *    have an adapter for the given class
+     */
+    public Object getAdapter(ClassInfo adapter);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/IAdapterFactory.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.IAdapterFactory;
+
+import java.lang.all;
+
+/**
+ * An adapter factory defines behavioral extensions for
+ * one or more classes that implements the <code>IAdaptable</code>
+ * interface. Adapter factories are registered with an
+ * adapter manager.
+ * <p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * Clients may implement this interface.
+ * </p>
+ * @see IAdapterManager
+ * @see IAdaptable
+ */
+public interface IAdapterFactory {
+    /**
+     * Returns an object which is an instance of the given class
+     * associated with the given object. Returns <code>null</code> if
+     * no such object can be found.
+     *
+     * @param adaptableObject the adaptable object being queried
+     *   (usually an instance of <code>IAdaptable</code>)
+     * @param adapterType the type of adapter to look up
+     * @return a object castable to the given adapter type,
+     *    or <code>null</code> if this adapter factory
+     *    does not have an adapter of the given type for the
+     *    given object
+     */
+    public Object getAdapter(Object adaptableObject, ClassInfo adapterType);
+
+    /**
+     * Returns the collection of adapter types handled by this
+     * factory.
+     * <p>
+     * This method is generally used by an adapter manager
+     * to discover which adapter types are supported, in advance
+     * of dispatching any actual <code>getAdapter</code> requests.
+     * </p>
+     *
+     * @return the collection of adapter types
+     */
+    public ClassInfo[] getAdapterList();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/IAdapterManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,268 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.IAdapterManager;
+
+import org.eclipse.core.runtime.IAdapterFactory;
+
+import java.lang.all;
+
+/**
+ * An adapter manager maintains a registry of adapter factories. Clients
+ * directly invoke methods on an adapter manager to register and unregister
+ * adapters. All adaptable objects (that is, objects that implement the <code>IAdaptable</code>
+ * interface) funnel <code>IAdaptable.getAdapter</code> invocations to their
+ * adapter manager's <code>IAdapterManger.getAdapter</code> method. The
+ * adapter manager then forwards this request unmodified to the <code>IAdapterFactory.getAdapter</code>
+ * method on one of the registered adapter factories.
+ * <p>
+ * Adapter factories can be registered programmatically using the <code>registerAdapters</code>
+ * method.  Alternatively, they can be registered declaratively using the
+ * <code>org.eclipse.core.runtime.adapters</code> extension point.  Factories registered
+ * with this extension point will not be able to provide adapters until their
+ * corresponding plugin has been activated.
+ * <p>
+ * The following code snippet shows how one might register an adapter of type
+ * <code>com.example.acme.Sticky</code> on resources in the workspace.
+ * <p>
+ *
+ * <pre>
+ *  IAdapterFactory pr = new IAdapterFactory() {
+ *      public Class[] getAdapterList() {
+ *          return new Class[] { com.example.acme.Sticky.class };
+ *      }
+ *      public Object getAdapter(Object adaptableObject, Class adapterType) {
+ *          IResource res = (IResource) adaptableObject;
+ *          QualifiedName key = new QualifiedName(&quot;com.example.acme&quot;, &quot;sticky-note&quot;);
+ *          try {
+ *              com.example.acme.Sticky v = (com.example.acme.Sticky) res.getSessionProperty(key);
+ *              if (v is null) {
+ *                  v = new com.example.acme.Sticky();
+ *                  res.setSessionProperty(key, v);
+ *              }
+ *          } catch (CoreException e) {
+ *              // unable to access session property - ignore
+ *          }
+ *          return v;
+ *      }
+ *  }
+ *  Platform.getAdapterManager().registerAdapters(pr, IResource.class);
+ *   </pre>
+ *
+ * </p><p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * This interface is not intended to be implemented by clients.
+ * </p>
+ * @see IAdaptable
+ * @see IAdapterFactory
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IAdapterManager {
+
+    /**
+     * This value can be returned to indicate that no applicable adapter factory
+     * was found.
+     * @since org.eclipse.equinox.common 3.3
+     */
+    public static const int NONE = 0;
+
+    /**
+     * This value can be returned to indicate that an adapter factory was found,
+     * but has not been loaded.
+     * @since org.eclipse.equinox.common 3.3
+     */
+    public static const int NOT_LOADED = 1;
+
+    /**
+     * This value can be returned to indicate that an adapter factory is loaded.
+     * @since org.eclipse.equinox.common 3.3
+     */
+    public static const int LOADED = 2;
+
+    /**
+     * Returns the types that can be obtained by converting <code>adaptableClass</code>
+     * via this manager. Converting means that subsequent calls to <code>getAdapter()</code>
+     * or <code>loadAdapter()</code> could result in an adapted object.
+     * <p>
+     * Note that the returned types do not guarantee that
+     * a subsequent call to <code>getAdapter</code> with the same type as an argument
+     * will return a non-null result. If the factory's plug-in has not yet been
+     * loaded, or if the factory itself returns <code>null</code>, then
+     * <code>getAdapter</code> will still return <code>null</code>.
+     * </p>
+     * @param adaptableClass the adaptable class being queried
+     * @return an array of type names that can be obtained by converting
+     * <code>adaptableClass</code> via this manager. An empty array
+     * is returned if there are none.
+     * @since 3.1
+     */
+    public String[] computeAdapterTypes(ClassInfo adaptableClass);
+
+    /**
+     * Returns the class search order for a given class. The search order from a
+     * class with the definition <br>
+     * <code>class X extends Y implements A, B</code><br>
+     * is as follows:
+     * <ul>
+     * <li>the target's class: X
+     * <li>X's superclasses in order to <code>Object</code>
+     * <li>a breadth-first traversal of the target class's interfaces in the
+     * order returned by <code>getInterfaces</code> (in the example, A and its
+     * superinterfaces then B and its superinterfaces) </li>
+     * </ul>
+     *
+     * @param clazz the class for which to return the class order.
+     * @return the class search order for the given class. The returned
+     * search order will minimally  contain the target class.
+     * @since 3.1
+     */
+    public ClassInfo[] computeClassOrder(ClassInfo clazz);
+
+    /**
+     * Returns an object which is an instance of the given class associated
+     * with the given object. Returns <code>null</code> if no such object can
+     * be found.
+     * <p>
+     * Note that this method will never cause plug-ins to be loaded. If the
+     * only suitable factory is not yet loaded, this method will return <code>null</code>.
+     *
+     * @param adaptable the adaptable object being queried (usually an instance
+     * of <code>IAdaptable</code>)
+     * @param adapterType the type of adapter to look up
+     * @return an object castable to the given adapter type, or <code>null</code>
+     * if the given adaptable object does not have an available adapter of the
+     * given type
+     */
+    public Object getAdapter(Object adaptable, ClassInfo adapterType);
+
+    /**
+     * Returns an object which is an instance of the given class name associated
+     * with the given object. Returns <code>null</code> if no such object can
+     * be found.
+     * <p>
+     * Note that this method will never cause plug-ins to be loaded. If the
+     * only suitable factory is not yet loaded, this method will return <code>null</code>.
+     * If activation of the plug-in providing the factory is required, use the
+     * <code>loadAdapter</code> method instead.
+     *
+     * @param adaptable the adaptable object being queried (usually an instance
+     * of <code>IAdaptable</code>)
+     * @param adapterTypeName the fully qualified name of the type of adapter to look up
+     * @return an object castable to the given adapter type, or <code>null</code>
+     * if the given adaptable object does not have an available adapter of the
+     * given type
+     * @since 3.0
+     */
+    public Object getAdapter(Object adaptable, String adapterTypeName);
+
+    /**
+     * Returns whether there is an adapter factory registered that may be able
+     * to convert <code>adaptable</code> to an object of type <code>adapterTypeName</code>.
+     * <p>
+     * Note that a return value of <code>true</code> does not guarantee that
+     * a subsequent call to <code>getAdapter</code> with the same arguments
+     * will return a non-null result. If the factory's plug-in has not yet been
+     * loaded, or if the factory itself returns <code>null</code>, then
+     * <code>getAdapter</code> will still return <code>null</code>.
+     *
+     * @param adaptable the adaptable object being queried (usually an instance
+     * of <code>IAdaptable</code>)
+     * @param adapterTypeName the fully qualified class name of an adapter to
+     * look up
+     * @return <code>true</code> if there is an adapter factory that claims
+     * it can convert <code>adaptable</code> to an object of type <code>adapterType</code>,
+     * and <code>false</code> otherwise.
+     * @since 3.0
+     */
+    public bool hasAdapter(Object adaptable, String adapterTypeName);
+
+    /**
+     * Returns a status of an adapter factory registered that may be able
+     * to convert <code>adaptable</code> to an object of type <code>adapterTypeName</code>.
+     * <p>
+     * One of the following values can be returned:<ul>
+     * <li>{@link org.eclipse.core.runtime.IAdapterManager#NONE} if no applicable adapter factory was found;</li>
+     * <li>{@link org.eclipse.core.runtime.IAdapterManager#NOT_LOADED} if an adapter factory was found, but has not been loaded;</li>
+     * <li>{@link org.eclipse.core.runtime.IAdapterManager#LOADED} if an adapter factory was found, and it is loaded.</li>
+     * </ul></p>
+     * @param adaptable the adaptable object being queried (usually an instance
+     * of <code>IAdaptable</code>)
+     * @param adapterTypeName the fully qualified class name of an adapter to
+     * look up
+     * @return a status of the adapter
+     * @since org.eclipse.equinox.common 3.3
+     */
+    public int queryAdapter(Object adaptable, String adapterTypeName);
+
+    /**
+     * Returns an object that is an instance of the given class name associated
+     * with the given object. Returns <code>null</code> if no such object can
+     * be found.
+     * <p>
+     * Note that unlike the <code>getAdapter</code> methods, this method
+     * will cause the plug-in that contributes the adapter factory to be loaded
+     * if necessary. As such, this method should be used judiciously, in order
+     * to avoid unnecessary plug-in activations. Most clients should avoid
+     * activation by using <code>getAdapter</code> instead.
+     *
+     * @param adaptable the adaptable object being queried (usually an instance
+     * of <code>IAdaptable</code>)
+     * @param adapterTypeName the fully qualified name of the type of adapter to look up
+     * @return an object castable to the given adapter type, or <code>null</code>
+     * if the given adaptable object does not have an available adapter of the
+     * given type
+     * @since 3.0
+     */
+    public Object loadAdapter(Object adaptable, String adapterTypeName);
+
+    /**
+     * Registers the given adapter factory as extending objects of the given
+     * type.
+     * <p>
+     * If the type being extended is a class, the given factory's adapters are
+     * available on instances of that class and any of its subclasses. If it is
+     * an interface, the adapters are available to all classes that directly or
+     * indirectly implement that interface.
+     * </p>
+     *
+     * @param factory the adapter factory
+     * @param adaptable the type being extended
+     * @see #unregisterAdapters(IAdapterFactory)
+     * @see #unregisterAdapters(IAdapterFactory, Class)
+     */
+    public void registerAdapters(IAdapterFactory factory, ClassInfo adaptable);
+
+    /**
+     * Removes the given adapter factory completely from the list of registered
+     * factories. Equivalent to calling <code>unregisterAdapters(IAdapterFactory,Class)</code>
+     * on all classes against which it had been explicitly registered. Does
+     * nothing if the given factory is not currently registered.
+     *
+     * @param factory the adapter factory to remove
+     * @see #registerAdapters(IAdapterFactory, Class)
+     */
+    public void unregisterAdapters(IAdapterFactory factory);
+
+    /**
+     * Removes the given adapter factory from the list of factories registered
+     * as extending the given class. Does nothing if the given factory and type
+     * combination is not registered.
+     *
+     * @param factory the adapter factory to remove
+     * @param adaptable one of the types against which the given factory is
+     * registered
+     * @see #registerAdapters(IAdapterFactory, Class)
+     */
+    public void unregisterAdapters(IAdapterFactory factory, ClassInfo adaptable);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/ILogListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.ILogListener;
+
+import org.eclipse.core.runtime.IStatus;
+
+import java.lang.all;
+import java.util.EventListener;
+
+/**
+ * A log listener is notified of entries added to a plug-in's log.
+ * <p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * Clients may implement this interface.
+ * </p>
+ */
+public interface ILogListener : EventListener {
+    /**
+     * Notifies this listener that given status has been logged by
+     * a plug-in.  The listener is free to retain or ignore this status.
+     *
+     * @param status the status being logged
+     * @param plugin the plugin of the log which generated this event
+     */
+    public void logging(IStatus status, String plugin);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/IPath.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,512 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.IPath;
+
+import java.lang.all;
+import tango.io.FilePath;
+
+/**
+ * A path is an ordered collection of string segments,
+ * separated by a standard separator character, "/".
+ * A path may also have a leading and/or a trailing separator.
+ * Paths may also be prefixed by an optional device id, which includes
+ * the character(s) which separate the device id from the rest
+ * of the path. For example, "C:" and "Server/Volume:" are typical
+ * device ids.
+ * A device independent path has <code>null</code> for a device id.
+ * <p>
+ * Note that paths are value objects; all operations on paths
+ * return a new path; the path that is operated on is unscathed.
+ * </p>
+ * <p>
+ * UNC paths are denoted by leading double-slashes such
+ * as <code>//Server/Volume/My/Path</code>. When a new path
+ * is constructed all double-slashes are removed except those
+ * appearing at the beginning of the path.
+ * </p>
+ * <p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * This interface is not intended to be implemented by clients.
+ * </p>
+ * @see Path
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IPath : Cloneable {
+
+    /**
+     * Path separator character constant "/" used in paths.
+     */
+    public static const char SEPARATOR = '/';
+
+    /**
+     * Device separator character constant ":" used in paths.
+     */
+    public static const char DEVICE_SEPARATOR = ':';
+
+    /**
+     * Returns a new path which is the same as this path but with
+     * the given file extension added.  If this path is empty, root or has a
+     * trailing separator, this path is returned.  If this path already
+     * has an extension, the existing extension is left and the given
+     * extension simply appended.  Clients wishing to replace
+     * the current extension should first remove the extension and
+     * then add the desired one.
+     * <p>
+     * The file extension portion is defined as the string
+     * following the last period (".") character in the last segment.
+     * The given extension should not include a leading ".".
+     * </p>
+     *
+     * @param extension the file extension to append
+     * @return the new path
+     */
+    public IPath addFileExtension(String extension);
+
+    /**
+     * Returns a path with the same segments as this path
+     * but with a trailing separator added.
+     * This path must have at least one segment.
+     * <p>
+     * If this path already has a trailing separator,
+     * this path is returned.
+     * </p>
+     *
+     * @return the new path
+     * @see #hasTrailingSeparator()
+     * @see #removeTrailingSeparator()
+     */
+    public IPath addTrailingSeparator();
+
+    /**
+     * Returns the canonicalized path obtained from the
+     * concatenation of the given string path to the
+     * end of this path. The given string path must be a valid
+     * path. If it has a trailing separator,
+     * the result will have a trailing separator.
+     * The device id of this path is preserved (the one
+     * of the given string is ignored). Duplicate slashes
+     * are removed from the path except at the beginning
+     * where the path is considered to be UNC.
+     *
+     * @param path the string path to concatenate
+     * @return the new path
+     * @see #isValidPath(String)
+     */
+    public IPath append(String path);
+
+    /**
+     * Returns the canonicalized path obtained from the
+     * concatenation of the given path's segments to the
+     * end of this path.  If the given path has a trailing
+     * separator, the result will have a trailing separator.
+     * The device id of this path is preserved (the one
+     * of the given path is ignored). Duplicate slashes
+     * are removed from the path except at the beginning
+     * where the path is considered to be UNC.
+     *
+     * @param path the path to concatenate
+     * @return the new path
+     */
+    public IPath append(IPath path);
+
+    /**
+     * Returns a copy of this path.
+     *
+     * @return the cloned path
+     */
+    public Object clone();
+
+    /**
+     * Returns whether this path equals the given object.
+     * <p>
+     * Equality for paths is defined to be: same sequence of segments,
+     * same absolute/relative status, and same device.
+     * Trailing separators are disregarded.
+     * Paths are not generally considered equal to objects other than paths.
+     * </p>
+     *
+     * @param obj the other object
+     * @return <code>true</code> if the paths are equivalent,
+     *    and <code>false</code> if they are not
+     */
+    public int opEquals(Object obj);
+
+    /**
+     * Returns the device id for this path, or <code>null</code> if this
+     * path has no device id. Note that the result will end in ':'.
+     *
+     * @return the device id, or <code>null</code>
+     * @see #setDevice(String)
+     */
+    public String getDevice();
+
+    /**
+     * Returns the file extension portion of this path,
+     * or <code>null</code> if there is none.
+     * <p>
+     * The file extension portion is defined as the string
+     * following the last period (".") character in the last segment.
+     * If there is no period in the last segment, the path has no
+     * file extension portion. If the last segment ends in a period,
+     * the file extension portion is the empty string.
+     * </p>
+     *
+     * @return the file extension or <code>null</code>
+     */
+    public String getFileExtension();
+
+    /**
+     * Returns whether this path has a trailing separator.
+     * <p>
+     * Note: In the root path ("/"), the separator is considered to
+     * be leading rather than trailing.
+     * </p>
+     *
+     * @return <code>true</code> if this path has a trailing
+     *    separator, and <code>false</code> otherwise
+     * @see #addTrailingSeparator()
+     * @see #removeTrailingSeparator()
+     */
+    public bool hasTrailingSeparator();
+
+    /**
+     * Returns whether this path is an absolute path (ignoring
+     * any device id).
+     * <p>
+     * Absolute paths start with a path separator.
+     * A root path, like <code>/</code> or <code>C:/</code>,
+     * is considered absolute.  UNC paths are always absolute.
+     * </p>
+     *
+     * @return <code>true</code> if this path is an absolute path,
+     *    and <code>false</code> otherwise
+     */
+    public bool isAbsolute();
+
+    /**
+     * Returns whether this path has no segments and is not
+     * a root path.
+     *
+     * @return <code>true</code> if this path is empty,
+     *    and <code>false</code> otherwise
+     */
+    public bool isEmpty();
+
+    /**
+     * Returns whether this path is a prefix of the given path.
+     * To be a prefix, this path's segments must
+     * appear in the argument path in the same order,
+     * and their device ids must match.
+     * <p>
+     * An empty path is a prefix of all paths with the same device; a root path is a prefix of
+     * all absolute paths with the same device.
+     * </p>
+     * @param anotherPath the other path
+     * @return <code>true</code> if this path is a prefix of the given path,
+     *    and <code>false</code> otherwise
+     */
+    public bool isPrefixOf(IPath anotherPath);
+
+    /**
+     * Returns whether this path is a root path.
+     * <p>
+     * The root path is the absolute non-UNC path with zero segments;
+     * e.g., <code>/</code> or <code>C:/</code>.
+     * The separator is considered a leading separator, not a trailing one.
+     * </p>
+     *
+     * @return <code>true</code> if this path is a root path,
+     *    and <code>false</code> otherwise
+     */
+    public bool isRoot();
+
+    /**
+     * Returns a bool value indicating whether or not this path
+     * is considered to be in UNC form. Return false if this path
+     * has a device set or if the first 2 characters of the path string
+     * are not <code>Path.SEPARATOR</code>.
+     *
+     * @return bool indicating if this path is UNC
+     */
+    public bool isUNC();
+
+    /**
+     * Returns whether the given string is syntactically correct as
+     * a path.  The device id is the prefix up to and including the device
+     * separator for the local file system; the path proper is everything to
+     * the right of it, or the entire string if there is no device separator.
+     * When the platform location is a file system with no meaningful device
+     * separator, the entire string is treated as the path proper.
+     * The device id is not checked for validity; the path proper is correct
+     * if each of the segments in its canonicalized form is valid.
+     *
+     * @param path the path to check
+     * @return <code>true</code> if the given string is a valid path,
+     *    and <code>false</code> otherwise
+     * @see #isValidSegment(String)
+     */
+    public bool isValidPath(String path);
+
+    /**
+     * Returns whether the given string is valid as a segment in
+     * a path. The rules for valid segments are as follows:
+     * <ul>
+     * <li> the empty string is not valid
+     * <li> any string containing the slash character ('/') is not valid
+     * <li>any string containing segment or device separator characters
+     * on the local file system, such as the backslash ('\') and colon (':')
+     * on some file systems.
+     * </ul>
+     *
+     * @param segment the path segment to check
+     * @return <code>true</code> if the given path segment is valid,
+     *    and <code>false</code> otherwise
+     */
+    public bool isValidSegment(String segment);
+
+    /**
+     * Returns the last segment of this path, or
+     * <code>null</code> if it does not have any segments.
+     *
+     * @return the last segment of this path, or <code>null</code>
+     */
+    public String lastSegment();
+
+    /**
+     * Returns an absolute path with the segments and device id of this path.
+     * Absolute paths start with a path separator. If this path is absolute,
+     * it is simply returned.
+     *
+     * @return the new path
+     */
+    public IPath makeAbsolute();
+
+    /**
+     * Returns a relative path with the segments and device id of this path.
+     * Absolute paths start with a path separator and relative paths do not.
+     * If this path is relative, it is simply returned.
+     *
+     * @return the new path
+     */
+    public IPath makeRelative();
+
+    /**
+     * Return a new path which is the equivalent of this path converted to UNC
+     * form (if the given bool is true) or this path not as a UNC path (if the given
+     * bool is false). If UNC, the returned path will not have a device and the
+     * first 2 characters of the path string will be <code>Path.SEPARATOR</code>. If not UNC, the
+     *  first 2 characters of the returned path string will not be <code>Path.SEPARATOR</code>.
+     *
+     * @param toUNC true if converting to UNC, false otherwise
+     * @return the new path, either in UNC form or not depending on the bool parameter
+     */
+    public IPath makeUNC(bool toUNC);
+
+    /**
+     * Returns a count of the number of segments which match in
+     * this path and the given path (device ids are ignored),
+     * comparing in increasing segment number order.
+     *
+     * @param anotherPath the other path
+     * @return the number of matching segments
+     */
+    public int matchingFirstSegments(IPath anotherPath);
+
+    /**
+     * Returns a new path which is the same as this path but with
+     * the file extension removed.  If this path does not have an
+     * extension, this path is returned.
+     * <p>
+     * The file extension portion is defined as the string
+     * following the last period (".") character in the last segment.
+     * If there is no period in the last segment, the path has no
+     * file extension portion. If the last segment ends in a period,
+     * the file extension portion is the empty string.
+     * </p>
+     *
+     * @return the new path
+     */
+    public IPath removeFileExtension();
+
+    /**
+     * Returns a copy of this path with the given number of segments
+     * removed from the beginning. The device id is preserved.
+     * The number must be greater or equal zero.
+     * If the count is zero, this path is returned.
+     * The resulting path will always be a relative path with respect
+     * to this path.  If the number equals or exceeds the number
+     * of segments in this path, an empty relative path is returned.
+     *
+     * @param count the number of segments to remove
+     * @return the new path
+     */
+    public IPath removeFirstSegments(int count);
+
+    /**
+     * Returns a copy of this path with the given number of segments
+     * removed from the end. The device id is preserved.
+     * The number must be greater or equal zero.
+     * If the count is zero, this path is returned.
+     * <p>
+     * If this path has a trailing separator, it will still
+     * have a trailing separator after the last segments are removed
+     * (assuming there are some segments left).  If there is no
+     * trailing separator, the result will not have a trailing
+     * separator.
+     * If the number equals or exceeds the number
+     * of segments in this path, a path with no segments is returned.
+     * </p>
+     *
+     * @param count the number of segments to remove
+     * @return the new path
+     */
+    public IPath removeLastSegments(int count);
+
+    /**
+     * Returns a path with the same segments as this path
+     * but with a trailing separator removed.
+     * Does nothing if this path does not have at least one segment.
+     * The device id is preserved.
+     * <p>
+     * If this path does not have a trailing separator,
+     * this path is returned.
+     * </p>
+     *
+     * @return the new path
+     * @see #addTrailingSeparator()
+     * @see #hasTrailingSeparator()
+     */
+    public IPath removeTrailingSeparator();
+
+    /**
+     * Returns the specified segment of this path, or
+     * <code>null</code> if the path does not have such a segment.
+     *
+     * @param index the 0-based segment index
+     * @return the specified segment, or <code>null</code>
+     */
+    public String segment(int index);
+
+    /**
+     * Returns the number of segments in this path.
+     * <p>
+     * Note that both root and empty paths have 0 segments.
+     * </p>
+     *
+     * @return the number of segments
+     */
+    public int segmentCount();
+
+    /**
+     * Returns the segments in this path in order.
+     *
+     * @return an array of string segments
+     */
+    public String[] segments();
+
+    /**
+     * Returns a new path which is the same as this path but with
+     * the given device id.  The device id must end with a ":".
+     * A device independent path is obtained by passing <code>null</code>.
+     * <p>
+     * For example, "C:" and "Server/Volume:" are typical device ids.
+     * </p>
+     *
+     * @param device the device id or <code>null</code>
+     * @return a new path
+     * @see #getDevice()
+     */
+    public IPath setDevice(String device);
+
+    /**
+     * Returns a <code>java.io.File</code> corresponding to this path.
+     *
+     * @return the file corresponding to this path
+     */
+    public tango.io.FilePath.FilePath toFile();
+
+    /**
+     * Returns a string representation of this path which uses the
+     * platform-dependent path separator defined by <code>java.io.File</code>.
+     * This method is like <code>toString()</code> except that the
+     * latter always uses the same separator (<code>/</code>) regardless of platform.
+     * <p>
+     * This string is suitable for passing to <code>java.io.File(String)</code>.
+     * </p>
+     *
+     * @return a platform-dependent string representation of this path
+     */
+    public String toOSString();
+
+    /**
+     * Returns a platform-neutral string representation of this path. The
+     * format is not specified, except that the resulting string can be
+     * passed back to the <code>Path#fromPortableString(String)</code>
+     * constructor to produce the exact same path on any platform.
+     * <p>
+     * This string is suitable for passing to <code>Path#fromPortableString(String)</code>.
+     * </p>
+     *
+     * @return a platform-neutral string representation of this path
+     * @see Path#fromPortableString(String)
+     * @since 3.1
+     */
+    public String toPortableString();
+
+    /**
+     * Returns a string representation of this path, including its
+     * device id.  The same separator, "/", is used on all platforms.
+     * <p>
+     * Example result strings (without and with device id):
+     * <pre>
+     * "/foo/bar.txt"
+     * "bar.txt"
+     * "/foo/"
+     * "foo/"
+     * ""
+     * "/"
+     * "C:/foo/bar.txt"
+     * "C:bar.txt"
+     * "C:/foo/"
+     * "C:foo/"
+     * "C:"
+     * "C:/"
+     * </pre>
+     * This string is suitable for passing to <code>Path(String)</code>.
+     * </p>
+     *
+     * @return a string representation of this path
+     * @see Path
+     */
+    public String toString();
+
+    /**
+     * Returns a copy of this path truncated after the
+     * given number of segments. The number must not be negative.
+     * The device id is preserved.
+     * <p>
+     * If this path has a trailing separator, the result will too
+     * (assuming there are some segments left). If there is no
+     * trailing separator, the result will not have a trailing
+     * separator.
+     * Copying up to segment zero simply means making an copy with
+     * no path segments.
+     * </p>
+     *
+     * @param count the segment number at which to truncate the path
+     * @return the new path
+     */
+    public IPath uptoSegment(int count);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/IProgressMonitor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.IProgressMonitor;
+
+import java.lang.all;
+
+/**
+ * The <code>IProgressMonitor</code> interface is implemented
+ * by objects that monitor the progress of an activity; the methods
+ * in this interface are invoked by code that performs the activity.
+ * <p>
+ * All activity is broken down into a linear sequence of tasks against
+ * which progress is reported. When a task begins, a <code>beginTask(String, int)
+ * </code> notification is reported, followed by any number and mixture of
+ * progress reports (<code>worked()</code>) and subtask notifications
+ * (<code>subTask(String)</code>).  When the task is eventually completed, a
+ * <code>done()</code> notification is reported.  After the <code>done()</code>
+ * notification, the progress monitor cannot be reused;  i.e., <code>
+ * beginTask(String, int)</code> cannot be called again after the call to
+ * <code>done()</code>.
+ * </p>
+ * <p>
+ * A request to cancel an operation can be signaled using the
+ * <code>setCanceled</code> method.  Operations taking a progress
+ * monitor are expected to poll the monitor (using <code>isCanceled</code>)
+ * periodically and abort at their earliest convenience.  Operation can however
+ * choose to ignore cancelation requests.
+ * </p>
+ * <p>
+ * Since notification is synchronous with the activity itself, the listener should
+ * provide a fast and robust implementation. If the handling of notifications would
+ * involve blocking operations, or operations which might throw uncaught exceptions,
+ * the notifications should be queued, and the actual processing deferred (or perhaps
+ * delegated to a separate thread).
+ * </p><p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * Clients may implement this interface.
+ * </p>
+ */
+public interface IProgressMonitor {
+
+    /** Constant indicating an unknown amount of work.
+     */
+    public static const int UNKNOWN = -1;
+
+    /**
+     * Notifies that the main task is beginning.  This must only be called once
+     * on a given progress monitor instance.
+     *
+     * @param name the name (or description) of the main task
+     * @param totalWork the total number of work units into which
+     *  the main task is been subdivided. If the value is <code>UNKNOWN</code>
+     *  the implementation is free to indicate progress in a way which
+     *  doesn't require the total number of work units in advance.
+     */
+    public void beginTask(String name, int totalWork);
+
+    /**
+     * Notifies that the work is done; that is, either the main task is completed
+     * or the user canceled it. This method may be called more than once
+     * (implementations should be prepared to handle this case).
+     */
+    public void done();
+
+    /**
+     * Internal method to handle scaling correctly. This method
+     * must not be called by a client. Clients should
+     * always use the method </code>worked(int)</code>.
+     *
+     * @param work the amount of work done
+     */
+    public void internalWorked(double work);
+
+    /**
+     * Returns whether cancelation of current operation has been requested.
+     * Long-running operations should poll to see if cancelation
+     * has been requested.
+     *
+     * @return <code>true</code> if cancellation has been requested,
+     *    and <code>false</code> otherwise
+     * @see #setCanceled(bool)
+     */
+    public bool isCanceled();
+
+    /**
+     * Sets the cancel state to the given value.
+     *
+     * @param value <code>true</code> indicates that cancelation has
+     *     been requested (but not necessarily acknowledged);
+     *     <code>false</code> clears this flag
+     * @see #isCanceled()
+     */
+    public void setCanceled(bool value);
+
+    /**
+     * Sets the task name to the given value. This method is used to
+     * restore the task label after a nested operation was executed.
+     * Normally there is no need for clients to call this method.
+     *
+     * @param name the name (or description) of the main task
+     * @see #beginTask(java.lang.String, int)
+     */
+    public void setTaskName(String name);
+
+    /**
+     * Notifies that a subtask of the main task is beginning.
+     * Subtasks are optional; the main task might not have subtasks.
+     *
+     * @param name the name (or description) of the subtask
+     */
+    public void subTask(String name);
+
+    /**
+     * Notifies that a given number of work unit of the main task
+     * has been completed. Note that this amount represents an
+     * installment, as opposed to a cumulative amount of work done
+     * to date.
+     *
+     * @param work a non-negative number of work units just completed
+     */
+    public void worked(int work);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/IProgressMonitorWithBlocking.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.IProgressMonitorWithBlocking;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+
+import java.lang.all;
+
+/**
+ * An extension to the IProgressMonitor interface for monitors that want to
+ * support feedback when an activity is blocked due to concurrent activity in
+ * another thread.
+ * <p>
+ * When a monitor that supports this extension is passed to an operation, the
+ * operation should call <code>setBlocked</code> whenever it knows that it
+ * must wait for a lock that is currently held by another thread. The operation
+ * should continue to check for and respond to cancelation requests while
+ * blocked. When the operation is no longer blocked, it must call <code>clearBlocked</code>
+ * to clear the blocked state.
+ * <p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * Clients may implement this interface.
+ * </p>
+ * @see IProgressMonitor
+ * @since 3.0
+ */
+public interface IProgressMonitorWithBlocking : IProgressMonitor {
+    /**
+     * Indicates that this operation is blocked by some background activity. If
+     * a running operation ever calls <code>setBlocked</code>, it must
+     * eventually call <code>clearBlocked</code> before the operation
+     * completes.
+     * <p>
+     * If the caller is blocked by a currently executing job, this method will return
+     * an <code>IJobStatus</code> indicating the job that is currently blocking
+     * the caller. If this blocking job is not known, this method will return a plain
+     * informational <code>IStatus</code> object.
+     * </p>
+     *
+     * @param reason an optional status object whose message describes the
+     * reason why this operation is blocked, or <code>null</code> if this
+     * information is not available.
+     * @see #clearBlocked()
+     */
+    public void setBlocked(IStatus reason);
+
+    /**
+     * Clears the blocked state of the running operation. If a running
+     * operation ever calls <code>setBlocked</code>, it must eventually call
+     * <code>clearBlocked</code> before the operation completes.
+     *
+     * @see #setBlocked(IStatus)
+     */
+    public void clearBlocked();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/ISafeRunnable.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.ISafeRunnable;
+
+// import org.eclipse.swt.internal.Platform;
+import java.lang.all;
+/**
+ * Safe runnables represent blocks of code and associated exception
+ * handlers.  They are typically used when a plug-in needs to call some
+ * untrusted code (e.g., code contributed by another plug-in via an
+ * extension).
+ * <p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * Clients may implement this interface.
+ * </p>
+ * @see SafeRunner#run(ISafeRunnable)
+ */
+public interface ISafeRunnable {
+    /**
+     * Handles an exception thrown by this runnable's <code>run</code>
+     * method.  The processing done here should be specific to the
+     * particular usecase for this runnable.  Generalized exception
+     * processing (e.g., logging in the platform's log) is done by the
+     * Platform's run mechanism.
+     *
+     * @param exception an exception which occurred during processing
+     *      the body of this runnable (i.e., in <code>run()</code>)
+     * @see SafeRunner#run(ISafeRunnable)
+     */
+    public void handleException(Exception exception);
+
+    /**
+     * Runs this runnable.  Any exceptions thrown from this method will
+     * be passed to this runnable's <code>handleException</code>
+     * method.
+     *
+     * @exception Exception if a problem occurred while running this method.
+     *      The exception will be processed by <code>handleException</code>
+     * @see SafeRunner#run(ISafeRunnable)
+     */
+    public void run();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/IStatus.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.IStatus;
+
+import java.lang.all;
+
+/**
+ * A status object represents the outcome of an operation.
+ * All <code>CoreException</code>s carry a status object to indicate
+ * what went wrong. Status objects are also returned by methods needing
+ * to provide details of failures (e.g., validation methods).
+ * <p>
+ * A status carries the following information:
+ * <ul>
+ * <li> plug-in identifier (required)</li>
+ * <li> severity (required)</li>
+ * <li> status code (required)</li>
+ * <li> message (required) - localized to current locale</li>
+ * <li> exception (optional) - for problems stemming from a failure at
+ *    a lower level</li>
+ * </ul>
+ * Some status objects, known as multi-statuses, have other status objects
+ * as children.
+ * </p>
+ * <p>
+ * The class <code>Status</code> is the standard public implementation
+ * of status objects; the subclass <code>MultiStatus</code> is the
+ * implements multi-status objects.
+ * </p><p>
+ * This interface can be used without OSGi running.
+ * </p>
+ * @see MultiStatus
+ * @see Status
+ */
+public interface IStatus {
+
+    /** Status severity constant (value 0) indicating this status represents the nominal case.
+     * This constant is also used as the status code representing the nominal case.
+     * @see #getSeverity()
+     * @see #isOK()
+     */
+    public static const int OK = 0;
+
+    /** Status type severity (bit mask, value 1) indicating this status is informational only.
+     * @see #getSeverity()
+     * @see #matches(int)
+     */
+    public static const int INFO = 0x01;
+
+    /** Status type severity (bit mask, value 2) indicating this status represents a warning.
+     * @see #getSeverity()
+     * @see #matches(int)
+     */
+    public static const int WARNING = 0x02;
+
+    /** Status type severity (bit mask, value 4) indicating this status represents an error.
+     * @see #getSeverity()
+     * @see #matches(int)
+     */
+    public static const int ERROR = 0x04;
+
+    /** Status type severity (bit mask, value 8) indicating this status represents a
+     * cancelation
+     * @see #getSeverity()
+     * @see #matches(int)
+     * @since 3.0
+     */
+    public static const int CANCEL = 0x08;
+
+    /**
+     * Returns a list of status object immediately contained in this
+     * multi-status, or an empty list if this is not a multi-status.
+     *
+     * @return an array of status objects
+     * @see #isMultiStatus()
+     */
+    public IStatus[] getChildren();
+
+    /**
+     * Returns the plug-in-specific status code describing the outcome.
+     *
+     * @return plug-in-specific status code
+     */
+    public int getCode();
+
+    /**
+     * Returns the relevant low-level exception, or <code>null</code> if none.
+     * For example, when an operation fails because of a network communications
+     * failure, this might return the <code>java.io.IOException</code>
+     * describing the exact nature of that failure.
+     *
+     * @return the relevant low-level exception, or <code>null</code> if none
+     */
+    public Exception getException();
+
+    /**
+     * Returns the message describing the outcome.
+     * The message is localized to the current locale.
+     *
+     * @return a localized message
+     */
+    public String getMessage();
+
+    /**
+     * Returns the unique identifier of the plug-in associated with this status
+     * (this is the plug-in that defines the meaning of the status code).
+     *
+     * @return the unique identifier of the relevant plug-in
+     */
+    public String getPlugin();
+
+    /**
+     * Returns the severity. The severities are as follows (in
+     * descending order):
+     * <ul>
+     * <li><code>CANCEL</code> - cancelation occurred</li>
+     * <li><code>ERROR</code> - a serious error (most severe)</li>
+     * <li><code>WARNING</code> - a warning (less severe)</li>
+     * <li><code>INFO</code> - an informational ("fyi") message (least severe)</li>
+     * <li><code>OK</code> - everything is just fine</li>
+     * </ul>
+     * <p>
+     * The severity of a multi-status is defined to be the maximum
+     * severity of any of its children, or <code>OK</code> if it has
+     * no children.
+     * </p>
+     *
+     * @return the severity: one of <code>OK</code>, <code>ERROR</code>,
+     * <code>INFO</code>, <code>WARNING</code>,  or <code>CANCEL</code>
+     * @see #matches(int)
+     */
+    public int getSeverity();
+
+    /**
+     * Returns whether this status is a multi-status.
+     * A multi-status describes the outcome of an operation
+     * involving multiple operands.
+     * <p>
+     * The severity of a multi-status is derived from the severities
+     * of its children; a multi-status with no children is
+     * <code>OK</code> by definition.
+     * A multi-status carries a plug-in identifier, a status code,
+     * a message, and an optional exception. Clients may treat
+     * multi-status objects in a multi-status unaware way.
+     * </p>
+     *
+     * @return <code>true</code> for a multi-status,
+     *    <code>false</code> otherwise
+     * @see #getChildren()
+     */
+    public bool isMultiStatus();
+
+    /**
+     * Returns whether this status indicates everything is okay
+     * (neither info, warning, nor error).
+     *
+     * @return <code>true</code> if this status has severity
+     *    <code>OK</code>, and <code>false</code> otherwise
+     */
+    public bool isOK();
+
+    /**
+     * Returns whether the severity of this status matches the given
+     * severity mask. Note that a status with severity <code>OK</code>
+     * will never match; use <code>isOK</code> instead to detect
+     * a status with a severity of <code>OK</code>.
+     *
+     * @param severityMask a mask formed by bitwise or'ing severity mask
+     *    constants (<code>ERROR</code>, <code>WARNING</code>,
+     *    <code>INFO</code>, <code>CANCEL</code>)
+     * @return <code>true</code> if there is at least one match,
+     *    <code>false</code> if there are no matches
+     * @see #getSeverity()
+     * @see #CANCEL
+     * @see #ERROR
+     * @see #WARNING
+     * @see #INFO
+     */
+    public bool matches(int severityMask);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/ListenerList.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.ListenerList;
+
+import java.lang.all;
+
+/**
+ * This class is a thread safe list that is designed for storing lists of listeners.
+ * The implementation is optimized for minimal memory footprint, frequent reads
+ * and infrequent writes.  Modification of the list is synchronized and relatively
+ * expensive, while accessing the listeners is very fast.  Readers are given access
+ * to the underlying array data structure for reading, with the trust that they will
+ * not modify the underlying array.
+ * <p>
+ * <a name="same">A listener list handles the <i>same</i> listener being added
+ * multiple times, and tolerates removal of listeners that are the same as other
+ * listeners in the list.  For this purpose, listeners can be compared with each other
+ * using either equality or identity, as specified in the list constructor.
+ * </p>
+ * <p>
+ * Use the <code>getListeners</code> method when notifying listeners. The recommended
+ * code sequence for notifying all registered listeners of say,
+ * <code>FooListener.eventHappened</code>, is:
+ *
+ * <pre>
+ * Object[] listeners = myListenerList.getListeners();
+ * for (int i = 0; i &lt; listeners.length; ++i) {
+ *  ((FooListener) listeners[i]).eventHappened(event);
+ * }
+ * </pre>
+ *
+ * </p><p>
+ * This class can be used without OSGi running.
+ * </p>
+ * @since org.eclipse.equinox.common 3.2
+ */
+public class ListenerList {
+
+    /**
+     * The empty array singleton instance.
+     */
+    private static const Object[] EmptyArray;
+
+    /**
+     * Mode constant (value 0) indicating that listeners should be considered
+     * the <a href="#same">same</a> if they are equal.
+     */
+    public static const int EQUALITY = 0;
+
+    /**
+     * Mode constant (value 1) indicating that listeners should be considered
+     * the <a href="#same">same</a> if they are identical.
+     */
+    public static const int IDENTITY = 1;
+
+    /**
+     * Indicates the comparison mode used to determine if two
+     * listeners are equivalent
+     */
+    private final bool identity;
+
+    /**
+     * The list of listeners.  Initially empty but initialized
+     * to an array of size capacity the first time a listener is added.
+     * Maintains invariant: listeners !is null
+     */
+    private Object[] listeners;
+
+    /**
+     * Creates a listener list in which listeners are compared using equality.
+     */
+    public this() {
+        this(EQUALITY);
+    }
+
+    /**
+     * Creates a listener list using the provided comparison mode.
+     *
+     * @param mode The mode used to determine if listeners are the <a href="#same">same</a>.
+     */
+    public this(int mode) {
+        if (mode !is EQUALITY && mode !is IDENTITY)
+            throw new IllegalArgumentException( null );
+        this.identity = mode is IDENTITY;
+    }
+
+    /**
+     * Adds a listener to this list. This method has no effect if the <a href="#same">same</a>
+     * listener is already registered.
+     *
+     * @param listener the non-<code>null</code> listener to add
+     */
+    public synchronized void add(Object listener) {
+        // This method is synchronized to protect against multiple threads adding
+        // or removing listeners concurrently. This does not block concurrent readers.
+        if (listener is null)
+            throw new IllegalArgumentException( null );
+        // check for duplicates
+        final int oldSize = listeners.length;
+        for (int i = 0; i < oldSize; ++i) {
+            Object listener2 = listeners[i];
+            if (identity ? listener is listener2 : listener.opEquals(listener2))
+                return;
+        }
+        // Thread safety: create new array to avoid affecting concurrent readers
+        Object[] newListeners = new Object[oldSize + 1];
+        System.arraycopy(listeners, 0, newListeners, 0, oldSize);
+        newListeners[oldSize] = listener;
+        //atomic assignment
+        this.listeners = newListeners;
+    }
+
+    /**
+     * Returns an array containing all the registered listeners.
+     * The resulting array is unaffected by subsequent adds or removes.
+     * If there are no listeners registered, the result is an empty array.
+     * Use this method when notifying listeners, so that any modifications
+     * to the listener list during the notification will have no effect on
+     * the notification itself.
+     * <p>
+     * Note: Callers of this method <b>must not</b> modify the returned array.
+     *
+     * @return the list of registered listeners
+     */
+    public Object[] getListeners() {
+        return listeners;
+    }
+
+    /**
+     * Returns whether this listener list is empty.
+     *
+     * @return <code>true</code> if there are no registered listeners, and
+     *   <code>false</code> otherwise
+     */
+    public bool isEmpty() {
+        return listeners.length is 0;
+    }
+
+    /**
+     * Removes a listener from this list. Has no effect if the <a href="#same">same</a>
+     * listener was not already registered.
+     *
+     * @param listener the non-<code>null</code> listener to remove
+     */
+    public synchronized void remove(Object listener) {
+        // This method is synchronized to protect against multiple threads adding
+        // or removing listeners concurrently. This does not block concurrent readers.
+        if (listener is null)
+            throw new IllegalArgumentException( null );
+        int oldSize = listeners.length;
+        for (int i = 0; i < oldSize; ++i) {
+            Object listener2 = listeners[i];
+            if (identity ? listener is listener2 : listener.opEquals(listener2)) {
+                if (oldSize is 1) {
+                    listeners = EmptyArray;
+                } else {
+                    // Thread safety: create new array to avoid affecting concurrent readers
+                    Object[] newListeners = new Object[oldSize - 1];
+                    System.arraycopy(listeners, 0, newListeners, 0, i);
+                    System.arraycopy(listeners, i + 1, newListeners, i, oldSize - i - 1);
+                    //atomic assignment to field
+                    this.listeners = newListeners;
+                }
+                return;
+            }
+        }
+    }
+
+    /**
+     * Returns the number of registered listeners.
+     *
+     * @return the number of registered listeners
+     */
+    public int size() {
+        return listeners.length;
+    }
+
+    /**
+     * Removes all listeners from this list.
+     */
+    public synchronized void clear() {
+            listeners = EmptyArray;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/MultiStatus.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.MultiStatus;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.IStatus;
+
+import java.lang.all;
+static import tango.text.Text;
+
+/**
+ * A concrete multi-status implementation,
+ * suitable either for instantiating or subclassing.
+ * <p>
+ * This class can be used without OSGi running.
+ * </p>
+ */
+public class MultiStatus : Status {
+
+    /** List of child statuses.
+     */
+    private IStatus[] children;
+
+    /**
+     * Creates and returns a new multi-status object with the given children.
+     *
+     * @param pluginId the unique identifier of the relevant plug-in
+     * @param code the plug-in-specific status code
+     * @param newChildren the list of children status objects
+     * @param message a human-readable message, localized to the
+     *    current locale
+     * @param exception a low-level exception, or <code>null</code> if not
+     *    applicable
+     */
+    public this(String pluginId, int code, IStatus[] newChildren, String message, Exception exception) {
+        this(pluginId, code, message, exception);
+        Assert.isLegal(newChildren !is null);
+        int maxSeverity = getSeverity();
+        for (int i = 0; i < newChildren.length; i++) {
+            Assert.isLegal(newChildren[i] !is null);
+            int severity = newChildren[i].getSeverity();
+            if (severity > maxSeverity)
+                maxSeverity = severity;
+        }
+        this.children = new IStatus[newChildren.length];
+        setSeverity(maxSeverity);
+        SimpleType!(IStatus).arraycopy(newChildren, 0, this.children, 0, newChildren.length);
+    }
+
+    /**
+     * Creates and returns a new multi-status object with no children.
+     *
+     * @param pluginId the unique identifier of the relevant plug-in
+     * @param code the plug-in-specific status code
+     * @param message a human-readable message, localized to the
+     *    current locale
+     * @param exception a low-level exception, or <code>null</code> if not
+     *    applicable
+     */
+    public this(String pluginId, int code, String message, Exception exception) {
+        super(OK, pluginId, code, message, exception);
+        children = new IStatus[0];
+    }
+
+    /**
+     * Adds the given status to this multi-status.
+     *
+     * @param status the new child status
+     */
+    public void add(IStatus status) {
+        Assert.isLegal(status !is null);
+        IStatus[] result = new IStatus[children.length + 1];
+        SimpleType!(IStatus).arraycopy(children, 0, result, 0, children.length);
+        result[result.length - 1] = status;
+        children = result;
+        int newSev = status.getSeverity();
+        if (newSev > getSeverity()) {
+            setSeverity(newSev);
+        }
+    }
+
+    /**
+     * Adds all of the children of the given status to this multi-status.
+     * Does nothing if the given status has no children (which includes
+     * the case where it is not a multi-status).
+     *
+     * @param status the status whose children are to be added to this one
+     */
+    public void addAll(IStatus status) {
+        Assert.isLegal(status !is null);
+        IStatus[] statuses = status.getChildren();
+        for (int i = 0; i < statuses.length; i++) {
+            add(statuses[i]);
+        }
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public override IStatus[] getChildren() {
+        return children;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public override bool isMultiStatus() {
+        return true;
+    }
+
+    /**
+     * Merges the given status into this multi-status.
+     * Equivalent to <code>add(status)</code> if the
+     * given status is not a multi-status.
+     * Equivalent to <code>addAll(status)</code> if the
+     * given status is a multi-status.
+     *
+     * @param status the status to merge into this one
+     * @see #add(IStatus)
+     * @see #addAll(IStatus)
+     */
+    public void merge(IStatus status) {
+        Assert.isLegal(status !is null);
+        if (!status.isMultiStatus()) {
+            add(status);
+        } else {
+            addAll(status);
+        }
+    }
+
+    /**
+     * Returns a string representation of the status, suitable
+     * for debugging purposes only.
+     */
+    public override String toString() {
+        tango.text.Text.Text!(char) buf = new tango.text.Text.Text!(char);
+        buf.append(super.toString());
+        buf.append(" children=["); //$NON-NLS-1$
+        for (int i = 0; i < children.length; i++) {
+            if (i !is 0) {
+                buf.append(" "); //$NON-NLS-1$
+            }
+            buf.append( (cast(Object)children[i]).toString());
+        }
+        buf.append("]"); //$NON-NLS-1$
+        return buf.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/NullProgressMonitor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.NullProgressMonitor;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import java.lang.all;
+
+/**
+ * A default progress monitor implementation suitable for
+ * subclassing.
+ * <p>
+ * This implementation supports cancelation. The default
+ * implementations of the other methods do nothing.
+ * </p><p>
+ * This class can be used without OSGi running.
+ * </p>
+ */
+public class NullProgressMonitor : IProgressMonitor {
+
+    /**
+     * Indicates whether cancel has been requested.
+     */
+    private bool cancelled = false;
+
+    /**
+     * Constructs a new progress monitor.
+     */
+    public this() {
+    }
+
+    /**
+     * This implementation does nothing.
+     * Subclasses may override this method to do interesting
+     * processing when a task begins.
+     *
+     * @see IProgressMonitor#beginTask(String, int)
+     */
+    public void beginTask(String name, int totalWork) {
+        // do nothing
+    }
+
+    /**
+     * This implementation does nothing.
+     * Subclasses may override this method to do interesting
+     * processing when a task is done.
+     *
+     * @see IProgressMonitor#done()
+     */
+    public void done() {
+        // do nothing
+    }
+
+    /**
+     * This implementation does nothing.
+     * Subclasses may override this method.
+     *
+     * @see IProgressMonitor#internalWorked(double)
+     */
+    public void internalWorked(double work) {
+        // do nothing
+    }
+
+    /**
+     * This implementation returns the value of the internal
+     * state variable set by <code>setCanceled</code>.
+     * Subclasses which override this method should
+     * override <code>setCanceled</code> as well.
+     *
+     * @see IProgressMonitor#isCanceled()
+     * @see IProgressMonitor#setCanceled(bool)
+     */
+    public bool isCanceled() {
+        return cancelled;
+    }
+
+    /**
+     * This implementation sets the value of an internal state variable.
+     * Subclasses which override this method should override
+     * <code>isCanceled</code> as well.
+     *
+     * @see IProgressMonitor#isCanceled()
+     * @see IProgressMonitor#setCanceled(bool)
+     */
+    public void setCanceled(bool cancelled) {
+        this.cancelled = cancelled;
+    }
+
+    /**
+     * This implementation does nothing.
+     * Subclasses may override this method to do something
+     * with the name of the task.
+     *
+     * @see IProgressMonitor#setTaskName(String)
+     */
+    public void setTaskName(String name) {
+        // do nothing
+    }
+
+    /**
+     * This implementation does nothing.
+     * Subclasses may override this method to do interesting
+     * processing when a subtask begins.
+     *
+     * @see IProgressMonitor#subTask(String)
+     */
+    public void subTask(String name) {
+        // do nothing
+    }
+
+    /**
+     * This implementation does nothing.
+     * Subclasses may override this method to do interesting
+     * processing when some work has been completed.
+     *
+     * @see IProgressMonitor#worked(int)
+     */
+    public void worked(int work) {
+        // do nothing
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/OperationCanceledException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.OperationCanceledException;
+
+import java.lang.all;
+
+/**
+ * This exception is thrown to blow out of a long-running method
+ * when the user cancels it.
+ * <p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * This class is not intended to be subclassed by clients but
+ * may be instantiated.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public final class OperationCanceledException : RuntimeException {
+    /**
+     * All serializable objects should have a stable serialVersionUID
+     */
+    private static const long serialVersionUID = 1L;
+
+    /**
+     * Creates a new exception.
+     */
+    public this() {
+        super();
+    }
+
+    /**
+     * Creates a new exception with the given message.
+     *
+     * @param message the message for the exception
+     */
+    public this(String message) {
+        super(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/Path.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1009 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.Path;
+
+import tango.io.FilePath;
+static import tango.io.Path;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+
+import tango.io.model.IFile;
+
+
+static import tango.text.Text;
+alias tango.text.Text.Text!(char) StringBuffer;
+
+/**
+ * The standard implementation of the <code>IPath</code> interface.
+ * Paths are always maintained in canonicalized form.  That is, parent
+ * references (i.e., <code>../../</code>) and duplicate separators are
+ * resolved.  For example,
+ * <pre>     new Path("/a/b").append("../foo/bar")</pre>
+ * will yield the path
+ * <pre>     /a/foo/bar</pre>
+ * <p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * This class is not intended to be subclassed by clients but
+ * may be instantiated.
+ * </p>
+ * @see IPath
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class Path : IPath, Cloneable {
+    /** masks for separator values */
+    private static const int HAS_LEADING = 1;
+    private static const int IS_UNC = 2;
+    private static const int HAS_TRAILING = 4;
+
+    private static const int ALL_SEPARATORS = HAS_LEADING | IS_UNC | HAS_TRAILING;
+
+    /** Constant empty string value. */
+    private static const String EMPTY_STRING = ""; //$NON-NLS-1$
+
+    /** Constant value indicating no segments */
+    private static const String[] NO_SEGMENTS = null;
+
+    /** Constant value containing the empty path with no device. */
+    public static const Path EMPTY;
+
+    /** Mask for all bits that are involved in the hash code */
+    private static const int HASH_MASK = ~HAS_TRAILING;
+
+    /** Constant root path string (<code>"/"</code>). */
+    private static const String ROOT_STRING = "/"; //$NON-NLS-1$
+
+    /** Constant value containing the root path with no device. */
+    public static const Path ROOT;
+
+    /** Constant value indicating if the current platform is Windows */
+    version(Windows){
+        private static const bool WINDOWS = true;
+    }
+    else {
+        private static const bool WINDOWS = false;
+    }
+
+    static this(){
+        EMPTY = new Path(EMPTY_STRING);
+        ROOT = new Path(ROOT_STRING);
+    }
+
+    /** The device id string. May be null if there is no device. */
+    private String device = null;
+
+    //Private implementation note: the segments and separators
+    //arrays are never modified, so that they can be shared between
+    //path instances
+
+    /** The path segments */
+    private String[] segments_;
+
+    /** flags indicating separators (has leading, is UNC, has trailing) */
+    private int separators;
+
+    /**
+     * Constructs a new path from the given string path.
+     * The string path must represent a valid file system path
+     * on the local file system.
+     * The path is canonicalized and double slashes are removed
+     * except at the beginning. (to handle UNC paths). All forward
+     * slashes ('/') are treated as segment delimiters, and any
+     * segment and device delimiters for the local file system are
+     * also respected.
+     *
+     * @param pathString the portable string path
+     * @see IPath#toPortableString()
+     * @since 3.1
+     */
+    public static IPath fromOSString(String pathString) {
+        return new Path(pathString);
+    }
+
+    /**
+     * Constructs a new path from the given path string.
+     * The path string must have been produced by a previous
+     * call to <code>IPath.toPortableString</code>.
+     *
+     * @param pathString the portable path string
+     * @see IPath#toPortableString()
+     * @since 3.1
+     */
+    public static IPath fromPortableString(String pathString) {
+        int firstMatch = pathString.indexOf(DEVICE_SEPARATOR) + 1;
+        //no extra work required if no device characters
+        if (firstMatch <= 0)
+            return (new Path()).initialize(null, pathString);
+        //if we find a single colon, then the path has a device
+        String devicePart = null;
+        int pathLength = pathString.length;
+        if (firstMatch is pathLength || pathString.charAt(firstMatch) !is DEVICE_SEPARATOR) {
+            devicePart = pathString.substring(0, firstMatch);
+            pathString = pathString.substring(firstMatch, pathLength);
+        }
+        //optimize for no colon literals
+        if (pathString.indexOf(DEVICE_SEPARATOR) is -1)
+            return (new Path()).initialize(devicePart, pathString);
+        //contract colon literals
+        char[] chars = pathString/+.toCharArray()+/;
+        int readOffset = 0, writeOffset = 0, length = chars.length;
+        while (readOffset < length) {
+            if (chars[readOffset] is DEVICE_SEPARATOR)
+                if (++readOffset >= length)
+                    break;
+            chars[writeOffset++] = chars[readOffset++];
+        }
+        return (new Path()).initialize(devicePart, chars[ 0 .. writeOffset] );
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Private constructor.
+     */
+    private this() {
+        // not allowed
+    }
+
+    /**
+     * Constructs a new path from the given string path.
+     * The string path must represent a valid file system path
+     * on the local file system.
+     * The path is canonicalized and double slashes are removed
+     * except at the beginning. (to handle UNC paths). All forward
+     * slashes ('/') are treated as segment delimiters, and any
+     * segment and device delimiters for the local file system are
+     * also respected (such as colon (':') and backslash ('\') on some file systems).
+     *
+     * @param fullPath the string path
+     * @see #isValidPath(String)
+     */
+    public this(String fullPath) {
+        String devicePart = null;
+        if (WINDOWS) {
+            //convert backslash to forward slash
+            fullPath = fullPath.indexOf('\\') is -1 ? fullPath : fullPath.replace('\\', SEPARATOR);
+            //extract device
+            int i = fullPath.indexOf(DEVICE_SEPARATOR);
+            if (i !is -1) {
+                //remove leading slash from device part to handle output of URL.getFile()
+                int start = fullPath.charAt(0) is SEPARATOR ? 1 : 0;
+                devicePart = fullPath.substring(start, i + 1);
+                fullPath = fullPath.substring(i + 1, fullPath.length);
+            }
+        }
+        initialize(devicePart, fullPath);
+    }
+
+    /**
+     * Constructs a new path from the given device id and string path.
+     * The given string path must be valid.
+     * The path is canonicalized and double slashes are removed except
+     * at the beginning (to handle UNC paths). All forward
+     * slashes ('/') are treated as segment delimiters, and any
+     * segment delimiters for the local file system are
+     * also respected (such as backslash ('\') on some file systems).
+     *
+     * @param device the device id
+     * @param path the string path
+     * @see #isValidPath(String)
+     * @see #setDevice(String)
+     */
+    public this(String device, String path) {
+        if (WINDOWS) {
+            //convert backslash to forward slash
+            path = path.indexOf('\\') is -1 ? path : path.replace('\\', SEPARATOR);
+        }
+        initialize(device, path);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Private constructor.
+     */
+    private this(String device, String[] segments_, int _separators) {
+        // no segment validations are done for performance reasons
+        this.segments_ = segments_;
+        this.device = device;
+        //hash code is cached in all but the bottom three bits of the separators field
+        this.separators = (computeHashCode() << 3) | (_separators & ALL_SEPARATORS);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#addFileExtension
+     */
+    public IPath addFileExtension(String extension) {
+        if (isRoot() || isEmpty() || hasTrailingSeparator())
+            return this;
+        int len = segments_.length;
+        String[] newSegments = new String[len];
+        System.arraycopy(segments_, 0, newSegments, 0, len - 1);
+        newSegments[len - 1] = segments_[len - 1] ~ '.' ~ extension;
+        return new Path(device, newSegments, separators);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#addTrailingSeparator
+     */
+    public IPath addTrailingSeparator() {
+        if (hasTrailingSeparator() || isRoot()) {
+            return this;
+        }
+        //XXX workaround, see 1GIGQ9V
+        if (isEmpty()) {
+            return new Path(device, segments_, HAS_LEADING);
+        }
+        return new Path(device, segments_, separators | HAS_TRAILING);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#append(IPath)
+     */
+    public IPath append(IPath tail) {
+        //optimize some easy cases
+        if (tail is null || tail.segmentCount() is 0)
+            return this;
+        //these call chains look expensive, but in most cases they are no-ops
+        if (this.isEmpty())
+            return tail.setDevice(device).makeRelative().makeUNC(isUNC());
+        if (this.isRoot())
+            return tail.setDevice(device).makeAbsolute().makeUNC(isUNC());
+
+        //concatenate the two segment arrays
+        int myLen = segments_.length;
+        int tailLen = tail.segmentCount();
+        String[] newSegments = new String[myLen + tailLen];
+        System.arraycopy(segments_, 0, newSegments, 0, myLen);
+        for (int i = 0; i < tailLen; i++) {
+            newSegments[myLen + i] = tail.segment(i);
+        }
+        //use my leading separators and the tail's trailing separator
+        Path result = new Path(device, newSegments, (separators & (HAS_LEADING | IS_UNC)) | (tail.hasTrailingSeparator() ? HAS_TRAILING : 0));
+        String tailFirstSegment = newSegments[myLen];
+        if (tailFirstSegment.equals("..") || tailFirstSegment.equals(".")) { //$NON-NLS-1$ //$NON-NLS-2$
+            result.canonicalize();
+        }
+        return result;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#append(java.lang.String)
+     */
+    public IPath append(String tail) {
+        //optimize addition of a single segment
+        if (tail.indexOf(SEPARATOR) is -1 && tail.indexOf("\\") is -1 && tail.indexOf(DEVICE_SEPARATOR) is -1) { //$NON-NLS-1$
+            int tailLength = tail.length;
+            if (tailLength < 3) {
+                //some special cases
+                if (tailLength is 0 || ".".equals(tail)) { //$NON-NLS-1$
+                    return this;
+                }
+                if ("..".equals(tail)) //$NON-NLS-1$
+                    return removeLastSegments(1);
+            }
+            //just add the segment
+            int myLen = segments_.length;
+            String[] newSegments = new String[myLen + 1];
+            System.arraycopy(segments_, 0, newSegments, 0, myLen);
+            newSegments[myLen] = tail;
+            return new Path(device, newSegments, separators & ~HAS_TRAILING);
+        }
+        //go with easy implementation
+        return append(new Path(tail));
+    }
+
+    /**
+     * Destructively converts this path to its canonical form.
+     * <p>
+     * In its canonical form, a path does not have any
+     * "." segments, and parent references ("..") are collapsed
+     * where possible.
+     * </p>
+     * @return true if the path was modified, and false otherwise.
+     */
+    private bool canonicalize() {
+        //look for segments that need canonicalizing
+        for (int i = 0, max = segments_.length; i < max; i++) {
+            String segment = segments_[i];
+            if (segment.charAt(0) is '.' && (segment.equals("..") || segment.equals("."))) { //$NON-NLS-1$ //$NON-NLS-2$
+                //path needs to be canonicalized
+                collapseParentReferences();
+                //paths of length 0 have no trailing separator
+                if (segments_.length is 0)
+                    separators &= (HAS_LEADING | IS_UNC);
+                //recompute hash because canonicalize affects hash
+                separators = (separators & ALL_SEPARATORS) | (computeHashCode() << 3);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Clones this object.
+     */
+    public Path clone() {
+        return new Path(device, segments_, separators);
+    }
+
+    /**
+     * Destructively removes all occurrences of ".." segments from this path.
+     */
+    private void collapseParentReferences() {
+        int segmentCount = segments_.length;
+        String[] stack = new String[segmentCount];
+        int stackPointer = 0;
+        for (int i = 0; i < segmentCount; i++) {
+            String segment = segments_[i];
+            if (segment.equals("..")) { //$NON-NLS-1$
+                if (stackPointer is 0) {
+                    // if the stack is empty we are going out of our scope
+                    // so we need to accumulate segments.  But only if the original
+                    // path is relative.  If it is absolute then we can't go any higher than
+                    // root so simply toss the .. references.
+                    if (!isAbsolute())
+                        stack[stackPointer++] = segment; //stack push
+                } else {
+                    // if the top is '..' then we are accumulating segments so don't pop
+                    if ("..".equals(stack[stackPointer - 1])) //$NON-NLS-1$
+                        stack[stackPointer++] = ".."; //$NON-NLS-1$
+                    else
+                        stackPointer--;
+                    //stack pop
+                }
+                //collapse current references
+            } else if (!segment.equals(".") || segmentCount is 1) //$NON-NLS-1$
+                stack[stackPointer++] = segment; //stack push
+        }
+        //if the number of segments hasn't changed, then no modification needed
+        if (stackPointer is segmentCount)
+            return;
+        //build the new segment array backwards by popping the stack
+        String[] newSegments = new String[stackPointer];
+        System.arraycopy(stack, 0, newSegments, 0, stackPointer);
+        this.segments_ = newSegments;
+    }
+
+    /**
+     * Removes duplicate slashes from the given path, with the exception
+     * of leading double slash which represents a UNC path.
+     */
+    private String collapseSlashes(String path) {
+        int length = path.length;
+        // if the path is only 0, 1 or 2 chars long then it could not possibly have illegal
+        // duplicate slashes.
+        if (length < 3)
+            return path;
+        // check for an occurrence of // in the path.  Start at index 1 to ensure we skip leading UNC //
+        // If there are no // then there is nothing to collapse so just return.
+        if (path.indexOf("//", 1) is -1) //$NON-NLS-1$
+            return path;
+        // We found an occurrence of // in the path so do the slow collapse.
+        char[] result = new char[path.length];
+        int count = 0;
+        bool hasPrevious = false;
+        char[] characters = path/+.toCharArray()+/;
+        for (int index = 0; index < characters.length; index++) {
+            char c = characters[index];
+            if (c is SEPARATOR) {
+                if (hasPrevious) {
+                    // skip double slashes, except for beginning of UNC.
+                    // note that a UNC path can't have a device.
+                    if (device is null && index is 1) {
+                        result[count] = c;
+                        count++;
+                    }
+                } else {
+                    hasPrevious = true;
+                    result[count] = c;
+                    count++;
+                }
+            } else {
+                hasPrevious = false;
+                result[count] = c;
+                count++;
+            }
+        }
+        return result[ 0 .. count];
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Computes the hash code for this object.
+     */
+    private int computeHashCode() {
+        int hash = device.length is 0 ? 17 : java.lang.all.toHash(device);
+        int segmentCount = segments_.length;
+        for (int i = 0; i < segmentCount; i++) {
+            //this function tends to given a fairly even distribution
+            hash = hash * 37 + java.lang.all.toHash(segments_[i]);
+        }
+        return hash;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Returns the size of the string that will be created by toString or toOSString.
+     */
+    private int computeLength() {
+        int length = 0;
+        if (device !is null)
+            length += device.length;
+        if ((separators & HAS_LEADING) !is 0)
+            length++;
+        if ((separators & IS_UNC) !is 0)
+            length++;
+        //add the segment lengths
+        int max = segments_.length;
+        if (max > 0) {
+            for (int i = 0; i < max; i++) {
+                length += segments_[i].length;
+            }
+            //add the separator lengths
+            length += max - 1;
+        }
+        if ((separators & HAS_TRAILING) !is 0)
+            length++;
+        return length;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Returns the number of segments in the given path
+     */
+    private int computeSegmentCount(String path) {
+        int len = path.length;
+        if (len is 0 || (len is 1 && path.charAt(0) is SEPARATOR)) {
+            return 0;
+        }
+        int count = 1;
+        int prev = -1;
+        int i;
+        while ((i = path.indexOf(SEPARATOR, prev + 1)) !is -1) {
+            if (i !is prev + 1 && i !is len) {
+                ++count;
+            }
+            prev = i;
+        }
+        if (path.charAt(len - 1) is SEPARATOR) {
+            --count;
+        }
+        return count;
+    }
+
+    /**
+     * Computes the segment array for the given canonicalized path.
+     */
+    private String[] computeSegments(String path) {
+        // performance sensitive --- avoid creating garbage
+        int segmentCount = computeSegmentCount(path);
+        if (segmentCount is 0)
+            return NO_SEGMENTS;
+        String[] newSegments = new String[segmentCount];
+        int len = path.length;
+        // check for initial slash
+        int firstPosition = (path.charAt(0) is SEPARATOR) ? 1 : 0;
+        // check for UNC
+        if (firstPosition is 1 && len > 1 && (path.charAt(1) is SEPARATOR))
+            firstPosition = 2;
+        int lastPosition = (path.charAt(len - 1) !is SEPARATOR) ? len - 1 : len - 2;
+        // for non-empty paths, the number of segments is
+        // the number of slashes plus 1, ignoring any leading
+        // and trailing slashes
+        int next = firstPosition;
+        for (int i = 0; i < segmentCount; i++) {
+            int start = next;
+            int end = path.indexOf(SEPARATOR, next);
+            if (end is -1) {
+                newSegments[i] = path.substring(start, lastPosition + 1);
+            } else {
+                newSegments[i] = path.substring(start, end);
+            }
+            next = end + 1;
+        }
+        return newSegments;
+    }
+
+    /**
+     * Returns the platform-neutral encoding of the given segment onto
+     * the given string buffer. This escapes literal colon characters with double colons.
+     */
+    private void encodeSegment(String string, StringBuffer buf) {
+        int len = string.length;
+        for (int i = 0; i < len; i++) {
+            char c = string.charAt(i);
+            buf.append(c);
+            if (c is DEVICE_SEPARATOR)
+                buf.append(DEVICE_SEPARATOR);
+        }
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Compares objects for equality.
+     */
+    public override int opEquals(Object obj) {
+        if (this is obj)
+            return true;
+        if (!(cast(Path)obj))
+            return false;
+        Path target = cast(Path) obj;
+        //check leading separators and hash code
+        if ((separators & HASH_MASK) !is (target.separators & HASH_MASK))
+            return false;
+        String[] targetSegments = target.segments_;
+        int i = segments_.length;
+        //check segment count
+        if (i !is targetSegments.length)
+            return false;
+        //check segments in reverse order - later segments more likely to differ
+        while (--i >= 0)
+            if (!segments_[i].equals(targetSegments[i]))
+                return false;
+        //check device last (least likely to differ)
+        return device is target.device || (device !is null && device.equals(target.device));
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#getDevice
+     */
+    public String getDevice() {
+        return device;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#getFileExtension
+     */
+    public String getFileExtension() {
+        if (hasTrailingSeparator()) {
+            return null;
+        }
+        String lastSegment = lastSegment();
+        if (lastSegment is null) {
+            return null;
+        }
+        int index = lastSegment.lastIndexOf('.');
+        if (index is -1) {
+            return null;
+        }
+        return lastSegment.substring(index + 1);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Computes the hash code for this object.
+     */
+    public override hash_t toHash() {
+        return separators & HASH_MASK;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#hasTrailingSeparator2
+     */
+    public bool hasTrailingSeparator() {
+        return (separators & HAS_TRAILING) !is 0;
+    }
+
+    /*
+     * Initialize the current path with the given string.
+     */
+    private IPath initialize(String deviceString, String path) {
+        //Assert.isNotNull(path); // allow for SWT
+        this.device = deviceString;
+
+        path = collapseSlashes(path);
+        int len = path.length;
+
+        //compute the separators array
+        if (len < 2) {
+            if (len is 1 && path.charAt(0) is SEPARATOR) {
+                this.separators = HAS_LEADING;
+            } else {
+                this.separators = 0;
+            }
+        } else {
+            bool hasLeading = path.charAt(0) is SEPARATOR;
+            bool isUNC = hasLeading && path.charAt(1) is SEPARATOR;
+            //UNC path of length two has no trailing separator
+            bool hasTrailing = !(isUNC && len is 2) && path.charAt(len - 1) is SEPARATOR;
+            separators = hasLeading ? HAS_LEADING : 0;
+            if (isUNC)
+                separators |= IS_UNC;
+            if (hasTrailing)
+                separators |= HAS_TRAILING;
+        }
+        //compute segments and ensure canonical form
+        segments_ = computeSegments(path);
+        if (!canonicalize()) {
+            //compute hash now because canonicalize didn't need to do it
+            separators = (separators & ALL_SEPARATORS) | (computeHashCode() << 3);
+        }
+        return this;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#isAbsolute
+     */
+    public bool isAbsolute() {
+        //it's absolute if it has a leading separator
+        return (separators & HAS_LEADING) !is 0;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#isEmpty
+     */
+    public bool isEmpty() {
+        //true if no segments and no leading prefix
+        return segments_.length is 0 && ((separators & ALL_SEPARATORS) !is HAS_LEADING);
+
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#isPrefixOf
+     */
+    public bool isPrefixOf(IPath anotherPath) {
+        if (device is null) {
+            if (anotherPath.getDevice() !is null) {
+                return false;
+            }
+        } else {
+            if (!device.equalsIgnoreCase(anotherPath.getDevice())) {
+                return false;
+            }
+        }
+        if (isEmpty() || (isRoot() && anotherPath.isAbsolute())) {
+            return true;
+        }
+        int len = segments_.length;
+        if (len > anotherPath.segmentCount()) {
+            return false;
+        }
+        for (int i = 0; i < len; i++) {
+            if (!segments_[i].equals(anotherPath.segment(i)))
+                return false;
+        }
+        return true;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#isRoot
+     */
+    public bool isRoot() {
+        //must have no segments, a leading separator, and not be a UNC path.
+        return this is ROOT || (segments_.length is 0 && ((separators & ALL_SEPARATORS) is HAS_LEADING));
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#isUNC
+     */
+    public bool isUNC() {
+        if (device !is null)
+            return false;
+        return (separators & IS_UNC) !is 0;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#isValidPath(String)
+     */
+    public bool isValidPath(String path) {
+        Path test = new Path(path);
+        for (int i = 0, max = test.segmentCount(); i < max; i++)
+            if (!isValidSegment(test.segment(i)))
+                return false;
+        return true;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#isValidSegment(String)
+     */
+    public bool isValidSegment(String segment) {
+        int size = segment.length;
+        if (size is 0)
+            return false;
+        for (int i = 0; i < size; i++) {
+            char c = segment.charAt(i);
+            if (c is '/')
+                return false;
+            if (WINDOWS && (c is '\\' || c is ':'))
+                return false;
+        }
+        return true;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#lastSegment()
+     */
+    public String lastSegment() {
+        int len = segments_.length;
+        return len is 0 ? null : segments_[len - 1];
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#makeAbsolute()
+     */
+    public IPath makeAbsolute() {
+        if (isAbsolute()) {
+            return this;
+        }
+        Path result = new Path(device, segments_, separators | HAS_LEADING);
+        //may need canonicalizing if it has leading ".." or "." segments
+        if (result.segmentCount() > 0) {
+            String first = result.segment(0);
+            if (first.equals("..") || first.equals(".")) { //$NON-NLS-1$ //$NON-NLS-2$
+                result.canonicalize();
+            }
+        }
+        return result;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#makeRelative()
+     */
+    public IPath makeRelative() {
+        if (!isAbsolute()) {
+            return this;
+        }
+        return new Path(device, segments_, separators & HAS_TRAILING);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#makeUNC(bool)
+     */
+    public IPath makeUNC(bool toUNC) {
+        // if we are already in the right form then just return
+        if (!(toUNC ^ isUNC()))
+            return this;
+
+        int newSeparators = this.separators;
+        if (toUNC) {
+            newSeparators |= HAS_LEADING | IS_UNC;
+        } else {
+            //mask out the UNC bit
+            newSeparators &= HAS_LEADING | HAS_TRAILING;
+        }
+        return new Path(toUNC ? null : device, segments_, newSeparators);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#matchingFirstSegments(IPath)
+     */
+    public int matchingFirstSegments(IPath anotherPath) {
+        Assert.isNotNull( cast(Object) anotherPath);
+        int anotherPathLen = anotherPath.segmentCount();
+        int max = Math.min(segments_.length, anotherPathLen);
+        int count = 0;
+        for (int i = 0; i < max; i++) {
+            if (!segments_[i].equals(anotherPath.segment(i))) {
+                return count;
+            }
+            count++;
+        }
+        return count;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#removeFileExtension()
+     */
+    public IPath removeFileExtension() {
+        String extension = getFileExtension();
+        if (extension is null || extension.equals("")) { //$NON-NLS-1$
+            return this;
+        }
+        String lastSegment = lastSegment();
+        int index = lastSegment.lastIndexOf(extension) - 1;
+        return removeLastSegments(1).append(lastSegment.substring(0, index));
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#removeFirstSegments(int)
+     */
+    public IPath removeFirstSegments(int count) {
+        if (count is 0)
+            return this;
+        if (count >= segments_.length) {
+            return new Path(device, NO_SEGMENTS, 0);
+        }
+        Assert.isLegal(count > 0);
+        int newSize = segments_.length - count;
+        String[] newSegments = new String[newSize];
+        System.arraycopy(this.segments_, count, newSegments, 0, newSize);
+
+        //result is always a relative path
+        return new Path(device, newSegments, separators & HAS_TRAILING);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#removeLastSegments(int)
+     */
+    public IPath removeLastSegments(int count) {
+        if (count is 0)
+            return this;
+        if (count >= segments_.length) {
+            //result will have no trailing separator
+            return new Path(device, NO_SEGMENTS, separators & (HAS_LEADING | IS_UNC));
+        }
+        Assert.isLegal(count > 0);
+        int newSize = segments_.length - count;
+        String[] newSegments = new String[newSize];
+        System.arraycopy(this.segments_, 0, newSegments, 0, newSize);
+        return new Path(device, newSegments, separators);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#removeTrailingSeparator()
+     */
+    public IPath removeTrailingSeparator() {
+        if (!hasTrailingSeparator()) {
+            return this;
+        }
+        return new Path(device, segments_, separators & (HAS_LEADING | IS_UNC));
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#segment(int)
+     */
+    public String segment(int index) {
+        if (index >= segments_.length)
+            return null;
+        return segments_[index];
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#segmentCount()
+     */
+    public int segmentCount() {
+        return segments_.length;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#segments()
+     */
+    public String[] segments() {
+        String[] segmentCopy = new String[](segments_.length);
+        System.arraycopy(segments_, 0, segmentCopy, 0, segments_.length);
+        return segmentCopy;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#setDevice(String)
+     */
+    public IPath setDevice(String value) {
+        if (value !is null) {
+            Assert.isTrue(value.indexOf(IPath.DEVICE_SEPARATOR) is (value.length - 1), "Last character should be the device separator"); //$NON-NLS-1$
+        }
+        //return the receiver if the device is the same
+        if (value is device || (value !is null && value.equals(device)))
+            return this;
+
+        return new Path(value, segments_, separators);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#toFile()
+     */
+    public FilePath toFile() {
+        return new FilePath(tango.io.Path.standard(toOSString()));
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#toOSString()
+     */
+    public String toOSString() {
+        //Note that this method is identical to toString except
+        //it uses the OS file separator instead of the path separator
+        int resultSize = computeLength();
+        if (resultSize <= 0)
+            return EMPTY_STRING;
+        char FILE_SEPARATOR = FileConst.PathSeparatorChar;
+        char[] result = new char[resultSize];
+        int offset = 0;
+        if (device !is null) {
+            int size = device.length;
+            device.getChars(0, size, result, offset);
+            offset += size;
+        }
+        if ((separators & HAS_LEADING) !is 0)
+            result[offset++] = FILE_SEPARATOR;
+        if ((separators & IS_UNC) !is 0)
+            result[offset++] = FILE_SEPARATOR;
+        int len = segments_.length - 1;
+        if (len >= 0) {
+            //append all but the last segment, with separators
+            for (int i = 0; i < len; i++) {
+                int size = segments_[i].length;
+                segments_[i].getChars(0, size, result, offset);
+                offset += size;
+                result[offset++] = FILE_SEPARATOR;
+            }
+            //append the last segment
+            int size = segments_[len].length;
+            segments_[len].getChars(0, size, result, offset);
+            offset += size;
+        }
+        if ((separators & HAS_TRAILING) !is 0)
+            result[offset++] = FILE_SEPARATOR;
+        return result;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#toPortableString()
+     */
+    public String toPortableString() {
+        int resultSize = computeLength();
+        if (resultSize <= 0)
+            return EMPTY_STRING;
+        StringBuffer result = new StringBuffer(resultSize);
+        if (device !is null)
+            result.append(device);
+        if ((separators & HAS_LEADING) !is 0)
+            result.append(SEPARATOR);
+        if ((separators & IS_UNC) !is 0)
+            result.append(SEPARATOR);
+        int len = segments_.length;
+        //append all segments with separators
+        for (int i = 0; i < len; i++) {
+            if (segments_[i].indexOf(DEVICE_SEPARATOR) >= 0)
+                encodeSegment(segments_[i], result);
+            else
+                result.append(segments_[i]);
+            if (i < len - 1 || (separators & HAS_TRAILING) !is 0)
+                result.append(SEPARATOR);
+        }
+        return result.toString();
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#toString()
+     */
+    public override String toString() {
+        int resultSize = computeLength();
+        if (resultSize <= 0)
+            return EMPTY_STRING;
+        char[] result = new char[resultSize];
+        int offset = 0;
+        if (device !is null) {
+            int size = device.length;
+            device.getChars(0, size, result, offset);
+            offset += size;
+        }
+        if ((separators & HAS_LEADING) !is 0)
+            result[offset++] = SEPARATOR;
+        if ((separators & IS_UNC) !is 0)
+            result[offset++] = SEPARATOR;
+        int len = segments_.length - 1;
+        if (len >= 0) {
+            //append all but the last segment, with separators
+            for (int i = 0; i < len; i++) {
+                int size = segments_[i].length;
+                segments_[i].getChars(0, size, result, offset);
+                offset += size;
+                result[offset++] = SEPARATOR;
+            }
+            //append the last segment
+            int size = segments_[len].length;
+            segments_[len].getChars(0, size, result, offset);
+            offset += size;
+        }
+        if ((separators & HAS_TRAILING) !is 0)
+            result[offset++] = SEPARATOR;
+        return result;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#uptoSegment(int)
+     */
+    public IPath uptoSegment(int count) {
+        if (count is 0)
+            return new Path(device, NO_SEGMENTS, separators & (HAS_LEADING | IS_UNC));
+        if (count >= segments_.length)
+            return this;
+        Assert.isTrue(count > 0, "Invalid parameter to Path.uptoSegment"); //$NON-NLS-1$
+        String[] newSegments = new String[count];
+        System.arraycopy(segments_, 0, newSegments, 0, count);
+        return new Path(device, newSegments, separators);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/PlatformObject.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.PlatformObject;
+
+import java.lang.all;
+
+import org.eclipse.core.runtime.IAdaptable;
+// import org.eclipse.core.internal.runtime.AdapterManager;
+
+/**
+ * An abstract superclass implementing the <code>IAdaptable</code>
+ * interface. <code>getAdapter</code> invocations are directed
+ * to the platform's adapter manager.
+ * <p>
+ * Note: In situations where it would be awkward to subclass this
+ * class, the same affect can be achieved simply by implementing
+ * the {@link IAdaptable} interface and explicitly forwarding
+ * the <code>getAdapter</code> request to an implementation
+ * of the {@link IAdapterManager} service. The method would look like:
+ * <pre>
+ *     public Object getAdapter(Class adapter) {
+ *         IAdapterManager manager = ...;//lookup the IAdapterManager service
+ *         return manager.getAdapter(this, adapter);
+ *     }
+ * </pre>
+ * </p><p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * Clients may subclass.
+ * </p>
+ *
+ * @see IAdapterManager
+ * @see IAdaptable
+ */
+public abstract class PlatformObject : IAdaptable {
+    /**
+     * Constructs a new platform object.
+     */
+    public this() {
+//         super();
+    }
+
+    /**
+     * Returns an object which is an instance of the given class
+     * associated with this object. Returns <code>null</code> if
+     * no such object can be found.
+     * <p>
+     * This implementation of the method declared by <code>IAdaptable</code>
+     * passes the request along to the platform's adapter manager; roughly
+     * <code>Platform.getAdapterManager().getAdapter(this, adapter)</code>.
+     * Subclasses may override this method (however, if they do so, they
+     * should invoke the method on their superclass to ensure that the
+     * Platform's adapter manager is consulted).
+     * </p>
+     *
+     * @param adapter the class to adapt to
+     * @return the adapted object or <code>null</code>
+     * @see IAdaptable#getAdapter(Class)
+     */
+    public Object getAdapter(ClassInfo adapter) {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+//         return AdapterManager.getDefault().getAdapter(this, adapter);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/ProgressMonitorWrapper.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.ProgressMonitorWrapper;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IProgressMonitorWithBlocking;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+
+/**
+ * An abstract wrapper around a progress monitor which,
+ * unless overridden, forwards <code>IProgressMonitor</code>
+ * and <code>IProgressMonitorWithBlocking</code> methods to the wrapped progress monitor.
+ * <p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * Clients may subclass.
+ * </p>
+ */
+public abstract class ProgressMonitorWrapper : IProgressMonitor, IProgressMonitorWithBlocking {
+
+    /** The wrapped progress monitor. */
+    private IProgressMonitor progressMonitor;
+
+    /**
+     * Creates a new wrapper around the given monitor.
+     *
+     * @param monitor the progress monitor to forward to
+     */
+    protected this(IProgressMonitor monitor) {
+        Assert.isNotNull(cast(Object)monitor);
+        progressMonitor = monitor;
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#beginTask(String, int)
+     */
+    public void beginTask(String name, int totalWork) {
+        progressMonitor.beginTask(name, totalWork);
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitorWithBlocking</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitorWithBlocking#clearBlocked()
+     * @since 3.0
+     */
+    public void clearBlocked() {
+        if ( auto mon = cast(IProgressMonitorWithBlocking)progressMonitor )
+            mon.clearBlocked();
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#done()
+     */
+    public void done() {
+        progressMonitor.done();
+    }
+
+    /**
+     * Returns the wrapped progress monitor.
+     *
+     * @return the wrapped progress monitor
+     */
+    public IProgressMonitor getWrappedProgressMonitor() {
+        return progressMonitor;
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#internalWorked(double)
+     */
+    public void internalWorked(double work) {
+        progressMonitor.internalWorked(work);
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#isCanceled()
+     */
+    public bool isCanceled() {
+        return progressMonitor.isCanceled();
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitorWithBlocking</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitorWithBlocking#setBlocked(IStatus)
+     * @since 3.0
+     */
+    public void setBlocked(IStatus reason) {
+        if ( auto mon = cast(IProgressMonitorWithBlocking)progressMonitor)
+            mon.setBlocked(reason);
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#setCanceled(bool)
+     */
+    public void setCanceled(bool b) {
+        progressMonitor.setCanceled(b);
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#setTaskName(String)
+     */
+    public void setTaskName(String name) {
+        progressMonitor.setTaskName(name);
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#subTask(String)
+     */
+    public void subTask(String name) {
+        progressMonitor.subTask(name);
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#worked(int)
+     */
+    public void worked(int work) {
+        progressMonitor.worked(work);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/QualifiedName.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.QualifiedName;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+
+/**
+ * Qualified names are two-part names: qualifier and local name.
+ * The qualifier must be in URI form (see RFC2396).
+ * Note however that the qualifier may be <code>null</code> if
+ * the default name space is being used.  The empty string is not
+ * a valid local name.
+ * <p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * This class is not intended to be subclassed by clients.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public final class QualifiedName {
+
+    /** Qualifier part (potentially <code>null</code>). */
+    /*package*/
+    String qualifier = null;
+
+    /** Local name part. */
+    /*package*/
+    String localName = null;
+
+    /**
+     * Creates and returns a new qualified name with the given qualifier
+     * and local name.  The local name must not be the empty string.
+     * The qualifier may be <code>null</code>.
+     * <p>
+     * Clients may instantiate.
+     * </p>
+     * @param qualifier the qualifier string, or <code>null</code>
+     * @param localName the local name string
+     */
+    public this(String qualifier, String localName) {
+        Assert.isLegal(localName !is null && localName.length !is 0);
+        this.qualifier = qualifier;
+        this.localName = localName;
+    }
+
+    /**
+     * Returns whether this qualified name is equivalent to the given object.
+     * <p>
+     * Qualified names are equal if and only if they have the same
+     * qualified parts and local parts.
+     * Qualified names are not equal to objects other than qualified names.
+     * </p>
+     *
+     * @param obj the object to compare to
+     * @return <code>true</code> if these are equivalent qualified
+     *    names, and <code>false</code> otherwise
+     */
+    public override int opEquals(Object obj) {
+        if (obj is this) {
+            return true;
+        }
+        if (!(cast(QualifiedName)obj )) {
+            return false;
+        }
+        QualifiedName qName = cast(QualifiedName) obj;
+        /* There may or may not be a qualifier */
+        if (qualifier is null && qName.getQualifier() !is null) {
+            return false;
+        }
+        if (qualifier !is null && !qualifier.equals(qName.getQualifier())) {
+            return false;
+        }
+        return localName.equals(qName.getLocalName());
+    }
+
+    /**
+     * Returns the local part of this name.
+     *
+     * @return the local name string
+     */
+    public String getLocalName() {
+        return localName;
+    }
+
+    /**
+     * Returns the qualifier part for this qualified name, or <code>null</code>
+     * if none.
+     *
+     * @return the qualifier string, or <code>null</code>
+     */
+    public String getQualifier() {
+        return qualifier;
+    }
+
+    /* (Intentionally omitted from javadoc)
+     * Implements the method <code>Object.hashCode</code>.
+     *
+     * Returns the hash code for this qualified name.
+     */
+    public override hash_t toHash() {
+        return (qualifier is null ? 0 : .toHash(qualifier)) + .toHash(localName);
+    }
+
+    /**
+     * Converts this qualified name into a string, suitable for
+     * debug purposes only.
+     */
+    public override String toString() {
+        return (getQualifier() is null ? "" : getQualifier() ~ ':') ~ getLocalName(); //$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/SafeRunner.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.SafeRunner;
+
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.core.internal.runtime.IRuntimeConstants;
+
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+
+/**
+ * Runs the given ISafeRunnable in a protected mode: exceptions
+ * thrown in the runnable are logged and passed to the runnable's
+ * exception handler.  Such exceptions are not rethrown by this method.
+ * <p>
+ * This class can be used without OSGi running.
+ * </p>
+ * @since org.eclipse.equinox.common 3.2
+ */
+public final class SafeRunner {
+
+    /**
+     * Runs the given runnable in a protected mode.   Exceptions
+     * thrown in the runnable are logged and passed to the runnable's
+     * exception handler.  Such exceptions are not rethrown by this method.
+     *
+     * @param code the runnable to run
+     */
+    public static void run(ISafeRunnable code) {
+        Assert.isNotNull(cast(Object)code);
+        try {
+            code.run();
+        } catch (Exception e) {
+            handleException(code, e);
+// SWT not in D
+//         } catch (LinkageError e) {
+//             handleException(code, e);
+        }
+    }
+
+    private static void handleException(ISafeRunnable code, Exception e) {
+        if( null is cast(OperationCanceledException) e ){
+
+            // try to obtain the correct plug-in id for the bundle providing the safe runnable
+//          Activator activator = Activator.getDefault();
+            String pluginId = null;
+//          if (activator !is null)
+//              pluginId = activator.getBundleId(code);
+            if (pluginId is null)
+                pluginId = IRuntimeConstants.PI_COMMON;
+
+            String message = null;
+//          String message = NLS.bind(CommonMessages.meta_pluginProblems, pluginId);
+            IStatus status;
+            if ( auto ce = cast(CoreException) e ) {
+                status = new MultiStatus(pluginId, IRuntimeConstants.PLUGIN_ERROR, message, e);
+                (cast(MultiStatus) status).merge( ce.getStatus());
+            } else {
+                status = new Status(IStatus.ERROR, pluginId, IRuntimeConstants.PLUGIN_ERROR, message, e);
+            }
+            // Make sure user sees the exception: if the log is empty, log the exceptions on stderr
+            //if (!RuntimeLog.isEmpty())
+            //    RuntimeLog.log(status);
+            //else
+            ExceptionPrintStackTrace(e);
+        }
+
+        code.handleException(e);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/Status.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,282 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.Status;
+
+import org.eclipse.core.internal.runtime.IRuntimeConstants;
+import org.eclipse.core.internal.runtime.LocalizationUtils;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import tango.text.convert.Format;
+
+/**
+ * A concrete status implementation, suitable either for
+ * instantiating or subclassing.
+ * <p>
+ * This class can be used without OSGi running.
+ * </p>
+ */
+public class Status : IStatus {
+
+    /**
+     * A standard OK status with an "ok"  message.
+     *
+     * @since 3.0
+     */
+    public static const IStatus OK_STATUS;
+    /**
+     * A standard CANCEL status with no message.
+     *
+     * @since 3.0
+     */
+    public static const IStatus CANCEL_STATUS;
+
+    static this(){
+        OK_STATUS = new Status(OK, IRuntimeConstants.PI_RUNTIME, OK, LocalizationUtils.safeLocalize("ok"), null); //$NON-NLS-1$
+        CANCEL_STATUS = new Status(CANCEL, IRuntimeConstants.PI_RUNTIME, 1, "", null); //$NON-NLS-1$
+    }
+
+    /**
+     * The severity. One of
+     * <ul>
+     * <li><code>CANCEL</code></li>
+     * <li><code>ERROR</code></li>
+     * <li><code>WARNING</code></li>
+     * <li><code>INFO</code></li>
+     * <li>or <code>OK</code> (0)</li>
+     * </ul>
+     */
+    private int severity = OK;
+
+    /** Unique identifier of plug-in.
+     */
+    private String pluginId;
+
+    /** Plug-in-specific status code.
+     */
+    private int code;
+
+    /** Message, localized to the current locale.
+     */
+    private String message;
+
+    /** Wrapped exception, or <code>null</code> if none.
+     */
+    private Exception exception = null;
+
+    /** Constant to avoid generating garbage.
+     */
+    private static const IStatus[] theEmptyStatusArray = null;
+
+    /**
+     * Creates a new status object.  The created status has no children.
+     *
+     * @param severity the severity; one of <code>OK</code>, <code>ERROR</code>,
+     * <code>INFO</code>, <code>WARNING</code>,  or <code>CANCEL</code>
+     * @param pluginId the unique identifier of the relevant plug-in
+     * @param code the plug-in-specific status code, or <code>OK</code>
+     * @param message a human-readable message, localized to the
+     *    current locale
+     * @param exception a low-level exception, or <code>null</code> if not
+     *    applicable
+     */
+    public this(int severity, String pluginId, int code, String message, Exception exception) {
+        setSeverity(severity);
+        setPlugin(pluginId);
+        setCode(code);
+        setMessage(message);
+        setException(exception);
+    }
+
+    /**
+     * Simplified constructor of a new status object; assumes that code is <code>OK</code>.
+     * The created status has no children.
+     *
+     * @param severity the severity; one of <code>OK</code>, <code>ERROR</code>,
+     * <code>INFO</code>, <code>WARNING</code>,  or <code>CANCEL</code>
+     * @param pluginId the unique identifier of the relevant plug-in
+     * @param message a human-readable message, localized to the
+     *    current locale
+     * @param exception a low-level exception, or <code>null</code> if not
+     *    applicable
+     *
+     * @since org.eclipse.equinox.common 3.3
+     */
+    public this(int severity, String pluginId, String message, Exception exception) {
+        setSeverity(severity);
+        setPlugin(pluginId);
+        setMessage(message);
+        setException(exception);
+        setCode(OK);
+    }
+
+    /**
+     * Simplified constructor of a new status object; assumes that code is <code>OK</code> and
+     * exception is <code>null</code>. The created status has no children.
+     *
+     * @param severity the severity; one of <code>OK</code>, <code>ERROR</code>,
+     * <code>INFO</code>, <code>WARNING</code>,  or <code>CANCEL</code>
+     * @param pluginId the unique identifier of the relevant plug-in
+     * @param message a human-readable message, localized to the
+     *    current locale
+     *
+     * @since org.eclipse.equinox.common 3.3
+     */
+    public this(int severity, String pluginId, String message) {
+        setSeverity(severity);
+        setPlugin(pluginId);
+        setMessage(message);
+        setCode(OK);
+        setException(null);
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public IStatus[] getChildren() {
+        return theEmptyStatusArray;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public int getCode() {
+        return code;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public Exception getException() {
+        return exception;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public String getMessage() {
+        return message;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public String getPlugin() {
+        return pluginId;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public int getSeverity() {
+        return severity;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public bool isMultiStatus() {
+        return false;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public bool isOK() {
+        return severity is OK;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public bool matches(int severityMask) {
+        return (severity & severityMask) !is 0;
+    }
+
+    /**
+     * Sets the status code.
+     *
+     * @param code the plug-in-specific status code, or <code>OK</code>
+     */
+    protected void setCode(int code) {
+        this.code = code;
+    }
+
+    /**
+     * Sets the exception.
+     *
+     * @param exception a low-level exception, or <code>null</code> if not
+     *    applicable
+     */
+    protected void setException(Exception exception) {
+        this.exception = exception;
+    }
+
+    /**
+     * Sets the message. If null is passed, message is set to an empty
+     * string.
+     *
+     * @param message a human-readable message, localized to the
+     *    current locale
+     */
+    protected void setMessage(String message) {
+        if (message is null)
+            this.message = ""; //$NON-NLS-1$
+        else
+            this.message = message;
+    }
+
+    /**
+     * Sets the plug-in id.
+     *
+     * @param pluginId the unique identifier of the relevant plug-in
+     */
+    protected void setPlugin(String pluginId) {
+        Assert.isLegal(pluginId !is null && pluginId.length > 0);
+        this.pluginId = pluginId;
+    }
+
+    /**
+     * Sets the severity.
+     *
+     * @param severity the severity; one of <code>OK</code>, <code>ERROR</code>,
+     * <code>INFO</code>, <code>WARNING</code>,  or <code>CANCEL</code>
+     */
+    protected void setSeverity(int severity) {
+        Assert.isLegal(severity is OK || severity is ERROR || severity is WARNING || severity is INFO || severity is CANCEL);
+        this.severity = severity;
+    }
+
+    /**
+     * Returns a string representation of the status, suitable
+     * for debugging purposes only.
+     */
+    public override String toString() {
+        String sev;
+        if (severity is OK) {
+            sev="OK"; //$NON-NLS-1$
+        } else if (severity is ERROR) {
+            sev="ERROR"; //$NON-NLS-1$
+        } else if (severity is WARNING) {
+            sev="WARNING"; //$NON-NLS-1$
+        } else if (severity is INFO) {
+            sev="INFO"; //$NON-NLS-1$
+        } else if (severity is CANCEL) {
+            sev="CANCEL"; //$NON-NLS-1$
+        } else {
+            sev=Format( "severity={}", severity);
+        }
+        return Format("Status {}: {} code={} {} {}", sev, pluginId, code, message, exception.toString );
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/SubMonitor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,790 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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:
+ *     Stefan Xenos - initial API and implementation
+ *     Stefan Xenos - bug 174539 - add a 1-argument convert(...) method
+ *     Stefan Xenos - bug 174040 - SubMonitor#convert doesn't always set task name
+ *     Stefan Xenos - bug 206942 - updated javadoc to recommend better constants for infinite progress
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.runtime.SubMonitor;
+
+import org.eclipse.core.runtime.IProgressMonitorWithBlocking;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+
+import java.lang.all;
+
+/**
+ * <p>A progress monitor that uses a given amount of work ticks from a parent monitor. This is intended as a
+ * safer, easier-to-use alternative to SubProgressMonitor. The main benefits of SubMonitor over
+ * SubProgressMonitor are:</p>
+ * <ul>
+ * <li>It is not necessary to call beginTask() or done() on an instance of SubMonitor.</li>
+ * <li>SubMonitor has a simpler syntax for creating nested monitors.</li>
+ * <li>SubMonitor is more efficient for deep recursion chains.</li>
+ * <li>SubMonitor has a setWorkRemining method that allows the remaining space on the monitor to be
+ * redistributed without reporting any work.</li>
+ * <li>SubMonitor protects the caller from common progress reporting bugs in a called method. For example,
+ * if a called method fails to call done() on the given monitor or fails to consume all the ticks on
+ * the given monitor, the parent will correct the problem after the method returns.</li>
+ * </ul>
+ * <p></p>
+ * <p><b>USAGE:</b></p>
+ *
+ * <p>When implementing a method that accepts an IProgressMonitor:</p>
+ * <ul>
+ * <li>At the start of your method, use <code>SubMonitor.convert(...).</code> to convert the IProgressMonitor
+ * into a SubMonitor. </li>
+ * <li>Use <code>SubMonitor.newChild(...)</code> whenever you need to call another method that
+ * accepts an IProgressMonitor.</li>
+ * </ul>
+ * <p></p>
+ * <p><b>DEFAULT BEHAVIOR:</b></p>
+ *
+ * <p>When writing JavaDoc for a method that accepts an IProgressMonitor, you should assume the
+ * following default behavior unless the method's JavaDoc says otherwise:</p>
+ * <ul>
+ * <li>It WILL call beginTask on the IProgressMonitor.</li>
+ * <li>It WILL NOT accept a null argument.</li>
+ * <li>It WILL call done on the IProgressMonitor.</li>
+ * </ul>
+ * <p></p>
+ * <p><b>BEST PRACTISES:</b></p>
+ *
+ * <p>We recommend that newly-written methods follow the given contract:</p>
+ * <ul>
+ * <li>It WILL call beginTask on the IProgressMonitor.</li>
+ * <li>It WILL accept a null argument, indicating that no progress should be reported and the operation cannot be cancelled.</li>
+ * <li>It WILL NOT call done on the IProgressMonitor, leaving this responsibility up to the caller.</li>
+ * </ul>
+ * <p>If you wish to follow these conventions, you may copy and paste the following text into your method's JavaDoc:</p>
+ *
+ * <pre>@param monitor the progress monitor to use for reporting progress to the user. It is the caller's responsibility
+ *        to call done() on the given monitor. Accepts <code>null</code>, indicating that no progress should be
+ *        reported and that the operation cannot be cancelled.</pre>
+ *
+ * <p></p>
+ * <p><b>Example: Recommended usage</b></p>
+ *
+ * <p>This example demonstrates how the recommended usage of <code>SubMonitor</code> makes it unnecessary to call
+ * IProgressMonitor.done() in most situations.</p>
+ *
+ * <p>It is never necessary to call done() on a monitor obtained from <code>convert</code> or <code>progress.newChild()</code>.
+ * In this example, there is no guarantee that <code>monitor</code> is an instance of <code>SubMonitor</code>, making it
+ * necessary to call <code>monitor.done()</code>. The JavaDoc contract makes this the responsibility of the caller.</p>
+ *
+ * <pre>
+ *      // param monitor the progress monitor to use for reporting progress to the user. It is the caller's responsibility
+ *      //        to call done() on the given monitor. Accepts <code>null</code>, indicating that no progress should be
+ *      //        reported and that the operation cannot be cancelled.
+ *      //
+ *      void doSomething(IProgressMonitor monitor) {
+ *          // Convert the given monitor into a progress instance
+ *          SubMonitor progress = SubMonitor.convert(monitor, 100);
+ *
+ *          // Use 30% of the progress to do some work
+ *          doSomeWork(progress.newChild(30));
+ *
+ *          // Advance the monitor by another 30%
+ *          progress.worked(30);
+ *
+ *          // Use the remaining 40% of the progress to do some more work
+ *          doSomeWork(progress.newChild(40));
+ *      }
+ * </pre>
+ *
+ *
+ * <p></p>
+ * <p><b>Example: Default usage</b></p>
+ *
+ * <p>You will often need to implement a method that does not explicitly stipulate that calling done() is the responsibility
+ * of the caller. In this case, you should use the following pattern:</p>
+ *
+ * <pre>
+ *      // param monitor the progress monitor to use for reporting progress to the user, or <code>null</code> indicating
+ *      //        that no progress should be reported and the operation cannot be cancelled.
+ *      //
+ *      void doSomething(IProgressMonitor monitor) {
+ *          // Convert the given monitor into a progress instance
+ *          SubMonitor progress = SubMonitor.convert(monitor, 100);
+ *          try {
+ *              // Use 30% of the progress to do some work
+ *              doSomeWork(progress.newChild(30));
+ *
+ *              // Advance the monitor by another 30%
+ *              progress.worked(30);
+ *
+ *              // Use the remaining 40% of the progress to do some more work
+ *              doSomeWork(progress.newChild(40));
+ *
+ *          } finally {
+ *              if (monitor !is null) {
+ *                  monitor.done();
+ *              }
+ *          }
+ *      }
+ * </pre>
+ *
+ * <p></p>
+ * <p><b>Example: Branches</b></p>
+ *
+ * <p>This example demonstrates how to smoothly report progress in situations where some of the work is optional.</p>
+ *
+ * <pre>
+ *      void doSomething(IProgressMonitor monitor) {
+ *          SubMonitor progress = SubMonitor.convert(monitor, 100);
+ *
+ *          if (condition) {
+ *              // Use 50% of the progress to do some work
+ *              doSomeWork(progress.newChild(50));
+ *          }
+ *
+ *          // Don't report any work, but ensure that we have 50 ticks remaining on the progress monitor.
+ *          // If we already consumed 50 ticks in the above branch, this is a no-op. Otherwise, the remaining
+ *          // space in the monitor is redistributed into 50 ticks.
+ *
+ *          progress.setWorkRemaining(50);
+ *
+ *          // Use the remainder of the progress monitor to do the rest of the work
+ *          doSomeWork(progress.newChild(50));
+ *      }
+ * </pre>
+ *
+ * <p>Please beware of the following anti-pattern:</p>
+ *
+ * <pre>
+ *          if (condition) {
+ *              // Use 50% of the progress to do some work
+ *              doSomeWork(progress.newChild(50));
+ *          } else {
+ *              // Bad: Causes the progress monitor to appear to start at 50%, wasting half of the
+ *              // space in the monitor.
+ *              progress.worked(50);
+ *          }
+ * </pre>
+ *
+ *
+ * <p></p>
+ * <p><b>Example: Loops</b></p>
+ *
+ * <p>This example demonstrates how to report progress in a loop.</p>
+ *
+ * <pre>
+ *      void doSomething(IProgressMonitor monitor, Collection someCollection) {
+ *          SubMonitor progress = SubMonitor.convert(monitor, 100);
+ *
+ *          // Create a new progress monitor that uses 70% of the total progress and will allocate one tick
+ *          // for each element of the given collection.
+ *          SubMonitor loopProgress = progress.newChild(70).setWorkRemaining(someCollection.size());
+ *
+ *          for (Iterator iter = someCollection.iterator(); iter.hasNext();) {
+ *              Object next = iter.next();
+ *
+ *              doWorkOnElement(next, loopProgress.newChild(1));
+ *          }
+ *
+ *          // Use the remaining 30% of the progress monitor to do some work outside the loop
+ *          doSomeWork(progress.newChild(30));
+ *      }
+ * </pre>
+ *
+ *
+ * <p></p>
+ * <p><b>Example: Infinite progress</b></p>
+ *
+ * <p>This example demonstrates how to report logarithmic progress in situations where the number of ticks
+ * cannot be easily computed in advance.</p>
+ *
+ * <pre>
+ *      void doSomething(IProgressMonitor monitor, LinkedListNode node) {
+ *          SubMonitor progress = SubMonitor.convert(monitor);
+ *
+ *          while (node !is null) {
+ *              // Regardless of the amount of progress reported so far,
+ *              // use 0.01% of the space remaining in the monitor to process the next node.
+ *              progress.setWorkRemaining(10000);
+ *
+ *              doWorkOnElement(node, progress.newChild(1));
+ *
+ *              node = node.next;
+ *          }
+ *      }
+ * </pre>
+ *
+ * <p>
+ * This class can be used without OSGi running.
+ * </p>
+ *
+ * @since org.eclipse.equinox.common 3.3
+ */
+public final class SubMonitor : IProgressMonitorWithBlocking {
+
+    /**
+     * Minimum number of ticks to allocate when calling beginTask on an unknown IProgressMonitor.
+     * Pick a number that is big enough such that, no matter where progress is being displayed,
+     * the user would be unlikely to notice if progress were to be reported with higher accuracy.
+     */
+    private static final int MINIMUM_RESOLUTION = 1000;
+
+    /**
+     * The RootInfo struct holds information about the root progress monitor. A SubMonitor and
+     * its active descendents share the same RootInfo struct.
+     */
+    private static final class RootInfo {
+        private final IProgressMonitor root;
+
+        /**
+         * Remembers the last task name. Prevents us from setting the same task name multiple
+         * times in a row.
+         */
+        private String taskName = null;
+
+        /**
+         * Remembers the last subtask name. Prevents the SubMonitor from setting the same
+         * subtask string more than once in a row.
+         */
+        private String subTask_ = null;
+
+        /**
+         * Creates a RootInfo struct that delegates to the given progress
+         * monitor.
+         *
+         * @param root progress monitor to delegate to
+         */
+        public this(IProgressMonitor root) {
+            this.root = root;
+        }
+
+        public bool isCanceled() {
+            return root.isCanceled();
+        }
+
+        public void setCanceled(bool value) {
+            root.setCanceled(value);
+        }
+
+        public void setTaskName(String taskName) {
+            if (eq(taskName, this.taskName)) {
+                return;
+            }
+            this.taskName = taskName;
+            root.setTaskName(taskName);
+        }
+
+        public void subTask(String name) {
+            if (eq(subTask_, name)) {
+                return;
+            }
+
+            this.subTask_ = name;
+            root.subTask(name);
+        }
+
+        public void worked(int i) {
+            root.worked(i);
+        }
+
+        public void clearBlocked() {
+            if ( auto mon = cast(IProgressMonitorWithBlocking)root )
+                mon.clearBlocked();
+        }
+
+        public void setBlocked(IStatus reason) {
+            if ( auto mon = cast(IProgressMonitorWithBlocking)root )
+                mon.setBlocked(reason);
+        }
+
+    }
+
+    /**
+     * Total number of ticks that this progress monitor is permitted to consume
+     * from the root.
+     */
+    private int totalParent;
+
+    /**
+     * Number of ticks that this progress monitor has already reported in the root.
+     */
+    private int usedForParent = 0;
+
+    /**
+     * Number of ticks that have been consumed by this instance's children.
+     */
+    private double usedForChildren = 0.0;
+
+    /**
+     * Number of ticks allocated for this instance's children. This is the total number
+     * of ticks that may be passed into worked(int) or newChild(int).
+     */
+    private int totalForChildren;
+
+    /**
+     * Children created by newChild will be completed automatically the next time
+     * the parent progress monitor is touched. This points to the last incomplete child
+     * created with newChild.
+     */
+    private IProgressMonitor lastSubMonitor = null;
+
+    /**
+     * Used to communicate with the root of this progress monitor tree
+     */
+    private const RootInfo root;
+
+    /**
+     * A bitwise combination of the SUPPRESS_* flags.
+     */
+    private const int flags;
+
+    /**
+     * May be passed as a flag to newChild. Indicates that the calls
+     * to subTask on the child should be ignored. Without this flag,
+     * calling subTask on the child will result in a call to subTask
+     * on its parent.
+     */
+    public static const int SUPPRESS_SUBTASK = 0x0001;
+
+    /**
+     * May be passed as a flag to newChild. Indicates that strings
+     * passed into beginTask should be ignored. If this flag is
+     * specified, then the progress monitor instance will accept null
+     * as the first argument to beginTask. Without this flag, any
+     * string passed to beginTask will result in a call to
+     * setTaskName on the parent.
+     */
+    public static const int SUPPRESS_BEGINTASK = 0x0002;
+
+    /**
+     * May be passed as a flag to newChild. Indicates that strings
+     * passed into setTaskName should be ignored. If this string
+     * is omitted, then a call to setTaskName on the child will
+     * result in a call to setTaskName on the parent.
+     */
+    public static const int SUPPRESS_SETTASKNAME = 0x0004;
+
+    /**
+     * May be passed as a flag to newChild. Indicates that strings
+     * passed to setTaskName, subTask, and beginTask should all be ignored.
+     */
+    public static const int SUPPRESS_ALL_LABELS = SUPPRESS_SETTASKNAME | SUPPRESS_BEGINTASK | SUPPRESS_SUBTASK;
+
+    /**
+     * May be passed as a flag to newChild. Indicates that strings
+     * passed to setTaskName, subTask, and beginTask should all be propogated
+     * to the parent.
+     */
+    public static const int SUPPRESS_NONE = 0;
+
+    /**
+     * Creates a new SubMonitor that will report its progress via
+     * the given RootInfo.
+     * @param rootInfo the root of this progress monitor tree
+     * @param totalWork total work to perform on the given progress monitor
+     * @param availableToChildren number of ticks allocated for this instance's children
+     * @param flags a bitwise combination of the SUPPRESS_* constants
+     */
+    private this(RootInfo rootInfo, int totalWork, int availableToChildren, int flags) {
+        root = rootInfo;
+        totalParent = (totalWork > 0) ? totalWork : 0;
+        this.totalForChildren = availableToChildren;
+        this.flags = flags;
+    }
+
+    /**
+     * <p>Converts an unknown (possibly null) IProgressMonitor into a SubMonitor. It is
+     * not necessary to call done() on the result, but the caller is responsible for calling
+     * done() on the argument. Calls beginTask on the argument.</p>
+     *
+     * <p>This method should generally be called at the beginning of a method that accepts
+     * an IProgressMonitor in order to convert the IProgressMonitor into a SubMonitor.</p>
+     *
+     * @param monitor monitor to convert to a SubMonitor instance or null. Treats null
+     * as a new instance of <code>NullProgressMonitor</code>.
+     * @return a SubMonitor instance that adapts the argument
+     */
+    public static SubMonitor convert(IProgressMonitor monitor) {
+        return convert(monitor, "", 0); //$NON-NLS-1$
+    }
+
+    /**
+     * <p>Converts an unknown (possibly null) IProgressMonitor into a SubMonitor allocated
+     * with the given number of ticks. It is not necessary to call done() on the result,
+     * but the caller is responsible for calling done() on the argument. Calls beginTask
+     * on the argument.</p>
+     *
+     * <p>This method should generally be called at the beginning of a method that accepts
+     * an IProgressMonitor in order to convert the IProgressMonitor into a SubMonitor.</p>
+     *
+     * @param monitor monitor to convert to a SubMonitor instance or null. Treats null
+     * as a new instance of <code>NullProgressMonitor</code>.
+     * @param work number of ticks that will be available in the resulting monitor
+     * @return a SubMonitor instance that adapts the argument
+     */
+    public static SubMonitor convert(IProgressMonitor monitor, int work) {
+        return convert(monitor, "", work); //$NON-NLS-1$
+    }
+
+    /**
+     * <p>Converts an unknown (possibly null) IProgressMonitor into a SubMonitor allocated
+     * with the given number of ticks. It is not necessary to call done() on the result,
+     * but the caller is responsible for calling done() on the argument. Calls beginTask
+     * on the argument.</p>
+     *
+     * <p>This method should generally be called at the beginning of a method that accepts
+     * an IProgressMonitor in order to convert the IProgressMonitor into a SubMonitor.</p>
+     *
+     * @param monitor to convert into a SubMonitor instance or null. If given a null argument,
+     * the resulting SubMonitor will not report its progress anywhere.
+     * @param taskName user readable name to pass to monitor.beginTask. Never null.
+     * @param work initial number of ticks to allocate for children of the SubMonitor
+     * @return a new SubMonitor instance that is a child of the given monitor
+     */
+    public static SubMonitor convert(IProgressMonitor monitor, String taskName, int work) {
+        if (monitor is null)
+            monitor = new NullProgressMonitor();
+
+        // Optimization: if the given monitor already a SubMonitor, no conversion is necessary
+        if ( cast(SubMonitor) monitor ) {
+            monitor.beginTask(taskName, work);
+            return cast(SubMonitor) monitor;
+        }
+
+        monitor.beginTask(taskName, MINIMUM_RESOLUTION);
+        return new SubMonitor(new RootInfo(monitor), MINIMUM_RESOLUTION, work, SUPPRESS_NONE);
+    }
+
+    /**
+     * <p>Sets the work remaining for this SubMonitor instance. This is the total number
+     * of ticks that may be reported by all subsequent calls to worked(int), newChild(int), etc.
+     * This may be called many times for the same SubMonitor instance. When this method
+     * is called, the remaining space on the progress monitor is redistributed into the given
+     * number of ticks.</p>
+     *
+     * <p>It doesn't matter how much progress has already been reported with this SubMonitor
+     * instance. If you call setWorkRemaining(100), you will be able to report 100 more ticks of
+     * work before the progress meter reaches 100%.</p>
+     *
+     * @param workRemaining total number of remaining ticks
+     * @return the receiver
+     */
+    public SubMonitor setWorkRemaining(int workRemaining) {
+        // Ensure we don't try to allocate negative ticks
+        workRemaining = Math.max(0, workRemaining);
+
+        // Ensure we don't cause division by zero
+        if (totalForChildren > 0 && totalParent > usedForParent) {
+            // Note: We want the following value to remain invariant after this method returns
+            double remainForParent = totalParent * (1.0 - (usedForChildren / totalForChildren));
+            usedForChildren = (workRemaining * (1.0 - remainForParent / (totalParent - usedForParent)));
+        } else
+            usedForChildren = 0.0;
+
+        totalParent = totalParent - usedForParent;
+        usedForParent = 0;
+        totalForChildren = workRemaining;
+        return this;
+    }
+
+    /**
+     * Consumes the given number of child ticks, given as a double. Must only
+     * be called if the monitor is in floating-point mode.
+     *
+     * @param ticks the number of ticks to consume
+     * @return ticks the number of ticks to be consumed from parent
+     */
+    private int consume(double ticks) {
+        if (totalParent is 0 || totalForChildren is 0) // this monitor has no available work to report
+            return 0;
+
+        usedForChildren += ticks;
+
+        if (usedForChildren > totalForChildren)
+            usedForChildren = totalForChildren;
+        else if (usedForChildren < 0.0)
+            usedForChildren = 0.0;
+
+        int parentPosition = cast(int) (totalParent * usedForChildren / totalForChildren);
+        int delta = parentPosition - usedForParent;
+
+        usedForParent = parentPosition;
+        return delta;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.IProgressMonitor#isCanceled()
+     */
+    public bool isCanceled() {
+        return root.isCanceled();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.IProgressMonitor#setTaskName(java.lang.String)
+     */
+    public void setTaskName(String name) {
+        if ((flags & SUPPRESS_SETTASKNAME) is 0)
+            root.setTaskName(name);
+    }
+
+    /**
+     * Starts a new main task. The string argument is ignored
+     * if and only if the SUPPRESS_BEGINTASK flag has been set on this SubMonitor
+     * instance.
+     *
+     * <p>This method is equivalent calling setWorkRemaining(...) on the reciever. Unless
+     * the SUPPRESS_BEGINTASK flag is set, this will also be equivalent to calling
+     * setTaskName(...) on the parent.</p>
+     *
+     * @param name new main task name
+     * @param totalWork number of ticks to allocate
+     *
+     * @see org.eclipse.core.runtime.IProgressMonitor#beginTask(java.lang.String, int)
+     */
+    public void beginTask(String name, int totalWork) {
+        if ((flags & SUPPRESS_BEGINTASK) is 0 && name !is null)
+            root.setTaskName(name);
+        setWorkRemaining(totalWork);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.IProgressMonitor#done()
+     */
+    public void done() {
+        cleanupActiveChild();
+        int delta = totalParent - usedForParent;
+        if (delta > 0)
+            root.worked(delta);
+
+        totalParent = 0;
+        usedForParent = 0;
+        totalForChildren = 0;
+        usedForChildren = 0.0;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.IProgressMonitor#internalWorked(double)
+     */
+    public void internalWorked(double work) {
+        cleanupActiveChild();
+
+        int delta = consume((work > 0.0) ? work : 0.0);
+        if (delta !is 0)
+            root.worked(delta);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.IProgressMonitor#subTask(java.lang.String)
+     */
+    public void subTask(String name) {
+        if ((flags & SUPPRESS_SUBTASK) is 0)
+            root.subTask(name);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.IProgressMonitor#worked(int)
+     */
+    public void worked(int work) {
+        internalWorked(work);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.IProgressMonitor#setCanceled(bool)
+     */
+    public void setCanceled(bool b) {
+        root.setCanceled(b);
+    }
+
+    /**
+     * <p>Creates a sub progress monitor that will consume the given number of ticks from the
+     * receiver. It is not necessary to call <code>beginTask</code> or <code>done</code> on the
+     * result. However, the resulting progress monitor will not report any work after the first
+     * call to done() or before ticks are allocated. Ticks may be allocated by calling beginTask
+     * or setWorkRemaining.</p>
+     *
+     * <p>Each SubMonitor only has one active child at a time. Each time newChild() is called, the
+     * result becomes the new active child and any unused progress from the previously-active child is
+     * consumed.</p>
+     *
+     * <p>This is property makes it unnecessary to call done() on a SubMonitor instance, since child
+     * monitors are automatically cleaned up the next time the parent is touched.</p>
+     *
+     * <code><pre>
+     *      ////////////////////////////////////////////////////////////////////////////
+     *      // Example 1: Typical usage of newChild
+     *      void myMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *          doSomething(progress.newChild(50));
+     *          doSomethingElse(progress.newChild(50));
+     *      }
+     *
+     *      ////////////////////////////////////////////////////////////////////////////
+     *      // Example 2: Demonstrates the function of active children. Creating children
+     *      // is sufficient to smoothly report progress, even if worked(...) and done()
+     *      // are never called.
+     *      void myMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *
+     *          for (int i = 0; i < 100; i++) {
+     *              // Creating the next child monitor will clean up the previous one,
+     *              // causing progress to be reported smoothly even if we don't do anything
+     *              // with the monitors we create
+     *              progress.newChild(1);
+     *          }
+     *      }
+     *
+     *      ////////////////////////////////////////////////////////////////////////////
+     *      // Example 3: Demonstrates a common anti-pattern
+     *      void wrongMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *
+     *          // WRONG WAY: Won't have the intended effect, as only one of these progress
+     *          // monitors may be active at a time and the other will report no progress.
+     *          callMethod(progress.newChild(50), computeValue(progress.newChild(50)));
+     *      }
+     *
+     *      void rightMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *
+     *          // RIGHT WAY: Break up method calls so that only one SubMonitor is in use at a time.
+     *          Object someValue = computeValue(progress.newChild(50));
+     *          callMethod(progress.newChild(50), someValue);
+     *      }
+     * </pre></code>
+     *
+     * @param totalWork number of ticks to consume from the reciever
+     * @return new sub progress monitor that may be used in place of a new SubMonitor
+     */
+    public SubMonitor newChild(int totalWork) {
+        return newChild(totalWork, SUPPRESS_BEGINTASK);
+    }
+
+    /**
+     * <p>Creates a sub progress monitor that will consume the given number of ticks from the
+     * receiver. It is not necessary to call <code>beginTask</code> or <code>done</code> on the
+     * result. However, the resulting progress monitor will not report any work after the first
+     * call to done() or before ticks are allocated. Ticks may be allocated by calling beginTask
+     * or setWorkRemaining.</p>
+     *
+     * <p>Each SubMonitor only has one active child at a time. Each time newChild() is called, the
+     * result becomes the new active child and any unused progress from the previously-active child is
+     * consumed.</p>
+     *
+     * <p>This is property makes it unnecessary to call done() on a SubMonitor instance, since child
+     * monitors are automatically cleaned up the next time the parent is touched.</p>
+     *
+     * <code><pre>
+     *      ////////////////////////////////////////////////////////////////////////////
+     *      // Example 1: Typical usage of newChild
+     *      void myMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *          doSomething(progress.newChild(50));
+     *          doSomethingElse(progress.newChild(50));
+     *      }
+     *
+     *      ////////////////////////////////////////////////////////////////////////////
+     *      // Example 2: Demonstrates the function of active children. Creating children
+     *      // is sufficient to smoothly report progress, even if worked(...) and done()
+     *      // are never called.
+     *      void myMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *
+     *          for (int i = 0; i < 100; i++) {
+     *              // Creating the next child monitor will clean up the previous one,
+     *              // causing progress to be reported smoothly even if we don't do anything
+     *              // with the monitors we create
+     *              progress.newChild(1);
+     *          }
+     *      }
+     *
+     *      ////////////////////////////////////////////////////////////////////////////
+     *      // Example 3: Demonstrates a common anti-pattern
+     *      void wrongMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *
+     *          // WRONG WAY: Won't have the intended effect, as only one of these progress
+     *          // monitors may be active at a time and the other will report no progress.
+     *          callMethod(progress.newChild(50), computeValue(progress.newChild(50)));
+     *      }
+     *
+     *      void rightMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *
+     *          // RIGHT WAY: Break up method calls so that only one SubMonitor is in use at a time.
+     *          Object someValue = computeValue(progress.newChild(50));
+     *          callMethod(progress.newChild(50), someValue);
+     *      }
+     * </pre></code>
+     *
+     * @param totalWork number of ticks to consume from the reciever
+     * @return new sub progress monitor that may be used in place of a new SubMonitor
+     */
+    public SubMonitor newChild(int totalWork, int suppressFlags) {
+        double totalWorkDouble = (totalWork > 0) ? totalWork : 0.0;
+        totalWorkDouble = Math.min(totalWorkDouble, totalForChildren - usedForChildren);
+        cleanupActiveChild();
+
+        // Compute the flags for the child. We want the net effect to be as though the child is
+        // delegating to its parent, even though it is actually talking directly to the root.
+        // This means that we need to compute the flags such that - even if a label isn't
+        // suppressed by the child - if that same label would have been suppressed when the
+        // child delegated to its parent, the child must explicitly suppress the label.
+        int childFlags = SUPPRESS_NONE;
+
+        if ((flags & SUPPRESS_SETTASKNAME) !is 0) {
+            // If the parent was ignoring labels passed to setTaskName, then the child will ignore
+            // labels passed to either beginTask or setTaskName - since both delegate to setTaskName
+            // on the parent
+            childFlags |= SUPPRESS_SETTASKNAME | SUPPRESS_BEGINTASK;
+        }
+
+        if ((flags & SUPPRESS_SUBTASK) !is 0) {
+            // If the parent was suppressing labels passed to subTask, so will the child.
+            childFlags |= SUPPRESS_SUBTASK;
+        }
+
+        // Note: the SUPPRESS_BEGINTASK flag does not affect the child since there
+        // is no method on the child that would delegate to beginTask on the parent.
+        childFlags |= suppressFlags;
+
+        SubMonitor result = new SubMonitor(root, consume(totalWorkDouble), 0, childFlags);
+        lastSubMonitor = result;
+        return result;
+    }
+
+    private void cleanupActiveChild() {
+        if (lastSubMonitor is null)
+            return;
+
+        IProgressMonitor child = lastSubMonitor;
+        lastSubMonitor = null;
+        child.done();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#clearBlocked()
+     */
+    public void clearBlocked() {
+        root.clearBlocked();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#setBlocked(org.eclipse.core.runtime.IStatus)
+     */
+    public void setBlocked(IStatus reason) {
+        root.setBlocked(reason);
+    }
+
+    protected static bool eq(String o1, String o2) {
+        if (o1.length is 0)
+            return (o2.length is 0);
+        if (o2.length is 0)
+            return false;
+        return o1.equals(o2);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.equinox.common/src/org/eclipse/core/runtime/SubProgressMonitor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * 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 org.eclipse.core.runtime.SubProgressMonitor;
+
+import org.eclipse.core.runtime.ProgressMonitorWrapper;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import java.lang.all;
+
+/**
+ * For new implementations consider using {@link SubMonitor}.
+ *
+ * A progress monitor that uses a given amount of work ticks
+ * from a parent monitor. It can be used as follows:
+ * <pre>
+ *     try {
+ *         pm.beginTask("Main Task", 100);
+ *         doSomeWork(pm, 30);
+ *         SubProgressMonitor subMonitor= new SubProgressMonitor(pm, 40);
+ *         try {
+ *             subMonitor.beginTask("", 300);
+ *             doSomeWork(subMonitor, 300);
+ *         } finally {
+ *             subMonitor.done();
+ *         }
+ *         doSomeWork(pm, 30);
+ *     } finally {
+ *         pm.done();
+ *     }
+ * </pre>
+ * <p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * This class may be instantiated or subclassed by clients.
+ * </p>
+ *
+ * @see SubMonitor
+ */
+public class SubProgressMonitor : ProgressMonitorWrapper {
+
+    /**
+     * Style constant indicating that calls to <code>subTask</code>
+     * should not have any effect.
+     *
+     * @see #SubProgressMonitor(IProgressMonitor,int,int)
+     */
+    public static const int SUPPRESS_SUBTASK_LABEL = 1 << 1;
+    /**
+     * Style constant indicating that the main task label
+     * should be prepended to the subtask label.
+     *
+     * @see #SubProgressMonitor(IProgressMonitor,int,int)
+     */
+    public static const int PREPEND_MAIN_LABEL_TO_SUBTASK = 1 << 2;
+
+    private int parentTicks = 0;
+    private double sentToParent = 0.0;
+    private double scale = 0.0;
+    private int nestedBeginTasks = 0;
+    private bool usedUp = false;
+    private bool hasSubTask = false;
+    private int style;
+    private String mainTaskLabel;
+
+    /**
+     * Creates a new sub-progress monitor for the given monitor. The sub
+     * progress monitor uses the given number of work ticks from its
+     * parent monitor.
+     *
+     * @param monitor the parent progress monitor
+     * @param ticks the number of work ticks allocated from the
+     *    parent monitor
+     */
+    public this(IProgressMonitor monitor, int ticks) {
+        this(monitor, ticks, 0);
+    }
+
+    /**
+     * Creates a new sub-progress monitor for the given monitor. The sub
+     * progress monitor uses the given number of work ticks from its
+     * parent monitor.
+     *
+     * @param monitor the parent progress monitor
+     * @param ticks the number of work ticks allocated from the
+     *    parent monitor
+     * @param style one of
+     *    <ul>
+     *    <li> <code>SUPPRESS_SUBTASK_LABEL</code> </li>
+     *    <li> <code>PREPEND_MAIN_LABEL_TO_SUBTASK</code> </li>
+     *    </ul>
+     * @see #SUPPRESS_SUBTASK_LABEL
+     * @see #PREPEND_MAIN_LABEL_TO_SUBTASK
+     */
+    public this(IProgressMonitor monitor, int ticks, int style) {
+        super(monitor);
+        this.parentTicks = (ticks > 0) ? ticks : 0;
+        this.style = style;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the method <code>IProgressMonitor.beginTask</code>.
+     *
+     * Starts a new main task. Since this progress monitor is a sub
+     * progress monitor, the given name will NOT be used to update
+     * the progress bar's main task label. That means the given
+     * string will be ignored. If style <code>PREPEND_MAIN_LABEL_TO_SUBTASK
+     * <code> is specified, then the given string will be prepended to
+     * every string passed to <code>subTask(String)</code>.
+     */
+    public override void beginTask(String name, int totalWork) {
+        nestedBeginTasks++;
+        // Ignore nested begin task calls.
+        if (nestedBeginTasks > 1) {
+            return;
+        }
+        // be safe:  if the argument would cause math errors (zero or
+        // negative), just use 0 as the scale.  This disables progress for
+        // this submonitor.
+        scale = totalWork <= 0 ? 0 : cast(double) parentTicks / cast(double) totalWork;
+        if ((style & PREPEND_MAIN_LABEL_TO_SUBTASK) !is 0) {
+            mainTaskLabel = name;
+        }
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the method <code>IProgressMonitor.done</code>.
+     */
+    public override void done() {
+        // Ignore if more done calls than beginTask calls or if we are still
+        // in some nested beginTasks
+        if (nestedBeginTasks is 0 || --nestedBeginTasks > 0)
+            return;
+        // Send any remaining ticks and clear out the subtask text
+        double remaining = parentTicks - sentToParent;
+        if (remaining > 0)
+            super.internalWorked(remaining);
+        //clear the sub task if there was one
+        if (hasSubTask)
+            subTask(""); //$NON-NLS-1$
+        sentToParent = 0;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the internal method <code>IProgressMonitor.internalWorked</code>.
+     */
+    public override void internalWorked(double work) {
+        if (usedUp || nestedBeginTasks !is 1) {
+            return;
+        }
+
+        double realWork = (work > 0.0) ? scale * work : 0.0;
+        super.internalWorked(realWork);
+        sentToParent += realWork;
+        if (sentToParent >= parentTicks) {
+            usedUp = true;
+        }
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the method <code>IProgressMonitor.subTask</code>.
+     */
+    public override void subTask(String name) {
+        if ((style & SUPPRESS_SUBTASK_LABEL) !is 0) {
+            return;
+        }
+        hasSubTask = true;
+        String label = name;
+        if ((style & PREPEND_MAIN_LABEL_TO_SUBTASK) !is 0 && mainTaskLabel !is null && mainTaskLabel.length > 0) {
+            label = mainTaskLabel ~ ' ' ~ label;
+        }
+        super.subTask(label);
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the method <code>IProgressMonitor.worked</code>.
+     */
+    public override void worked(int work) {
+        internalWorked(work);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/AnnotationBag.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.projection.AnnotationBag;
+
+import org.eclipse.jface.text.source.projection.ProjectionViewer; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSupport; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionPosition; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSummary; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionRulerColumn; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.projection.SourceViewerInformationControl; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionListener; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotation; // packageimport
+
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.HashSet;
+
+
+
+
+import org.eclipse.jface.text.source.Annotation;
+
+/**
+ * A bag of annotations.
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class AnnotationBag : Annotation {
+
+    private Set fAnnotations;
+
+    /**
+     * Creates a new annotation bag.
+     *
+     * @param type the annotation type
+     */
+    public this(String type) {
+        super(type, false, null);
+    }
+
+    /**
+     * Adds the given annotation to the annotation bag.
+     *
+     * @param annotation the annotation to add
+     */
+    public void add(Annotation annotation) {
+        if (fAnnotations is null)
+            fAnnotations= new HashSet(2);
+        fAnnotations.add(annotation);
+    }
+
+    /**
+     * Removes the given annotation from the annotation bag.
+     *
+     * @param annotation the annotation to remove
+     */
+    public void remove(Annotation annotation) {
+        if (fAnnotations !is null) {
+            fAnnotations.remove(annotation);
+            if (fAnnotations.isEmpty())
+                fAnnotations= null;
+        }
+    }
+
+    /**
+     * Returns whether the annotation bag is empty.
+     *
+     * @return <code>true</code> if the annotation bag is empty, <code>false</code> otherwise
+     */
+    public bool isEmpty() {
+        return fAnnotations is null;
+    }
+
+    /**
+     * Returns an iterator for all annotation inside this
+     * annotation bag or <code>null</code> if the bag is empty.
+     *
+     * @return an iterator for all annotations in the bag or <code>null</code>
+     * @since 3.1
+     */
+    public Iterator iterator() {
+        if (!isEmpty())
+            return fAnnotations.iterator();
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/IProjectionListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.source.projection.IProjectionListener;
+
+import org.eclipse.jface.text.source.projection.ProjectionViewer; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSupport; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionPosition; // packageimport
+import org.eclipse.jface.text.source.projection.AnnotationBag; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSummary; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionRulerColumn; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.projection.SourceViewerInformationControl; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotation; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Implementers registered with a
+ * {@link org.eclipse.jface.text.source.projection.ProjectionViewer} get
+ * informed when the projection mode of the viewer gets enabled and when it gets
+ * disabled.
+ *
+ * @since 3.0
+ */
+public interface IProjectionListener {
+
+    /**
+     * Tells this listener that projection has been enabled.
+     */
+    void projectionEnabled();
+
+    /**
+     * Tells this listener that projection has been disabled.
+     */
+    void projectionDisabled();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/IProjectionPosition.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.source.projection.IProjectionPosition;
+
+import org.eclipse.jface.text.source.projection.ProjectionViewer; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSupport; // packageimport
+import org.eclipse.jface.text.source.projection.AnnotationBag; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSummary; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionRulerColumn; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.projection.SourceViewerInformationControl; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionListener; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotation; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+
+
+/**
+ * An <code>IProjectionPosition</code> is a position that is associated with a
+ * <code>ProjectionAnnotation</code> and that is able to compute its collapsed
+ * regions. That is, if a <code>Position</code> implements this interface,
+ * <code>ProjectionViewer</code> will delegate to the
+ * {@link #computeProjectionRegions(IDocument) computeProjectionRegions} method
+ * when determining the document regions that should be collapsed for a certain
+ * <code>ProjectionAnnotation</code>.
+ *
+ * @since 3.1
+ */
+public interface IProjectionPosition {
+
+    /**
+     * Returns an array of regions that should be collapsed when the annotation
+     * belonging to this position is collapsed. May return null instead of
+     * an empty array.
+     *
+     * @param document the document that this position is attached to
+     * @return the foldable regions for this position
+     * @throws BadLocationException if accessing the document fails
+     */
+    IRegion[] computeProjectionRegions(IDocument document) ;
+
+    /**
+     * Returns the offset of the caption (the anchor region) of this projection
+     * position. The returned offset is relative to the receivers offset into
+     * the document.
+     *
+     * @param document the document that this position is attached to
+     * @return the caption offset relative to the position's offset
+     * @throws BadLocationException if accessing the document fails
+     */
+    int computeCaptionOffset(IDocument document) ;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionAnnotation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,192 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.source.projection.ProjectionAnnotation;
+
+import org.eclipse.jface.text.source.projection.ProjectionViewer; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSupport; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionPosition; // packageimport
+import org.eclipse.jface.text.source.projection.AnnotationBag; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSummary; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionRulerColumn; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.projection.SourceViewerInformationControl; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionListener; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationPresentation;
+import org.eclipse.jface.text.source.ImageUtilities;
+
+/**
+ * Annotation used to represent the projection of a master document onto a
+ * {@link org.eclipse.jface.text.projection.ProjectionDocument}. A projection
+ * annotation can be either expanded or collapsed. If expanded it corresponds to
+ * a segment of the projection document. If collapsed, it represents a region of
+ * the master document that does not have a corresponding segment in the
+ * projection document.
+ * <p>
+ * Clients may subclass or use as is.
+ * </p>
+ *
+ * @since 3.0
+ */
+public class ProjectionAnnotation : Annotation , IAnnotationPresentation {
+
+    private static class DisplayDisposeRunnable : Runnable {
+
+        public void run() {
+            if (fgCollapsedImage !is null) {
+                fgCollapsedImage.dispose();
+                fgCollapsedImage= null;
+            }
+            if (fgExpandedImage !is null) {
+                fgExpandedImage.dispose();
+                fgExpandedImage= null;
+            }
+        }
+    }
+
+    /**
+     * The type of projection annotations.
+     */
+    public static const String TYPE= "org.eclipse.projection"; //$NON-NLS-1$
+
+
+    private static const int COLOR= SWT.COLOR_GRAY;
+    private static Image fgCollapsedImage;
+    private static Image fgExpandedImage;
+
+
+    /** The state of this annotation */
+    private bool fIsCollapsed= false;
+    /** Indicates whether this annotation should be painted as range */
+    private bool fIsRangeIndication= false;
+
+    /**
+     * Creates a new expanded projection annotation.
+     */
+    public this() {
+        this(false);
+    }
+
+    /**
+     * Creates a new projection annotation. When <code>isCollapsed</code>
+     * is <code>true</code> the annotation is initially collapsed.
+     *
+     * @param isCollapsed <code>true</code> if the annotation should initially be collapsed, <code>false</code> otherwise
+     */
+    public this(bool isCollapsed) {
+        super(TYPE, false, null);
+        fIsCollapsed= isCollapsed;
+    }
+
+    /**
+     * Enables and disables the range indication for this annotation.
+     *
+     * @param rangeIndication the enable state for the range indication
+     */
+    public void setRangeIndication(bool rangeIndication) {
+        fIsRangeIndication= rangeIndication;
+    }
+
+    private void drawRangeIndication(GC gc, Canvas canvas, Rectangle r) {
+        final int MARGIN= 3;
+
+        /* cap the height - at least on GTK, large numbers are converted to
+         * negatives at some point */
+        int height= Math.min(r.y + r.height - MARGIN, canvas.getSize().y);
+
+        gc.setForeground(canvas.getDisplay().getSystemColor(COLOR));
+        gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
+        gc.drawLine(r.x + 4, r.y + 12, r.x + 4, height);
+        gc.drawLine(r.x + 4, height, r.x + r.width - MARGIN, height);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationPresentation#paint(org.eclipse.swt.graphics.GC, org.eclipse.swt.widgets.Canvas, org.eclipse.swt.graphics.Rectangle)
+     */
+    public void paint(GC gc, Canvas canvas, Rectangle rectangle) {
+        Image image= getImage(canvas.getDisplay());
+        if (image !is null) {
+            ImageUtilities.drawImage(image, gc, canvas, rectangle, SWT.CENTER, SWT.TOP);
+            if (fIsRangeIndication) {
+                FontMetrics fontMetrics= gc.getFontMetrics();
+                int delta= (fontMetrics.getHeight() - image.getBounds().height)/2;
+                rectangle.y += delta;
+                rectangle.height -= delta;
+                drawRangeIndication(gc, canvas, rectangle);
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationPresentation#getLayer()
+     */
+    public int getLayer() {
+        return IAnnotationPresentation.DEFAULT_LAYER;
+    }
+
+    private Image getImage(Display display) {
+        initializeImages(display);
+        return isCollapsed() ? fgCollapsedImage : fgExpandedImage;
+    }
+
+    private void initializeImages(Display display) {
+        if (fgCollapsedImage is null) {
+            ImageDescriptor descriptor= ImageDescriptor.createFromFile( getImportData!("org.eclipse.jface.text.source.projection.collapsed.gif")); //$NON-NLS-1$
+            fgCollapsedImage= descriptor.createImage(display);
+            descriptor= ImageDescriptor.createFromFile( getImportData!( "org.eclipse.jface.text.source.projection.expanded.gif")); //$NON-NLS-1$
+            fgExpandedImage= descriptor.createImage(display);
+
+            display.disposeExec(new DisplayDisposeRunnable());
+        }
+    }
+
+    /**
+     * Returns the state of this annotation.
+     *
+     * @return <code>true</code> if collapsed
+     */
+    public bool isCollapsed() {
+        return fIsCollapsed;
+    }
+
+    /**
+     * Marks this annotation as being collapsed.
+     */
+    public void markCollapsed() {
+        fIsCollapsed= true;
+    }
+
+    /**
+     * Marks this annotation as being unfolded.
+     */
+    public void markExpanded() {
+        fIsCollapsed= false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionAnnotationHover.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.projection.ProjectionAnnotationHover;
+
+import org.eclipse.jface.text.source.projection.ProjectionViewer; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSupport; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionPosition; // packageimport
+import org.eclipse.jface.text.source.projection.AnnotationBag; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSummary; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionRulerColumn; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.projection.SourceViewerInformationControl; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionListener; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotation; // packageimport
+
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Set;
+
+
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.information.IInformationProviderExtension2;
+import org.eclipse.jface.text.source.IAnnotationHover;
+import org.eclipse.jface.text.source.IAnnotationHoverExtension;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.IAnnotationModelExtension;
+import org.eclipse.jface.text.source.ILineRange;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.text.source.ISourceViewerExtension2;
+import org.eclipse.jface.text.source.LineRange;
+
+/**
+ * Annotation hover for projection annotations.
+ *
+ * @since 3.0
+ */
+class ProjectionAnnotationHover : IAnnotationHover, IAnnotationHoverExtension, IInformationProviderExtension2 {
+
+
+    private IInformationControlCreator fInformationControlCreator;
+    private IInformationControlCreator fInformationPresenterControlCreator;
+
+    /**
+     * Sets the hover control creator for this projection annotation hover.
+     *
+     * @param creator the creator
+     */
+    public void setHoverControlCreator(IInformationControlCreator creator) {
+        fInformationControlCreator= creator;
+    }
+
+    /**
+     * Sets the information presenter control creator for this projection annotation hover.
+     *
+     * @param creator the creator
+     * @since 3.3
+     */
+    public void setInformationPresenterControlCreator(IInformationControlCreator creator) {
+        fInformationPresenterControlCreator= creator;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationHover#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer, int)
+     */
+    public String getHoverInfo(ISourceViewer sourceViewer, int lineNumber) {
+        // this is a no-op as semantics is defined by the implementation of the annotation hover extension
+        return null;
+    }
+
+    /*
+     * @since 3.1
+     */
+    private bool isCaptionLine(ProjectionAnnotation annotation, Position position, IDocument document, int line) {
+        if (position.getOffset() > -1 && position.getLength() > -1) {
+            try {
+                int captionOffset;
+                if ( cast(IProjectionPosition)position )
+                    captionOffset= (cast(IProjectionPosition) position).computeCaptionOffset(document);
+                else
+                    captionOffset= 0;
+                int startLine= document.getLineOfOffset(position.getOffset() + captionOffset);
+                return line is startLine;
+            } catch (BadLocationException x) {
+            }
+        }
+        return false;
+    }
+
+    private String getProjectionTextAtLine(ISourceViewer viewer, int line, int numberOfLines) {
+
+        IAnnotationModel model= null;
+        if ( cast(ISourceViewerExtension2)viewer ) {
+            ISourceViewerExtension2 viewerExtension= cast(ISourceViewerExtension2) viewer;
+            IAnnotationModel visual= viewerExtension.getVisualAnnotationModel();
+            if ( cast(IAnnotationModelExtension)visual ) {
+                IAnnotationModelExtension modelExtension= cast(IAnnotationModelExtension) visual;
+                model= modelExtension.getAnnotationModel(ProjectionSupport.PROJECTION);
+            }
+        }
+
+        if (model !is null) {
+            try {
+                IDocument document= viewer.getDocument();
+                Iterator e= model.getAnnotationIterator();
+                while (e.hasNext()) {
+                    ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next();
+                    if (!annotation.isCollapsed())
+                        continue;
+
+                    Position position= model.getPosition(annotation);
+                    if (position is null)
+                        continue;
+
+                    if (isCaptionLine(annotation, position, document, line))
+                        return getText(document, position.getOffset(), position.getLength(), numberOfLines);
+
+                }
+            } catch (BadLocationException x) {
+            }
+        }
+
+        return null;
+    }
+
+    private String getText(IDocument document, int offset, int length, int numberOfLines)  {
+        int endOffset= offset + length;
+
+        try {
+            int endLine= document.getLineOfOffset(offset) + Math.max(0, numberOfLines -1);
+            IRegion lineInfo= document.getLineInformation(endLine);
+            endOffset= Math.min(endOffset, lineInfo.getOffset() + lineInfo.getLength());
+        } catch (BadLocationException x) {
+        }
+
+        return document.get(offset, endOffset - offset);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer, org.eclipse.jface.text.source.ILineRange, int)
+     */
+    public Object getHoverInfo(ISourceViewer sourceViewer, ILineRange lineRange, int visibleLines) {
+        return stringcast(getProjectionTextAtLine(sourceViewer, lineRange.getStartLine(), visibleLines));
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverLineRange(org.eclipse.jface.text.source.ISourceViewer, int)
+     */
+    public ILineRange getHoverLineRange(ISourceViewer viewer, int lineNumber) {
+        return new LineRange(lineNumber, 1);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#canHandleMouseCursor()
+     */
+    public bool canHandleMouseCursor() {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverControlCreator()
+     */
+    public IInformationControlCreator getHoverControlCreator() {
+        if (fInformationControlCreator is null) {
+            fInformationControlCreator= new class()  IInformationControlCreator {
+                public IInformationControl createInformationControl(Shell parent) {
+                    return new SourceViewerInformationControl(parent, false, JFaceResources.TEXT_FONT, null);
+                }
+            };
+        }
+        return fInformationControlCreator;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.information.IInformationProviderExtension2#getInformationPresenterControlCreator()
+     * @since 3.3
+     */
+    public IInformationControlCreator getInformationPresenterControlCreator() {
+        if (fInformationPresenterControlCreator is null) {
+            fInformationPresenterControlCreator= new class()  IInformationControlCreator {
+                public IInformationControl createInformationControl(Shell parent) {
+                    return new SourceViewerInformationControl(parent, true, JFaceResources.TEXT_FONT, null);
+                }
+            };
+        }
+        return fInformationPresenterControlCreator;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionAnnotationModel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,204 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
+
+import org.eclipse.jface.text.source.projection.ProjectionViewer; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSupport; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionPosition; // packageimport
+import org.eclipse.jface.text.source.projection.AnnotationBag; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSummary; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionRulerColumn; // packageimport
+import org.eclipse.jface.text.source.projection.SourceViewerInformationControl; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionListener; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotation; // packageimport
+
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.AnnotationModel;
+
+
+/**
+ * A projection annotation model. It provides methods for modifying the
+ * expansion state of the managed projection annotations.
+ * <p>
+ * Do not subclass. Use it as is.
+ * </p>
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ProjectionAnnotationModel : AnnotationModel {
+
+
+    /**
+     * Creates a new, empty projection annotation model.
+     */
+    public this() {
+    }
+
+    /**
+     * Changes the state of the given annotation to collapsed. An appropriate
+     * annotation model change event is sent out.
+     *
+     * @param annotation the annotation
+     */
+    public void collapse(Annotation annotation) {
+        if ( cast(ProjectionAnnotation)annotation ) {
+            ProjectionAnnotation projection= cast(ProjectionAnnotation) annotation;
+            if (!projection.isCollapsed()) {
+                projection.markCollapsed();
+                modifyAnnotation(projection, true);
+            }
+        }
+    }
+
+    /**
+     * Changes the state of the given annotation to expanded. An appropriate
+     * annotation model change event is sent out.
+     *
+     * @param annotation the annotation
+     */
+    public void expand(Annotation annotation) {
+        if ( cast(ProjectionAnnotation)annotation ) {
+            ProjectionAnnotation projection= cast(ProjectionAnnotation) annotation;
+            if (projection.isCollapsed()) {
+                projection.markExpanded();
+                modifyAnnotation(projection, true);
+            }
+        }
+    }
+
+    /**
+     * Toggles the expansion state of the given annotation. An appropriate
+     * annotation model change event is sent out.
+     *
+     * @param annotation the annotation
+     */
+    public void toggleExpansionState(Annotation annotation) {
+        if ( cast(ProjectionAnnotation)annotation ) {
+            ProjectionAnnotation projection= cast(ProjectionAnnotation) annotation;
+
+            if (projection.isCollapsed())
+                projection.markExpanded();
+            else
+                projection.markCollapsed();
+
+            modifyAnnotation(projection, true);
+        }
+    }
+
+    /**
+     * Expands all annotations that overlap with the given range and are collapsed.
+     *
+     * @param offset the range offset
+     * @param length the range length
+     * @return <code>true</code> if any annotation has been expanded, <code>false</code> otherwise
+     */
+    public bool expandAll(int offset, int length) {
+        return expandAll(offset, length, true);
+    }
+
+    /**
+     * Collapses all annotations that overlap with the given range and are collapsed.
+     *
+     * @param offset the range offset
+     * @param length the range length
+     * @return <code>true</code> if any annotation has been collapse, <code>false</code>
+     *         otherwise
+     * @since 3.2
+     */
+    public bool collapseAll(int offset, int length) {
+
+        bool collapsing= false;
+
+        Iterator iterator= getAnnotationIterator();
+        while (iterator.hasNext()) {
+            ProjectionAnnotation annotation= cast(ProjectionAnnotation) iterator.next();
+            if (!annotation.isCollapsed()) {
+                Position position= getPosition(annotation);
+                if (position !is null && position.overlapsWith(offset, length) /* || is a delete at the boundary */ ) {
+                    annotation.markCollapsed();
+                    modifyAnnotation(annotation, false);
+                    collapsing= true;
+                }
+            }
+        }
+
+        if (collapsing)
+            fireModelChanged();
+
+        return collapsing;
+    }
+
+    /**
+     * Expands all annotations that overlap with the given range and are collapsed. Fires a model change event if
+     * requested.
+     *
+     * @param offset the offset of the range
+     * @param length the length of the range
+     * @param fireModelChanged <code>true</code> if a model change event
+     *            should be fired, <code>false</code> otherwise
+     * @return <code>true</code> if any annotation has been expanded, <code>false</code> otherwise
+     */
+    protected bool expandAll(int offset, int length, bool fireModelChanged_) {
+
+        bool expanding= false;
+
+        Iterator iterator= getAnnotationIterator();
+        while (iterator.hasNext()) {
+            ProjectionAnnotation annotation= cast(ProjectionAnnotation) iterator.next();
+            if (annotation.isCollapsed()) {
+                Position position= getPosition(annotation);
+                if (position !is null && position.overlapsWith(offset, length) /* || is a delete at the boundary */ ) {
+                    annotation.markExpanded();
+                    modifyAnnotation(annotation, false);
+                    expanding= true;
+                }
+            }
+        }
+
+        if (expanding && fireModelChanged_)
+            fireModelChanged();
+
+        return expanding;
+    }
+
+    /**
+     * Modifies the annotation model.
+     *
+     * @param deletions the list of deleted annotations
+     * @param additions the set of annotations to add together with their associated position
+     * @param modifications the list of modified annotations
+     */
+    public void modifyAnnotations(Annotation[] deletions, Map additions, Annotation[] modifications) {
+        try {
+            replaceAnnotations(deletions, additions, false);
+            if (modifications !is null) {
+                for (int i= 0; i < modifications.length; i++)
+                    modifyAnnotation(modifications[i], false);
+            }
+        } catch (BadLocationException x) {
+        }
+        fireModelChanged();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionRulerColumn.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,250 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.source.projection.ProjectionRulerColumn;
+
+import org.eclipse.jface.text.source.projection.ProjectionViewer; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSupport; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionPosition; // packageimport
+import org.eclipse.jface.text.source.projection.AnnotationBag; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSummary; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.projection.SourceViewerInformationControl; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionListener; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotation; // packageimport
+
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackAdapter;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.source.AnnotationRulerColumn;
+import org.eclipse.jface.text.source.CompositeRuler;
+import org.eclipse.jface.text.source.IAnnotationAccess;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.IAnnotationModelExtension;
+
+
+/**
+ * A ruler column for controlling the behavior of a
+ * {@link org.eclipse.jface.text.source.projection.ProjectionViewer}.
+ *
+ * @since 3.0
+ */
+class ProjectionRulerColumn : AnnotationRulerColumn {
+
+    private ProjectionAnnotation fCurrentAnnotation;
+
+    /**
+     * Creates a new projection ruler column.
+     *
+     * @param model the column's annotation model
+     * @param width the width in pixels
+     * @param annotationAccess the annotation access
+     */
+    public this(IAnnotationModel model, int width, IAnnotationAccess annotationAccess) {
+        super(model, width, annotationAccess);
+    }
+
+    /**
+     * Creates a new projection ruler column.
+     *
+     * @param width the width in pixels
+     * @param annotationAccess the annotation access
+     */
+    public this(int width, IAnnotationAccess annotationAccess) {
+        super(width, annotationAccess);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.AnnotationRulerColumn#mouseClicked(int)
+     */
+    protected void mouseClicked(int line) {
+        clearCurrentAnnotation();
+        ProjectionAnnotation annotation= findAnnotation(line, true);
+        if (annotation !is null) {
+            ProjectionAnnotationModel model= cast(ProjectionAnnotationModel) getModel();
+            model.toggleExpansionState(annotation);
+        }
+    }
+
+    /**
+     * Returns the projection annotation of the column's annotation
+     * model that contains the given line.
+     *
+     * @param line the line
+     * @param exact <code>true</code> if the annotation range must match exactly
+     * @return the projection annotation containing the given line
+     */
+    private ProjectionAnnotation findAnnotation(int line, bool exact) {
+
+        ProjectionAnnotation previousAnnotation= null;
+
+        IAnnotationModel model= getModel();
+        if (model !is null) {
+            IDocument document= getCachedTextViewer().getDocument();
+
+            int previousDistance= Integer.MAX_VALUE;
+
+            Iterator e= model.getAnnotationIterator();
+            while (e.hasNext()) {
+                Object next= e.next();
+                if ( cast(ProjectionAnnotation)next ) {
+                    ProjectionAnnotation annotation= cast(ProjectionAnnotation) next;
+                    Position p= model.getPosition(annotation);
+                    if (p is null)
+                        continue;
+
+                    int distance= getDistance(annotation, p, document, line);
+                    if (distance is -1)
+                        continue;
+
+                    if (!exact) {
+                        if (distance < previousDistance) {
+                            previousAnnotation= annotation;
+                            previousDistance= distance;
+                        }
+                    } else if (distance is 0) {
+                        previousAnnotation= annotation;
+                    }
+                }
+            }
+        }
+
+        return previousAnnotation;
+    }
+
+    /**
+     * Returns the distance of the given line to the start line of the given position in the given document. The distance is
+     * <code>-1</code> when the line is not included in the given position.
+     *
+     * @param annotation the annotation
+     * @param position the position
+     * @param document the document
+     * @param line the line
+     * @return <code>-1</code> if line is not contained, a position number otherwise
+     */
+    private int getDistance(ProjectionAnnotation annotation, Position position, IDocument document, int line) {
+        if (position.getOffset() > -1 && position.getLength() > -1) {
+            try {
+                int startLine= document.getLineOfOffset(position.getOffset());
+                int endLine= document.getLineOfOffset(position.getOffset() + position.getLength());
+                if (startLine <= line && line < endLine) {
+                    if (annotation.isCollapsed()) {
+                        int captionOffset;
+                        if ( cast(IProjectionPosition)position )
+                            captionOffset= (cast(IProjectionPosition) position).computeCaptionOffset(document);
+                        else
+                            captionOffset= 0;
+
+                        int captionLine= document.getLineOfOffset(position.getOffset() + captionOffset);
+                        if (startLine <= captionLine && captionLine < endLine)
+                            return Math.abs(line - captionLine);
+                    }
+                    return line - startLine;
+                }
+            } catch (BadLocationException x) {
+            }
+        }
+        return -1;
+    }
+
+    private bool clearCurrentAnnotation() {
+        if (fCurrentAnnotation !is null) {
+            fCurrentAnnotation.setRangeIndication(false);
+            fCurrentAnnotation= null;
+            return true;
+        }
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerColumn#createControl(org.eclipse.jface.text.source.CompositeRuler, org.eclipse.swt.widgets.Composite)
+     */
+    public Control createControl(CompositeRuler parentRuler, Composite parentControl) {
+        Control control= super.createControl(parentRuler, parentControl);
+
+        // set background
+        Display display= parentControl.getDisplay();
+        Color background= display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
+        control.setBackground(background);
+
+        // install hover listener
+        control.addMouseTrackListener(new class()  MouseTrackAdapter {
+            public void mouseExit(MouseEvent e) {
+                if (clearCurrentAnnotation())
+                    redraw();
+            }
+        });
+
+        // install mouse move listener
+        control.addMouseMoveListener(new class()  MouseMoveListener {
+            public void mouseMove(MouseEvent e) {
+                bool redraw_= false;
+                ProjectionAnnotation annotation= findAnnotation(toDocumentLineNumber(e.y), false);
+                if (annotation !is fCurrentAnnotation) {
+                    if (fCurrentAnnotation !is null) {
+                        fCurrentAnnotation.setRangeIndication(false);
+                        redraw_= true;
+                    }
+                    fCurrentAnnotation= annotation;
+                    if (fCurrentAnnotation !is null && !fCurrentAnnotation.isCollapsed()) {
+                        fCurrentAnnotation.setRangeIndication(true);
+                        redraw_= true;
+                    }
+                }
+                if (redraw_)
+                    redraw();
+            }
+        });
+        return control;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.AnnotationRulerColumn#setModel(org.eclipse.jface.text.source.IAnnotationModel)
+     */
+    public void setModel(IAnnotationModel model) {
+        if ( cast(IAnnotationModelExtension)model ) {
+            IAnnotationModelExtension extension= cast(IAnnotationModelExtension) model;
+            model= extension.getAnnotationModel(ProjectionSupport.PROJECTION);
+        }
+        super.setModel(model);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.AnnotationRulerColumn#isPropagatingMouseListener()
+     */
+    protected bool isPropagatingMouseListener() {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.AnnotationRulerColumn#hasAnnotation(int)
+     */
+    protected bool hasAnnotation(int lineNumber) {
+        return findAnnotation(lineNumber, true) !is null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionSummary.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,351 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.projection.ProjectionSummary;
+
+import org.eclipse.jface.text.source.projection.ProjectionViewer; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSupport; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionPosition; // packageimport
+import org.eclipse.jface.text.source.projection.AnnotationBag; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionRulerColumn; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.projection.SourceViewerInformationControl; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionListener; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotation; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.lang.JThread;
+
+
+
+
+
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ISynchronizable;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationAccess;
+import org.eclipse.jface.text.source.IAnnotationAccessExtension;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.IAnnotationModelExtension;
+
+
+/**
+ * Strategy for managing annotation summaries for collapsed ranges.
+ *
+ * @since 3.0
+ */
+class ProjectionSummary {
+
+    private class Summarizer {
+
+        JThread thread;
+        private bool fReset= true;
+
+        /**
+         * Creates a new thread.
+         */
+        public this() {
+            thread = new JThread( &run );
+            fProgressMonitor= new NullProgressMonitor(); // might be given by client in the future
+            thread.setDaemon(true);
+            thread.start();
+        }
+
+        /**
+         * Resets the thread.
+         */
+        public void reset() {
+            synchronized (fLock) {
+                fReset= true;
+                fProgressMonitor.setCanceled(true);
+            }
+        }
+
+        /*
+         * @see java.lang.Thread#run()
+         */
+        public void run() {
+            while (true) {
+                synchronized (fLock) {
+                    if (!fReset)
+                        break;
+                    fReset= false;
+                    fProgressMonitor.setCanceled(false);
+                }
+                internalUpdateSummaries(fProgressMonitor);
+            }
+
+            synchronized (fLock) {
+                fSummarizer= null;
+            }
+        }
+    }
+
+
+    private ProjectionViewer fProjectionViewer;
+    private IAnnotationModel fAnnotationModel;
+    private IAnnotationAccess fAnnotationAccess;
+    private List fConfiguredAnnotationTypes;
+
+    private Object fLock;
+    private IProgressMonitor fProgressMonitor;
+    private /+volatile+/ Summarizer fSummarizer;
+
+    /**
+     * Creates a new projection summary.
+     *
+     * @param projectionViewer the projection viewer
+     * @param annotationAccess the annotation access
+     */
+    public this(ProjectionViewer projectionViewer, IAnnotationAccess annotationAccess) {
+//         super();
+        fLock= new Object();
+
+        fProjectionViewer= projectionViewer;
+        fAnnotationAccess= annotationAccess;
+    }
+
+    /**
+     * Adds the given annotation type. For now on, annotations of that type are
+     * also reflected in their enclosing collapsed regions.
+     *
+     * @param annotationType the annotation type to add
+     */
+    public void addAnnotationType(String annotationType) {
+        synchronized(fLock) {
+            if (fConfiguredAnnotationTypes is null) {
+                fConfiguredAnnotationTypes= new ArrayList();
+                fConfiguredAnnotationTypes.add(annotationType);
+            } else if (!fConfiguredAnnotationTypes.contains(annotationType))
+                fConfiguredAnnotationTypes.add(annotationType);
+        }
+    }
+
+    /**
+     * Removes the given annotation. Annotation of that type are no
+     * longer reflected in their enclosing collapsed region.
+     *
+     * @param annotationType the annotation type to remove
+     */
+    public void removeAnnotationType(String annotationType) {
+        synchronized (fLock) {
+            if (fConfiguredAnnotationTypes !is null) {
+                fConfiguredAnnotationTypes.remove(annotationType);
+                if (fConfiguredAnnotationTypes.size() is 0)
+                    fConfiguredAnnotationTypes= null;
+            }
+        }
+    }
+
+    /**
+     * Forces an updated of the annotation summary.
+     */
+    public void updateSummaries() {
+        synchronized (fLock) {
+            if (fConfiguredAnnotationTypes !is null) {
+                if (fSummarizer is null)
+                    fSummarizer= new Summarizer();
+                fSummarizer.reset();
+            }
+        }
+    }
+
+    private void internalUpdateSummaries(IProgressMonitor monitor) {
+
+        Object previousLockObject= null;
+        fAnnotationModel= fProjectionViewer.getVisualAnnotationModel();
+        if (fAnnotationModel is null)
+            return;
+
+        try {
+
+
+            IDocument document= fProjectionViewer.getDocument();
+            if ( cast(ISynchronizable)document  && cast(ISynchronizable)fAnnotationModel ) {
+                ISynchronizable sync= cast(ISynchronizable) fAnnotationModel;
+                previousLockObject= sync.getLockObject();
+                sync.setLockObject((cast(ISynchronizable) document).getLockObject());
+            }
+
+
+            removeSummaries(monitor);
+
+            if (isCanceled(monitor))
+                return;
+
+            createSummaries(monitor);
+
+        } finally {
+
+            if ( cast(ISynchronizable)fAnnotationModel ) {
+                ISynchronizable sync= cast(ISynchronizable) fAnnotationModel;
+                sync.setLockObject(previousLockObject);
+            }
+            fAnnotationModel= null;
+
+        }
+    }
+
+    private bool isCanceled(IProgressMonitor monitor) {
+        return monitor !is null && monitor.isCanceled();
+    }
+
+    private void removeSummaries(IProgressMonitor monitor) {
+        IAnnotationModelExtension extension= null;
+        List bags= null;
+
+        if ( cast(IAnnotationModelExtension)fAnnotationModel ) {
+            extension= cast(IAnnotationModelExtension) fAnnotationModel;
+            bags= new ArrayList();
+        }
+
+        Iterator e= fAnnotationModel.getAnnotationIterator();
+        while (e.hasNext()) {
+            Annotation annotation= cast(Annotation) e.next();
+            if ( cast(AnnotationBag)annotation ) {
+                if (bags is null)
+                    fAnnotationModel.removeAnnotation(annotation);
+                else
+                    bags.add(annotation);
+            }
+
+            if (isCanceled(monitor))
+                return;
+        }
+
+        if (bags !is null && bags.size() > 0) {
+            Annotation[] deletions= new Annotation[bags.size()];
+            bags.toArray(deletions);
+            if (!isCanceled(monitor))
+                extension.replaceAnnotations(deletions, null);
+        }
+    }
+
+    private void createSummaries(IProgressMonitor monitor) {
+        ProjectionAnnotationModel model= fProjectionViewer.getProjectionAnnotationModel();
+        if (model is null)
+            return;
+
+        Map additions= new HashMap();
+
+        Iterator e= model.getAnnotationIterator();
+        while (e.hasNext()) {
+            ProjectionAnnotation projection= cast(ProjectionAnnotation) e.next();
+            if (projection.isCollapsed()) {
+                Position position= model.getPosition(projection);
+                if (position !is null) {
+                    IRegion[] summaryRegions= fProjectionViewer.computeCollapsedRegions(position);
+                    if (summaryRegions !is null) {
+                        Position summaryAnchor= fProjectionViewer.computeCollapsedRegionAnchor(position);
+                        if (summaryAnchor !is null)
+                            createSummary(additions, summaryRegions, summaryAnchor);
+                    }
+                }
+            }
+
+            if (isCanceled(monitor))
+                return;
+        }
+
+        if (additions.size() > 0) {
+            if ( cast(IAnnotationModelExtension)fAnnotationModel ) {
+                IAnnotationModelExtension extension= cast(IAnnotationModelExtension) fAnnotationModel;
+                if (!isCanceled(monitor))
+                    extension.replaceAnnotations(null, additions);
+            } else {
+                Iterator e1= additions.keySet().iterator();
+                while (e1.hasNext()) {
+                    AnnotationBag bag= cast(AnnotationBag) e1.next();
+                    Position position= cast(Position) additions.get(bag);
+                    if (isCanceled(monitor))
+                        return;
+                    fAnnotationModel.addAnnotation(bag, position);
+                }
+            }
+        }
+    }
+
+    private void createSummary(Map additions, IRegion[] summaryRegions, Position summaryAnchor) {
+
+        int size= 0;
+        Map map= null;
+
+        synchronized (fLock) {
+            if (fConfiguredAnnotationTypes !is null) {
+                size= fConfiguredAnnotationTypes.size();
+                map= new HashMap();
+                for (int i= 0; i < size; i++) {
+                    String type= stringcast( fConfiguredAnnotationTypes.get(i));
+                    map.put(type, new AnnotationBag(type));
+                }
+            }
+        }
+
+        if (map is null)
+            return;
+
+        IAnnotationModel model= fProjectionViewer.getAnnotationModel();
+        if (model is null)
+            return;
+        Iterator e= model.getAnnotationIterator();
+        while (e.hasNext()) {
+            Annotation annotation= cast(Annotation) e.next();
+            AnnotationBag bag= findBagForType(map, annotation.getType());
+            if (bag !is null) {
+                Position position= model.getPosition(annotation);
+                if (includes(summaryRegions, position))
+                    bag.add(annotation);
+            }
+        }
+
+        for (int i= 0; i < size; i++) {
+            AnnotationBag bag= cast(AnnotationBag) map.get(fConfiguredAnnotationTypes.get(i));
+            if (!bag.isEmpty())
+                additions.put(bag, new Position(summaryAnchor.getOffset(), summaryAnchor.getLength()));
+        }
+    }
+
+    private AnnotationBag findBagForType(Map bagMap, String annotationType) {
+        AnnotationBag bag= cast(AnnotationBag) bagMap.get(annotationType);
+        if (bag is null && cast(IAnnotationAccessExtension)fAnnotationAccess ) {
+            IAnnotationAccessExtension extension= cast(IAnnotationAccessExtension) fAnnotationAccess;
+            Object[] superTypes= extension.getSupertypes(stringcast(annotationType));
+            for (int i= 0; i < superTypes.length && bag is null; i++) {
+                bag= cast(AnnotationBag) bagMap.get(superTypes[i]);
+            }
+        }
+        return bag;
+    }
+
+    private bool includes(IRegion[] regions, Position position) {
+        for (int i= 0; i < regions.length; i++) {
+            IRegion region= regions[i];
+            if (position !is null && !position.isDeleted()
+                    && region.getOffset() <= position.getOffset() &&  position.getOffset() + position.getLength() <= region.getOffset() + region.getLength())
+                return true;
+        }
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionSupport.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,401 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.projection.ProjectionSupport;
+
+import org.eclipse.jface.text.source.projection.ProjectionViewer; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionPosition; // packageimport
+import org.eclipse.jface.text.source.projection.AnnotationBag; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSummary; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionRulerColumn; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.projection.SourceViewerInformationControl; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionListener; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotation; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.custom.StyledTextContent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.AnnotationPainter;
+import org.eclipse.jface.text.source.IAnnotationAccess;
+import org.eclipse.jface.text.source.IAnnotationHover;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.ISharedTextColors;
+import org.eclipse.jface.text.source.ISourceViewer;
+
+/**
+ * Supports the configuration of projection capabilities a {@link org.eclipse.jface.text.source.projection.ProjectionViewer}.
+ * <p>
+ * This class is not intended to be subclassed. Clients are supposed to configure and use it as is.</p>
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ProjectionSupport {
+
+    /**
+     * Key of the projection annotation model inside the visual annotation
+     * model. Also internally used as key for the projection drawing strategy.
+     */
+    private static Object PROJECTION_;
+    public static Object PROJECTION(){
+        if( PROJECTION_ is null ){
+            synchronized(ProjectionSupport.classinfo){
+                if( PROJECTION_ is null ){
+                    PROJECTION_ = new Object();
+                }
+            }
+        }
+        return PROJECTION_;
+    }
+
+    private static class ProjectionAnnotationsPainter : AnnotationPainter {
+
+        /**
+         * Creates a new painter indicating the location of collapsed regions.
+         *
+         * @param sourceViewer the source viewer for the painter
+         * @param access the annotation access
+         */
+        public this(ISourceViewer sourceViewer, IAnnotationAccess access) {
+            super(sourceViewer, access);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.source.AnnotationPainter#findAnnotationModel(org.eclipse.jface.text.source.ISourceViewer)
+         */
+        protected IAnnotationModel findAnnotationModel(ISourceViewer sourceViewer) {
+            if ( cast(ProjectionViewer)sourceViewer ) {
+                ProjectionViewer projectionViewer= cast(ProjectionViewer) sourceViewer;
+                return projectionViewer.getProjectionAnnotationModel();
+            }
+            return null;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.source.AnnotationPainter#skip(org.eclipse.jface.text.source.Annotation)
+         */
+        protected bool skip(Annotation annotation) {
+            if ( cast(ProjectionAnnotation)annotation )
+                return !(cast(ProjectionAnnotation) annotation).isCollapsed();
+
+            return super.skip(annotation);
+        }
+    }
+
+    private static class ProjectionDrawingStrategy : AnnotationPainter_IDrawingStrategy {
+        /*
+         * @see org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy#draw(org.eclipse.swt.graphics.GC, org.eclipse.swt.custom.StyledText, int, int, org.eclipse.swt.graphics.Color)
+         */
+        public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) {
+            if ( cast(ProjectionAnnotation)annotation ) {
+                ProjectionAnnotation projectionAnnotation= cast(ProjectionAnnotation) annotation;
+                if (projectionAnnotation.isCollapsed()) {
+
+                    if (gc !is null) {
+
+                        StyledTextContent content= textWidget.getContent();
+                        int line= content.getLineAtOffset(offset);
+                        int lineStart= content.getOffsetAtLine(line);
+                        String text= content.getLine(line);
+                        int lineLength= text is null ? 0 : text.length;
+                        int lineEnd= lineStart + lineLength;
+                        Point p= textWidget.getLocationAtOffset(lineEnd);
+
+                        Color c= gc.getForeground();
+                        gc.setForeground(color);
+
+                        FontMetrics metrics= gc.getFontMetrics();
+
+                        // baseline: where the dots are drawn
+                        int baseline= textWidget.getBaseline(offset);
+                        // descent: number of pixels that the box extends over baseline
+                        int descent= Math.min(2, textWidget.getLineHeight(offset) - baseline);
+                        // ascent: so much does the box stand up from baseline
+                        int ascent= metrics.getAscent();
+                        // leading: free space from line top to box upper line
+                        int leading= baseline - ascent;
+                        // height: height of the box
+                        int height= ascent + descent;
+
+                        int width= metrics.getAverageCharWidth();
+                        gc.drawRectangle(p.x, p.y + leading, width, height);
+                        int third= width/3;
+                        int dotsVertical= p.y + baseline - 1;
+                        gc.drawPoint(p.x + third, dotsVertical);
+                        gc.drawPoint(p.x + width - third, dotsVertical);
+
+                        gc.setForeground(c);
+
+                    } else {
+                        textWidget.redrawRange(offset, length, true);
+                    }
+                }
+            }
+        }
+    }
+
+    private class ProjectionListener : IProjectionListener {
+
+        /*
+         * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionEnabled()
+         */
+        public void projectionEnabled() {
+            doEnableProjection();
+        }
+
+        /*
+         * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionDisabled()
+         */
+        public void projectionDisabled() {
+            doDisableProjection();
+        }
+    }
+
+    private ProjectionViewer fViewer;
+    private IAnnotationAccess fAnnotationAccess;
+    private ISharedTextColors fSharedTextColors;
+    private List fSummarizableTypes;
+    private IInformationControlCreator fInformationControlCreator;
+    private IInformationControlCreator fInformationPresenterControlCreator;
+    private ProjectionListener fProjectionListener;
+    private ProjectionAnnotationsPainter fPainter;
+    private ProjectionRulerColumn fColumn;
+    /**
+     * @since 3.1
+     */
+    private AnnotationPainter_IDrawingStrategy fDrawingStrategy;
+
+    /**
+     * Creates new projection support for the given projection viewer. Initially,
+     * no annotation types are summarized. A default hover control creator and a
+     * default drawing strategy are used.
+     *
+     * @param viewer the projection viewer
+     * @param annotationAccess the annotation access
+     * @param sharedTextColors the shared text colors to use
+     */
+    public this(ProjectionViewer viewer, IAnnotationAccess annotationAccess, ISharedTextColors sharedTextColors) {
+        fViewer= viewer;
+        fAnnotationAccess= annotationAccess;
+        fSharedTextColors= sharedTextColors;
+    }
+
+    /**
+     * Marks the given annotation type to be considered when creating summaries for
+     * collapsed regions of the projection viewer.
+     * <p>
+     * A summary is an annotation that gets created out of all annotations with a
+     * type that has been registered through this method and that are inside the
+     * folded region.
+     * </p>
+     *
+     * @param annotationType the annotation type to consider
+     */
+    public void addSummarizableAnnotationType(String annotationType) {
+        if (fSummarizableTypes is null) {
+            fSummarizableTypes= new ArrayList();
+            fSummarizableTypes.add(annotationType);
+        } else if (!fSummarizableTypes.contains(annotationType))
+            fSummarizableTypes.add(annotationType);
+    }
+
+    /**
+     * Marks the given annotation type to be ignored when creating summaries for
+     * collapsed regions of the projection viewer. This method has only an effect
+     * when <code>addSummarizableAnnotationType</code> has been called before for
+     * the give annotation type.
+     * <p>
+     * A summary is an annotation that gets created out of all annotations with a
+     * type that has been registered through this method and that are inside the
+     * folded region.
+     * </p>
+     *
+     * @param annotationType the annotation type to remove
+     */
+    public void removeSummarizableAnnotationType(String annotationType) {
+        if (fSummarizableTypes !is null)
+            fSummarizableTypes.remove(annotationType);
+        if (fSummarizableTypes.size() is 0)
+            fSummarizableTypes= null;
+    }
+
+    /**
+     * Sets the hover control creator that is used for the annotation hovers
+     * that are shown in the projection viewer's projection ruler column.
+     *
+     * @param creator the hover control creator
+     */
+    public void setHoverControlCreator(IInformationControlCreator creator) {
+        fInformationControlCreator= creator;
+    }
+
+    /**
+     * Sets the information presenter control creator that is used for the annotation
+     * hovers that are shown in the projection viewer's projection ruler column.
+     *
+     * @param creator the information presenter control creator
+     * @since 3.3
+     */
+    public void setInformationPresenterControlCreator(IInformationControlCreator creator) {
+        fInformationPresenterControlCreator= creator;
+    }
+
+    /**
+     * Sets the drawing strategy that the projection support's annotation
+     * painter uses to draw the indication of collapsed regions onto the
+     * projection viewer's text widget. When <code>null</code> is passed in,
+     * the drawing strategy is reset to the default. In order to avoid any
+     * representation use {@link org.eclipse.jface.text.source.AnnotationPainter.NullStrategy}.
+     *
+     * @param strategy the drawing strategy or <code>null</code> to reset the
+     *            strategy to the default
+     * @since 3.1
+     */
+    public void setAnnotationPainterDrawingStrategy(AnnotationPainter_IDrawingStrategy strategy) {
+        fDrawingStrategy= strategy;
+    }
+
+    /**
+     * Returns the drawing strategy to be used by the support's annotation painter.
+     *
+     * @return the drawing strategy to be used by the support's annotation painter
+     * @since 3.1
+     */
+    private AnnotationPainter_IDrawingStrategy getDrawingStrategy() {
+        if (fDrawingStrategy is null)
+            fDrawingStrategy= new ProjectionDrawingStrategy();
+        return fDrawingStrategy;
+    }
+
+    /**
+     * Installs this projection support on its viewer.
+     */
+    public void install() {
+        fViewer.setProjectionSummary(createProjectionSummary());
+
+        fProjectionListener= new ProjectionListener();
+        fViewer.addProjectionListener(fProjectionListener);
+    }
+
+    /**
+     * Disposes this projection support.
+     */
+    public void dispose() {
+        if (fProjectionListener !is null) {
+            fViewer.removeProjectionListener(fProjectionListener);
+            fProjectionListener= null;
+        }
+    }
+
+    /**
+     * Enables projection mode. If not yet done, installs the projection ruler
+     * column in the viewer's vertical ruler and installs a painter that
+     * indicate the locations of collapsed regions.
+     *
+     */
+    protected void doEnableProjection() {
+
+        if (fPainter is null) {
+            fPainter= new ProjectionAnnotationsPainter(fViewer, fAnnotationAccess);
+            fPainter.addDrawingStrategy(PROJECTION, getDrawingStrategy());
+            fPainter.addAnnotationType(stringcast(ProjectionAnnotation.TYPE), PROJECTION);
+            fPainter.setAnnotationTypeColor(stringcast(ProjectionAnnotation.TYPE), fSharedTextColors.getColor(getColor()));
+            fViewer.addPainter(fPainter);
+        }
+
+        if (fColumn is null) {
+            fColumn= new ProjectionRulerColumn(9, fAnnotationAccess);
+            fColumn.addAnnotationType(stringcast(ProjectionAnnotation.TYPE));
+            fColumn.setHover(createProjectionAnnotationHover());
+            fViewer.addVerticalRulerColumn(fColumn);
+        }
+
+        fColumn.setModel(fViewer.getVisualAnnotationModel());
+    }
+
+    /**
+     * Removes the projection ruler column and the painter from the projection
+     * viewer.
+     */
+    protected void doDisableProjection() {
+        if (fPainter !is null) {
+            fViewer.removePainter(fPainter);
+            fPainter.dispose();
+            fPainter= null;
+        }
+
+        if (fColumn !is null) {
+            fViewer.removeVerticalRulerColumn(fColumn);
+            fColumn= null;
+        }
+    }
+
+    private ProjectionSummary createProjectionSummary() {
+        ProjectionSummary summary= new ProjectionSummary(fViewer, fAnnotationAccess);
+        if (fSummarizableTypes !is null) {
+            int size= fSummarizableTypes.size();
+            for (int i= 0; i < size; i++)
+                summary.addAnnotationType(stringcast(fSummarizableTypes.get(i)));
+        }
+        return summary;
+    }
+
+    private IAnnotationHover createProjectionAnnotationHover() {
+        ProjectionAnnotationHover hover= new ProjectionAnnotationHover();
+        hover.setHoverControlCreator(fInformationControlCreator);
+        hover.setInformationPresenterControlCreator(fInformationPresenterControlCreator);
+        return hover;
+    }
+
+    /**
+     * Implements the contract of {@link org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)}
+     * by forwarding the adapter requests to the given viewer.
+     *
+     * @param viewer the viewer
+     * @param required the required class of the adapter
+     * @return the adapter or <code>null</code>
+     *
+     */
+    public Object getAdapter(ISourceViewer viewer, ClassInfo required) {
+        if (ProjectionAnnotationModel.classinfo ==/*eq*/ required) {
+            if ( cast(ProjectionViewer)viewer ) {
+                ProjectionViewer projectionViewer= cast(ProjectionViewer) viewer;
+                return projectionViewer.getProjectionAnnotationModel();
+            }
+        }
+        return null;
+    }
+
+    private RGB getColor() {
+        // TODO read out preference settings
+        Color c= Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY);
+        return c.getRGB();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1843 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.projection.ProjectionViewer;
+
+import org.eclipse.jface.text.source.projection.ProjectionSupport; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionPosition; // packageimport
+import org.eclipse.jface.text.source.projection.AnnotationBag; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSummary; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionRulerColumn; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.projection.SourceViewerInformationControl; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionListener; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotation; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import tango.core.Exception;
+
+
+
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.custom.ST;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.FindReplaceDocumentAdapter;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentInformationMappingExtension;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ISlaveDocumentManager;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.projection.ProjectionDocument;
+import org.eclipse.jface.text.projection.ProjectionDocumentEvent;
+import org.eclipse.jface.text.projection.ProjectionDocumentManager;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.AnnotationModelEvent;
+import org.eclipse.jface.text.source.CompositeRuler;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.IAnnotationModelExtension;
+import org.eclipse.jface.text.source.IAnnotationModelListener;
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension;
+import org.eclipse.jface.text.source.IOverviewRuler;
+import org.eclipse.jface.text.source.IVerticalRuler;
+import org.eclipse.jface.text.source.IVerticalRulerColumn;
+import org.eclipse.jface.text.source.SourceViewer;
+
+
+/**
+ * A projection source viewer is a source viewer which supports multiple visible
+ * regions which can dynamically be changed.
+ * <p>
+ * A projection source viewer uses a <code>ProjectionDocumentManager</code>
+ * for the management of the visible document.</p>
+ * <p>
+ * NOTE: The <code>ProjectionViewer</code> only supports projections that cover full lines.
+ * </p>
+ * <p>
+ * This class should not be subclassed.</p>
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ProjectionViewer : SourceViewer , ITextViewerExtension5 {
+
+    public override IRegion getModelCoverage() {
+        return super.getModelCoverage();
+    }
+    public override int modelLine2WidgetLine(int modelLine) {
+        return super.modelLine2WidgetLine(modelLine);
+    }
+    public override int modelOffset2WidgetOffset(int modelOffset) {
+        return super.modelOffset2WidgetOffset(modelOffset);
+    }
+    public override IRegion modelRange2WidgetRange(IRegion modelRange) {
+        return super.modelRange2WidgetRange(modelRange);
+    }
+    protected override IRegion modelRange2WidgetRange(Position modelPosition) {
+        return super.modelRange2WidgetRange(modelPosition);
+    }
+    public override int widgetOffset2ModelOffset(int widgetOffset) {
+        return super.widgetOffset2ModelOffset(widgetOffset);
+    }
+    public override IRegion widgetRange2ModelRange(IRegion widgetRange) {
+        return super.widgetRange2ModelRange(widgetRange);
+    }
+    public int widgetLine2ModelLine(int widgetLine) {
+        return super.widgetLine2ModelLine(widgetLine);
+    }
+    public int widgetLineOfWidgetOffset(int widgetOffset) {
+        return super.widgetLineOfWidgetOffset(widgetOffset);
+    }
+
+    private static const int BASE= INFORMATION; // see ISourceViewer.INFORMATION
+
+    /** Operation constant for the expand operation. */
+    public static const int EXPAND= BASE + 1;
+    /** Operation constant for the collapse operation. */
+    public static const int COLLAPSE= BASE + 2;
+    /** Operation constant for the toggle projection operation. */
+    public static const int TOGGLE= BASE + 3;
+    /** Operation constant for the expand all operation. */
+    public static const int EXPAND_ALL= BASE + 4;
+    /**
+     * Operation constant for the collapse all operation.
+     *
+     * @since 3.2
+     */
+    public static const int COLLAPSE_ALL= BASE + 5;
+
+    /**
+     * Internal listener to changes of the annotation model.
+     */
+    private class AnnotationModelListener : IAnnotationModelListener, IAnnotationModelListenerExtension {
+
+        /*
+         * @see org.eclipse.jface.text.source.IAnnotationModelListener#modelChanged(org.eclipse.jface.text.source.IAnnotationModel)
+         */
+        public void modelChanged(IAnnotationModel model) {
+            processModelChanged(model, null);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.source.IAnnotationModelListenerExtension#modelChanged(org.eclipse.jface.text.source.AnnotationModelEvent)
+         */
+        public void modelChanged(AnnotationModelEvent event) {
+            processModelChanged(event.getAnnotationModel(), event);
+        }
+
+        private void processModelChanged(IAnnotationModel model, AnnotationModelEvent event) {
+            if (model is fProjectionAnnotationModel) {
+
+                if (fProjectionSummary !is null)
+                    fProjectionSummary.updateSummaries();
+                processCatchupRequest(event);
+
+            } else if (model is getAnnotationModel() && fProjectionSummary !is null)
+                fProjectionSummary.updateSummaries();
+        }
+    }
+
+    /**
+     * Executes the 'replaceVisibleDocument' operation when called the first time. Self-destructs afterwards.
+     */
+    private class ReplaceVisibleDocumentExecutor : IDocumentListener {
+
+        private IDocument fSlaveDocument;
+        private IDocument fExecutionTrigger;
+
+        /**
+         * Creates a new executor in order to free the given slave document.
+         *
+         * @param slaveDocument the slave document to free
+         */
+        public this(IDocument slaveDocument) {
+            fSlaveDocument= slaveDocument;
+        }
+
+        /**
+         * Installs this executor on the given trigger document.
+         *
+         * @param executionTrigger the trigger document
+         */
+        public void install(IDocument executionTrigger) {
+            if (executionTrigger !is null && fSlaveDocument !is null) {
+                fExecutionTrigger= executionTrigger;
+                fExecutionTrigger.addDocumentListener(this);
+            }
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
+         */
+        public void documentAboutToBeChanged(DocumentEvent event) {
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
+         */
+        public void documentChanged(DocumentEvent event) {
+            fExecutionTrigger.removeDocumentListener(this);
+            executeReplaceVisibleDocument(fSlaveDocument);
+        }
+    }
+
+    /**
+     * A command representing a change of the projection document. This can be either
+     * adding a master document range, removing a master document change, or invalidating
+     * the viewer text presentation.
+     */
+    private static class ProjectionCommand {
+
+        const static int ADD= 0;
+        const static int REMOVE= 1;
+        const static int INVALIDATE_PRESENTATION= 2;
+
+        ProjectionDocument fProjection;
+        int fType;
+        int fOffset;
+        int fLength;
+
+        this(ProjectionDocument projection, int type, int offset, int length) {
+            fProjection= projection;
+            fType= type;
+            fOffset= offset;
+            fLength= length;
+        }
+
+        this(int offset, int length) {
+            fType= INVALIDATE_PRESENTATION;
+            fOffset= offset;
+            fLength= length;
+        }
+
+        int computeExpectedCosts() {
+
+            switch(fType) {
+                case ADD: {
+                    try {
+                        IRegion[] gaps= fProjection.computeUnprojectedMasterRegions(fOffset, fLength);
+                        return gaps is null ? 0 : gaps.length;
+                    } catch (BadLocationException x) {
+                    }
+                    break;
+                }
+                case REMOVE: {
+                    try {
+                        IRegion[] fragments= fProjection.computeProjectedMasterRegions(fOffset, fLength);
+                        return fragments is null ? 0 : fragments.length;
+                    } catch (BadLocationException x) {
+                    }
+                    break;
+                }
+                default:
+            }
+            return 0;
+        }
+    }
+
+    /**
+     * The queue of projection command objects.
+     */
+    private static class ProjectionCommandQueue {
+
+        final static int REDRAW_COSTS= 15;
+        final static int INVALIDATION_COSTS= 10;
+
+        List fList;
+        int fExpectedExecutionCosts= -1;
+
+        this(){
+            fList= new ArrayList(15);
+        }
+
+        void add(ProjectionCommand command) {
+            fList.add(command);
+        }
+
+        Iterator iterator() {
+            return fList.iterator();
+        }
+
+        void clear() {
+            fList.clear();
+            fExpectedExecutionCosts= -1;
+        }
+
+        bool passedRedrawCostsThreshold() {
+            if (fExpectedExecutionCosts is -1)
+                computeExpectedExecutionCosts();
+            return fExpectedExecutionCosts > REDRAW_COSTS;
+        }
+
+        bool passedInvalidationCostsThreshold() {
+            if (fExpectedExecutionCosts is -1)
+                computeExpectedExecutionCosts();
+            return fExpectedExecutionCosts > INVALIDATION_COSTS;
+        }
+
+        private void computeExpectedExecutionCosts() {
+            int max_costs= Math.max(REDRAW_COSTS, INVALIDATION_COSTS);
+            fExpectedExecutionCosts= fList.size();
+            if (fExpectedExecutionCosts <= max_costs) {
+                ProjectionCommand command;
+                Iterator e= fList.iterator();
+                while (e.hasNext()) {
+                    command= cast(ProjectionCommand) e.next();
+                    fExpectedExecutionCosts += command.computeExpectedCosts();
+                    if (fExpectedExecutionCosts > max_costs)
+                        break;
+                }
+            }
+        }
+    }
+
+    /** The projection annotation model used by this viewer. */
+    private ProjectionAnnotationModel fProjectionAnnotationModel;
+    /** The annotation model listener */
+    private IAnnotationModelListener fAnnotationModelListener;
+    /** The projection summary. */
+    private ProjectionSummary fProjectionSummary;
+    /** Indication that an annotation world change has not yet been processed. */
+    private bool fPendingAnnotationWorldChange= false;
+    /** Indication whether projection changes in the visible document should be considered. */
+    private bool fHandleProjectionChanges= true;
+    /** The list of projection listeners. */
+    private List fProjectionListeners;
+    /** Internal lock for protecting the list of pending requests */
+    private Object fLock;
+    /** The list of pending requests */
+    private List fPendingRequests;
+    /** The replace-visible-document execution trigger */
+    private IDocument fReplaceVisibleDocumentExecutionTrigger;
+    /** <code>true</code> if projection was on the last time we switched to segmented mode. */
+    private bool fWasProjectionEnabled;
+    /** The queue of projection commands used to assess the costs of projection changes. */
+    private ProjectionCommandQueue fCommandQueue;
+    /**
+     * The amount of lines deleted by the last document event issued by the
+     * visible document event.
+     * @since 3.1
+     */
+    private int fDeletedLines;
+
+
+    /**
+     * Creates a new projection source viewer.
+     *
+     * @param parent the SWT parent control
+     * @param ruler the vertical ruler
+     * @param overviewRuler the overview ruler
+     * @param showsAnnotationOverview <code>true</code> if the overview ruler should be shown
+     * @param styles the SWT style bits
+     */
+    public this(Composite parent, IVerticalRuler ruler, IOverviewRuler overviewRuler, bool showsAnnotationOverview, int styles) {
+
+        fAnnotationModelListener= new AnnotationModelListener();
+        fLock= new Object();
+        fPendingRequests= new ArrayList();
+
+        super(parent, ruler, overviewRuler, showsAnnotationOverview, styles);
+    }
+
+    /**
+     * Sets the projection summary for this viewer.
+     *
+     * @param projectionSummary the projection summary.
+     */
+    public void setProjectionSummary(ProjectionSummary projectionSummary) {
+        fProjectionSummary= projectionSummary;
+    }
+
+    /**
+     * Adds the projection annotation model to the given annotation model.
+     *
+     * @param model the model to which the projection annotation model is added
+     */
+    private void addProjectionAnnotationModel(IAnnotationModel model) {
+        if ( cast(IAnnotationModelExtension)model ) {
+            IAnnotationModelExtension extension= cast(IAnnotationModelExtension) model;
+            extension.addAnnotationModel(ProjectionSupport.PROJECTION, fProjectionAnnotationModel);
+            model.addAnnotationModelListener(fAnnotationModelListener);
+        }
+    }
+
+    /**
+     * Removes the projection annotation model from the given annotation model.
+     *
+     * @param model the mode from which the projection annotation model is removed
+     * @return the removed projection annotation model or <code>null</code> if there was none
+     */
+    private IAnnotationModel removeProjectionAnnotationModel(IAnnotationModel model) {
+        if ( cast(IAnnotationModelExtension)model ) {
+            model.removeAnnotationModelListener(fAnnotationModelListener);
+            IAnnotationModelExtension extension= cast(IAnnotationModelExtension) model;
+            return extension.removeAnnotationModel(ProjectionSupport.PROJECTION);
+        }
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.SourceViewer#setDocument(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.source.IAnnotationModel, int, int)
+     */
+    public void setDocument(IDocument document, IAnnotationModel annotationModel, int modelRangeOffset, int modelRangeLength) {
+        bool wasProjectionEnabled= false;
+
+        synchronized (fLock) {
+            fPendingRequests.clear();
+        }
+
+        if (fProjectionAnnotationModel !is null) {
+            wasProjectionEnabled= removeProjectionAnnotationModel(getVisualAnnotationModel()) !is null;
+            fProjectionAnnotationModel= null;
+        }
+
+        super.setDocument(document, annotationModel, modelRangeOffset, modelRangeLength);
+
+        if (wasProjectionEnabled && document !is null)
+            enableProjection();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.SourceViewer#createVisualAnnotationModel(org.eclipse.jface.text.source.IAnnotationModel)
+     */
+    protected IAnnotationModel createVisualAnnotationModel(IAnnotationModel annotationModel) {
+        IAnnotationModel model= super.createVisualAnnotationModel(annotationModel);
+        fProjectionAnnotationModel= new ProjectionAnnotationModel();
+        return model;
+    }
+
+    /**
+     * Returns the projection annotation model.
+     *
+     * @return the projection annotation model
+     */
+    public ProjectionAnnotationModel getProjectionAnnotationModel() {
+        IAnnotationModel model= getVisualAnnotationModel();
+        if ( cast(IAnnotationModelExtension)model ) {
+            IAnnotationModelExtension extension= cast(IAnnotationModelExtension) model;
+            return cast(ProjectionAnnotationModel) extension.getAnnotationModel(ProjectionSupport.PROJECTION);
+        }
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.TextViewer#createSlaveDocumentManager()
+     */
+    protected ISlaveDocumentManager createSlaveDocumentManager() {
+        return new ProjectionDocumentManager();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.TextViewer#updateSlaveDocument(org.eclipse.jface.text.IDocument, int, int)
+     */
+    protected bool updateSlaveDocument(IDocument slaveDocument, int modelRangeOffset, int modelRangeLength)  {
+        if ( cast(ProjectionDocument)slaveDocument ) {
+            ProjectionDocument projection= cast(ProjectionDocument) slaveDocument;
+
+            int offset= modelRangeOffset;
+            int length= modelRangeLength;
+
+            if (!isProjectionMode()) {
+                // mimic original TextViewer behavior
+                IDocument master= projection.getMasterDocument();
+                int line= master.getLineOfOffset(modelRangeOffset);
+                offset= master.getLineOffset(line);
+                length= (modelRangeOffset - offset) + modelRangeLength;
+
+            }
+
+            try {
+                fHandleProjectionChanges= false;
+                projection.replaceMasterDocumentRanges(offset, length);
+            } finally {
+                fHandleProjectionChanges= true;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Adds a projection annotation listener to this viewer. The listener may
+     * not be <code>null</code>. If the listener is already registered, this method
+     * does not have any effect.
+     *
+     * @param listener the listener to add
+     */
+    public void addProjectionListener(IProjectionListener listener) {
+
+        Assert.isNotNull(cast(Object)listener);
+
+        if (fProjectionListeners is null)
+            fProjectionListeners= new ArrayList();
+
+        if (!fProjectionListeners.contains(cast(Object)listener))
+            fProjectionListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes the given listener from this viewer. The listener may not be
+     * <code>null</code>. If the listener is not registered with this viewer,
+     * this method is without effect.
+     *
+     * @param listener the listener to remove
+     */
+    public void removeProjectionListener(IProjectionListener listener) {
+
+        Assert.isNotNull(cast(Object)listener);
+
+        if (fProjectionListeners !is null) {
+            fProjectionListeners.remove(cast(Object)listener);
+            if (fProjectionListeners.size() is 0)
+                fProjectionListeners= null;
+        }
+    }
+
+    /**
+     * Notifies all registered projection listeners
+     * that projection mode has been enabled.
+     */
+    protected void fireProjectionEnabled() {
+        if (fProjectionListeners !is null) {
+            Iterator e= (new ArrayList(fProjectionListeners)).iterator();
+            while (e.hasNext()) {
+                IProjectionListener l= cast(IProjectionListener) e.next();
+                l.projectionEnabled();
+            }
+        }
+    }
+
+    /**
+     * Notifies all registered projection listeners
+     * that projection mode has been disabled.
+     */
+    protected void fireProjectionDisabled() {
+        if (fProjectionListeners !is null) {
+            Iterator e= (new ArrayList(fProjectionListeners)).iterator();
+            while (e.hasNext()) {
+                IProjectionListener l= cast(IProjectionListener) e.next();
+                l.projectionDisabled();
+            }
+        }
+    }
+
+    /**
+     * Returns whether this viewer is in projection mode.
+     *
+     * @return <code>true</code> if this viewer is in projection mode,
+     *         <code>false</code> otherwise
+     */
+    public final bool isProjectionMode() {
+        return getProjectionAnnotationModel() !is null;
+    }
+
+    /**
+     * Disables the projection mode.
+     */
+    public final void disableProjection() {
+        if (isProjectionMode()) {
+            removeProjectionAnnotationModel(getVisualAnnotationModel());
+            fProjectionAnnotationModel.removeAllAnnotations();
+            fFindReplaceDocumentAdapter= null;
+            fireProjectionDisabled();
+        }
+    }
+
+    /**
+     * Enables the projection mode.
+     */
+    public final void enableProjection() {
+        if (!isProjectionMode()) {
+            addProjectionAnnotationModel(getVisualAnnotationModel());
+            fFindReplaceDocumentAdapter= null;
+            fireProjectionEnabled();
+        }
+    }
+
+    private void expandAll() {
+        int offset= 0;
+        IDocument doc= getDocument();
+        int length= doc is null ? 0 : doc.getLength();
+        if (isProjectionMode()) {
+            fProjectionAnnotationModel.expandAll(offset, length);
+        }
+    }
+
+    private void expand() {
+        if (isProjectionMode()) {
+            Position found= null;
+            Annotation bestMatch= null;
+            Point selection= getSelectedRange();
+            for (Iterator e= fProjectionAnnotationModel.getAnnotationIterator(); e.hasNext();) {
+                ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next();
+                if (annotation.isCollapsed()) {
+                    Position position= fProjectionAnnotationModel.getPosition(annotation);
+                    // take the first most fine grained match
+                    if (position !is null && touches(selection, position))
+                        if (found is null || position.includes(found.offset) && position.includes(found.offset + found.length)) {
+                            found= position;
+                            bestMatch= annotation;
+                        }
+                }
+            }
+
+            if (bestMatch !is null) {
+                fProjectionAnnotationModel.expand(bestMatch);
+                revealRange(selection.x, selection.y);
+            }
+        }
+    }
+
+    private bool touches(Point selection, Position position) {
+        return position.overlapsWith(selection.x, selection.y) || selection.y is 0 && position.offset + position.length is selection.x + selection.y;
+    }
+
+    private void collapse() {
+        if (isProjectionMode()) {
+            Position found= null;
+            Annotation bestMatch= null;
+            Point selection= getSelectedRange();
+            for (Iterator e= fProjectionAnnotationModel.getAnnotationIterator(); e.hasNext();) {
+                ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next();
+                if (!annotation.isCollapsed()) {
+                    Position position= fProjectionAnnotationModel.getPosition(annotation);
+                    // take the first most fine grained match
+                    if (position !is null && touches(selection, position))
+                        if (found is null || found.includes(position.offset) && found.includes(position.offset + position.length)) {
+                            found= position;
+                            bestMatch= annotation;
+                        }
+                }
+            }
+
+            if (bestMatch !is null) {
+                fProjectionAnnotationModel.collapse(bestMatch);
+                revealRange(selection.x, selection.y);
+            }
+        }
+    }
+
+    /*
+     * @since 3.2
+     */
+    private void collapseAll() {
+        int offset= 0;
+        IDocument doc= getDocument();
+        int length= doc is null ? 0 : doc.getLength();
+        if (isProjectionMode()) {
+            fProjectionAnnotationModel.collapseAll(offset, length);
+        }
+    }
+
+    /**
+     * Adds the given master range to the given projection document. While the
+     * modification is processed, the viewer no longer handles projection
+     * changes, as it is causing them.
+     *
+     * @param projection the projection document
+     * @param offset the offset in the master document
+     * @param length the length in the master document
+     * @throws BadLocationException in case the specified range is invalid
+     *
+     * @see ProjectionDocument#addMasterDocumentRange(int, int)
+     */
+    private void addMasterDocumentRange(ProjectionDocument projection, int offset, int length)  {
+
+        if (fCommandQueue !is null) {
+            fCommandQueue.add(new ProjectionCommand(projection, ProjectionCommand.ADD, offset, length));
+        } else {
+            try {
+                fHandleProjectionChanges= false;
+                // https://bugs.eclipse.org/bugs/show_bug.cgi?id=108258
+                // make sure the document range is strictly line based
+                int end= offset + length;
+                offset= toLineStart(projection.getMasterDocument(), offset, false);
+                length= toLineStart(projection.getMasterDocument(), end, true) - offset;
+                projection.addMasterDocumentRange(offset, length);
+            } finally {
+                fHandleProjectionChanges= true;
+            }
+        }
+    }
+
+    /**
+     * Removes the given master range from the given projection document. While the
+     * modification is processed, the viewer no longer handles projection
+     * changes, as it is causing them.
+     *
+     * @param projection the projection document
+     * @param offset the offset in the master document
+     * @param length the length in the master document
+     * @throws BadLocationException in case the specified range is invalid
+     *
+     * @see ProjectionDocument#removeMasterDocumentRange(int, int)
+     */
+    private void removeMasterDocumentRange(ProjectionDocument projection, int offset, int length)  {
+        if (fCommandQueue !is null) {
+            fCommandQueue.add(new ProjectionCommand(projection, ProjectionCommand.REMOVE, offset, length));
+        } else {
+            try {
+                fHandleProjectionChanges= false;
+                // https://bugs.eclipse.org/bugs/show_bug.cgi?id=108258
+                // make sure the document range is strictly line based
+                int end= offset + length;
+                offset= toLineStart(projection.getMasterDocument(), offset, false);
+                length= toLineStart(projection.getMasterDocument(), end, true) - offset;
+                projection.removeMasterDocumentRange(offset, length);
+            } finally {
+                fHandleProjectionChanges= true;
+            }
+        }
+    }
+
+    /**
+     * Returns the first line offset &lt;= <code>offset</code>. If <code>testLastLine</code>
+     * is <code>true</code> and the offset is on last line then <code>offset</code> is returned.
+     *
+     * @param document the document
+     * @param offset the master document offset
+     * @param testLastLine <code>true</code> if the test for the last line should be performed
+     * @return the closest line offset &gt;= <code>offset</code>
+     * @throws BadLocationException if the offset is invalid
+     * @since 3.2
+     */
+    private int toLineStart(IDocument document, int offset, bool testLastLine)  {
+        if (document is null)
+            return offset;
+
+        if (testLastLine && offset >= document.getLineInformationOfOffset(document.getLength() - 1).getOffset())
+            return offset;
+
+        return document.getLineInformationOfOffset(offset).getOffset();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.TextViewer#setVisibleRegion(int, int)
+     */
+    public void setVisibleRegion(int start, int length) {
+        fWasProjectionEnabled= isProjectionMode();
+        disableProjection();
+        super.setVisibleRegion(start, length);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.TextViewer#setVisibleDocument(org.eclipse.jface.text.IDocument)
+     */
+    protected void setVisibleDocument(IDocument document) {
+        if (!isProjectionMode()) {
+            super.setVisibleDocument(document);
+            return;
+        }
+
+        // In projection mode we don't want to throw away the find/replace document adapter
+        FindReplaceDocumentAdapter adapter= fFindReplaceDocumentAdapter;
+        super.setVisibleDocument(document);
+        fFindReplaceDocumentAdapter= adapter;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.TextViewer#resetVisibleRegion()
+     */
+    public void resetVisibleRegion() {
+        super.resetVisibleRegion();
+        if (fWasProjectionEnabled)
+            enableProjection();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextViewer#getVisibleRegion()
+     */
+    public IRegion getVisibleRegion() {
+        disableProjection();
+        IRegion visibleRegion= getModelCoverage();
+        if (visibleRegion is null)
+            visibleRegion= new Region(0, 0);
+
+        return visibleRegion;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextViewer#overlapsWithVisibleRegion(int,int)
+     */
+    public bool overlapsWithVisibleRegion(int offset, int length) {
+        disableProjection();
+        IRegion coverage= getModelCoverage();
+        if (coverage is null)
+            return false;
+
+        bool appending= (offset is coverage.getOffset() + coverage.getLength()) && length is 0;
+        return appending || TextUtilities.overlaps(coverage, new Region(offset, length));
+    }
+
+    /**
+     * Replace the visible document with the given document. Maintains the
+     * scroll offset and the selection.
+     *
+     * @param slave the visible document
+     */
+    private void replaceVisibleDocument(IDocument slave) {
+        if (fReplaceVisibleDocumentExecutionTrigger !is null) {
+            ReplaceVisibleDocumentExecutor executor= new ReplaceVisibleDocumentExecutor(slave);
+            executor.install(fReplaceVisibleDocumentExecutionTrigger);
+        } else
+            executeReplaceVisibleDocument(slave);
+    }
+
+
+    private void executeReplaceVisibleDocument(IDocument visibleDocument) {
+        StyledText textWidget= getTextWidget();
+        try {
+            if (textWidget !is null && !textWidget.isDisposed())
+                textWidget.setRedraw(false);
+
+            int topIndex= getTopIndex();
+            Point selection= getSelectedRange();
+            setVisibleDocument(visibleDocument);
+            Point currentSelection= getSelectedRange();
+            if (currentSelection.x !is selection.x || currentSelection.y !is selection.y)
+                setSelectedRange(selection.x, selection.y);
+            setTopIndex(topIndex);
+
+        } finally {
+            if (textWidget !is null && !textWidget.isDisposed())
+                textWidget.setRedraw(true);
+        }
+    }
+
+    /**
+     * Hides the given range by collapsing it. If requested, a redraw request is issued.
+     *
+     * @param offset the offset of the range to hide
+     * @param length the length of the range to hide
+     * @param fireRedraw <code>true</code> if a redraw request should be issued, <code>false</code> otherwise
+     * @throws BadLocationException in case the range is invalid
+     */
+    private void collapse(int offset, int length, bool fireRedraw)  {
+        ProjectionDocument projection= null;
+
+        IDocument visibleDocument= getVisibleDocument();
+        if ( cast(ProjectionDocument)visibleDocument )
+            projection= cast(ProjectionDocument) visibleDocument;
+        else {
+            IDocument master= getDocument();
+            IDocument slave= createSlaveDocument(getDocument());
+            if ( cast(ProjectionDocument)slave ) {
+                projection= cast(ProjectionDocument) slave;
+                addMasterDocumentRange(projection, 0, master.getLength());
+                replaceVisibleDocument(projection);
+            }
+        }
+
+        if (projection !is null)
+            removeMasterDocumentRange(projection, offset, length);
+
+        if (projection !is null && fireRedraw) {
+            // repaint line above to get the folding box
+            IDocument document= getDocument();
+            int line= document.getLineOfOffset(offset);
+            if (line > 0) {
+                IRegion info= document.getLineInformation(line - 1);
+                internalInvalidateTextPresentation(info.getOffset(), info.getLength());
+            }
+        }
+    }
+
+    /**
+     * Makes the given range visible again while not changing the folding state of any contained
+     * ranges. If requested, a redraw request is issued.
+     *
+     * @param offset the offset of the range to be expanded
+     * @param length the length of the range to be expanded
+     * @param fireRedraw <code>true</code> if a redraw request should be issued,
+     *        <code>false</code> otherwise
+     * @throws BadLocationException in case the range is invalid
+     */
+    private void expand(int offset, int length, bool fireRedraw)  {
+        IDocument slave= getVisibleDocument();
+        if ( cast(ProjectionDocument)slave ) {
+            ProjectionDocument projection= cast(ProjectionDocument) slave;
+
+            // expand
+            addMasterDocumentRange(projection, offset, length);
+
+            // collapse contained regions
+            ProjectionAnnotation[] collapsed= computeCollapsedNestedAnnotations(offset, length);
+            if (collapsed !is null) {
+                for (int i= 0; i < collapsed.length; i++) {
+                    IRegion[] regions= computeCollapsedRegions(fProjectionAnnotationModel.getPosition(collapsed[i]));
+                    if (regions !is null)
+                        for (int j= 0; j < regions.length; j++)
+                            removeMasterDocumentRange(projection, regions[j].getOffset(), regions[j].getLength());
+                }
+            }
+
+            // redraw if requested
+            if (fireRedraw)
+                internalInvalidateTextPresentation(offset, length);
+        }
+    }
+
+    /**
+     * Processes the request for catch up with the annotation model in the UI thread. If the current
+     * thread is not the UI thread or there are pending catch up requests, a new request is posted.
+     *
+     * @param event the annotation model event
+     */
+    protected final void processCatchupRequest(AnnotationModelEvent event) {
+        if (Display.getCurrent() !is null) {
+            bool run= false;
+            synchronized (fLock) {
+                run= fPendingRequests.isEmpty();
+            }
+            if (run) {
+
+                try {
+                    catchupWithProjectionAnnotationModel(event);
+                } catch (BadLocationException x) {
+                    throw new IllegalArgumentException(null);
+                }
+
+            } else
+                postCatchupRequest(event);
+        } else {
+            postCatchupRequest(event);
+        }
+    }
+
+    /**
+     * Posts the request for catch up with the annotation model into the UI thread.
+     *
+     * @param event the annotation model event
+     */
+    protected final void postCatchupRequest(AnnotationModelEvent event) {
+        synchronized (fLock) {
+            fPendingRequests.add(event);
+            if (fPendingRequests.size() is 1) {
+                StyledText widget= getTextWidget();
+                if (widget !is null) {
+                    Display display= widget.getDisplay();
+                    if (display !is null) {
+                        display.asyncExec(new class()  Runnable {
+                            public void run() {
+                                try {
+                                    while (true) {
+                                        AnnotationModelEvent ame= null;
+                                        synchronized (fLock) {
+                                            if (fPendingRequests.size() is 0)
+                                                return;
+                                            ame= cast(AnnotationModelEvent) fPendingRequests.remove(0);
+                                        }
+                                        catchupWithProjectionAnnotationModel(ame);
+                                    }
+                                } catch (BadLocationException x) {
+                                    try {
+                                        catchupWithProjectionAnnotationModel(null);
+                                    } catch (BadLocationException x1) {
+                                        throw new IllegalArgumentException(null);
+                                    } finally {
+                                        synchronized (fLock) {
+                                            fPendingRequests.clear();
+                                        }
+                                    }
+                                }
+                            }
+                        });
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Tests whether the visible document's master document
+     * is identical to this viewer's document.
+     *
+     * @return <code>true</code> if the visible document's master is
+     *          identical to this viewer's document
+     * @since 3.1
+     */
+    private bool isVisibleMasterDocumentSameAsDocument() {
+        IDocument visibleDocument= getVisibleDocument();
+        return ( cast(ProjectionDocument)visibleDocument ) && (cast(ProjectionDocument)visibleDocument).getMasterDocument() is getDocument();
+    }
+
+    /**
+     * Adapts the slave visual document of this viewer to the changes described
+     * in the annotation model event. When the event is <code>null</code>,
+     * this is identical to a world change event.
+     *
+     * @param event the annotation model event or <code>null</code>
+     * @exception BadLocationException in case the annotation model event is no longer in synchronization with the document
+     */
+    private void catchupWithProjectionAnnotationModel(AnnotationModelEvent event)  {
+
+        if (event is null || !isVisibleMasterDocumentSameAsDocument()) {
+
+            fPendingAnnotationWorldChange= false;
+            reinitializeProjection();
+
+        } else if (event.isWorldChange()) {
+
+            if (event.isValid()) {
+                fPendingAnnotationWorldChange= false;
+                reinitializeProjection();
+            } else
+                fPendingAnnotationWorldChange= true;
+
+        } else if (fPendingAnnotationWorldChange) {
+            if (event.isValid()) {
+                fPendingAnnotationWorldChange= false;
+                reinitializeProjection();
+            }
+        } else {
+
+            Annotation[] addedAnnotations= event.getAddedAnnotations();
+            Annotation[] changedAnnotation= event.getChangedAnnotations();
+            Annotation[] removedAnnotations= event.getRemovedAnnotations();
+
+            fCommandQueue= new ProjectionCommandQueue();
+
+            bool isRedrawing= redraws();
+            int topIndex= isRedrawing ? getTopIndex() : -1;
+
+            processDeletions(event, removedAnnotations, true);
+            List coverage= new ArrayList();
+            processChanges(addedAnnotations, true, coverage);
+            processChanges(changedAnnotation, true, coverage);
+
+            ProjectionCommandQueue commandQueue= fCommandQueue;
+            fCommandQueue= null;
+
+            if (commandQueue.passedRedrawCostsThreshold()) {
+                setRedraw(false);
+                try {
+                    executeProjectionCommands(commandQueue, false);
+                } catch (IllegalArgumentException x) {
+                    reinitializeProjection();
+                } finally {
+                    setRedraw(true, topIndex);
+                }
+            } else {
+                try {
+                    bool fireRedraw= !commandQueue.passedInvalidationCostsThreshold();
+                    executeProjectionCommands(commandQueue, fireRedraw);
+                    if (!fireRedraw)
+                        invalidateTextPresentation();
+                } catch (IllegalArgumentException x) {
+                    reinitializeProjection();
+                }
+            }
+        }
+    }
+
+    private void executeProjectionCommands(ProjectionCommandQueue commandQueue, bool fireRedraw)  {
+
+        ProjectionCommand command;
+        Iterator e= commandQueue.iterator();
+        while (e.hasNext()) {
+            command= cast(ProjectionCommand) e.next();
+            switch (command.fType) {
+                case ProjectionCommand.ADD:
+                    addMasterDocumentRange(command.fProjection, command.fOffset, command.fLength);
+                    break;
+                case ProjectionCommand.REMOVE:
+                    removeMasterDocumentRange(command.fProjection, command.fOffset, command.fLength);
+                    break;
+                case ProjectionCommand.INVALIDATE_PRESENTATION:
+                    if (fireRedraw)
+                        invalidateTextPresentation(command.fOffset, command.fLength);
+                    break;
+                default:
+            }
+        }
+
+        commandQueue.clear();
+    }
+
+    private bool covers(int offset, int length, Position position) {
+        if (!(position.offset is offset && position.length is length) && !position.isDeleted())
+            return offset <= position.getOffset() && position.getOffset() + position.getLength() <= offset + length;
+        return false;
+    }
+
+    private ProjectionAnnotation[] computeCollapsedNestedAnnotations(int offset, int length) {
+        List annotations= new ArrayList(5);
+        Iterator e= fProjectionAnnotationModel.getAnnotationIterator();
+        while (e.hasNext()) {
+            ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next();
+            if (annotation.isCollapsed()) {
+                Position position= fProjectionAnnotationModel.getPosition(annotation);
+                if (position is null) {
+                    // annotation might already be deleted, we will be informed later on about this deletion
+                    continue;
+                }
+                if (covers(offset, length, position))
+                    annotations.add(annotation);
+            }
+        }
+
+        if (annotations.size() > 0) {
+            ProjectionAnnotation[] result= new ProjectionAnnotation[annotations.size()];
+            annotations.toArray(result);
+            return result;
+        }
+
+        return null;
+    }
+
+    private void internalInvalidateTextPresentation(int offset, int length) {
+        if (fCommandQueue !is null) {
+            fCommandQueue.add(new ProjectionCommand(offset, length));
+        } else {
+            invalidateTextPresentation(offset, length);
+        }
+    }
+
+    /*
+     * We pass the removed annotation into this method for performance reasons only. Otherwise, they could be fetch from the event.
+     */
+    private void processDeletions(AnnotationModelEvent event, Annotation[] removedAnnotations, bool fireRedraw)  {
+        for (int i= 0; i < removedAnnotations.length; i++) {
+            ProjectionAnnotation annotation= cast(ProjectionAnnotation) removedAnnotations[i];
+            if (annotation.isCollapsed()) {
+                Position expanded= event.getPositionOfRemovedAnnotation(annotation);
+                expand(expanded.getOffset(), expanded.getLength(), fireRedraw);
+            }
+        }
+    }
+
+    /**
+     * Computes the region that must be collapsed when the given position is the
+     * position of an expanded projection annotation.
+     *
+     * @param position the position
+     * @return the range that must be collapsed
+     */
+    public IRegion computeCollapsedRegion(Position position) {
+        try {
+            IDocument document= getDocument();
+            if (document is null)
+                return null;
+
+            int line= document.getLineOfOffset(position.getOffset());
+            int offset= document.getLineOffset(line + 1);
+
+            int length= position.getLength() - (offset - position.getOffset());
+            if (length > 0)
+                return new Region(offset, length);
+        } catch (BadLocationException x) {
+        }
+
+        return null;
+    }
+
+    /**
+     * Computes the regions that must be collapsed when the given position is
+     * the position of an expanded projection annotation.
+     *
+     * @param position the position
+     * @return the ranges that must be collapsed, or <code>null</code> if
+     *         there are none
+     * @since 3.1
+     */
+    IRegion[] computeCollapsedRegions(Position position) {
+        try {
+            IDocument document= getDocument();
+            if (document is null)
+                return null;
+
+            if ( cast(IProjectionPosition)position ) {
+                IProjectionPosition projPosition= cast(IProjectionPosition) position;
+                return projPosition.computeProjectionRegions(document);
+            }
+
+            int line= document.getLineOfOffset(position.getOffset());
+            int offset= document.getLineOffset(line + 1);
+
+            int length= position.getLength() - (offset - position.getOffset());
+            if (length > 0)
+                return [new Region(offset, length)];
+
+            return null;
+        } catch (BadLocationException x) {
+            return null;
+        }
+    }
+
+    /**
+     * Computes the collapsed region anchor for the given position. Assuming
+     * that the position is the position of an expanded projection annotation,
+     * the anchor is the region that is still visible after the projection
+     * annotation has been collapsed.
+     *
+     * @param position the position
+     * @return the collapsed region anchor
+     */
+    public Position computeCollapsedRegionAnchor(Position position) {
+        try {
+            IDocument document= getDocument();
+            if (document is null)
+                return null;
+
+            int captionOffset= position.getOffset();
+            if ( cast(IProjectionPosition)position )
+                captionOffset+= (cast(IProjectionPosition) position).computeCaptionOffset(document);
+
+            IRegion lineInfo= document.getLineInformationOfOffset(captionOffset);
+            return new Position(lineInfo.getOffset() + lineInfo.getLength(), 0);
+        } catch (BadLocationException x) {
+        }
+        return null;
+    }
+
+    private void processChanges(Annotation[] annotations, bool fireRedraw, List coverage)  {
+        for (int i= 0; i < annotations.length; i++) {
+            ProjectionAnnotation annotation= cast(ProjectionAnnotation) annotations[i];
+            Position position= fProjectionAnnotationModel.getPosition(annotation);
+
+            if (position is null)
+                continue;
+
+            if (!covers(coverage, position)) {
+                if (annotation.isCollapsed()) {
+                    coverage.add(position);
+                    IRegion[] regions= computeCollapsedRegions(position);
+                    if (regions !is null)
+                        for (int j= 0; j < regions.length; j++)
+                            collapse(regions[j].getOffset(), regions[j].getLength(), fireRedraw);
+                } else {
+                    expand(position.getOffset(), position.getLength(), fireRedraw);
+                }
+            }
+        }
+    }
+
+    private bool covers(List coverage, Position position) {
+        Iterator e= coverage.iterator();
+        while (e.hasNext()) {
+            Position p= cast(Position) e.next();
+            if (p.getOffset() <= position.getOffset() && position.getOffset() + position.getLength() <= p.getOffset() + p.getLength())
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Forces this viewer to throw away any old state and to initialize its content
+     * from its projection annotation model.
+     *
+     * @throws BadLocationException in case something goes wrong during initialization
+     */
+    public final void reinitializeProjection()  {
+
+        ProjectionDocument projection= null;
+
+        ISlaveDocumentManager manager= getSlaveDocumentManager();
+        if (manager !is null) {
+            IDocument master= getDocument();
+            if (master !is null) {
+                IDocument slave= manager.createSlaveDocument(master);
+                if ( cast(ProjectionDocument)slave ) {
+                    projection= cast(ProjectionDocument) slave;
+                    addMasterDocumentRange(projection, 0, master.getLength());
+                }
+            }
+        }
+
+        if (projection !is null) {
+            Iterator e= fProjectionAnnotationModel.getAnnotationIterator();
+            while (e.hasNext()) {
+                ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next();
+                if (annotation.isCollapsed()) {
+                    Position position= fProjectionAnnotationModel.getPosition(annotation);
+                    if (position !is null) {
+                        IRegion[] regions= computeCollapsedRegions(position);
+                        if (regions !is null)
+                            for (int i= 0; i < regions.length; i++)
+                                removeMasterDocumentRange(projection, regions[i].getOffset(), regions[i].getLength());
+                    }
+                }
+            }
+
+        }
+
+        replaceVisibleDocument(projection);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.TextViewer#handleVerifyEvent(org.eclipse.swt.events.VerifyEvent)
+     */
+    protected void handleVerifyEvent(VerifyEvent e) {
+        IRegion modelRange= event2ModelRange(e);
+        if (exposeModelRange(modelRange))
+            e.doit= false;
+        else
+            super.handleVerifyEvent(e);
+    }
+
+    /**
+     * Adds the give column as last column to this viewer's vertical ruler.
+     *
+     * @param column the column to be added
+     */
+    public void addVerticalRulerColumn(IVerticalRulerColumn column) {
+        IVerticalRuler ruler= getVerticalRuler();
+        if ( cast(CompositeRuler)ruler ) {
+            CompositeRuler compositeRuler= cast(CompositeRuler) ruler;
+            compositeRuler.addDecorator(99, column);
+        }
+    }
+
+    /**
+     * Removes the give column from this viewer's vertical ruler.
+     *
+     * @param column the column to be removed
+     */
+    public void removeVerticalRulerColumn(IVerticalRulerColumn column) {
+        IVerticalRuler ruler= getVerticalRuler();
+        if ( cast(CompositeRuler)ruler ) {
+            CompositeRuler compositeRuler= cast(CompositeRuler) ruler;
+            compositeRuler.removeDecorator(column);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextViewerExtension5#exposeModelRange(org.eclipse.jface.text.IRegion)
+     */
+    public bool exposeModelRange(IRegion modelRange) {
+        if (isProjectionMode())
+            return fProjectionAnnotationModel.expandAll(modelRange.getOffset(), modelRange.getLength());
+
+        if (!overlapsWithVisibleRegion(modelRange.getOffset(), modelRange.getLength())) {
+            resetVisibleRegion();
+            return true;
+        }
+
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.SourceViewer#setRangeIndication(int, int, bool)
+     */
+    public void setRangeIndication(int offset, int length, bool moveCursor) {
+        IRegion rangeIndication= getRangeIndication();
+        if (moveCursor && fProjectionAnnotationModel !is null && (rangeIndication is null || offset !is rangeIndication.getOffset() || length !is rangeIndication.getLength())) {
+            List expand= new ArrayList(2);
+            // expand the immediate effected collapsed regions
+            Iterator iterator= fProjectionAnnotationModel.getAnnotationIterator();
+            while (iterator.hasNext()) {
+                ProjectionAnnotation annotation= cast(ProjectionAnnotation)iterator.next();
+                if (annotation.isCollapsed() && willAutoExpand(fProjectionAnnotationModel.getPosition(annotation), offset, length))
+                    expand.add(annotation);
+            }
+
+            if (!expand.isEmpty()) {
+                Iterator e= expand.iterator();
+                while (e.hasNext())
+                    fProjectionAnnotationModel.expand(cast(Annotation)e.next());
+            }
+        }
+        super.setRangeIndication(offset, length, moveCursor);
+    }
+
+    private bool willAutoExpand(Position position, int offset, int length) {
+        if (position is null || position.isDeleted())
+            return false;
+        // right or left boundary
+        if (position.getOffset() is offset || position.getOffset() + position.getLength() is offset + length)
+            return true;
+        // completely embedded in given position
+        if (position.getOffset() < offset && offset + length < position.getOffset() + position.getLength())
+            return true;
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.SourceViewer#handleDispose()
+     * @since 3.0
+     */
+    protected void handleDispose() {
+        fWasProjectionEnabled= false;
+        super.handleDispose();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.TextViewer#handleVisibleDocumentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
+     */
+    protected void handleVisibleDocumentChanged(DocumentEvent event) {
+        if (fHandleProjectionChanges && cast(ProjectionDocumentEvent)event  && isProjectionMode()) {
+            ProjectionDocumentEvent e= cast(ProjectionDocumentEvent) event;
+
+            DocumentEvent master= e.getMasterEvent();
+            if (master !is null)
+                fReplaceVisibleDocumentExecutionTrigger= master.getDocument();
+
+            try {
+
+                int replaceLength= e.getText() is null ? 0 : e.getText().length();
+                if (ProjectionDocumentEvent.PROJECTION_CHANGE is e.getChangeType()) {
+                    if (e.getLength() is 0 && replaceLength !is 0)
+                        fProjectionAnnotationModel.expandAll(e.getMasterOffset(), e.getMasterLength());
+                } else if (master !is null && (replaceLength > 0 || fDeletedLines > 1)) {
+                    try {
+                        int numberOfLines= e.getDocument().getNumberOfLines(e.getOffset(), replaceLength);
+                        if (numberOfLines > 1 || fDeletedLines > 1)
+                            fProjectionAnnotationModel.expandAll(master.getOffset(), master.getLength());
+                    } catch (BadLocationException x) {
+                    }
+                }
+
+            } finally {
+                fReplaceVisibleDocumentExecutionTrigger= null;
+            }
+
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.TextViewer#handleVisibleDocumentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
+     * @since 3.1
+     */
+    protected void handleVisibleDocumentAboutToBeChanged(DocumentEvent event) {
+        if (fHandleProjectionChanges && cast(ProjectionDocumentEvent)event  && isProjectionMode()) {
+            int deletedLines;
+            try {
+                deletedLines= event.getDocument().getNumberOfLines(event.getOffset(), event.getLength());
+            } catch (BadLocationException e1) {
+                deletedLines= 0;
+            }
+            fDeletedLines= deletedLines;
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextViewerExtension5#getCoveredModelRanges(org.eclipse.jface.text.IRegion)
+     */
+    public IRegion[] getCoveredModelRanges(IRegion modelRange) {
+        if (fInformationMapping is null)
+            return [ new Region(modelRange.getOffset(), modelRange.getLength()) ];
+
+        if ( cast(IDocumentInformationMappingExtension)fInformationMapping ) {
+            IDocumentInformationMappingExtension extension= cast(IDocumentInformationMappingExtension) fInformationMapping;
+            try {
+                return extension.getExactCoverage(modelRange);
+            } catch (BadLocationException x) {
+            }
+        }
+
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextOperationTarget#doOperation(int)
+     */
+    public void doOperation(int operation) {
+        switch (operation) {
+            case TOGGLE:
+                if (canDoOperation(TOGGLE)) {
+                    if (!isProjectionMode()) {
+                        enableProjection();
+                    } else {
+                        expandAll();
+                        disableProjection();
+                    }
+                    return;
+                }
+            default:
+        }
+
+        if (!isProjectionMode()) {
+            super.doOperation(operation);
+            return;
+        }
+
+        StyledText textWidget= getTextWidget();
+        if (textWidget is null)
+            return;
+
+        Point selection= null;
+        switch (operation) {
+
+            case CUT:
+
+                if (redraws()) {
+                    selection= getSelectedRange();
+                    if (exposeModelRange(new Region(selection.x, selection.y)))
+                        return;
+
+                    if (selection.y is 0)
+                        copyMarkedRegion(true);
+                    else
+                        copyToClipboard(selection.x, selection.y, true, textWidget);
+
+                    selection= textWidget.getSelectionRange();
+                    fireSelectionChanged(selection.x, selection.y);
+                }
+                break;
+
+            case COPY:
+
+                if (redraws()) {
+                    selection= getSelectedRange();
+                    if (selection.y is 0)
+                        copyMarkedRegion(false);
+                    else
+                        copyToClipboard(selection.x, selection.y, false, textWidget);
+                }
+                break;
+
+            case DELETE:
+
+                if (redraws()) {
+                    try {
+                        selection= getSelectedRange();
+                        Point widgetSelection= textWidget.getSelectionRange();
+                        if (selection.y is 0 || selection.y is widgetSelection.y)
+                            getTextWidget().invokeAction(ST.DELETE_NEXT);
+                        else
+                            deleteTextRange(selection.x, selection.y, textWidget);
+
+                        selection= textWidget.getSelectionRange();
+                        fireSelectionChanged(selection.x, selection.y);
+
+                    } catch (BadLocationException x) {
+                        // ignore
+                    }
+                }
+                break;
+
+
+            case EXPAND_ALL:
+                if (redraws())
+                    expandAll();
+                break;
+
+            case EXPAND:
+                if (redraws()) {
+                    expand();
+                }
+                break;
+
+            case COLLAPSE_ALL:
+                if (redraws())
+                    collapseAll();
+                break;
+
+            case COLLAPSE:
+                if (redraws()) {
+                    collapse();
+                }
+                break;
+
+            default:
+                super.doOperation(operation);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.SourceViewer#canDoOperation(int)
+     */
+    public bool canDoOperation(int operation) {
+
+        switch (operation) {
+            case COLLAPSE:
+            case COLLAPSE_ALL:
+            case EXPAND:
+            case EXPAND_ALL:
+                return isProjectionMode();
+            case TOGGLE:
+                return isProjectionMode() || !isSegmented();
+            default:
+        }
+
+        return super.canDoOperation(operation);
+    }
+
+    private bool isSegmented() {
+        IDocument document= getDocument();
+        int length= document is null ? 0 : document.getLength();
+        IRegion visible= getModelCoverage();
+        bool isSegmented= visible !is null && !(cast(Object)visible).opEquals(new Region(0, length));
+        return isSegmented;
+    }
+
+    private IRegion getMarkedRegion() {
+        if (getTextWidget() is null)
+            return null;
+
+        if (fMarkPosition is null || fMarkPosition.isDeleted())
+            return null;
+
+        int start= fMarkPosition.getOffset();
+        int end= getSelectedRange().x;
+
+        return start > end ? new Region (end, start - end) : new Region(start, end - start);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.TextViewer#copyMarkedRegion(bool)
+     */
+    protected void copyMarkedRegion(bool delete_) {
+        IRegion markedRegion= getMarkedRegion();
+        if (markedRegion !is null)
+            copyToClipboard(markedRegion.getOffset(), markedRegion.getLength(), delete_, getTextWidget());
+    }
+
+    private void copyToClipboard(int offset, int length, bool delete_, StyledText textWidget) {
+
+        String copyText= null;
+
+        try {
+            IDocument document= getDocument();
+            copyText= document.get(offset, length);
+        } catch (BadLocationException ex) {
+            // XXX: should log here, but JFace Text has no Log
+            // As a fallback solution let the widget handle this
+            textWidget.copy();
+        }
+
+        if (copyText !is null && copyText.equals(textWidget.getSelectionText())) {
+            /*
+             * XXX: Reduce pain of https://bugs.eclipse.org/bugs/show_bug.cgi?id=64498
+             * by letting the widget handle the copy operation in this special case.
+             */
+            textWidget.copy();
+        } else if (copyText !is null) {
+
+            Clipboard clipboard= new Clipboard(textWidget.getDisplay());
+
+            try {
+                Transfer[] dataTypes= [ TextTransfer.getInstance() ];
+                Object[] data= [ stringcast(copyText) ];
+                try {
+                    clipboard.setContents(data, dataTypes);
+                } catch (SWTError e) {
+                    if (e.code !is DND.ERROR_CANNOT_SET_CLIPBOARD)
+                        throw e;
+                    /*
+                     * TODO see https://bugs.eclipse.org/bugs/show_bug.cgi?id=59459
+                     * we should either log and/or inform the user
+                     * silently fail for now.
+                     */
+                    return;
+                }
+
+            } finally {
+                clipboard.dispose();
+            }
+        }
+
+        if (delete_) {
+            try {
+                deleteTextRange(offset, length, textWidget);
+            } catch (BadLocationException x) {
+                // XXX: should log here, but JFace Text has no Log
+            }
+        }
+    }
+
+    private void deleteTextRange(int offset, int length, StyledText textWidget)  {
+        getDocument().replace(offset, length, ""); //$NON-NLS-1$
+        int widgetCaret= modelOffset2WidgetOffset(offset);
+        if (widgetCaret > -1)
+            textWidget.setSelection(widgetCaret);
+    }
+
+    /**
+     * Adapts the behavior of the super class to respect line based folding.
+     *
+     * @param widgetSelection the widget selection
+     * @return the model selection while respecting line based folding
+     */
+    protected Point widgetSelection2ModelSelection(Point widgetSelection) {
+
+        if (!isProjectionMode())
+            return super.widgetSelection2ModelSelection(widgetSelection);
+
+        /*
+         * There is one requirement that governs preservation of logical
+         * positions:
+         *
+         * 1) a selection with widget_length is 0 should never expand to have
+         * model_length > 0.
+         *
+         * There are a number of ambiguities to resolve with projection regions.
+         * A projected region P has a widget-length of zero. Its widget offset
+         * may interact with the selection S in various ways:
+         *
+         * A) P.widget_offset lies at the caret, S.widget_length is zero.
+         * Requirement 1 applies. S is *behind* P (done so by widgetRange2ModelRange).
+         *
+         * B) P.widget_offset lies inside the widget selection. This case is
+         * easy: P is included in S, which is automatically done so by
+         * widgetRange2ModelRange.
+         *
+         * C) P.widget_offset lies at S.widget_end: This is
+         * arguable - our policy is to include P if it belongs to a projection
+         * annotation that overlaps with the widget selection.
+         *
+         * D) P.widget_offset lies at S.widget_offset: Arguable - our policy
+         * is to include P if it belongs to a projection annotation that
+         * overlaps with the widget selection
+         */
+        IRegion modelSelection= widgetRange2ModelRange(new Region(widgetSelection.x, widgetSelection.y));
+        if (modelSelection is null)
+            return null;
+
+        int modelOffset= modelSelection.getOffset();
+        int modelEndOffset= modelOffset + modelSelection.getLength();
+
+        /* Case A: never expand a zero-length selection. S is *behind* P. */
+        if (widgetSelection.y is 0)
+            return new Point(modelEndOffset, 0);
+
+        int widgetSelectionExclusiveEnd= widgetSelection.x + widgetSelection.y;
+        Position[] annotationPositions= computeOverlappingAnnotationPositions(modelSelection);
+        for (int i= 0; i < annotationPositions.length; i++) {
+            IRegion[] regions= computeCollapsedRegions(annotationPositions[i]);
+            if (regions is null)
+                continue;
+            for (int j= 0; j < regions.length; j++) {
+                IRegion modelRange= regions[j];
+                IRegion widgetRange= modelRange2ClosestWidgetRange(modelRange);
+                // only take collapsed ranges, i.e. widget length is 0
+                if (widgetRange !is null && widgetRange.getLength() is 0) {
+                    int widgetOffset= widgetRange.getOffset();
+                    // D) region is collapsed at S.widget_offset
+                    if (widgetOffset is widgetSelection.x)
+                        modelOffset= Math.min(modelOffset, modelRange.getOffset());
+                    // C) region is collapsed at S.widget_end
+                    else if (widgetOffset is widgetSelectionExclusiveEnd)
+                        modelEndOffset= Math.max(modelEndOffset, modelRange.getOffset() + modelRange.getLength());
+                }
+            }
+        }
+        return new Point(modelOffset, modelEndOffset - modelOffset);
+    }
+
+    /**
+     * Returns the positions of all annotations that intersect with
+     * <code>modelSelection</code> and that are at least partly visible.
+     * @param modelSelection a model range
+     * @return the positions of all annotations that intersect with
+     *         <code>modelSelection</code>
+     * @since 3.1
+     */
+    private Position[] computeOverlappingAnnotationPositions(IRegion modelSelection) {
+        List positions= new ArrayList();
+        for (Iterator e= fProjectionAnnotationModel.getAnnotationIterator(); e.hasNext();) {
+            ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next();
+            Position position= fProjectionAnnotationModel.getPosition(annotation);
+            if (position !is null && position.overlapsWith(modelSelection.getOffset(), modelSelection.getLength()) && modelRange2WidgetRange(position) !is null)
+                positions.add(position);
+        }
+        return arraycast!(Position)( positions.toArray());
+    }
+
+    /*
+     * @see org.eclipse.jface.text.TextViewer#getFindReplaceDocumentAdapter()
+     */
+    protected FindReplaceDocumentAdapter getFindReplaceDocumentAdapter() {
+        if (fFindReplaceDocumentAdapter is null) {
+            IDocument document= isProjectionMode() ? getDocument() : getVisibleDocument();
+            fFindReplaceDocumentAdapter= new FindReplaceDocumentAdapter(document);
+        }
+        return fFindReplaceDocumentAdapter;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.TextViewer#findAndSelect(int, java.lang.String, bool, bool, bool, bool)
+     */
+    protected int findAndSelect(int startPosition, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord, bool regExSearch) {
+
+        if (!isProjectionMode())
+            return super.findAndSelect(startPosition, findString, forwardSearch, caseSensitive, wholeWord, regExSearch);
+
+        StyledText textWidget= getTextWidget();
+        if (textWidget is null)
+            return -1;
+
+        try {
+
+            IRegion matchRegion= getFindReplaceDocumentAdapter().find(startPosition, findString, forwardSearch, caseSensitive, wholeWord, regExSearch);
+            if (matchRegion !is null) {
+                exposeModelRange(matchRegion);
+                revealRange(matchRegion.getOffset(), matchRegion.getLength());
+                setSelectedRange(matchRegion.getOffset(), matchRegion.getLength());
+                return matchRegion.getOffset();
+            }
+
+        } catch (BadLocationException x) {
+        }
+
+        return -1;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.TextViewer#findAndSelectInRange(int, java.lang.String, bool, bool, bool, int, int, bool)
+     */
+    protected int findAndSelectInRange(int startPosition, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord, int rangeOffset, int rangeLength, bool regExSearch) {
+
+        if (!isProjectionMode())
+            return super.findAndSelectInRange(startPosition, findString, forwardSearch, caseSensitive, wholeWord, rangeOffset, rangeLength, regExSearch);
+
+        StyledText textWidget= getTextWidget();
+        if (textWidget is null)
+            return -1;
+
+        try {
+
+            int modelOffset= startPosition;
+            if (forwardSearch && (startPosition is -1 || startPosition < rangeOffset)) {
+                modelOffset= rangeOffset;
+            } else if (!forwardSearch && (startPosition is -1 || startPosition > rangeOffset + rangeLength)) {
+                modelOffset= rangeOffset + rangeLength;
+            }
+
+            IRegion matchRegion= getFindReplaceDocumentAdapter().find(modelOffset, findString, forwardSearch, caseSensitive, wholeWord, regExSearch);
+            if (matchRegion !is null) {
+                int offset= matchRegion.getOffset();
+                int length= matchRegion.getLength();
+                if (rangeOffset <= offset && offset + length <= rangeOffset + rangeLength) {
+                    exposeModelRange(matchRegion);
+                    revealRange(offset, length);
+                    setSelectedRange(offset, length);
+                    return offset;
+                }
+            }
+
+        } catch (BadLocationException x) {
+        }
+
+        return -1;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/SourceViewerInformationControl.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,452 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.projection.SourceViewerInformationControl;
+
+import org.eclipse.jface.text.source.projection.ProjectionViewer; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSupport; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionPosition; // packageimport
+import org.eclipse.jface.text.source.projection.AnnotationBag; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionSummary; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionRulerColumn; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.projection.IProjectionListener; // packageimport
+import org.eclipse.jface.text.source.projection.ProjectionAnnotation; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IInformationControlExtension;
+import org.eclipse.jface.text.IInformationControlExtension3;
+import org.eclipse.jface.text.IInformationControlExtension5;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.text.source.SourceViewerConfiguration;
+
+/**
+ * Source viewer based implementation of {@link org.eclipse.jface.text.IInformationControl}.
+ * Displays information in a source viewer.
+ *
+ * @since 3.0
+ */
+class SourceViewerInformationControl : IInformationControl, IInformationControlExtension, IInformationControlExtension3, IInformationControlExtension5, DisposeListener {
+
+    /** The control's shell */
+    private Shell fShell;
+    /** The control's text widget */
+    private StyledText fText;
+    /** The symbolic font name of the text font */
+    private const String fSymbolicFontName;
+    /** The text font (do not dispose!) */
+    private Font fTextFont;
+    /** The control's source viewer */
+    private SourceViewer fViewer;
+    /** The optional status field. */
+    private Label fStatusField;
+    /** The separator for the optional status field. */
+    private Label fSeparator;
+    /** The font of the optional status text label.*/
+    private Font fStatusTextFont;
+    /** The maximal widget width. */
+    private int fMaxWidth;
+    /** The maximal widget height. */
+    private int fMaxHeight;
+
+
+    /**
+     * Creates a source viewer information control with the given shell as parent. The given shell
+     * styles are applied to the created shell. The given styles are applied to the created styled
+     * text widget. The text widget will be initialized with the given font. The status field will
+     * contain the given text or be hidden.
+     *
+     * @param parent the parent shell
+     * @param isResizable <code>true</code> if resizable
+     * @param symbolicFontName the symbolic font name
+     * @param statusFieldText the text to be used in the optional status field or <code>null</code>
+     *            if the status field should be hidden
+     */
+    public this(Shell parent, bool isResizable, String symbolicFontName, String statusFieldText) {
+        GridLayout layout;
+        GridData gd;
+
+        int shellStyle= SWT.TOOL | SWT.ON_TOP | (isResizable ? SWT.RESIZE : 0);
+        int textStyle= isResizable ? SWT.V_SCROLL | SWT.H_SCROLL : SWT.NONE;
+
+        fShell= new Shell(parent, SWT.NO_FOCUS | SWT.ON_TOP | shellStyle);
+        Display display= fShell.getDisplay();
+
+        Composite composite= fShell;
+        layout= new GridLayout(1, false);
+        layout.marginHeight= 0;
+        layout.marginWidth= 0;
+        composite.setLayout(layout);
+        gd= new GridData(GridData.FILL_HORIZONTAL);
+        composite.setLayoutData(gd);
+
+        if (statusFieldText !is null) {
+            composite= new Composite(composite, SWT.NONE);
+            layout= new GridLayout(1, false);
+            layout.marginHeight= 0;
+            layout.marginWidth= 0;
+            composite.setLayout(layout);
+            gd= new GridData(GridData.FILL_BOTH);
+            composite.setLayoutData(gd);
+            composite.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+            composite.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+        }
+
+        // Source viewer
+        fViewer= new SourceViewer(composite, null, textStyle);
+        fViewer.configure(new SourceViewerConfiguration());
+        fViewer.setEditable(false);
+
+        fText= fViewer.getTextWidget();
+        gd= new GridData(GridData.BEGINNING | GridData.FILL_BOTH);
+        fText.setLayoutData(gd);
+        fText.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+        fText.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+        fSymbolicFontName= symbolicFontName;
+        fTextFont= JFaceResources.getFont(symbolicFontName);
+        fText.setFont(fTextFont);
+
+        fText.addKeyListener(new class()  KeyListener {
+
+            public void keyPressed(KeyEvent e)  {
+                if (e.character is 0x1B) // ESC
+                    fShell.dispose();
+            }
+
+            public void keyReleased(KeyEvent e) {}
+        });
+
+        // Status field
+        if (statusFieldText !is null) {
+
+            // Horizontal separator line
+            fSeparator= new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL | SWT.LINE_DOT);
+            fSeparator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+            // Status field label
+            fStatusField= new Label(composite, SWT.RIGHT);
+            fStatusField.setText(statusFieldText);
+            Font font= fStatusField.getFont();
+            FontData[] fontDatas= font.getFontData();
+            for (int i= 0; i < fontDatas.length; i++)
+                fontDatas[i].setHeight(fontDatas[i].getHeight() * 9 / 10);
+            fStatusTextFont= new Font(fStatusField.getDisplay(), fontDatas);
+            fStatusField.setFont(fStatusTextFont);
+            GridData gd2= new GridData(GridData.FILL_VERTICAL | GridData.FILL_HORIZONTAL | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.VERTICAL_ALIGN_BEGINNING);
+            fStatusField.setLayoutData(gd2);
+
+            // Regarding the color see bug 41128
+            fStatusField.setForeground(display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW));
+
+            fStatusField.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+        }
+
+        addDisposeListener(this);
+    }
+
+    /**
+     * @see org.eclipse.jface.text.IInformationControlExtension2#setInput(java.lang.Object)
+     * @param input the input object
+     */
+    public void setInput(Object input) {
+        if ( cast(ArrayWrapperString)input )
+            setInformation(stringcast(input));
+        else
+            setInformation(null);
+    }
+
+    /*
+     * @see IInformationControl#setInformation(String)
+     */
+    public void setInformation(String content) {
+        if (content is null) {
+            fViewer.setInput(null);
+            return;
+        }
+
+        IDocument doc= new Document(content);
+        fViewer.setInput(cast(Object)doc);
+    }
+
+    /*
+     * @see IInformationControl#setVisible(bool)
+     */
+    public void setVisible(bool visible) {
+            fShell.setVisible(visible);
+    }
+
+    /*
+     * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
+     */
+    public void widgetDisposed(DisposeEvent event) {
+        if (fStatusTextFont !is null && !fStatusTextFont.isDisposed())
+            fStatusTextFont.dispose();
+
+        fStatusTextFont= null;
+        fTextFont= null;
+        fShell= null;
+        fText= null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControl#dispose()
+     */
+    public final void dispose() {
+        if (fShell !is null && !fShell.isDisposed())
+            fShell.dispose();
+        else
+            widgetDisposed(null);
+    }
+
+    /*
+     * @see IInformationControl#setSize(int, int)
+     */
+    public void setSize(int width, int height) {
+
+        if (fStatusField !is null) {
+            GridData gd= cast(GridData)fViewer.getTextWidget().getLayoutData();
+            Point statusSize= fStatusField.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+            Point separatorSize= fSeparator.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+            gd.heightHint= height - statusSize.y - separatorSize.y;
+        }
+        fShell.setSize(width, height);
+
+        if (fStatusField !is null)
+            fShell.pack(true);
+    }
+
+    /*
+     * @see IInformationControl#setLocation(Point)
+     */
+    public void setLocation(Point location) {
+        fShell.setLocation(location);
+    }
+
+    /*
+     * @see IInformationControl#setSizeConstraints(int, int)
+     */
+    public void setSizeConstraints(int maxWidth, int maxHeight) {
+        fMaxWidth= maxWidth;
+        fMaxHeight= maxHeight;
+    }
+
+    /*
+     * @see IInformationControl#computeSizeHint()
+     */
+    public Point computeSizeHint() {
+        // compute the preferred size
+        int x= SWT.DEFAULT;
+        int y= SWT.DEFAULT;
+        Point size= fShell.computeSize(x, y);
+        if (size.x > fMaxWidth)
+            x= fMaxWidth;
+        if (size.y > fMaxHeight)
+            y= fMaxHeight;
+
+        // recompute using the constraints if the preferred size is larger than the constraints
+        if (x !is SWT.DEFAULT || y !is SWT.DEFAULT)
+            size= fShell.computeSize(x, y, false);
+
+        return size;
+    }
+
+    /*
+     * @see IInformationControl#addDisposeListener(DisposeListener)
+     */
+    public void addDisposeListener(DisposeListener listener) {
+        fShell.addDisposeListener(listener);
+    }
+
+    /*
+     * @see IInformationControl#removeDisposeListener(DisposeListener)
+     */
+    public void removeDisposeListener(DisposeListener listener) {
+        fShell.removeDisposeListener(listener);
+    }
+
+    /*
+     * @see IInformationControl#setForegroundColor(Color)
+     */
+    public void setForegroundColor(Color foreground) {
+        fText.setForeground(foreground);
+    }
+
+    /*
+     * @see IInformationControl#setBackgroundColor(Color)
+     */
+    public void setBackgroundColor(Color background) {
+        fText.setBackground(background);
+    }
+
+    /*
+     * @see IInformationControl#isFocusControl()
+     */
+    public bool isFocusControl() {
+        return fShell.getDisplay().getActiveShell() is fShell;
+    }
+
+    /*
+     * @see IInformationControl#setFocus()
+     */
+    public void setFocus() {
+        fShell.forceFocus();
+        fText.setFocus();
+    }
+
+    /*
+     * @see IInformationControl#addFocusListener(FocusListener)
+     */
+    public void addFocusListener(FocusListener listener) {
+        fText.addFocusListener(listener);
+    }
+
+    /*
+     * @see IInformationControl#removeFocusListener(FocusListener)
+     */
+    public void removeFocusListener(FocusListener listener) {
+        fText.removeFocusListener(listener);
+    }
+
+    /*
+     * @see IInformationControlExtension#hasContents()
+     */
+    public bool hasContents() {
+        return fText.getCharCount() > 0;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlExtension3#computeTrim()
+     * @since 3.4
+     */
+    public Rectangle computeTrim() {
+        Rectangle trim= fShell.computeTrim(0, 0, 0, 0);
+        addInternalTrim(trim);
+        return trim;
+    }
+
+    /**
+     * Adds the internal trimmings to the given trim of the shell.
+     *
+     * @param trim the shell's trim, will be updated
+     * @since 3.4
+     */
+    private void addInternalTrim(Rectangle trim) {
+        if (fStatusField !is null) {
+            trim.height+= fSeparator.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
+            trim.height+= fStatusField.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlExtension3#getBounds()
+     * @since 3.4
+     */
+    public Rectangle getBounds() {
+        return fShell.getBounds();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlExtension3#restoresLocation()
+     * @since 3.4
+     */
+    public bool restoresLocation() {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlExtension3#restoresSize()
+     * @since 3.4
+     */
+    public bool restoresSize() {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlExtension5#getInformationPresenterControlCreator()
+     * @since 3.4
+     */
+    public IInformationControlCreator getInformationPresenterControlCreator() {
+        return new class()  IInformationControlCreator {
+            public IInformationControl createInformationControl(Shell parent) {
+                return new SourceViewerInformationControl(parent, true, fSymbolicFontName, null);
+            }
+        };
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlExtension5#containsControl(org.eclipse.swt.widgets.Control)
+     * @since 3.4
+     */
+    public bool containsControl(Control control) {
+        do {
+            if (control is fShell)
+                return true;
+            if ( cast(Shell)control )
+                return false;
+            control= control.getParent();
+        } while (control !is null);
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlExtension5#isVisible()
+     * @since 3.4
+     */
+    public bool isVisible() {
+        return fShell !is null && !fShell.isDisposed() && fShell.isVisible();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlExtension5#computeSizeConstraints(int, int)
+     */
+    public Point computeSizeConstraints(int widthInChars, int heightInChars) {
+        GC gc= new GC(fText);
+        gc.setFont(fTextFont);
+        int width= gc.getFontMetrics().getAverageCharWidth();
+        int height = gc.getFontMetrics().getHeight();
+        gc.dispose();
+
+        return new Point (widthInChars * width, heightInChars * height);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/contentassist/IContentAssistSubjectControl.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,272 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.contentassist.IContentAssistSubjectControl;
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.custom.VerifyKeyListener;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IEventConsumer;
+
+
+/**
+ * A content assist subject control can request assistance provided by a
+ * {@linkplain org.eclipse.jface.contentassist.ISubjectControlContentAssistant subject control content assistant}.
+ *
+ * @since 3.0
+ */
+public interface IContentAssistSubjectControl {
+
+    /**
+     * Returns the control of this content assist subject control.
+     *
+     * @return the control of this content assist subject control
+     */
+    Control getControl();
+
+    /**
+     * Returns the line height.
+     *
+     * @return line height in pixel
+     * @exception org.eclipse.swt.SWTException
+     *               <ul>
+     *                  <li>{@link org.eclipse.swt.SWT#ERROR_WIDGET_DISPOSED} - if the receiver has been
+     *                      disposed</li>
+     *                  <li>{@link org.eclipse.swt.SWT#ERROR_THREAD_INVALID_ACCESS} - if not called from the
+     *                       thread that created the receiver</li>
+     *               </ul>
+     */
+    int getLineHeight();
+
+    /**
+     * Returns the caret position relative to the start of the text in widget
+     * coordinates.
+     *
+     * @return the caret position relative to the start of the text in widget
+     *         coordinates
+     * @exception org.eclipse.swt.SWTException
+     *               <ul>
+     *                  <li>{@link org.eclipse.swt.SWT#ERROR_WIDGET_DISPOSED} - if the receiver has been
+     *                      disposed</li>
+     *                  <li>{@link org.eclipse.swt.SWT#ERROR_THREAD_INVALID_ACCESS} - if not called from the
+     *                      thread that created the receiver</li>
+     *               </ul>
+     */
+    int getCaretOffset();
+
+    /**
+     * Returns the x, y location of the upper left corner of the character
+     * bounding box at the specified offset in the text. The point is relative
+     * to the upper left corner of the widget client area.
+     *
+     * @param offset widget offset relative to the start of the content 0
+     *           <= offset <= getCharCount()
+     * @return x, y location of the upper left corner of the character bounding
+     *         box at the specified offset in the text
+     * @exception org.eclipse.swt.SWTException
+     *          <ul>
+     *              <li>{@link org.eclipse.swt.SWT#ERROR_WIDGET_DISPOSED} - if the receiver has been disposed</li>
+     *              <li>{@link org.eclipse.swt.SWT#ERROR_THREAD_INVALID_ACCESS} - if not called from the thread that created the receiver</li>
+     *          </ul>
+     * @exception IllegalArgumentException when the offset is outside the valid range
+     */
+    Point getLocationAtOffset(int offset);
+
+    /**
+     * Returns the line delimiter used for entering new lines by key down or
+     * paste operation.
+     *
+     * @return line delimiter used for entering new lines by key down or paste
+     *         operation
+     * @exception org.eclipse.swt.SWTException
+     *          <ul>
+     *              <li>{@link org.eclipse.swt.SWT#ERROR_WIDGET_DISPOSED} - if the receiver has been disposed</li>
+     *              <li>{@link org.eclipse.swt.SWT#ERROR_THREAD_INVALID_ACCESS} - if not called from the thread that created the receiver</li>
+     *          </ul>
+     */
+    String getLineDelimiter();
+
+    /**
+     * Returns the selected range in the subject's widget.
+     *
+     * @return start and length of the selection, x is the offset of the
+     * @exception org.eclipse.swt.SWTException
+     *          <ul>
+     *              <li>{@link org.eclipse.swt.SWT#ERROR_WIDGET_DISPOSED} - if the receiver has been disposed</li>
+     *              <li>{@link org.eclipse.swt.SWT#ERROR_THREAD_INVALID_ACCESS} - if not called from the thread that created the receiver</li>
+     *          </ul>
+     */
+    Point getWidgetSelectionRange();
+
+    /**
+     * Returns the selected range.
+     *
+     * @return start and length of the selection, x is the offset and y the
+     *         length based on the subject's model (e.g. document)
+     */
+    Point getSelectedRange();
+
+    /**
+     * Sets the selected range. Offset and length based on the subject's
+     * model (e.g. document).
+     *
+     * @param offset the offset of the selection based on the subject's model e.g. document
+     * @param length the length of the selection based on the subject's model e.g. document
+     */
+    void setSelectedRange(int offset, int length);
+
+    /**
+     * Reveals the given region. Offset and length based on the subject's
+     * model (e.g. document).
+     *
+     * @param offset the offset of the selection based on the subject's model e.g. document
+     * @param length the length of the selection based on the subject's model e.g. document
+     */
+    void revealRange(int offset, int length);
+
+    /**
+     * Returns this content assist subject control's document.
+     *
+     * @return the viewer's input document
+     */
+    IDocument getDocument();
+
+    /**
+     * If supported, appends a verify key listener to the viewer's list of verify key
+     * listeners. If the listener is already registered with the viewer this
+     * call moves the listener to the end of the list.
+     * <p>
+     * Note: This content assist subject control may not support appending a verify
+     * listener, in which case <code>false</code> will be returned. If this
+     * content assist subject control only supports <code>addVerifyKeyListener</code>
+     * then this method can be used but <code>prependVerifyKeyListener</code>
+     * must return <code>false</code>.
+     * </p>
+     *
+     * @param verifyKeyListener the listener to be added
+     * @return <code>true</code> if the listener was added
+     */
+    bool appendVerifyKeyListener(VerifyKeyListener verifyKeyListener);
+
+    /**
+     * If supported, inserts the verify key listener at the beginning of this content assist
+     * subject's list of verify key listeners. If the listener is already
+     * registered with the viewer this call moves the listener to the beginning
+     * of the list.
+     * <p>
+     * Note: This content assist subject control may not support prepending a verify
+     * listener, in which case <code>false</code> will be returned. However,
+     * {@link #appendVerifyKeyListener(VerifyKeyListener)} might work.
+     * </p>
+     *
+     * @param verifyKeyListener the listener to be inserted
+     * @return <code>true</code> if the listener was added
+     */
+    bool prependVerifyKeyListener(VerifyKeyListener verifyKeyListener);
+
+    /**
+     * Removes the verify key listener from this content assist subject control's
+     * list of verify key listeners. If the listener is not registered, this
+     * call has no effect.
+     *
+     * @param verifyKeyListener the listener to be removed
+     */
+    void removeVerifyKeyListener(VerifyKeyListener verifyKeyListener);
+
+    /**
+     * Tests whether a verify key listener can be added either using <code>prependVerifyKeyListener</code>
+     * or {@link #appendVerifyKeyListener(VerifyKeyListener)}.
+     *
+     * @return <code>true</code> if adding verify key listeners is supported
+     */
+    bool supportsVerifyKeyListener();
+
+    /**
+     * Adds the listener to the collection of listeners who will be notified
+     * when keys are pressed and released on the system keyboard, by sending it
+     * one of the messages defined in the {@link KeyListener} interface.
+     *
+     * @param keyListener the listener which should be notified
+     * @exception IllegalArgumentException if the listener is <code>null</code>
+     * @exception org.eclipse.swt.SWTException
+     *          <ul>
+     *              <li>{@link org.eclipse.swt.SWT#ERROR_WIDGET_DISPOSED} - if the receiver has been disposed</li>
+     *              <li>{@link org.eclipse.swt.SWT#ERROR_THREAD_INVALID_ACCESS} - if not called from the thread that created the receiver</li>
+     *          </ul>
+     *
+     * @see KeyListener
+     * @see #removeKeyListener(KeyListener)
+     */
+    void addKeyListener(KeyListener keyListener);
+
+    /**
+     * Removes the listener from the collection of listeners who will be
+     * notified when keys are pressed and released on the system keyboard.
+     *
+     * @param keyListener the listener which should be notified
+     * @exception IllegalArgumentException if the listener is null</li>
+     * @exception org.eclipse.swt.SWTException
+     *          <ul>
+     *              <li>{@link org.eclipse.swt.SWT#ERROR_WIDGET_DISPOSED} - if the receiver has been disposed</li>
+     *              <li>{@link org.eclipse.swt.SWT#ERROR_THREAD_INVALID_ACCESS} - if not called from the thread that created the receiver</li>
+     *          </ul>
+     * @see KeyListener
+     * @see #addKeyListener(KeyListener)
+     */
+    void removeKeyListener(KeyListener keyListener);
+
+    /**
+     * If supported, registers an event consumer with this content assist
+     * subject.
+     *
+     * @param eventConsumer the content assist subject control's event consumer. <code>null</code>
+     *           is a valid argument.
+     */
+    void setEventConsumer(IEventConsumer eventConsumer);
+
+    /**
+     * Removes the specified selection listener.
+     * <p>
+     *
+     * @param selectionListener the listener
+     * @exception org.eclipse.swt.SWTException <ul>
+     *          <ul>
+     *              <li>{@link org.eclipse.swt.SWT#ERROR_WIDGET_DISPOSED} - if the receiver has been disposed</li>
+     *              <li>{@link org.eclipse.swt.SWT#ERROR_THREAD_INVALID_ACCESS} - if not called from the thread that created the receiver</li>
+     *          </ul>
+     * @exception IllegalArgumentException if listener is <code>null</code>
+     */
+    void removeSelectionListener(SelectionListener selectionListener);
+
+    /**
+     * If supported, adds a selection listener. A Selection event is sent by the widget when the
+     * selection has changed.
+     * <p>
+     *
+     * @param selectionListener the listener
+     * @return <code>true</code> if adding a selection listener is supported
+     *          <ul>
+     *              <li>{@link org.eclipse.swt.SWT#ERROR_WIDGET_DISPOSED} - if the receiver has been disposed</li>
+     *              <li>{@link org.eclipse.swt.SWT#ERROR_THREAD_INVALID_ACCESS} - if not called from the thread that created the receiver</li>
+     *          </ul>
+     * @exception IllegalArgumentException if listener is <code>null</code>
+     */
+    bool addSelectionListener(SelectionListener selectionListener);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/contentassist/ISubjectControlContentAssistProcessor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.contentassist.ISubjectControlContentAssistProcessor;
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.contentassist.IContentAssistProcessor}
+ * which provides the context for the
+ * {@linkplain org.eclipse.jface.contentassist.ISubjectControlContentAssistant subject control content assistant}.
+ *
+ * @since 3.0
+ * @deprecated As of 3.2, replaced by Platform UI's field assist support
+ */
+public interface ISubjectControlContentAssistProcessor : IContentAssistProcessor {
+
+    /**
+     * Returns a list of completion proposals based on the specified location
+     * within the document that corresponds to the current cursor position
+     * within the text viewer.
+     *
+     * @param contentAssistSubjectControl the content assist subject control whose
+     *           document is used to compute the proposals
+     * @param documentOffset an offset within the document for which
+     *           completions should be computed
+     * @return an array of completion proposals or <code>null</code> if no
+     *         proposals are possible
+     */
+    ICompletionProposal[] computeCompletionProposals(IContentAssistSubjectControl contentAssistSubjectControl, int documentOffset);
+
+    /**
+     * Returns information about possible contexts based on the specified
+     * location within the document that corresponds to the current cursor
+     * position within the content assist subject control.
+     *
+     * @param contentAssistSubjectControl the content assist subject control whose
+     *           document is used to compute the possible contexts
+     * @param documentOffset an offset within the document for which context
+     *           information should be computed
+     * @return an array of context information objects or <code>null</code>
+     *         if no context could be found
+     */
+    IContextInformation[] computeContextInformation(IContentAssistSubjectControl contentAssistSubjectControl, int documentOffset);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/contentassist/ISubjectControlContentAssistant.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.contentassist.ISubjectControlContentAssistant;
+
+import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
+
+import java.lang.all;
+
+import org.eclipse.jface.text.contentassist.IContentAssistant;
+
+
+/**
+ * Extends {@link org.eclipse.jface.text.contentassist.IContentAssistant} to
+ * allow to install a content assistant on the given
+ * {@linkplain org.eclipse.jface.contentassist.IContentAssistSubjectControl content assist subject control}.
+ *
+ * @since 3.0
+ * @deprecated As of 3.2, replaced by Platform UI's field assist support
+ */
+public interface ISubjectControlContentAssistant : IContentAssistant {
+
+    /**
+     * Installs content assist support on the given subject.
+     *
+     * @param contentAssistSubjectControl the one who requests content assist
+     */
+    void install(IContentAssistSubjectControl contentAssistSubjectControl);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/contentassist/ISubjectControlContextInformationPresenter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.contentassist.ISubjectControlContextInformationPresenter;
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter;
+import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
+
+/**
+ * Extends {@link org.eclipse.jface.text.contentassist.IContextInformationPresenter} to
+ * allow to install a content assistant on the given
+ * {@linkplain org.eclipse.jface.contentassist.IContentAssistSubjectControl content assist subject control}.
+ *
+ * @since 3.0
+ * @deprecated As of 3.2, replaced by Platform UI's field assist support
+ */
+public interface ISubjectControlContextInformationPresenter : IContextInformationPresenter {
+
+    /**
+     * Installs this presenter for the given context information.
+     *
+     * @param info the context information which this presenter should style
+     * @param contentAssistSubjectControl the content assist subject control
+     * @param offset the document offset for which the information has been computed
+     */
+    void install(IContextInformation info, IContentAssistSubjectControl contentAssistSubjectControl, int offset);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/contentassist/ISubjectControlContextInformationValidator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.contentassist.ISubjectControlContextInformationValidator;
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.contentassist.IContextInformationValidator;
+import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
+
+
+/**
+ * Extends {@link org.eclipse.jface.text.contentassist.IContextInformationValidator} to
+ * allow to install a content assistant on the given
+ * {@linkplain org.eclipse.jface.contentassist.IContentAssistSubjectControl content assist subject control}.
+ *
+ * @since 3.0
+ * @deprecated As of 3.2, replaced by Platform UI's field assist support
+ */
+public interface ISubjectControlContextInformationValidator : IContextInformationValidator {
+
+    /**
+     * Installs this validator for the given context information.
+     *
+     * @param info the context information which this validator should check
+     * @param contentAssistSubjectControl the content assist subject control
+     * @param offset the document offset for which the information
+     *           has been computed
+     */
+    void install(IContextInformation info, IContentAssistSubjectControl contentAssistSubjectControl, int offset);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/DelayedInputChangeListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.text.DelayedInputChangeListener;
+
+import org.eclipse.jface.internal.text.NonDeletingPositionUpdater; // packageimport
+import org.eclipse.jface.internal.text.InternalAccessor; // packageimport
+import org.eclipse.jface.internal.text.StickyHoverManager; // packageimport
+import org.eclipse.jface.internal.text.InformationControlReplacer; // packageimport
+import org.eclipse.jface.internal.text.TableOwnerDrawSupport; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.IDelayedInputChangeProvider;
+import org.eclipse.jface.text.IInputChangedListener;
+
+
+/**
+ * A delayed input change listener that forwards delayed input changes to an information control replacer.
+ * 
+ * @since 3.4
+ */
+public final class DelayedInputChangeListener : IInputChangedListener {
+    
+    private const IDelayedInputChangeProvider fChangeProvider;
+    private const InformationControlReplacer fInformationControlReplacer;
+
+    /**
+     * Creates a new listener.
+     * 
+     * @param changeProvider the information control with delayed input changes
+     * @param informationControlReplacer the information control replacer, whose information control should get the new input
+     */
+    public this(IDelayedInputChangeProvider changeProvider, InformationControlReplacer informationControlReplacer) {
+        fChangeProvider= changeProvider;
+        fInformationControlReplacer= informationControlReplacer;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDelayedInputChangeListener#inputChanged(java.lang.Object)
+     */
+    public void inputChanged(Object newInput) {
+        fChangeProvider.setDelayedInputChangeListener(null);
+        fInformationControlReplacer.setDelayedInput(newInput);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/InformationControlReplacer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.text.InformationControlReplacer;
+
+import org.eclipse.jface.internal.text.NonDeletingPositionUpdater; // packageimport
+import org.eclipse.jface.internal.text.InternalAccessor; // packageimport
+import org.eclipse.jface.internal.text.StickyHoverManager; // packageimport
+import org.eclipse.jface.internal.text.TableOwnerDrawSupport; // packageimport
+import org.eclipse.jface.internal.text.DelayedInputChangeListener; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.jface.text.AbstractInformationControlManager;
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
+import org.eclipse.jface.text.DefaultInformationControl;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IInformationControlExtension2;
+import org.eclipse.jface.text.IInformationControlExtension3;
+import org.eclipse.jface.util.Geometry;
+
+
+/**
+ * An information control replacer can replace an
+ * {@link AbstractInformationControlManager}'s control.
+ *
+ * @see AbstractInformationControlManager#setInformationControlReplacer(InformationControlReplacer)
+ * @since 3.4
+ */
+public class InformationControlReplacer : AbstractInformationControlManager {
+    alias AbstractInformationControlManager.showInformationControl showInformationControl;
+    /**
+     * Minimal width in pixels.
+     */
+    private static const int MIN_WIDTH= 80;
+    /**
+     * Minimal height in pixels.
+     */
+    private static const int MIN_HEIGHT= 50;
+
+    /**
+     * Default control creator.
+     */
+    protected static class DefaultInformationControlCreator : AbstractReusableInformationControlCreator {
+        public IInformationControl doCreateInformationControl(Shell shell) {
+            return new DefaultInformationControl(shell, true);
+        }
+    }
+
+    private bool fIsReplacing;
+    private Object fReplacableInformation;
+    private bool fDelayedInformationSet;
+    private Rectangle fReplaceableArea;
+    private Rectangle fContentBounds;
+
+
+    /**
+     * Creates a new information control replacer.
+     *
+     * @param creator the default information control creator
+     */
+    public this(IInformationControlCreator creator) {
+        super(creator);
+        takesFocusWhenVisible(false);
+    }
+
+    /**
+     * Replace the information control.
+     *
+     * @param informationPresenterControlCreator the information presenter control creator
+     * @param contentBounds the bounds of the content area of the information control
+     * @param information the information to show
+     * @param subjectArea the subject area
+     * @param takeFocus <code>true</code> iff the replacing information control should take focus
+     */
+    public void replaceInformationControl(IInformationControlCreator informationPresenterControlCreator, Rectangle contentBounds, Object information, Rectangle subjectArea, bool takeFocus) {
+
+        try {
+            fIsReplacing= true;
+            if (! fDelayedInformationSet)
+                fReplacableInformation= information;
+            else
+                takeFocus= true; // delayed input has been set, so the original info control must have been focused
+            fContentBounds= contentBounds;
+            fReplaceableArea= subjectArea;
+
+            setCustomInformationControlCreator(informationPresenterControlCreator);
+
+            takesFocusWhenVisible(takeFocus);
+
+            showInformation();
+        } finally {
+            fIsReplacing= false;
+            fReplacableInformation= null;
+            fDelayedInformationSet= false;
+            fReplaceableArea= null;
+            setCustomInformationControlCreator(null);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#computeInformation()
+     */
+    protected void computeInformation() {
+        if (fIsReplacing && fReplacableInformation !is null) {
+            setInformation(fReplacableInformation, fReplaceableArea);
+            return;
+        }
+
+        if (DEBUG)
+            System.out_.println("InformationControlReplacer: no active replaceable"); //$NON-NLS-1$
+    }
+
+    /**
+     * Opens the information control with the given information and the specified
+     * subject area. It also activates the information control closer.
+     *
+     * @param subjectArea the information area
+     * @param information the information
+     */
+    public void showInformationControl(Rectangle subjectArea, Object information) {
+        IInformationControl informationControl= getInformationControl();
+
+        Rectangle controlBounds= fContentBounds;
+        if ( cast(IInformationControlExtension3)informationControl ) {
+            IInformationControlExtension3 iControl3= cast(IInformationControlExtension3) informationControl;
+            Rectangle trim= iControl3.computeTrim();
+            controlBounds= Geometry.add(controlBounds, trim);
+
+            /*
+             * Ensure minimal size. Interacting with a tiny information control
+             * (resizing, selecting text) would be a pain.
+             */
+            controlBounds.width= Math.max(controlBounds.width, MIN_WIDTH);
+            controlBounds.height= Math.max(controlBounds.height, MIN_HEIGHT);
+
+            getInternalAccessor().cropToClosestMonitor(controlBounds);
+        }
+
+        Point location= Geometry.getLocation(controlBounds);
+        Point size= Geometry.getSize(controlBounds);
+
+        // Caveat: some IInformationControls fail unless setSizeConstraints(..) is called with concrete values
+        informationControl.setSizeConstraints(size.x, size.y);
+
+        if ( cast(IInformationControlExtension2)informationControl )
+            (cast(IInformationControlExtension2) informationControl).setInput(information);
+        else
+            informationControl.setInformation(information.toString());
+
+        informationControl.setLocation(location);
+        informationControl.setSize(size.x, size.y);
+
+        showInformationControl(subjectArea);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#hideInformationControl()
+     */
+    public void hideInformationControl() {
+        super.hideInformationControl();
+    }
+
+    /**
+     * @param input the delayed input, or <code>null</code> to request cancellation
+     */
+    public void setDelayedInput(Object input) {
+        fReplacableInformation= input;
+        if (! isReplacing()) {
+            fDelayedInformationSet= true;
+        } else if (cast(IInformationControlExtension2)getCurrentInformationControl2() ) {
+            (cast(IInformationControlExtension2) getCurrentInformationControl2()).setInput(input);
+        } else if (getCurrentInformationControl2() !is null) {
+            getCurrentInformationControl2().setInformation(input.toString());
+        }
+    }
+
+    /**
+     * Tells whether the replacer is currently replacing another information control.
+     *
+     * @return <code>true</code> while code from {@link #replaceInformationControl(IInformationControlCreator, Rectangle, Object, Rectangle, bool)} is run
+     */
+    public bool isReplacing() {
+        return fIsReplacing;
+    }
+
+    /**
+     * @return the current information control, or <code>null</code> if none available
+     */
+    public IInformationControl getCurrentInformationControl2() {
+        return getInternalAccessor().getCurrentInformationControl();
+    }
+
+    /**
+     * The number of pixels to blow up the keep-up zone.
+     *
+     * @return the margin in pixels
+     */
+    public int getKeepUpMargin() {
+        return 15;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/InternalAccessor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.text.InternalAccessor;
+
+import org.eclipse.jface.internal.text.NonDeletingPositionUpdater; // packageimport
+import org.eclipse.jface.internal.text.StickyHoverManager; // packageimport
+import org.eclipse.jface.internal.text.InformationControlReplacer; // packageimport
+import org.eclipse.jface.internal.text.TableOwnerDrawSupport; // packageimport
+import org.eclipse.jface.internal.text.DelayedInputChangeListener; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.jface.text.AbstractInformationControlManager;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlExtension3;
+import org.eclipse.jface.text.ITextViewerExtension8;
+import org.eclipse.jface.text.ITextViewerExtension8;
+
+
+/**
+ * An internal class that gives access to internal methods of {@link
+ * AbstractInformationControlManager} and subclasses.
+ *
+ * @since 3.4
+ */
+public abstract class InternalAccessor {
+
+    /**
+     * Returns the current information control, or <code>null</code> if none.
+     *
+     * @return the current information control, or <code>null</code> if none
+     */
+    public abstract IInformationControl getCurrentInformationControl();
+
+    /**
+     * Sets the information control replacer for this manager and disposes the
+     * old one if set.
+     *
+     * @param replacer the information control replacer for this manager, or
+     *            <code>null</code> if no information control replacing should
+     *            take place
+     */
+    public abstract void setInformationControlReplacer(InformationControlReplacer replacer);
+
+    /**
+     * Returns the current information control replacer or <code>null</code> if none has been installed.
+     *
+     * @return the current information control replacer or <code>null</code> if none has been installed
+     */
+    public abstract InformationControlReplacer getInformationControlReplacer();
+
+    /**
+     * Tests whether the given information control is replaceable.
+     *
+     * @param iControl information control or <code>null</code> if none
+     * @return <code>true</code> if information control is replaceable, <code>false</code> otherwise
+     */
+    public abstract bool canReplace(IInformationControl iControl);
+
+    /**
+     * Tells whether this manager's information control is currently being replaced.
+     *
+     * @return <code>true</code> if a replace is in progress
+     */
+    public abstract bool isReplaceInProgress();
+
+    /**
+     * Crops the given bounds such that they lie completely on the closest monitor.
+     *
+     * @param bounds shell bounds to crop
+     */
+    public abstract void cropToClosestMonitor(Rectangle bounds);
+
+    /**
+     * Sets the hover enrich mode. Only applicable when an information
+     * control replacer has been set with
+     * {@link #setInformationControlReplacer(InformationControlReplacer)} .
+     *
+     * @param mode the enrich mode
+     * @see ITextViewerExtension8#setHoverEnrichMode(org.eclipse.jface.text.ITextViewerExtension8.EnrichMode)
+     */
+    public abstract void setHoverEnrichMode(ITextViewerExtension8_EnrichMode mode);
+
+    /**
+     * Indicates whether the mouse cursor is allowed to leave the subject area without closing the hover.
+     *
+     * @return whether the mouse cursor is allowed to leave the subject area without closing the hover
+     */
+    public abstract bool getAllowMouseExit();
+
+    /**
+     * Replaces this manager's information control as defined by
+     * the information control replacer.
+     * <strong>Must only be called when the information control is instanceof {@link IInformationControlExtension3}!</strong>
+     *
+     * @param takeFocus <code>true</code> iff the replacing information control should take focus
+     */
+    public abstract void replaceInformationControl(bool takeFocus);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/NonDeletingPositionUpdater.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.internal.text.NonDeletingPositionUpdater;
+
+import org.eclipse.jface.internal.text.InternalAccessor; // packageimport
+import org.eclipse.jface.internal.text.StickyHoverManager; // packageimport
+import org.eclipse.jface.internal.text.InformationControlReplacer; // packageimport
+import org.eclipse.jface.internal.text.TableOwnerDrawSupport; // packageimport
+import org.eclipse.jface.internal.text.DelayedInputChangeListener; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IPositionUpdater;
+import org.eclipse.jface.text.Position;
+
+
+/**
+ * A position updater that never deletes a position. If the region containing
+ * the position is deleted, the position is moved to the beginning/end (falling
+ * together) of the change. If the region containing the position is replaced,
+ * the position is placed at the same location inside the replacement text, but
+ * always inside the replacement text.
+ *
+ * @since 3.1
+ */
+public final class NonDeletingPositionUpdater : IPositionUpdater {
+    /** The position category. */
+    private const String fCategory;
+
+    /**
+     * Creates a new updater for the given <code>category</code>.
+     *
+     * @param category the new category.
+     */
+    public this(String category) {
+        fCategory= category;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent)
+     */
+    public void update(DocumentEvent event) {
+
+        int eventOffset= event.getOffset();
+        int eventOldEndOffset= eventOffset + event.getLength();
+        int eventNewLength= event.getText() is null ? 0 : event.getText().length();
+        int eventNewEndOffset= eventOffset + eventNewLength;
+        int deltaLength= eventNewLength - event.getLength();
+
+        try {
+            Position[] positions= event.getDocument().getPositions(fCategory);
+
+            for (int i= 0; i !is positions.length; i++) {
+
+                Position position= positions[i];
+
+                if (position.isDeleted())
+                    continue;
+
+                int offset= position.getOffset();
+                int length= position.getLength();
+                int end= offset + length;
+
+                if (offset > eventOldEndOffset) {
+                    // position comes way after change - shift
+                    position.setOffset(offset + deltaLength);
+                } else if (end < eventOffset) {
+                    // position comes way before change - leave alone
+                } else if (offset <= eventOffset && end >= eventOldEndOffset) {
+                    // event completely internal to the position - adjust length
+                    position.setLength(length + deltaLength);
+                } else if (offset < eventOffset) {
+                    // event extends over end of position - include the
+                    // replacement text into the position
+                    position.setLength(eventNewEndOffset - offset);
+                } else if (end > eventOldEndOffset) {
+                    // event extends from before position into it - adjust
+                    // offset and length, including the replacement text into
+                    // the position
+                    position.setOffset(eventOffset);
+                    int deleted= eventOldEndOffset - offset;
+                    position.setLength(length - deleted + eventNewLength);
+                } else {
+                    // event comprises the position - keep it at the same
+                    // position, but always inside the replacement text
+                    int newOffset= Math.min(offset, eventNewEndOffset);
+                    int newEndOffset= Math.min(end, eventNewEndOffset);
+                    position.setOffset(newOffset);
+                    position.setLength(newEndOffset - newOffset);
+                }
+            }
+        } catch (BadPositionCategoryException e) {
+            // ignore and return
+        }
+    }
+
+    /**
+     * Returns the position category.
+     *
+     * @return the position category
+     */
+    public String getCategory() {
+        return fCategory;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/StickyHoverManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,386 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 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 org.eclipse.jface.internal.text.StickyHoverManager;
+
+import org.eclipse.jface.internal.text.NonDeletingPositionUpdater; // packageimport
+import org.eclipse.jface.internal.text.InternalAccessor; // packageimport
+import org.eclipse.jface.internal.text.InformationControlReplacer; // packageimport
+import org.eclipse.jface.internal.text.TableOwnerDrawSupport; // packageimport
+import org.eclipse.jface.internal.text.DelayedInputChangeListener; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+import tango.text.convert.Format;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlExtension3;
+import org.eclipse.jface.text.IInformationControlExtension5;
+import org.eclipse.jface.text.IViewportListener;
+import org.eclipse.jface.text.IWidgetTokenKeeper;
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension;
+import org.eclipse.jface.text.IWidgetTokenOwner;
+import org.eclipse.jface.text.TextViewer;
+import org.eclipse.jface.util.Geometry;
+
+/**
+ * Implements a sticky hover control, i.e. a control that replaces a hover
+ * with an enriched and focusable control.
+ * <p>
+ * The information control is made visible on request by calling
+ * {@link #showInformationControl(Rectangle)}.
+ * </p>
+ * <p>
+ * Clients usually instantiate and configure this class before using it. The configuration
+ * must be consistent: This means the used {@link org.eclipse.jface.text.IInformationControlCreator}
+ * must create an information control expecting information in the same format the configured
+ * {@link org.eclipse.jface.text.information.IInformationProvider}s use to encode the information they provide.
+ * </p>
+ *
+ * @since 3.4
+ */
+public class StickyHoverManager : InformationControlReplacer , IWidgetTokenKeeper, IWidgetTokenKeeperExtension {
+
+    /**
+     * Priority of the info controls managed by this sticky hover manager.
+     * <p>
+     * Note: Only applicable when info control does not have focus.
+     * -5 as value has been chosen in order to be beaten by the hovers of TextViewerHoverManager.
+     * </p>
+     */
+    private static const int WIDGET_PRIORITY= -5;
+
+
+    /**
+     * Internal information control closer. Listens to several events issued by its subject control
+     * and closes the information control when necessary.
+     */
+    class Closer : IInformationControlCloser, ControlListener, MouseListener, IViewportListener, KeyListener, FocusListener, Listener {
+        //TODO: Catch 'Esc' key in fInformationControlToClose: Don't dispose, just hideInformationControl().
+        // This would allow to reuse the information control also when the user explicitly closes it.
+
+        //TODO: if subject control is a Scrollable, should add selection listeners to both scroll bars
+        // (and remove the ViewPortListener, which only listens to vertical scrolling)
+
+        /** The subject control. */
+        private Control fSubjectControl;
+        /** Indicates whether this closer is active. */
+        private bool fIsActive= false;
+        /** The display. */
+        private Display fDisplay;
+
+        /*
+         * @see IInformationControlCloser#setSubjectControl(Control)
+         */
+        public void setSubjectControl(Control control) {
+            fSubjectControl= control;
+        }
+
+        /*
+         * @see IInformationControlCloser#setInformationControl(IInformationControl)
+         */
+        public void setInformationControl(IInformationControl control) {
+            // NOTE: we use getCurrentInformationControl2() from the outer class
+        }
+
+        /*
+         * @see IInformationControlCloser#start(Rectangle)
+         */
+        public void start(Rectangle informationArea) {
+
+            if (fIsActive)
+                return;
+            fIsActive= true;
+
+            if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
+                fSubjectControl.addControlListener(this);
+                fSubjectControl.addMouseListener(this);
+                fSubjectControl.addKeyListener(this);
+            }
+
+            fTextViewer.addViewportListener(this);
+
+            IInformationControl fInformationControlToClose= getCurrentInformationControl2();
+            if (fInformationControlToClose !is null)
+                fInformationControlToClose.addFocusListener(this);
+
+            fDisplay= fSubjectControl.getDisplay();
+            if (!fDisplay.isDisposed()) {
+                fDisplay.addFilter(SWT.MouseMove, this);
+                fDisplay.addFilter(SWT.FocusOut, this);
+            }
+        }
+
+        /*
+         * @see IInformationControlCloser#stop()
+         */
+        public void stop() {
+
+            if (!fIsActive)
+                return;
+            fIsActive= false;
+
+            fTextViewer.removeViewportListener(this);
+
+            if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
+                fSubjectControl.removeControlListener(this);
+                fSubjectControl.removeMouseListener(this);
+                fSubjectControl.removeKeyListener(this);
+            }
+
+            IInformationControl fInformationControlToClose= getCurrentInformationControl2();
+            if (fInformationControlToClose !is null)
+                fInformationControlToClose.removeFocusListener(this);
+
+            if (fDisplay !is null && !fDisplay.isDisposed()) {
+                fDisplay.removeFilter(SWT.MouseMove, this);
+                fDisplay.removeFilter(SWT.FocusOut, this);
+            }
+
+            fDisplay= null;
+        }
+
+        /*
+         * @see ControlListener#controlResized(ControlEvent)
+         */
+         public void controlResized(ControlEvent e) {
+             hideInformationControl();
+        }
+
+        /*
+         * @see ControlListener#controlMoved(ControlEvent)
+         */
+         public void controlMoved(ControlEvent e) {
+             hideInformationControl();
+        }
+
+        /*
+         * @see MouseListener#mouseDown(MouseEvent)
+         */
+         public void mouseDown(MouseEvent e) {
+             hideInformationControl();
+        }
+
+        /*
+         * @see MouseListener#mouseUp(MouseEvent)
+         */
+        public void mouseUp(MouseEvent e) {
+        }
+
+        /*
+         * @see MouseListener#mouseDoubleClick(MouseEvent)
+         */
+        public void mouseDoubleClick(MouseEvent e) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see IViewportListenerListener#viewportChanged(int)
+         */
+        public void viewportChanged(int topIndex) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see KeyListener#keyPressed(KeyEvent)
+         */
+        public void keyPressed(KeyEvent e) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see KeyListener#keyReleased(KeyEvent)
+         */
+        public void keyReleased(KeyEvent e) {
+        }
+
+        /*
+         * @see org.eclipse.swt.events.FocusListener#focusGained(org.eclipse.swt.events.FocusEvent)
+         */
+        public void focusGained(FocusEvent e) {
+        }
+
+        /*
+         * @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent)
+         */
+        public void focusLost(FocusEvent e) {
+            if (DEBUG) System.out_.println(Format("StickyHoverManager.Closer.focusLost(): {}", e)); //$NON-NLS-1$
+            Display d= fSubjectControl.getDisplay();
+            d.asyncExec(new class()  Runnable {
+                // Without the asyncExec, mouse clicks to the workbench window are swallowed.
+                public void run() {
+                    hideInformationControl();
+                }
+            });
+        }
+
+        /*
+         * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+         */
+        public void handleEvent(Event event) {
+            if (event.type is SWT.MouseMove) {
+                if (!( cast(Control)event.widget ) || event.widget.isDisposed())
+                    return;
+
+                IInformationControl infoControl= getCurrentInformationControl2();
+                if (infoControl !is null && !infoControl.isFocusControl() && cast(IInformationControlExtension3)infoControl ) {
+//                  if (DEBUG) System.out_.println("StickyHoverManager.Closer.handleEvent(): activeShell= " + fDisplay.getActiveShell()); //$NON-NLS-1$
+                    IInformationControlExtension3 iControl3= cast(IInformationControlExtension3) infoControl;
+                    Rectangle controlBounds= iControl3.getBounds();
+                    if (controlBounds !is null) {
+                        Point mouseLoc= event.display.map(cast(Control) event.widget, null, event.x, event.y);
+                        int margin= getKeepUpMargin();
+                        Geometry.expand(controlBounds, margin, margin, margin, margin);
+                        if (!controlBounds.contains(mouseLoc)) {
+                            hideInformationControl();
+                        }
+                    }
+
+                } else {
+                    /*
+                     * TODO: need better understanding of why/if this is needed.
+                     * Looks like the same panic code we have in org.eclipse.jface.text.AbstractHoverInformationControlManager.Closer.handleMouseMove(Event)
+                     */
+                    if (fDisplay !is null && !fDisplay.isDisposed())
+                        fDisplay.removeFilter(SWT.MouseMove, this);
+                }
+
+            } else if (event.type is SWT.FocusOut) {
+                if (DEBUG) System.out_.println(Format("StickyHoverManager.Closer.handleEvent(): focusOut: {}", event)); //$NON-NLS-1$
+                IInformationControl iControl= getCurrentInformationControl2();
+                if (iControl !is null && ! iControl.isFocusControl())
+                    hideInformationControl();
+            }
+        }
+    }
+
+
+    private const TextViewer fTextViewer;
+
+
+    /**
+     * Creates a new sticky hover manager.
+     *
+     * @param textViewer the text viewer
+     */
+    public this(TextViewer textViewer) {
+        super(new DefaultInformationControlCreator());
+
+        fTextViewer= textViewer;
+        setCloser(new Closer());
+
+        install(fTextViewer.getTextWidget());
+    }
+
+    /*
+     * @see AbstractInformationControlManager#showInformationControl(Rectangle)
+     */
+    protected void showInformationControl(Rectangle subjectArea) {
+        if (fTextViewer !is null && fTextViewer.requestWidgetToken(this, WIDGET_PRIORITY))
+            super.showInformationControl(subjectArea);
+        else
+            if (DEBUG)
+                System.out_.println("cancelled StickyHoverManager.showInformationControl(..): did not get widget token (with prio)"); //$NON-NLS-1$
+    }
+
+    /*
+     * @see AbstractInformationControlManager#hideInformationControl()
+     */
+    public void hideInformationControl() {
+        try {
+            super.hideInformationControl();
+        } finally {
+            if (fTextViewer !is null)
+                fTextViewer.releaseWidgetToken(this);
+        }
+    }
+
+    /*
+     * @see AbstractInformationControlManager#handleInformationControlDisposed()
+     */
+    protected void handleInformationControlDisposed() {
+        try {
+            super.handleInformationControlDisposed();
+        } finally {
+            if (fTextViewer !is null)
+                fTextViewer.releaseWidgetToken(this);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner)
+     */
+    public bool requestWidgetToken(IWidgetTokenOwner owner) {
+        hideInformationControl();
+        if (DEBUG)
+            System.out_.println("StickyHoverManager gave up widget token (no prio)"); //$NON-NLS-1$
+        return true;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(org.eclipse.jface.text.IWidgetTokenOwner, int)
+     */
+    public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) {
+        if (getCurrentInformationControl2() !is null) {
+            if (getCurrentInformationControl2().isFocusControl()) {
+                if (DEBUG)
+                    System.out_.println("StickyHoverManager kept widget token (focused)"); //$NON-NLS-1$
+                return false;
+            } else if (priority > WIDGET_PRIORITY) {
+                hideInformationControl();
+                if (DEBUG)
+                    System.out_.println("StickyHoverManager gave up widget token (prio)"); //$NON-NLS-1$
+                return true;
+            } else {
+                if (DEBUG)
+                    System.out_.println("StickyHoverManager kept widget token (prio)"); //$NON-NLS-1$
+                return false;
+            }
+        }
+        if (DEBUG)
+            System.out_.println("StickyHoverManager gave up widget token (no iControl)"); //$NON-NLS-1$
+        return true;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#setFocus(org.eclipse.jface.text.IWidgetTokenOwner)
+     */
+    public bool setFocus(IWidgetTokenOwner owner) {
+        IInformationControl iControl= getCurrentInformationControl2();
+        if ( cast(IInformationControlExtension5)iControl ) {
+            IInformationControlExtension5 iControl5= cast(IInformationControlExtension5) iControl;
+            if (iControl5.isVisible()) {
+                iControl.setFocus();
+                return iControl.isFocusControl();
+            }
+            return false;
+        }
+        iControl.setFocus();
+        return iControl.isFocusControl();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/TableOwnerDrawSupport.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.text.TableOwnerDrawSupport;
+
+import org.eclipse.jface.internal.text.NonDeletingPositionUpdater; // packageimport
+import org.eclipse.jface.internal.text.InternalAccessor; // packageimport
+import org.eclipse.jface.internal.text.StickyHoverManager; // packageimport
+import org.eclipse.jface.internal.text.InformationControlReplacer; // packageimport
+import org.eclipse.jface.internal.text.DelayedInputChangeListener; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.TextLayout;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+
+/**
+ * Adds owner draw support for tables.
+ *
+ * @since 3.4
+ */
+public class TableOwnerDrawSupport : Listener {
+
+    private static const String STYLED_RANGES_KEY= "styled_ranges"; //$NON-NLS-1$
+
+    private TextLayout fLayout;
+
+    public static void install(Table table) {
+        TableOwnerDrawSupport listener= new TableOwnerDrawSupport(table);
+        table.addListener(SWT.Dispose, listener);
+        table.addListener(SWT.MeasureItem, listener);
+        table.addListener(SWT.EraseItem, listener);
+        table.addListener(SWT.PaintItem, listener);
+    }
+
+    /**
+     * Stores the styled ranges in the given table item.
+     *
+     * @param item table item
+     * @param column the column index
+     * @param ranges the styled ranges or <code>null</code> to remove them
+     */
+    public static void storeStyleRanges(TableItem item, int column, StyleRange[] ranges) {
+        item.setData(STYLED_RANGES_KEY ~ Integer.toString(column), new ArrayWrapperObject(arraycast!(Object)(ranges)));
+    }
+
+    /**
+     * Returns the styled ranges which are stored in the given table item.
+     *
+     * @param item table item
+     * @param column the column index
+     * @return the styled ranges
+     */
+    private static StyleRange[] getStyledRanges(TableItem item, int column) {
+        return arrayFromObject!(StyleRange)(item.getData(STYLED_RANGES_KEY ~ Integer.toString(column)));
+    }
+
+    private this(Table table) {
+        int orientation= table.getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT);
+        fLayout= new TextLayout(table.getDisplay());
+        fLayout.setOrientation(orientation);
+    }
+
+    /*
+     * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+     */
+    public void handleEvent(Event event) {
+        switch (event.type) {
+            case SWT.MeasureItem:
+                break;
+            case SWT.EraseItem:
+                event.detail &= ~SWT.FOREGROUND;
+                break;
+            case SWT.PaintItem:
+                performPaint(event);
+                break;
+            case SWT.Dispose:
+                widgetDisposed();
+                break;
+            default:
+            }
+    }
+
+    /**
+     * Performs the paint operation.
+     *
+     * @param event the event
+     */
+    private void performPaint(Event event) {
+        TableItem item= cast(TableItem) event.item;
+        GC gc= event.gc;
+        int index= event.index;
+
+        bool isSelected= (event.detail & SWT.SELECTED) !is 0;
+
+        // Remember colors to restore the GC later
+        Color oldForeground= gc.getForeground();
+        Color oldBackground= gc.getBackground();
+
+        if (!isSelected) {
+            Color foreground= item.getForeground(index);
+            gc.setForeground(foreground);
+
+            Color background= item.getBackground(index);
+            gc.setBackground(background);
+        }
+
+        Image image=item.getImage(index);
+        if (image !is null) {
+            Rectangle imageBounds=item.getImageBounds(index);
+            Rectangle bounds=image.getBounds();
+            int x=imageBounds.x + Math.max(0, (imageBounds.width - bounds.width) / 2);
+            int y=imageBounds.y + Math.max(0, (imageBounds.height - bounds.height) / 2);
+            gc.drawImage(image, x, y);
+        }
+
+        fLayout.setFont(item.getFont(index));
+
+        // XXX: needed to clear the style info, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=226090
+        fLayout.setText(""); //$NON-NLS-1$
+
+        fLayout.setText(item.getText(index));
+
+        StyleRange[] ranges= getStyledRanges(item, index);
+        if (ranges !is null) {
+            for (int i= 0; i < ranges.length; i++) {
+                StyleRange curr= ranges[i];
+                if (isSelected) {
+                    curr= cast(StyleRange) curr.clone();
+                    curr.foreground= null;
+                    curr.background= null;
+                }
+                fLayout.setStyle(curr, curr.start, curr.start + curr.length - 1);
+            }
+        }
+
+        Rectangle textBounds=item.getTextBounds(index);
+        if (textBounds !is null) {
+            Rectangle layoutBounds=fLayout.getBounds();
+            int x=textBounds.x;
+            int y=textBounds.y + Math.max(0, (textBounds.height - layoutBounds.height) / 2);
+            fLayout.draw(gc, x, y);
+        }
+
+        if ((event.detail & SWT.FOCUSED) !is 0) {
+            Rectangle focusBounds=item.getBounds();
+            gc.drawFocus(focusBounds.x, focusBounds.y, focusBounds.width, focusBounds.height);
+        }
+
+        if (!isSelected) {
+            gc.setForeground(oldForeground);
+            gc.setBackground(oldBackground);
+        }
+    }
+
+    private void widgetDisposed() {
+        fLayout.dispose();
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/BrowserInformationControl.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,678 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.internal.text.html.BrowserInformationControl;
+
+import org.eclipse.jface.internal.text.html.HTML2TextReader; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLPrinter; // packageimport
+import org.eclipse.jface.internal.text.html.SubstitutionTextReader; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLTextPresenter; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInput; // packageimport
+import org.eclipse.jface.internal.text.html.SingleCharReader; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControlInput; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLMessages; // packageimport
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.jface.text.AbstractInformationControl;
+import org.eclipse.jface.text.IInformationControlExtension2;
+import org.eclipse.jface.text.IDelayedInputChangeProvider;
+import org.eclipse.jface.text.IInputChangedListener;
+
+//pragma( msg, "org.eclipse.jface.internal.text.html.BrowserInformationControl: SWT browser control missing" );
+class BrowserInformationControl : AbstractInformationControl , IInformationControlExtension2, IDelayedInputChangeProvider {
+    public this(Shell parent, String symbolicFontName, bool resizable){
+        super(parent, resizable);
+        implMissing(__FILE__,__LINE__);
+    }
+    public static bool isAvailable(Composite parent){
+        implMissing(__FILE__,__LINE__);
+        return false;
+    }
+    public void setInformation(String content) {
+        implMissing(__FILE__,__LINE__);
+    }
+    protected void createContent(Composite parent) {
+        implMissing(__FILE__,__LINE__);
+    }
+    public void setInput(Object input) {
+        implMissing(__FILE__,__LINE__);
+    }
+    public void setDelayedInputChangeListener(IInputChangedListener inputChangeListener) {
+        implMissing(__FILE__,__LINE__);
+    }
+    public bool hasContents() {
+        implMissing(__FILE__,__LINE__);
+        return false;
+    }
+}
+
+// FIXME needs Browser :/
+/++
+
+import org.eclipse.dwtxhelper.StringReader;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.browser.LocationListener;
+import org.eclipse.swt.browser.ProgressAdapter;
+import org.eclipse.swt.browser.ProgressEvent;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.TextLayout;
+import org.eclipse.swt.graphics.TextStyle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Slider;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.text.AbstractInformationControl;
+import org.eclipse.jface.text.IDelayedInputChangeProvider;
+import org.eclipse.jface.text.IInformationControlExtension2;
+import org.eclipse.jface.text.IInputChangedListener;
+import org.eclipse.jface.text.TextPresentation;
+
+
+/**
+ * Displays HTML information in a {@link org.eclipse.swt.browser.Browser} widget.
+ * <p>
+ * This {@link IInformationControlExtension2} expects {@link #setInput(Object)} to be
+ * called with an argument of type {@link BrowserInformationControlInput}.
+ * </p>
+ * <p>
+ * Moved into this package from <code>org.eclipse.jface.internal.text.revisions</code>.</p>
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.</p>
+ * <p>
+ * Current problems:
+ * <ul>
+ *  <li>the size computation is too small</li>
+ *  <li>focusLost event is not sent - see https://bugs.eclipse.org/bugs/show_bug.cgi?id=84532</li>
+ * </ul>
+ * </p>
+ *
+ * @since 3.2
+ */
+public class BrowserInformationControl : AbstractInformationControl , IInformationControlExtension2, IDelayedInputChangeProvider {
+
+
+    /**
+     * Tells whether the SWT Browser widget and hence this information
+     * control is available.
+     *
+     * @param parent the parent component used for checking or <code>null</code> if none
+     * @return <code>true</code> if this control is available
+     */
+    public static bool isAvailable(Composite parent) {
+        if (!fgAvailabilityChecked) {
+            try {
+                Browser browser= new Browser(parent, SWT.NONE);
+                browser.dispose();
+                fgIsAvailable= true;
+
+                Slider sliderV= new Slider(parent, SWT.VERTICAL);
+                Slider sliderH= new Slider(parent, SWT.HORIZONTAL);
+                int width= sliderV.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
+                int height= sliderH.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
+                fgScrollBarSize= new Point(width, height);
+                sliderV.dispose();
+                sliderH.dispose();
+            } catch (SWTError er) {
+                fgIsAvailable= false;
+            } finally {
+                fgAvailabilityChecked= true;
+            }
+        }
+
+        return fgIsAvailable;
+    }
+
+
+    /**
+     * Minimal size constraints.
+     * @since 3.2
+     */
+    private static const int MIN_WIDTH= 80;
+    private static const int MIN_HEIGHT= 50;
+
+
+    /**
+     * Availability checking cache.
+     */
+    private static bool fgIsAvailable= false;
+    private static bool fgAvailabilityChecked= false;
+
+    /**
+     * Cached scroll bar width and height
+     * @since 3.4
+     */
+    private static Point fgScrollBarSize;
+
+    /** The control's browser widget */
+    private Browser fBrowser;
+    /** Tells whether the browser has content */
+    private bool fBrowserHasContent;
+    /** Text layout used to approximate size of content when rendered in browser */
+    private TextLayout fTextLayout;
+    /** Bold text style */
+    private TextStyle fBoldStyle;
+
+    private BrowserInformationControlInput fInput;
+
+    /**
+     * <code>true</code> iff the browser has completed loading of the last
+     * input set via {@link #setInformation(String)}.
+     * @since 3.4
+     */
+    private bool fCompleted= false;
+
+    /**
+     * The listener to be notified when a delayed location changing event happened.
+     * @since 3.4
+     */
+    private IInputChangedListener fDelayedInputChangeListener;
+
+    /**
+     * The listeners to be notified when the input changed.
+     * @since 3.4
+     */
+    private ListenerList/*<IInputChangedListener>*/ fInputChangeListeners= new ListenerList(ListenerList.IDENTITY);
+
+    /**
+     * The symbolic name of the font used for size computations, or <code>null</code> to use dialog font.
+     * @since 3.4
+     */
+    private const String fSymbolicFontName;
+
+
+    /**
+     * Creates a browser information control with the given shell as parent.
+     *
+     * @param parent the parent shell
+     * @param symbolicFontName the symbolic name of the font used for size computations
+     * @param resizable <code>true</code> if the control should be resizable
+     * @since 3.4
+     */
+    public this(Shell parent, String symbolicFontName, bool resizable) {
+        super(parent, resizable);
+        fSymbolicFontName= symbolicFontName;
+        create();
+    }
+
+    /**
+     * Creates a browser information control with the given shell as parent.
+     *
+     * @param parent the parent shell
+     * @param symbolicFontName the symbolic name of the font used for size computations
+     * @param statusFieldText the text to be used in the optional status field
+     *            or <code>null</code> if the status field should be hidden
+     * @since 3.4
+     */
+    public this(Shell parent, String symbolicFontName, String statusFieldText) {
+        super(parent, statusFieldText);
+        fSymbolicFontName= symbolicFontName;
+        create();
+    }
+
+    /**
+     * Creates a browser information control with the given shell as parent.
+     *
+     * @param parent the parent shell
+     * @param symbolicFontName the symbolic name of the font used for size computations
+     * @param toolBarManager the manager or <code>null</code> if toolbar is not desired
+     * @since 3.4
+     */
+    public this(Shell parent, String symbolicFontName, ToolBarManager toolBarManager) {
+        super(parent, toolBarManager);
+        fSymbolicFontName= symbolicFontName;
+        create();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControl#createContent(org.eclipse.swt.widgets.Composite)
+     */
+    protected void createContent(Composite parent) {
+        fBrowser= new Browser(parent, SWT.NONE);
+
+        Display display= getShell().getDisplay();
+        fBrowser.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+        fBrowser.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+        fBrowser.addKeyListener(new class()  KeyListener {
+
+            public void keyPressed(KeyEvent e)  {
+                if (e.character is 0x1B) // ESC
+                    getShell().dispose(); // XXX: Just hide? Would avoid constant recreations.
+            }
+
+            public void keyReleased(KeyEvent e) {}
+        });
+
+        fBrowser.addProgressListener(new class()  ProgressAdapter {
+            public void completed(ProgressEvent event) {
+                fCompleted= true;
+            }
+        });
+
+        // Replace browser's built-in context menu with none
+        fBrowser.setMenu(new Menu(getShell(), SWT.NONE));
+
+        createTextLayout();
+    }
+
+    /**
+     * {@inheritDoc}
+     * @deprecated use {@link #setInput(Object)}
+     */
+    public void setInformation(String content) {
+        setInput(new class(null,content)  BrowserInformationControlInput {
+            String content_;
+            this(BrowserInformationControlInput input, String a){
+                super(input);
+                content_=a;
+            }
+            public String getHtml() {
+                return content_;
+            }
+
+            public String getInputName() {
+                return ""; //$NON-NLS-1$
+            }
+
+            public Object getInputElement() {
+                return stringcast(content_);
+            }
+        });
+    }
+
+    /**
+     * {@inheritDoc} This control can handle {@link String} and
+     * {@link BrowserInformationControlInput}.
+     */
+    public void setInput(Object input) {
+        Assert.isLegal(input is null || cast(String)input  || cast(BrowserInformationControlInput)input );
+
+        if ( cast(String)input ) {
+            setInformation(cast(String)input);
+            return;
+        }
+
+        fInput= cast(BrowserInformationControlInput)input;
+
+        String content= null;
+        if (fInput !is null)
+            content= fInput.getHtml();
+
+        fBrowserHasContent= content !is null && content.length() > 0;
+
+        if (!fBrowserHasContent)
+            content= "<html><body ></html>"; //$NON-NLS-1$
+
+        bool RTL= (getShell().getStyle() & SWT.RIGHT_TO_LEFT) !is 0;
+        bool resizable= isResizable();
+
+        // The default "overflow:auto" would not result in a predictable width for the client area
+        // and the re-wrapping would cause visual noise
+        String[] styles= null;
+        if (RTL && resizable)
+            styles= [ "direction:rtl;", "overflow:scroll;", "word-wrap:break-word;" ]; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        else if (RTL && !resizable)
+            styles= [ "direction:rtl;", "overflow:hidden;", "word-wrap:break-word;" ]; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        else if (!resizable)
+            //XXX: In IE, "word-wrap: break-word;" causes bogus wrapping even in non-broken words :-(see e.g. Javadoc of String).
+            // Re-check whether we really still need this now that the Javadoc Hover header already sets this style.
+            styles= [ "overflow:hidden;"/*, "word-wrap: break-word;"*/ ]; //$NON-NLS-1$
+        else
+            styles= [ "overflow:scroll;" ]; //$NON-NLS-1$
+
+        StringBuffer buffer= new StringBuffer(content);
+        HTMLPrinter.insertStyles(buffer, styles);
+        content= buffer.toString();
+
+        /*
+         * XXX: Should add some JavaScript here that shows something like
+         * "(continued...)" or "..." at the end of the visible area when the page overflowed
+         * with "overflow:hidden;".
+         */
+
+        fCompleted= false;
+        fBrowser.setText(content);
+
+        Object[] listeners= fInputChangeListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++)
+            (cast(IInputChangedListener)listeners[i]).inputChanged(fInput);
+    }
+
+    /*
+     * @see IInformationControl#setVisible(bool)
+     */
+    public void setVisible(bool visible) {
+        Shell shell= getShell();
+        if (shell.isVisible() is visible)
+            return;
+
+        if (!visible) {
+            super.setVisible(false);
+            setInput(null);
+            return;
+        }
+
+        /*
+         * The Browser widget flickers when made visible while it is not completely loaded.
+         * The fix is to delay the call to setVisible until either loading is completed
+         * (see ProgressListener in constructor), or a timeout has been reached.
+         */
+        final Display display= shell.getDisplay();
+
+        // Make sure the display wakes from sleep after timeout:
+        display.timerExec(100, new class()  Runnable {
+            public void run() {
+                fCompleted= true;
+            }
+        });
+
+        while (!fCompleted) {
+            // Drive the event loop to process the events required to load the browser widget's contents:
+            if (!display.readAndDispatch()) {
+                display.sleep();
+            }
+        }
+
+        shell= getShell();
+        if (shell is null || shell.isDisposed())
+            return;
+
+        /*
+         * Avoids flickering when replacing hovers, especially on Vista in ON_CLICK mode.
+         * Causes flickering on GTK. Carbon does not care.
+         */
+        if ("win32".equals(SWT.getPlatform())) //$NON-NLS-1$
+            shell.moveAbove(null);
+
+        super.setVisible(true);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControl#setSize(int, int)
+     */
+    public void setSize(int width, int height) {
+        fBrowser.setRedraw(false); // avoid flickering
+        try {
+            super.setSize(width, height);
+        } finally {
+            fBrowser.setRedraw(true);
+        }
+    }
+
+    /**
+     * Creates and initializes the text layout used
+     * to compute the size hint.
+     *
+     * @since 3.2
+     */
+    private void createTextLayout() {
+        fTextLayout= new TextLayout(fBrowser.getDisplay());
+
+        // Initialize fonts
+        Font font= fSymbolicFontName is null ? JFaceResources.getDialogFont() : JFaceResources.getFont(fSymbolicFontName);
+        fTextLayout.setFont(font);
+        fTextLayout.setWidth(-1);
+        FontData[] fontData= font.getFontData();
+        for (int i= 0; i < fontData.length; i++)
+            fontData[i].setStyle(SWT.BOLD);
+        font= new Font(getShell().getDisplay(), fontData);
+        fBoldStyle= new TextStyle(font, null, null);
+
+        // Compute and set tab width
+        fTextLayout.setText("    "); //$NON-NLS-1$
+        int tabWidth = fTextLayout.getBounds().width;
+        fTextLayout.setTabs([tabWidth]);
+
+        fTextLayout.setText(""); //$NON-NLS-1$
+    }
+
+    /*
+     * @see IInformationControl#dispose()
+     */
+    public void dispose() {
+        if (fTextLayout !is null) {
+            fTextLayout.dispose();
+            fTextLayout= null;
+        }
+        if (fBoldStyle !is null) {
+            fBoldStyle.font.dispose();
+            fBoldStyle= null;
+        }
+        fBrowser= null;
+
+        super.dispose();
+    }
+
+    /*
+     * @see IInformationControl#computeSizeHint()
+     */
+    public Point computeSizeHint() {
+        Point sizeConstraints= getSizeConstraints();
+        Rectangle trim= computeTrim();
+        int height= trim.height;
+
+        //FIXME: The HTML2TextReader does not render <p> like a browser.
+        // Instead of inserting an empty line, it just adds a single line break.
+        // Furthermore, the indentation of <dl><dd> elements is too small (e.g with a long @see line)
+        TextPresentation presentation= new TextPresentation();
+        HTML2TextReader reader= new HTML2TextReader(new StringReader(fInput.getHtml()), presentation);
+        String text;
+        try {
+            text= reader.getString();
+        } catch (IOException e) {
+            text= ""; //$NON-NLS-1$
+        }
+
+        fTextLayout.setText(text);
+        fTextLayout.setWidth(sizeConstraints is null ? SWT.DEFAULT : sizeConstraints.x - trim.width);
+        Iterator iter= presentation.getAllStyleRangeIterator();
+        while (iter.hasNext()) {
+            StyleRange sr= cast(StyleRange)iter.next();
+            if (sr.fontStyle is SWT.BOLD)
+                fTextLayout.setStyle(fBoldStyle, sr.start, sr.start + sr.length - 1);
+        }
+
+        Rectangle bounds= fTextLayout.getBounds(); // does not return minimum width, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=217446
+        int lineCount= fTextLayout.getLineCount();
+        int textWidth= 0;
+        for (int i= 0; i < lineCount; i++) {
+            Rectangle rect= fTextLayout.getLineBounds(i);
+            int lineWidth= rect.x + rect.width;
+            if (i is 0)
+                lineWidth += fInput.getLeadingImageWidth();
+            textWidth= Math.max(textWidth, lineWidth);
+        }
+        bounds.width= textWidth;
+        fTextLayout.setText(""); //$NON-NLS-1$
+
+        int minWidth= bounds.width;
+        height= height + bounds.height;
+
+        // Add some air to accommodate for different browser renderings
+        minWidth+= 15;
+        height+= 15;
+
+
+        // Apply max size constraints
+        if (sizeConstraints !is null) {
+            if (sizeConstraints.x !is SWT.DEFAULT)
+                minWidth= Math.min(sizeConstraints.x, minWidth + trim.width);
+            if (sizeConstraints.y !is SWT.DEFAULT)
+                height= Math.min(sizeConstraints.y, height);
+        }
+
+        // Ensure minimal size
+        int width= Math.max(MIN_WIDTH, minWidth);
+        height= Math.max(MIN_HEIGHT, height);
+
+        return new Point(width, height);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlExtension3#computeTrim()
+     */
+    public Rectangle computeTrim() {
+        Rectangle trim= super.computeTrim();
+        if (isResizable()) {
+            bool RTL= (getShell().getStyle() & SWT.RIGHT_TO_LEFT) !is 0;
+            if (RTL) {
+                trim.x-= fgScrollBarSize.x;
+            }
+            trim.width+= fgScrollBarSize.x;
+            trim.height+= fgScrollBarSize.y;
+        }
+        return trim;
+    }
+
+    /**
+     * Adds the listener to the collection of listeners who will be
+     * notified when the current location has changed or is about to change.
+     *
+     * @param listener the location listener
+     * @since 3.4
+     */
+    public void addLocationListener(LocationListener listener) {
+        fBrowser.addLocationListener(listener);
+    }
+
+    /*
+     * @see IInformationControl#setForegroundColor(Color)
+     */
+    public void setForegroundColor(Color foreground) {
+        super.setForegroundColor(foreground);
+        fBrowser.setForeground(foreground);
+    }
+
+    /*
+     * @see IInformationControl#setBackgroundColor(Color)
+     */
+    public void setBackgroundColor(Color background) {
+        super.setBackgroundColor(background);
+        fBrowser.setBackground(background);
+    }
+
+    /*
+     * @see IInformationControlExtension#hasContents()
+     */
+    public bool hasContents() {
+        return fBrowserHasContent;
+    }
+
+    /**
+     * Adds a listener for input changes to this input change provider.
+     * Has no effect if an identical listener is already registered.
+     *
+     * @param inputChangeListener the listener to add
+     * @since 3.4
+     */
+    public void addInputChangeListener(IInputChangedListener inputChangeListener) {
+        Assert.isNotNull(inputChangeListener);
+        fInputChangeListeners.add(inputChangeListener);
+    }
+
+    /**
+     * Removes the given input change listener from this input change provider.
+     * Has no effect if an identical listener is not registered.
+     *
+     * @param inputChangeListener the listener to remove
+     * @since 3.4
+     */
+    public void removeInputChangeListener(IInputChangedListener inputChangeListener) {
+        fInputChangeListeners.remove(inputChangeListener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDelayedInputChangeProvider#setDelayedInputChangeListener(org.eclipse.jface.text.IInputChangedListener)
+     * @since 3.4
+     */
+    public void setDelayedInputChangeListener(IInputChangedListener inputChangeListener) {
+        fDelayedInputChangeListener= inputChangeListener;
+    }
+
+    /**
+     * Tells whether a delayed input change listener is registered.
+     *
+     * @return <code>true</code> iff a delayed input change
+     *         listener is currently registered
+     * @since 3.4
+     */
+    public bool hasDelayedInputChangeListener() {
+        return fDelayedInputChangeListener !is null;
+    }
+
+    /**
+     * Notifies listeners of a delayed input change.
+     *
+     * @param newInput the new input, or <code>null</code> to request cancellation
+     * @since 3.4
+     */
+    public void notifyDelayedInputChange(Object newInput) {
+        if (fDelayedInputChangeListener !is null)
+            fDelayedInputChangeListener.inputChanged(newInput);
+    }
+
+    /*
+     * @see java.lang.Object#toString()
+     * @since 3.4
+     */
+    public override String toString() {
+        String style= (getShell().getStyle() & SWT.RESIZE) is 0 ? "fixed" : "resizeable"; //$NON-NLS-1$ //$NON-NLS-2$
+        return super.toString() + " -  style: " + style; //$NON-NLS-1$
+    }
+
+    /**
+     * @return the current browser input or <code>null</code>
+     */
+    public BrowserInformationControlInput getInput() {
+        return fInput;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlExtension5#computeSizeConstraints(int, int)
+     */
+    public Point computeSizeConstraints(int widthInChars, int heightInChars) {
+        if (fSymbolicFontName is null)
+            return null;
+
+        GC gc= new GC(fBrowser);
+        Font font= fSymbolicFontName is null ? JFaceResources.getDialogFont() : JFaceResources.getFont(fSymbolicFontName);
+        gc.setFont(font);
+        int width= gc.getFontMetrics().getAverageCharWidth();
+        int height= gc.getFontMetrics().getHeight();
+        gc.dispose();
+
+        return new Point(widthInChars * width, heightInChars * height);
+    }
+
+}
+
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/BrowserInformationControlInput.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.text.html.BrowserInformationControlInput;
+
+import org.eclipse.jface.internal.text.html.HTML2TextReader; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLPrinter; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControl; // packageimport
+import org.eclipse.jface.internal.text.html.SubstitutionTextReader; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLTextPresenter; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInput; // packageimport
+import org.eclipse.jface.internal.text.html.SingleCharReader; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLMessages; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.jface.text.DefaultInformationControl;
+
+
+/**
+ * Provides input for a {@link BrowserInformationControl}.
+ *
+ * @since 3.4
+ */
+public abstract class BrowserInformationControlInput : BrowserInput {
+    
+    /**
+     * Returns the leading image width.
+     * 
+     * @return the size of the leading image, by default <code>0</code> is returned
+     * @since 3.4
+     */
+    public int getLeadingImageWidth() {
+        return 0;
+    }
+
+    /**
+     * Creates the next browser input with the given input as previous one.
+     * 
+     * @param previous the previous input or <code>null</code> if none
+     */
+    public this(BrowserInformationControlInput previous) {
+        super(previous);
+    }
+
+    /**
+     * @return the HTML contents
+     */
+    public abstract String getHtml();
+    
+    /**
+     * Returns the HTML from {@link #getHtml()}.
+     * This is a fallback mode for platforms where the {@link BrowserInformationControl}
+     * is not available and this input is passed to a {@link DefaultInformationControl}.
+     * 
+     * @return {@link #getHtml()}
+     */
+    public override String toString() {
+        return getHtml();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/BrowserInput.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.text.html.BrowserInput;
+
+import org.eclipse.jface.internal.text.html.HTML2TextReader; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLPrinter; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControl; // packageimport
+import org.eclipse.jface.internal.text.html.SubstitutionTextReader; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLTextPresenter; // packageimport
+import org.eclipse.jface.internal.text.html.SingleCharReader; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControlInput; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * A browser input contains an input element and
+ * a previous and a next input, if available.
+ * 
+ * The browser input also provides a human readable
+ * name of its input element.
+ * 
+ * @since 3.4
+ */
+public abstract class BrowserInput {
+
+    private const BrowserInput fPrevious;
+    private BrowserInput fNext;
+
+    /**
+     * Create a new Browser input.
+     * 
+     * @param previous the input previous to this or <code>null</code> if this is the first
+     */
+    public this(BrowserInput previous) {
+        fPrevious= previous;
+        if (previous !is null)
+            previous.fNext= this;
+    }
+
+    /**
+     * The previous input or <code>null</code> if this
+     * is the first.
+     * 
+     * @return the previous input or <code>null</code>
+     */
+    public BrowserInput getPrevious() {
+        return fPrevious;
+    }
+
+    /**
+     * The next input or <code>null</code> if this
+     * is the last.
+     * 
+     * @return the next input or <code>null</code>
+     */
+    public BrowserInput getNext() {
+        return fNext;
+    }
+
+    /**
+     * The element to use to set the browsers input.
+     * 
+     * @return the input element
+     */
+    public abstract Object getInputElement();
+
+    /**
+     * A human readable name for the input.
+     * 
+     * @return the input name
+     */
+    public abstract String getInputName();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTML2TextReader.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,360 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.internal.text.html.HTML2TextReader;
+
+import org.eclipse.jface.internal.text.html.HTMLPrinter; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControl; // packageimport
+import org.eclipse.jface.internal.text.html.SubstitutionTextReader; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLTextPresenter; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInput; // packageimport
+import org.eclipse.jface.internal.text.html.SingleCharReader; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControlInput; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLMessages; // packageimport
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+import org.eclipse.dwtxhelper.PushbackReader;
+static import tango.text.convert.Utf;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.jface.text.TextPresentation;
+
+
+/**
+ * Reads the text contents from a reader of HTML contents and translates
+ * the tags or cut them out.
+ * <p>
+ * Moved into this package from <code>org.eclipse.jface.internal.text.revisions</code>.</p>
+ */
+public class HTML2TextReader : SubstitutionTextReader {
+
+    private static const String EMPTY_STRING= ""; //$NON-NLS-1$
+    private static Map fgEntityLookup_;
+    private static Set fgTags_;
+    private static Map fgEntityLookup(){
+        if( fgEntityLookup_ is null ){
+            synchronized(HTML2TextReader.classinfo ){
+                if( fgEntityLookup_ is null ){
+                    fgEntityLookup_= new HashMap(7);
+                    fgEntityLookup_.put("lt", "<"); //$NON-NLS-1$ //$NON-NLS-2$
+                    fgEntityLookup_.put("gt", ">"); //$NON-NLS-1$ //$NON-NLS-2$
+                    fgEntityLookup_.put("nbsp", " "); //$NON-NLS-1$ //$NON-NLS-2$
+                    fgEntityLookup_.put("amp", "&"); //$NON-NLS-1$ //$NON-NLS-2$
+                    fgEntityLookup_.put("circ", "^"); //$NON-NLS-1$ //$NON-NLS-2$
+                    fgEntityLookup_.put("tilde", "~"); //$NON-NLS-2$ //$NON-NLS-1$
+                    fgEntityLookup_.put("quot", "\"");        //$NON-NLS-1$ //$NON-NLS-2$
+                }
+            }
+        }
+        return fgEntityLookup_;
+    }
+    private static Set fgTags(){
+        if( fgTags_ is null ){
+            synchronized(HTML2TextReader.classinfo ){
+                if( fgTags_ is null ){
+                    fgTags_= new HashSet();
+                    fgTags_.add("b"); //$NON-NLS-1$
+                    fgTags_.add("br"); //$NON-NLS-1$
+                    fgTags_.add("br/"); //$NON-NLS-1$
+                    fgTags_.add("div"); //$NON-NLS-1$
+                    fgTags_.add("h1"); //$NON-NLS-1$
+                    fgTags_.add("h2"); //$NON-NLS-1$
+                    fgTags_.add("h3"); //$NON-NLS-1$
+                    fgTags_.add("h4"); //$NON-NLS-1$
+                    fgTags_.add("h5"); //$NON-NLS-1$
+                    fgTags_.add("p"); //$NON-NLS-1$
+                    fgTags_.add("dl"); //$NON-NLS-1$
+                    fgTags_.add("dt"); //$NON-NLS-1$
+                    fgTags_.add("dd"); //$NON-NLS-1$
+                    fgTags_.add("li"); //$NON-NLS-1$
+                    fgTags_.add("ul"); //$NON-NLS-1$
+                    fgTags_.add("pre"); //$NON-NLS-1$
+                    fgTags_.add("head"); //$NON-NLS-1$
+                }
+            }
+        }
+        return fgTags_;
+    }
+
+    private int fCounter= 0;
+    private TextPresentation fTextPresentation;
+    private int fBold= 0;
+    private int fStartOffset= -1;
+    private bool fInParagraph= false;
+    private bool fIsPreformattedText= false;
+    private bool fIgnore= false;
+    private bool fHeaderDetected= false;
+
+    /**
+     * Transforms the HTML text from the reader to formatted text.
+     *
+     * @param reader the reader
+     * @param presentation If not <code>null</code>, formattings will be applied to
+     * the presentation.
+    */
+    public this(Reader reader, TextPresentation presentation) {
+        super(new PushbackReader(reader));
+        fTextPresentation= presentation;
+    }
+
+    public int read()  {
+        int c= super.read();
+        if (c !is -1)
+            ++ fCounter;
+        return c;
+    }
+
+    protected void startBold() {
+        if (fBold is 0)
+            fStartOffset= fCounter;
+        ++ fBold;
+    }
+
+    protected void startPreformattedText() {
+        fIsPreformattedText= true;
+        setSkipWhitespace(false);
+    }
+
+    protected void stopPreformattedText() {
+        fIsPreformattedText= false;
+        setSkipWhitespace(true);
+    }
+
+    protected void stopBold() {
+        -- fBold;
+        if (fBold is 0) {
+            if (fTextPresentation !is null) {
+                fTextPresentation.addStyleRange(new StyleRange(fStartOffset, fCounter - fStartOffset, null, null, SWT.BOLD));
+            }
+            fStartOffset= -1;
+        }
+    }
+
+    /*
+     * @see org.eclipse.jdt.internal.ui.text.SubstitutionTextReader#computeSubstitution(int)
+     */
+    protected String computeSubstitution(int c)  {
+
+        if (c is '<')
+            return  processHTMLTag();
+        else if (fIgnore)
+            return EMPTY_STRING;
+        else if (c is '&')
+            return processEntity();
+        else if (fIsPreformattedText)
+            return processPreformattedText(c);
+
+        return null;
+    }
+
+    private String html2Text(String html) {
+
+        if (html is null || html.length() is 0)
+            return EMPTY_STRING;
+
+        html= html.toLowerCase();
+
+        String tag= html;
+        if ('/' is tag.charAt(0))
+            tag= tag.substring(1);
+
+        if (!fgTags.contains(tag))
+            return EMPTY_STRING;
+
+
+        if ("pre".equals(html)) { //$NON-NLS-1$
+            startPreformattedText();
+            return EMPTY_STRING;
+        }
+
+        if ("/pre".equals(html)) { //$NON-NLS-1$
+            stopPreformattedText();
+            return EMPTY_STRING;
+        }
+
+        if (fIsPreformattedText)
+            return EMPTY_STRING;
+
+        if ("b".equals(html)) { //$NON-NLS-1$
+            startBold();
+            return EMPTY_STRING;
+        }
+
+        if ((html.length() > 1 && html.charAt(0) is 'h' && Character.isDigit(html.charAt(1))) || "dt".equals(html)) { //$NON-NLS-1$
+            startBold();
+            return EMPTY_STRING;
+        }
+
+        if ("dl".equals(html)) //$NON-NLS-1$
+            return LINE_DELIM;
+
+        if ("dd".equals(html)) //$NON-NLS-1$
+            return "\t"; //$NON-NLS-1$
+
+        if ("li".equals(html)) //$NON-NLS-1$
+            // FIXME: this hard-coded prefix does not work for RTL languages, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=91682
+            return LINE_DELIM ~ HTMLMessages.getString("HTML2TextReader.listItemPrefix"); //$NON-NLS-1$
+
+        if ("/b".equals(html)) { //$NON-NLS-1$
+            stopBold();
+            return EMPTY_STRING;
+        }
+
+        if ("p".equals(html))  { //$NON-NLS-1$
+            fInParagraph= true;
+            return LINE_DELIM;
+        }
+
+        if ("br".equals(html) || "br/".equals(html) || "div".equals(html)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            return LINE_DELIM;
+
+        if ("/p".equals(html))  { //$NON-NLS-1$
+            bool inParagraph= fInParagraph;
+            fInParagraph= false;
+            return inParagraph ? EMPTY_STRING : LINE_DELIM;
+        }
+
+        if ((html.startsWith("/h") && html.length() > 2 && Character.isDigit(html.charAt(2))) || "/dt".equals(html)) { //$NON-NLS-1$ //$NON-NLS-2$
+            stopBold();
+            return LINE_DELIM;
+        }
+
+        if ("/dd".equals(html)) //$NON-NLS-1$
+            return LINE_DELIM;
+
+        if ("head".equals(html) && !fHeaderDetected) { //$NON-NLS-1$
+            fHeaderDetected= true;
+            fIgnore= true;
+            return EMPTY_STRING;
+        }
+
+        if ("/head".equals(html) && fHeaderDetected && fIgnore) { //$NON-NLS-1$
+            fIgnore= false;
+            return EMPTY_STRING;
+        }
+
+        return EMPTY_STRING;
+    }
+
+    /*
+     * A '<' has been read. Process a html tag
+     */
+    private String processHTMLTag()  {
+
+        StringBuffer buf= new StringBuffer();
+        int ch;
+        do {
+
+            ch= nextDChar();
+
+            while (ch !is -1 && ch !is '>') {
+                buf.append(dcharToString(Character.toLowerCase(cast(dchar) ch)));
+                ch= nextDChar();
+                if (ch is '"'){
+                    buf.append(dcharToString(Character.toLowerCase(cast(dchar) ch)));
+                    ch= nextDChar();
+                    while (ch !is -1 && ch !is '"'){
+                        buf.append(dcharToString(Character.toLowerCase(cast(dchar) ch)));
+                        ch= nextDChar();
+                    }
+                }
+                if (ch is '<' && !isInComment(buf)) {
+                    unreadDChar(ch);
+                    return '<' ~ buf.toString();
+                }
+            }
+
+            if (ch is -1)
+                return null;
+
+            if (!isInComment(buf) || isCommentEnd(buf)) {
+                break;
+            }
+            // unfinished comment
+            buf.append(dcharToString(cast(dchar) ch));
+        } while (true);
+
+        return html2Text(buf.toString());
+    }
+
+    private static bool isInComment(StringBuffer buf) {
+        return buf.length() >= 3 && "!--".equals(buf.slice().substring(0, 3)); //$NON-NLS-1$
+    }
+
+    private static bool isCommentEnd(StringBuffer buf) {
+        int tagLen= buf.length();
+        return tagLen >= 5 && "--".equals(buf.slice().substring(tagLen - 2)); //$NON-NLS-1$
+    }
+
+    private String processPreformattedText(int c) {
+        if  (c is '\r' || c is '\n')
+            fCounter++;
+        return null;
+    }
+
+
+    private void unreadDChar(dchar ch)  {
+        char[4] buf;
+        dchar[1] ibuf;
+        ibuf[0] = ch;
+        foreach( char c; tango.text.convert.Utf.toString( ibuf[], buf[] )){
+            (cast(PushbackReader) getReader()).unread(c);
+        }
+    }
+
+    protected String entity2Text(String symbol) {
+        if (symbol.length() > 1 && symbol.charAt(0) is '#') {
+            int ch;
+            try {
+                if (symbol.charAt(1) is 'x') {
+                    ch= Integer.parseInt(symbol.substring(2), 16);
+                } else {
+                    ch= Integer.parseInt(symbol.substring(1), 10);
+                }
+                return dcharToString( cast(dchar)ch);
+            } catch (NumberFormatException e) {
+            }
+        } else {
+            String str= stringcast( fgEntityLookup.get(symbol));
+            if (str !is null) {
+                return str;
+            }
+        }
+        return "&" ~ symbol; // not found //$NON-NLS-1$
+    }
+
+    /*
+     * A '&' has been read. Process a entity
+     */
+    private String processEntity()  {
+        StringBuffer buf= new StringBuffer();
+        int ch= nextDChar();
+        while (Character.isLetterOrDigit(cast(dchar)ch) || ch is '#') {
+            buf.append(dcharToString(cast(dchar) ch));
+            ch= nextDChar();
+        }
+
+        if (ch is ';')
+            return entity2Text(buf.toString());
+
+        buf.select(0, 0);
+        buf.prepend("&");
+        if (ch !is -1)
+            buf.append(dcharToString(cast(dchar) ch));
+        return buf.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLMessages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.text.html.HTMLMessages;
+
+import org.eclipse.jface.internal.text.html.HTML2TextReader; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLPrinter; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControl; // packageimport
+import org.eclipse.jface.internal.text.html.SubstitutionTextReader; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLTextPresenter; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInput; // packageimport
+import org.eclipse.jface.internal.text.html.SingleCharReader; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControlInput; // packageimport
+
+
+import java.lang.all;
+
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+import java.text.MessageFormat;
+
+
+/**
+ * Helper class to get NLSed messages.
+ *
+ * @since 3.3
+ */
+class HTMLMessages {
+
+//     private static const String RESOURCE_BUNDLE= HTMLMessages.classinfo.getName();
+
+    private static ResourceBundle fgResourceBundle;//= ResourceBundle.getBundle(RESOURCE_BUNDLE);
+
+    static this() {
+        fgResourceBundle = ResourceBundle.getBundle(
+            getImportData!("org.eclipse.jface.internal.text.html.HTMLMessages.properties"));
+    }
+
+    private this() {
+    }
+
+    /**
+     * Gets a string from the resource bundle.
+     *
+     * @param key the string used to get the bundle value, must not be null
+     * @return the string from the resource bundle
+     */
+    public static String getString(String key) {
+        try {
+            return fgResourceBundle.getString(key);
+        } catch (MissingResourceException e) {
+            return "!" ~ key ~ "!";//$NON-NLS-2$ //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Gets a string from the resource bundle and formats it with the given arguments.
+     *
+     * @param key the string used to get the bundle value, must not be null
+     * @param args the arguments used to format the string
+     * @return the formatted string
+     */
+    public static String getFormattedString(String key, Object[] args...) {
+        String format= null;
+        try {
+            format= fgResourceBundle.getString(key);
+        } catch (MissingResourceException e) {
+            return "!" ~ key ~ "!";//$NON-NLS-2$ //$NON-NLS-1$
+        }
+        return MessageFormat.format(format, args);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLPrinter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,318 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.internal.text.html.HTMLPrinter;
+
+import org.eclipse.jface.internal.text.html.HTML2TextReader; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControl; // packageimport
+import org.eclipse.jface.internal.text.html.SubstitutionTextReader; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLTextPresenter; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInput; // packageimport
+import org.eclipse.jface.internal.text.html.SingleCharReader; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControlInput; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+import org.eclipse.dwtxhelper.URL;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+
+
+/**
+ * Provides a set of convenience methods for creating HTML pages.
+ * <p>
+ * Moved into this package from <code>org.eclipse.jface.internal.text.revisions</code>.</p>
+ */
+public class HTMLPrinter {
+
+    private static RGB BG_COLOR_RGB_;
+    private static RGB FG_COLOR_RGB_;
+
+    private static RGB BG_COLOR_RGB(){
+        COLOR_RGB_init();
+        return BG_COLOR_RGB_;
+    }
+    private static RGB FG_COLOR_RGB(){
+        COLOR_RGB_init();
+        return FG_COLOR_RGB_;
+    }
+
+    private static bool COLOR_RGB_init_complete = false;
+    private static void COLOR_RGB_init() {
+        if( COLOR_RGB_init_complete ){
+            return;
+        }
+        COLOR_RGB_init_complete = true;
+        BG_COLOR_RGB_= new RGB(255, 255, 225); // RGB value of info bg color on WindowsXP
+        FG_COLOR_RGB_= new RGB(0, 0, 0); // RGB value of info fg color on WindowsXP
+        Display display= Display.getDefault();
+        if (display !is null && !display.isDisposed()) {
+            try {
+                display.asyncExec( dgRunnable( (Display display_){
+                    BG_COLOR_RGB_= display_.getSystemColor(SWT.COLOR_INFO_BACKGROUND).getRGB();
+                    FG_COLOR_RGB_= display_.getSystemColor(SWT.COLOR_INFO_FOREGROUND).getRGB();
+                }, display ));
+            } catch (SWTError err) {
+                // see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=45294
+                if (err.code !is SWT.ERROR_DEVICE_DISPOSED)
+                    throw err;
+            }
+        }
+    }
+
+    private this() {
+    }
+
+    private static String replace(String text, char c, String s) {
+
+        int previous= 0;
+        int current= text.indexOf(c, previous);
+
+        if (current is -1)
+            return text;
+
+        StringBuffer buffer= new StringBuffer();
+        while (current > -1) {
+            buffer.append(text.substring(previous, current));
+            buffer.append(s);
+            previous= current + 1;
+            current= text.indexOf(c, previous);
+        }
+        buffer.append(text.substring(previous));
+
+        return buffer.toString();
+    }
+
+    public static String convertToHTMLContent(String content) {
+        content= replace(content, '&', "&amp;"); //$NON-NLS-1$
+        content= replace(content, '"', "&quot;"); //$NON-NLS-1$
+        content= replace(content, '<', "&lt;"); //$NON-NLS-1$
+        return replace(content, '>', "&gt;"); //$NON-NLS-1$
+    }
+
+    public static String read(Reader rd) {
+
+        StringBuffer buffer= new StringBuffer();
+        char[] readBuffer= new char[2048];
+
+        try {
+            int n= rd.read(readBuffer);
+            while (n > 0) {
+                buffer.append(readBuffer[ 0 .. n ]);
+                n= rd.read(readBuffer);
+            }
+            return buffer.toString();
+        } catch (IOException x) {
+        }
+
+        return null;
+    }
+
+    public static void insertPageProlog(StringBuffer buffer, int position, RGB fgRGB, RGB bgRGB, String styleSheet) {
+        if (fgRGB is null)
+            fgRGB= FG_COLOR_RGB;
+        if (bgRGB is null)
+            bgRGB= BG_COLOR_RGB;
+
+        StringBuffer pageProlog= new StringBuffer(300);
+
+        pageProlog.append("<html>"); //$NON-NLS-1$
+
+        appendStyleSheetURL(pageProlog, styleSheet);
+
+        appendColors(pageProlog, fgRGB, bgRGB);
+
+        buffer.select(position,0);
+        buffer.replace(pageProlog.toString());
+    }
+
+    private static void appendColors(StringBuffer pageProlog, RGB fgRGB, RGB bgRGB) {
+        pageProlog.append("<body text=\""); //$NON-NLS-1$
+        appendColor(pageProlog, fgRGB);
+        pageProlog.append("\" bgcolor=\""); //$NON-NLS-1$
+        appendColor(pageProlog, bgRGB);
+        pageProlog.append("\">"); //$NON-NLS-1$
+    }
+
+    private static void appendColor(StringBuffer buffer, RGB rgb) {
+        buffer.append('#');
+        appendAsHexString(buffer, rgb.red);
+        appendAsHexString(buffer, rgb.green);
+        appendAsHexString(buffer, rgb.blue);
+    }
+
+    private static void appendAsHexString(StringBuffer buffer, int intValue) {
+        String hexValue= Integer.toHexString(intValue);
+        if (hexValue.length() is 1)
+            buffer.append('0');
+        buffer.append(hexValue);
+    }
+
+    public static void insertStyles(StringBuffer buffer, String[] styles) {
+        if (styles is null || styles.length is 0)
+            return;
+
+        StringBuffer styleBuf= new StringBuffer(10 * styles.length);
+        for (int i= 0; styles !is null && i < styles.length; i++) {
+            styleBuf.append(" style=\""); //$NON-NLS-1$
+            styleBuf.append(styles[i]);
+            styleBuf.append('"');
+        }
+
+        // Find insertion index
+        // a) within existing body tag with trailing space
+        int index= buffer.slice().indexOf("<body "); //$NON-NLS-1$
+        if (index !is -1) {
+            buffer.select(index+5, 0);
+            buffer.replace(styleBuf);
+            return;
+        }
+
+        // b) within existing body tag without attributes
+        index= buffer.slice().indexOf("<body>"); //$NON-NLS-1$
+        if (index !is -1) {
+            buffer.select(index+5, 0);
+            buffer.replace( " " );
+            buffer.select(index+6, 0);
+            buffer.replace(styleBuf);
+            return;
+        }
+    }
+
+    private static void appendStyleSheetURL(StringBuffer buffer, String styleSheet) {
+        if (styleSheet is null)
+            return;
+
+        buffer.append("<head><style CHARSET=\"ISO-8859-1\" TYPE=\"text/css\">"); //$NON-NLS-1$
+        buffer.append(styleSheet);
+        buffer.append("</style></head>"); //$NON-NLS-1$
+    }
+
+    private static void appendStyleSheetURL(StringBuffer buffer, URL styleSheetURL) {
+        if (styleSheetURL is null)
+            return;
+
+        buffer.append("<head>"); //$NON-NLS-1$
+
+        buffer.append("<LINK REL=\"stylesheet\" HREF= \""); //$NON-NLS-1$
+        buffer.append(styleSheetURL.toString());
+        buffer.append("\" CHARSET=\"ISO-8859-1\" TYPE=\"text/css\">"); //$NON-NLS-1$
+
+        buffer.append("</head>"); //$NON-NLS-1$
+    }
+
+    public static void insertPageProlog(StringBuffer buffer, int position) {
+        StringBuffer pageProlog= new StringBuffer(60);
+        pageProlog.append("<html>"); //$NON-NLS-1$
+        appendColors(pageProlog, FG_COLOR_RGB, BG_COLOR_RGB);
+        buffer.select(position, 0);
+        buffer.replace(pageProlog.toString());
+    }
+
+    public static void insertPageProlog(StringBuffer buffer, int position, URL styleSheetURL) {
+        StringBuffer pageProlog= new StringBuffer(300);
+        pageProlog.append("<html>"); //$NON-NLS-1$
+        appendStyleSheetURL(pageProlog, styleSheetURL);
+        appendColors(pageProlog, FG_COLOR_RGB, BG_COLOR_RGB);
+        buffer.select(position, 0);
+        buffer.replace(pageProlog.toString());
+    }
+
+    public static void insertPageProlog(StringBuffer buffer, int position, String styleSheet) {
+        insertPageProlog(buffer, position, null, null, styleSheet);
+    }
+
+    public static void addPageProlog(StringBuffer buffer) {
+        insertPageProlog(buffer, buffer.length());
+    }
+
+    public static void addPageEpilog(StringBuffer buffer) {
+        buffer.append("</font></body></html>"); //$NON-NLS-1$
+    }
+
+    public static void startBulletList(StringBuffer buffer) {
+        buffer.append("<ul>"); //$NON-NLS-1$
+    }
+
+    public static void endBulletList(StringBuffer buffer) {
+        buffer.append("</ul>"); //$NON-NLS-1$
+    }
+
+    public static void addBullet(StringBuffer buffer, String bullet) {
+        if (bullet !is null) {
+            buffer.append("<li>"); //$NON-NLS-1$
+            buffer.append(bullet);
+            buffer.append("</li>"); //$NON-NLS-1$
+        }
+    }
+
+    public static void addSmallHeader(StringBuffer buffer, String header) {
+        if (header !is null) {
+            buffer.append("<h5>"); //$NON-NLS-1$
+            buffer.append(header);
+            buffer.append("</h5>"); //$NON-NLS-1$
+        }
+    }
+
+    public static void addParagraph(StringBuffer buffer, String paragraph) {
+        if (paragraph !is null) {
+            buffer.append("<p>"); //$NON-NLS-1$
+            buffer.append(paragraph);
+        }
+    }
+
+    public static void addParagraph(StringBuffer buffer, Reader paragraphReader) {
+        if (paragraphReader !is null)
+            addParagraph(buffer, read(paragraphReader));
+    }
+
+    /**
+     * Replaces the following style attributes of the font definition of the <code>html</code>
+     * element:
+     * <ul>
+     * <li>font-size</li>
+     * <li>font-weight</li>
+     * <li>font-style</li>
+     * <li>font-family</li>
+     * </ul>
+     * The font's name is used as font family, a <code>sans-serif</code> default font family is
+     * appended for the case that the given font name is not available.
+     * <p>
+     * If the listed font attributes are not contained in the passed style list, nothing happens.
+     * </p>
+     *
+     * @param styles CSS style definitions
+     * @param fontData the font information to use
+     * @return the modified style definitions
+     * @since 3.3
+     */
+    public static String convertTopLevelFont(String styles, FontData fontData) {
+        bool bold= (fontData.getStyle() & SWT.BOLD) !is 0;
+        bool italic= (fontData.getStyle() & SWT.ITALIC) !is 0;
+
+        // See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=155993
+        String size= Integer.toString(fontData.getHeight()) ~ ("carbon".equals(SWT.getPlatform()) ? "px" : "pt"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+        String family= "'" ~ fontData.getName() ~ "',sans-serif"; //$NON-NLS-1$ //$NON-NLS-2$
+        styles= styles.replaceFirst("(html\\s*\\{.*(?:\\s|;)font-size:\\s*)\\d+pt(\\;?.*\\})", "$1" ~ size ~ "$2"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        styles= styles.replaceFirst("(html\\s*\\{.*(?:\\s|;)font-weight:\\s*)\\w+(\\;?.*\\})", "$1" ~ (bold ? "bold" : "normal") ~ "$2"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+        styles= styles.replaceFirst("(html\\s*\\{.*(?:\\s|;)font-style:\\s*)\\w+(\\;?.*\\})", "$1" ~ (italic ? "italic" : "normal") ~ "$2"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+        styles= styles.replaceFirst("(html\\s*\\{.*(?:\\s|;)font-family:\\s*).+?(;.*\\})", "$1" ~ family ~ "$2"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        return styles;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLTextPresenter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,225 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.internal.text.html.HTMLTextPresenter;
+
+import org.eclipse.jface.internal.text.html.HTML2TextReader; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLPrinter; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControl; // packageimport
+import org.eclipse.jface.internal.text.html.SubstitutionTextReader; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInput; // packageimport
+import org.eclipse.jface.internal.text.html.SingleCharReader; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControlInput; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Set;
+import org.eclipse.dwtxhelper.StringReader;
+
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Drawable;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.jface.internal.text.link.contentassist.LineBreakingReader;
+import org.eclipse.jface.text.DefaultInformationControl;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextPresentation;
+
+
+/**
+ * <p>
+ * Moved into this package from <code>org.eclipse.jface.internal.text.revisions</code>.</p>
+ */
+public class HTMLTextPresenter : DefaultInformationControl_IInformationPresenter, DefaultInformationControl_IInformationPresenterExtension {
+
+    private static String LINE_DELIM_;
+    private static String LINE_DELIM() {
+        if( LINE_DELIM_ is null ){
+            LINE_DELIM_ = System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+        return LINE_DELIM_;
+    }
+
+    private int fCounter;
+    private bool fEnforceUpperLineLimit;
+
+    public this(bool enforceUpperLineLimit) {
+//         super();
+        fEnforceUpperLineLimit= enforceUpperLineLimit;
+    }
+
+    public this() {
+        this(true);
+    }
+
+    protected Reader createReader(String hoverInfo, TextPresentation presentation) {
+        return new HTML2TextReader(new StringReader(hoverInfo), presentation);
+    }
+
+    protected void adaptTextPresentation(TextPresentation presentation, int offset, int insertLength) {
+
+        int yoursStart= offset;
+        int yoursEnd=   offset + insertLength -1;
+        yoursEnd= Math.max(yoursStart, yoursEnd);
+
+        Iterator e= presentation.getAllStyleRangeIterator();
+        while (e.hasNext()) {
+
+            StyleRange range= cast(StyleRange) e.next();
+
+            int myStart= range.start;
+            int myEnd=   range.start + range.length -1;
+            myEnd= Math.max(myStart, myEnd);
+
+            if (myEnd < yoursStart)
+                continue;
+
+            if (myStart < yoursStart)
+                range.length += insertLength;
+            else
+                range.start += insertLength;
+        }
+    }
+
+    private void append(StringBuffer buffer, String string, TextPresentation presentation) {
+
+        int length= string.length();
+        buffer.append(string);
+
+        if (presentation !is null)
+            adaptTextPresentation(presentation, fCounter, length);
+
+        fCounter += length;
+    }
+
+    private String getIndent(String line) {
+        int length= line.length();
+
+        int i= 0;
+        while (i < length && Character.isWhitespace(line.getRelativeCodePoint(i,0)))
+            i += line.getRelativeCodePointOffset(i,1);
+
+        return (i is length ? line : line.substring(0, i)) ~ " "; //$NON-NLS-1$
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.eclipse.jface.text.DefaultInformationControl.IInformationPresenter#updatePresentation(org.eclipse.swt.widgets.Display, java.lang.String, org.eclipse.jface.text.TextPresentation, int, int)
+     * @deprecated
+     */
+    public String updatePresentation(Display display, String hoverInfo, TextPresentation presentation, int maxWidth, int maxHeight) {
+        return updatePresentation(cast(Drawable)display, hoverInfo, presentation, maxWidth, maxHeight);
+    }
+
+    /*
+     * @see IHoverInformationPresenterExtension#updatePresentation(Drawable drawable, String, TextPresentation, int, int)
+     * @since 3.2
+     */
+    public String updatePresentation(Drawable drawable, String hoverInfo, TextPresentation presentation, int maxWidth, int maxHeight) {
+
+        if (hoverInfo is null)
+            return null;
+
+        GC gc= new GC(drawable);
+        try {
+
+            StringBuffer buffer= new StringBuffer();
+            int maxNumberOfLines= cast(int) Math.round(maxHeight / gc.getFontMetrics().getHeight());
+
+            fCounter= 0;
+            LineBreakingReader reader= new LineBreakingReader(createReader(hoverInfo, presentation), gc, maxWidth);
+
+            bool lastLineFormatted= false;
+            String lastLineIndent= null;
+
+            String line=reader.readLine();
+            bool lineFormatted= reader.isFormattedLine();
+            bool firstLineProcessed= false;
+
+            while (line !is null) {
+
+                if (fEnforceUpperLineLimit && maxNumberOfLines <= 0)
+                    break;
+
+                if (firstLineProcessed) {
+                    if (!lastLineFormatted)
+                        append(buffer, LINE_DELIM, null);
+                    else {
+                        append(buffer, LINE_DELIM, presentation);
+                        if (lastLineIndent !is null)
+                            append(buffer, lastLineIndent, presentation);
+                    }
+                }
+
+                append(buffer, line, null);
+                firstLineProcessed= true;
+
+                lastLineFormatted= lineFormatted;
+                if (!lineFormatted)
+                    lastLineIndent= null;
+                else if (lastLineIndent is null)
+                    lastLineIndent= getIndent(line);
+
+                line= reader.readLine();
+                lineFormatted= reader.isFormattedLine();
+
+                maxNumberOfLines--;
+            }
+
+            if (line !is null) {
+                append(buffer, LINE_DELIM, lineFormatted ? presentation : null);
+                append(buffer, HTMLMessages.getString("HTMLTextPresenter.ellipse"), presentation); //$NON-NLS-1$
+            }
+
+            return trim(buffer, presentation);
+
+        } catch (IOException e) {
+
+            // ignore TODO do something else?
+            return null;
+
+        } finally {
+            gc.dispose();
+        }
+    }
+
+    private String trim(StringBuffer buffer, TextPresentation presentation) {
+
+        int length= buffer.length();
+
+        int end= length -1;
+
+        while (end >= 0 && Character.isWhitespace(buffer.slice().getRelativeCodePoint( end, -1 )))
+            end += buffer.slice().getRelativeCodePointOffset( end, -1 );
+
+        if (end <= -1)
+            return ""; //$NON-NLS-1$
+
+        if (end < buffer.slice().getAbsoluteCodePointOffset( length, -1 ))
+            buffer.truncate(buffer.slice().getAbsoluteCodePointOffset( end, 1));
+        else
+            end= length;
+
+        int start= 0;
+        while (start < end && Character.isWhitespace(buffer.slice().getRelativeCodePoint(start, 0)))
+            start += buffer.slice().getRelativeCodePointOffset( start, 1 );
+
+        buffer.select(0, start);
+        buffer.remove();
+        presentation.setResultWindow(new Region(start, buffer.length()));
+        return buffer.toString();
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/SingleCharReader.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.internal.text.html.SingleCharReader;
+
+import org.eclipse.jface.internal.text.html.HTML2TextReader; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLPrinter; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControl; // packageimport
+import org.eclipse.jface.internal.text.html.SubstitutionTextReader; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLTextPresenter; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInput; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControlInput; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLMessages; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * <p>
+ * Moved into this package from <code>org.eclipse.jface.internal.text.revisions</code>.</p>
+ */
+public abstract class SingleCharReader : Reader {
+
+    /**
+     * @see Reader#read()
+     */
+    public abstract int read() ;
+
+    /**
+     * @see Reader#read(char[],int,int)
+     */
+    public int read(char cbuf[], int off, int len)  {
+        int end= off + len;
+        for (int i= off; i < end; i++) {
+            int ch= read();
+            if (ch is -1) {
+                if (i is off)
+                    return -1;
+                return i - off;
+            }
+            cbuf[i]= cast(char)ch;
+        }
+        return len;
+    }
+
+    /**
+     * @see Reader#ready()
+     */
+    public bool ready()  {
+        return true;
+    }
+
+    /**
+     * Returns the readable content as string.
+     * @return the readable content as string
+     * @exception IOException in case reading fails
+     */
+    public String getString()  {
+        StringBuffer buf= new StringBuffer();
+        int ch;
+        while ((ch= read()) !is -1) {
+            buf.append(cast(char)ch);
+        }
+        return buf.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/SubstitutionTextReader.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,218 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.internal.text.html.SubstitutionTextReader;
+
+import org.eclipse.jface.internal.text.html.HTML2TextReader; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLPrinter; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControl; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLTextPresenter; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInput; // packageimport
+import org.eclipse.jface.internal.text.html.SingleCharReader; // packageimport
+import org.eclipse.jface.internal.text.html.BrowserInformationControlInput; // packageimport
+import org.eclipse.jface.internal.text.html.HTMLMessages; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+import tango.core.Exception;
+
+/**
+ * Reads the text contents from a reader and computes for each character
+ * a potential substitution. The substitution may eat more characters than
+ * only the one passed into the computation routine.
+ * <p>
+ * Moved into this package from <code>org.eclipse.jface.internal.text.revisions</code>.</p>
+ */
+public abstract class SubstitutionTextReader : SingleCharReader {
+
+    private static String LINE_DELIM_;
+    protected static String LINE_DELIM() {
+        if( LINE_DELIM_ is null ){
+            LINE_DELIM_ = System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+        return LINE_DELIM_;
+    }
+
+    private Reader fReader;
+    protected bool fWasWhiteSpace;
+    private int fCharAfterWhiteSpace;
+
+    /**
+     * Tells whether white space characters are skipped.
+     */
+    private bool fSkipWhiteSpace= true;
+
+    private bool fReadFromBuffer;
+    private StringBuffer fBuffer;
+    private int fIndex;
+
+
+    protected this(Reader reader) {
+        fReader= reader;
+        fBuffer= new StringBuffer();
+        fIndex= 0;
+        fReadFromBuffer= false;
+        fCharAfterWhiteSpace= -1;
+        fWasWhiteSpace= true;
+    }
+
+    /**
+     * Computes the substitution for the given character and if necessary
+     * subsequent characters. Implementation should use <code>nextChar</code>
+     * to read subsequent characters.
+     *
+     * @param c the character to be substituted
+     * @return the substitution for <code>c</code>
+     * @throws IOException in case computing the substitution fails
+     */
+    protected abstract String computeSubstitution(int c) ;
+
+    /**
+     * Returns the internal reader.
+     *
+     * @return the internal reader
+     */
+    protected Reader getReader() {
+        return fReader;
+    }
+
+    /**
+     * Returns the next character.
+     * @return the next character
+     * @throws IOException in case reading the character fails
+     */
+    protected int nextChar()  {
+        fReadFromBuffer= (fBuffer.length() > 0);
+        if (fReadFromBuffer) {
+            char ch= fBuffer.slice().charAt(fIndex++);
+            if (fIndex >= fBuffer.length()) {
+                fBuffer.truncate(0);
+                fIndex= 0;
+            }
+            return ch;
+        }
+
+        int ch= fCharAfterWhiteSpace;
+        if (ch is -1) {
+            ch= fReader.read();
+        }
+        if (fSkipWhiteSpace && Character.isWhitespace(cast(char)ch)) {
+            do {
+                ch= fReader.read();
+            } while (Character.isWhitespace(cast(char)ch));
+            if (ch !is -1) {
+                fCharAfterWhiteSpace= ch;
+                return ' ';
+            }
+        } else {
+            fCharAfterWhiteSpace= -1;
+        }
+        return ch;
+    }
+
+    /// SWT
+    protected int nextDChar()  {
+        char[4] buf = void;
+        int ch1 = nextChar();
+        if( ch1 is -1 ) return -1;
+        buf[0] = cast(char)ch1;
+        if(( ch1 & 0x80 ) is 0x00 ){
+            return ch1;
+        }
+        else if(( ch1 & 0xE0 ) is 0xC0 ){
+            int ch2 = nextChar();
+            if( ch2 is -1 ) throw new UnicodeException(__FILE__,__LINE__);
+            buf[1] = cast(char)ch2;
+        }
+        else if(( ch1 & 0xF0 ) is 0xE0 ){
+            int ch2 = nextChar();
+            if( ch1 is -1 ) throw new UnicodeException(__FILE__,__LINE__);
+            buf[1] = cast(char)ch2;
+            int ch3 = nextChar();
+            if( ch3 is -1 ) throw new UnicodeException(__FILE__,__LINE__);
+            buf[2] = cast(char)ch3;
+        }
+        else if(( ch1 & 0xF8 ) is 0xF0 ){
+            int ch2 = nextChar();
+            if( ch1 is -1 ) throw new UnicodeException(__FILE__,__LINE__);
+            buf[1] = cast(char)ch2;
+            int ch3 = nextChar();
+            if( ch3 is -1 ) throw new UnicodeException(__FILE__,__LINE__);
+            buf[2] = cast(char)ch3;
+            int ch4 = nextChar();
+            if( ch4 is -1 ) throw new UnicodeException(__FILE__,__LINE__);
+            buf[3] = cast(char)ch4;
+        }
+        else {
+            throw new UnicodeException(__FILE__,__LINE__);
+        }
+        uint ate;
+        return tango.text.convert.Utf.decode( buf, ate );
+    }
+
+    /**
+     * @see Reader#read()
+     */
+    public int read()  {
+        int c;
+        do {
+
+            c= nextChar();
+            while (!fReadFromBuffer) {
+                String s= computeSubstitution(c);
+                if (s is null)
+                    break;
+                if (s.length() > 0){
+                    fBuffer.select(0, 0);
+                    fBuffer.replace(s);
+                }
+                c= nextChar();
+            }
+
+        } while (fSkipWhiteSpace && fWasWhiteSpace && (c is ' '));
+        fWasWhiteSpace= (c is ' ' || c is '\r' || c is '\n');
+        return c;
+    }
+
+    /**
+     * @see Reader#ready()
+     */
+    public bool ready()  {
+        return fReader.ready();
+    }
+
+    /**
+     * @see Reader#close()
+     */
+    public void close()  {
+        fReader.close();
+    }
+
+    /**
+     * @see Reader#reset()
+     */
+    public void reset()  {
+        fReader.reset();
+        fWasWhiteSpace= true;
+        fCharAfterWhiteSpace= -1;
+        fBuffer.truncate(0);
+        fIndex= 0;
+    }
+
+    protected final void setSkipWhitespace(bool state) {
+        fSkipWhiteSpace= state;
+    }
+
+    protected final bool isSkippingWhitespace() {
+        return fSkipWhiteSpace;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/AdditionalInfoController2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,280 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.internal.text.link.contentassist.AdditionalInfoController2;
+
+import org.eclipse.jface.internal.text.link.contentassist.IProposalListener; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.LineBreakingReader; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.CompletionProposalPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContextInformationPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistMessages; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.Helper2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.PopupCloser2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.IContentAssistListener2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistant2; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+import java.lang.JThread;
+import tango.core.sync.Mutex;
+import tango.core.sync.Condition;
+
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.AbstractInformationControlManager;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3;
+
+
+/**
+ * Displays the additional information available for a completion proposal.
+ *
+ * @since 2.0
+ */
+class AdditionalInfoController2 : AbstractInformationControlManager , Runnable {
+
+    /**
+     * Internal table selection listener.
+     */
+    private class TableSelectionListener : SelectionListener {
+
+        /*
+         * @see SelectionListener#widgetSelected(SelectionEvent)
+         */
+        public void widgetSelected(SelectionEvent e) {
+            handleTableSelectionChanged();
+        }
+
+        /*
+         * @see SelectionListener#widgetDefaultSelected(SelectionEvent)
+         */
+        public void widgetDefaultSelected(SelectionEvent e) {
+        }
+    }
+
+
+    /** The proposal table */
+    private Table fProposalTable;
+    /** The thread controlling the delayed display of the additional info */
+    private JThread fThread;
+    /** Indicates whether the display delay has been reset */
+    private bool fIsReset= false;
+    /** Object to synchronize display thread and table selection changes */
+    private const Mutex     fMutex;
+    private const Condition fMutex_cond;
+    /** Thread access lock. */
+    private const Object fThreadAccess;
+    /** Object to synchronize initial display of additional info */
+    private Mutex     fStartSignal;
+    private Condition fStartSignal_cond;
+    /** The table selection listener */
+    private SelectionListener fSelectionListener;
+    /** The delay after which additional information is displayed */
+    private int fDelay;
+
+
+    /**
+     * Creates a new additional information controller.
+     *
+     * @param creator the information control creator to be used by this controller
+     * @param delay time in milliseconds after which additional info should be displayed
+     */
+    this(IInformationControlCreator creator, int delay) {
+        fSelectionListener= new TableSelectionListener();
+        fThreadAccess= new Object();
+        fMutex= new Mutex();
+        fMutex_cond= new Condition(fMutex);
+        super(creator);
+        fDelay= delay;
+        setAnchor(ANCHOR_RIGHT);
+        setFallbackAnchors([ANCHOR_RIGHT, ANCHOR_LEFT, ANCHOR_BOTTOM ]);
+    }
+
+    /*
+     * @see AbstractInformationControlManager#install(Control)
+     */
+    public void install(Control control) {
+
+        if (fProposalTable is control) {
+            // already installed
+            return;
+        }
+
+        super.install(control);
+
+        Assert.isTrue( null !is cast(Table)control );
+        fProposalTable= cast(Table) control;
+        fProposalTable.addSelectionListener(fSelectionListener);
+        synchronized (fThreadAccess) {
+            if (fThread !is null)
+                fThread.interrupt();
+            fThread= new JThread(this, ContentAssistMessages.getString("InfoPopup.info_delay_timer_name")); //$NON-NLS-1$
+
+            fStartSignal= new Mutex();
+            fStartSignal_cond= new Condition(fStartSignal);
+            synchronized (fStartSignal) {
+                fThread.start();
+                try {
+                    // wait until thread is ready
+                    fStartSignal_cond.wait();
+                } catch (InterruptedException x) {
+                }
+            }
+        }
+    }
+
+    /*
+     * @see AbstractInformationControlManager#disposeInformationControl()
+     */
+     public void disposeInformationControl() {
+
+        synchronized (fThreadAccess) {
+            if (fThread !is null) {
+                fThread.interrupt();
+                fThread= null;
+            }
+        }
+
+        if (fProposalTable !is null && !fProposalTable.isDisposed()) {
+            fProposalTable.removeSelectionListener(fSelectionListener);
+            fProposalTable= null;
+        }
+
+        super.disposeInformationControl();
+    }
+
+    /*
+     * @see java.lang.Runnable#run()
+     */
+    public void run() {
+        try {
+            while (true) {
+
+                synchronized (fMutex) {
+
+                    if (fStartSignal !is null) {
+                        synchronized (fStartSignal) {
+                            fStartSignal_cond.notifyAll();
+                            fStartSignal= null;
+                            fStartSignal_cond = null;
+                        }
+                    }
+
+                    // Wait for a selection event to occur.
+                    fMutex_cond.wait();
+
+                    while (true) {
+                        fIsReset= false;
+                        // Delay before showing the popup.
+                        fMutex_cond.wait(fDelay);
+                        if (!fIsReset)
+                            break;
+                    }
+                }
+
+                if (fProposalTable !is null && !fProposalTable.isDisposed()) {
+                    fProposalTable.getDisplay().asyncExec(new class()  Runnable {
+                        public void run() {
+                            if (!fIsReset)
+                                showInformation();
+                        }
+                    });
+                }
+
+            }
+        } catch (InterruptedException e) {
+        }
+
+        synchronized (fThreadAccess) {
+            // only null fThread if it is us!
+            if (JThread.currentThread() is fThread)
+                fThread= null;
+        }
+    }
+
+    /**
+     *Handles a change of the line selected in the associated selector.
+     */
+    public void handleTableSelectionChanged() {
+
+        if (fProposalTable !is null && !fProposalTable.isDisposed() && fProposalTable.isVisible()) {
+            synchronized (fMutex) {
+                fIsReset= true;
+                fMutex_cond.notifyAll();
+            }
+        }
+    }
+
+    /*
+     * @see AbstractInformationControlManager#computeInformation()
+     */
+    protected void computeInformation() {
+
+        if (fProposalTable is null || fProposalTable.isDisposed())
+            return;
+
+        TableItem[] selection= fProposalTable.getSelection();
+        if (selection !is null && selection.length > 0) {
+
+            TableItem item= selection[0];
+
+            // compute information
+            String information= null;
+            Object d= item.getData();
+
+            if ( cast(ICompletionProposal)d ) {
+                ICompletionProposal p= cast(ICompletionProposal) d;
+                information= p.getAdditionalProposalInfo();
+            }
+
+            if ( cast(ICompletionProposalExtension3)d )
+                setCustomInformationControlCreator((cast(ICompletionProposalExtension3) d).getInformationControlCreator());
+            else
+                setCustomInformationControlCreator(null);
+
+            // compute subject area
+            setMargins(4, -1);
+            Rectangle area= fProposalTable.getBounds();
+            area.x= 0; // subject area is the whole subject control
+            area.y= 0;
+
+            // set information & subject area
+            setInformation(information, area);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#computeSizeConstraints(Control, IInformationControl)
+     */
+    protected Point computeSizeConstraints(Control subjectControl, IInformationControl informationControl) {
+        // at least as big as the proposal table
+        Point sizeConstraint= super.computeSizeConstraints(subjectControl, informationControl);
+        Point size= subjectControl.getSize();
+        if (sizeConstraint.x < size.x)
+            sizeConstraint.x= size.x;
+        if (sizeConstraint.y < size.y)
+            sizeConstraint.y= size.y;
+        return sizeConstraint;
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/CompletionProposalPopup2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,970 @@
+/*******************************************************************************
+ * 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
+ *     Sean Montgomery, sean_montgomery@comcast.net - https://bugs.eclipse.org/bugs/show_bug.cgi?id=116454
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.internal.text.link.contentassist.CompletionProposalPopup2;
+
+import org.eclipse.jface.internal.text.link.contentassist.IProposalListener; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.LineBreakingReader; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContextInformationPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistMessages; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.Helper2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.PopupCloser2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.IContentAssistListener2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistant2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.AdditionalInfoController2; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.jface.internal.text.TableOwnerDrawSupport;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IEditingSupport;
+import org.eclipse.jface.text.IEditingSupportRegistry;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.IRewriteTarget;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.viewers.StyledString;
+
+
+
+/**
+ * This class is used to present proposals to the user. If additional
+ * information exists for a proposal, then selecting that proposal
+ * will result in the information being displayed in a secondary
+ * window.
+ *
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal
+ * @see org.eclipse.jface.internal.text.link.contentassist.AdditionalInfoController2
+ */
+class CompletionProposalPopup2 : IContentAssistListener2 {
+
+    /** The associated text viewer */
+    private ITextViewer fViewer;
+    /** The associated content assistant */
+    private ContentAssistant2 fContentAssistant;
+    /** The used additional info controller */
+    private AdditionalInfoController2 fAdditionalInfoController;
+    /** The closing strategy for this completion proposal popup */
+    private PopupCloser2 fPopupCloser;
+    /** The popup shell */
+    private Shell fProposalShell;
+    /** The proposal table */
+    private Table fProposalTable;
+    /** Indicates whether a completion proposal is being inserted */
+    private bool fInserting= false;
+    /** The key listener to control navigation */
+    private KeyListener fKeyListener;
+    /** List of document events used for filtering proposals */
+    private List fDocumentEvents;
+    /** Listener filling the document event queue */
+    private IDocumentListener fDocumentListener;
+    /** Reentrance count for <code>filterProposals</code> */
+    private long fInvocationCounter= 0;
+    /** The filter list of proposals */
+    private ICompletionProposal[] fFilteredProposals;
+    /** The computed list of proposals */
+    private ICompletionProposal[] fComputedProposals;
+    /** The offset for which the proposals have been computed */
+    private int fInvocationOffset;
+    /** The offset for which the computed proposals have been filtered */
+    private int fFilterOffset;
+    /** The default line delimiter of the viewer's widget */
+    private String fLineDelimiter;
+    /** The most recently selected proposal. */
+    private ICompletionProposal fLastProposal;
+    /**
+     * Tells whether colored labels support is enabled.
+     * Only valid while the popup is active.
+     *
+     * @since 3.4
+     */
+    private bool fIsColoredLabelsSupportEnabled= false;
+
+    private IEditingSupport fFocusEditingSupport;
+    private void fFocusEditingSupport_init() {
+        fFocusEditingSupport = new class() IEditingSupport {
+
+            public bool isOriginator(DocumentEvent event, IRegion focus) {
+                return false;
+            }
+
+            public bool ownsFocusShell() {
+                return Helper2.okToUse(fProposalShell) && fProposalShell.isFocusControl()
+                        || Helper2.okToUse(fProposalTable) && fProposalTable.isFocusControl();
+            }
+
+        };
+    }
+    private IEditingSupport fModificationEditingSupport;
+    private void fModificationEditingSupport_init() {
+        fModificationEditingSupport = new class()  IEditingSupport {
+
+            public bool isOriginator(DocumentEvent event, IRegion focus) {
+                if (fViewer !is null) {
+                    Point selection= fViewer.getSelectedRange();
+                    return selection.x <= focus.getOffset() + focus.getLength() && selection.x + selection.y >= focus.getOffset();
+                }
+                return false;
+            }
+
+            public bool ownsFocusShell() {
+                return false;
+            }
+
+        };
+    }
+
+    /**
+     * Creates a new completion proposal popup for the given elements.
+     *
+     * @param contentAssistant the content assistant feeding this popup
+     * @param viewer the viewer on top of which this popup appears
+     * @param infoController the info control collaborating with this popup
+     * @since 2.0
+     */
+    public this(ContentAssistant2 contentAssistant, ITextViewer viewer, AdditionalInfoController2 infoController) {
+        fPopupCloser= new PopupCloser2();
+        fDocumentEvents= new ArrayList();
+
+        fModificationEditingSupport_init();
+        fFocusEditingSupport_init();
+
+        fContentAssistant= contentAssistant;
+        fViewer= viewer;
+        fAdditionalInfoController= infoController;
+    }
+
+    /**
+     * Computes and presents completion proposals. The flag indicates whether this call has
+     * be made out of an auto activation context.
+     *
+     * @param autoActivated <code>true</code> if auto activation context
+     * @return an error message or <code>null</code> in case of no error
+     */
+    public String showProposals(bool autoActivated) {
+
+        if (fKeyListener is null) {
+            fKeyListener= new class()  KeyListener {
+                public void keyPressed(KeyEvent e) {
+                    if (!Helper2.okToUse(fProposalShell))
+                        return;
+
+                    if (e.character is 0 && e.keyCode is SWT.CTRL) {
+                        // http://dev.eclipse.org/bugs/show_bug.cgi?id=34754
+                        int index= fProposalTable.getSelectionIndex();
+                        if (index >= 0)
+                            selectProposal(index, true);
+                    }
+                }
+
+                public void keyReleased(KeyEvent e) {
+                    if (!Helper2.okToUse(fProposalShell))
+                        return;
+
+                    if (e.character is 0 && e.keyCode is SWT.CTRL) {
+                        // http://dev.eclipse.org/bugs/show_bug.cgi?id=34754
+                        int index= fProposalTable.getSelectionIndex();
+                        if (index >= 0)
+                            selectProposal(index, false);
+                    }
+                }
+            };
+        }
+
+        final StyledText styledText= fViewer.getTextWidget();
+        if (styledText !is null && !styledText.isDisposed())
+            styledText.addKeyListener(fKeyListener);
+
+//      BusyIndicator.showWhile(styledText.getDisplay(), new class()  Runnable {
+//          public void run() {
+
+                fInvocationOffset= fViewer.getSelectedRange().x;
+                // lazily compute proposals
+//              if (fComputedProposals is null) fComputedProposals= computeProposals(fContentAssistant.getCompletionPosition());
+                fComputedProposals= computeProposals(fInvocationOffset);
+
+                int count= (fComputedProposals is null ? 0 : fComputedProposals.length);
+                if (count is 0) {
+
+                    if (!autoActivated)
+                        styledText.getDisplay().beep();
+
+                } else {
+
+                    if (count is 1 && !autoActivated && fContentAssistant.isAutoInserting())
+
+                        insertProposal(fComputedProposals[0], cast(wchar) 0, 0, fInvocationOffset);
+
+                    else {
+
+                        if (fLineDelimiter is null)
+                            fLineDelimiter= styledText.getLineDelimiter();
+
+                        createProposalSelector();
+                        setProposals(fComputedProposals);
+                        resizeProposalSelector(true);
+                        displayProposals();
+                    }
+                }
+//          }
+//      });
+
+        return getErrorMessage();
+    }
+
+    /**
+     * Returns the completion proposal available at the given offset of the
+     * viewer's document. Delegates the work to the content assistant.
+     *
+     * @param offset the offset
+     * @return the completion proposals available at this offset
+     */
+    private ICompletionProposal[] computeProposals(int offset) {
+        return fContentAssistant.computeCompletionProposals(fViewer, offset);
+    }
+
+    /**
+     * Returns the error message.
+     *
+     * @return the error message
+     */
+    private String getErrorMessage() {
+        return fContentAssistant.getErrorMessage();
+    }
+
+    /**
+     * Creates the proposal selector.
+     */
+    private void createProposalSelector() {
+        if (Helper2.okToUse(fProposalShell))
+            return;
+
+        Control control= fViewer.getTextWidget();
+        fProposalShell= new Shell(control.getShell(), SWT.ON_TOP);
+//      fProposalShell= new Shell(control.getShell(), SWT.ON_TOP | SWT.RESIZE );
+        fProposalTable= new Table(fProposalShell, SWT.H_SCROLL | SWT.V_SCROLL);
+//      fProposalTable= new Table(fProposalShell, SWT.H_SCROLL | SWT.V_SCROLL);
+
+
+        fIsColoredLabelsSupportEnabled= fContentAssistant.isColoredLabelsSupportEnabled();
+        if (fIsColoredLabelsSupportEnabled)
+            TableOwnerDrawSupport.install(fProposalTable);
+
+        fProposalTable.setLocation(0, 0);
+        if (fAdditionalInfoController !is null)
+            fAdditionalInfoController.setSizeConstraints(50, 10, true, false);
+
+        GridLayout layout= new GridLayout();
+        layout.marginWidth= 0;
+        layout.marginHeight= 0;
+        fProposalShell.setLayout(layout);
+
+        GridData data= new GridData(GridData.FILL_BOTH);
+        fProposalTable.setLayoutData(data);
+
+        fProposalShell.pack();
+
+        // set location
+        Point currentLocation= fProposalShell.getLocation();
+        Point newLocation= getLocation();
+        if ((newLocation.x < currentLocation.x && newLocation.y is currentLocation.y) || newLocation.y < currentLocation.y)
+            fProposalShell.setLocation(newLocation);
+
+        if (fAdditionalInfoController !is null) {
+            fProposalShell.addControlListener(new class()  ControlListener {
+
+                public void controlMoved(ControlEvent e) {}
+
+                public void controlResized(ControlEvent e) {
+                    // resets the cached resize constraints
+                    fAdditionalInfoController.setSizeConstraints(50, 10, true, false);
+                }
+            });
+        }
+
+        fProposalShell.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_BLACK));
+
+        Color c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
+        fProposalTable.setBackground(c);
+
+        c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
+        fProposalTable.setForeground(c);
+
+        fProposalTable.addSelectionListener(new class()  SelectionListener {
+
+            public void widgetSelected(SelectionEvent e) {}
+
+            public void widgetDefaultSelected(SelectionEvent e) {
+                selectProposalWithMask(e.stateMask);
+            }
+        });
+
+        fPopupCloser.install(fContentAssistant, fProposalTable);
+
+        fProposalShell.addDisposeListener(new class()  DisposeListener {
+            public void widgetDisposed(DisposeEvent e) {
+                unregister(); // but don't dispose the shell, since we're being called from its disposal event!
+            }
+        });
+
+        fProposalTable.setHeaderVisible(false);
+        fContentAssistant.addToLayout(this, fProposalShell, ContentAssistant2.LayoutManager.LAYOUT_PROPOSAL_SELECTOR, fContentAssistant.getSelectionOffset());
+    }
+
+    /**
+     * Returns the proposal selected in the proposal selector.
+     *
+     * @return the selected proposal
+     * @since 2.0
+     */
+    private ICompletionProposal getSelectedProposal() {
+        int i= fProposalTable.getSelectionIndex();
+        if (i < 0 || i >= fFilteredProposals.length)
+            return null;
+        return fFilteredProposals[i];
+    }
+
+    /**
+     * Takes the selected proposal and applies it.
+     *
+     * @param stateMask the state mask
+     * @since 2.1
+     */
+    private void selectProposalWithMask(int stateMask) {
+        ICompletionProposal p= getSelectedProposal();
+        hide();
+        if (p !is null)
+            insertProposal(p, cast(wchar) 0, stateMask, fViewer.getSelectedRange().x);
+    }
+
+    /**
+     * Applies the given proposal at the given offset. The given character is the
+     * one that triggered the insertion of this proposal.
+     *
+     * @param p the completion proposal
+     * @param trigger the trigger character
+     * @param stateMask the state mask of the keyboard event triggering the insertion
+     * @param offset the offset
+     * @since 2.1
+     */
+    private void insertProposal(ICompletionProposal p, char trigger, int stateMask, int offset) {
+
+        fInserting= true;
+        IRewriteTarget target= null;
+        IEditingSupportRegistry registry= null;
+
+        try {
+
+            IDocument document= fViewer.getDocument();
+
+            if ( cast(ITextViewerExtension)fViewer ) {
+                ITextViewerExtension extension= cast(ITextViewerExtension) fViewer;
+                target= extension.getRewriteTarget();
+            }
+
+            if (target !is null)
+                target.beginCompoundChange();
+
+            if ( cast(IEditingSupportRegistry)fViewer ) {
+                registry= cast(IEditingSupportRegistry) fViewer;
+                registry.register(fModificationEditingSupport);
+            }
+
+            if ( cast(ICompletionProposalExtension2)p ) {
+                ICompletionProposalExtension2 e= cast(ICompletionProposalExtension2) p;
+                e.apply(fViewer, trigger, stateMask, offset);
+            } else if ( cast(ICompletionProposalExtension)p ) {
+                ICompletionProposalExtension e= cast(ICompletionProposalExtension) p;
+                e.apply(document, trigger, offset);
+            } else {
+                p.apply(document);
+            }
+
+            Point selection= p.getSelection(document);
+            if (selection !is null) {
+                fViewer.setSelectedRange(selection.x, selection.y);
+                fViewer.revealRange(selection.x, selection.y);
+            }
+
+            IContextInformation info= p.getContextInformation();
+            if (info !is null) {
+
+                int position;
+                if ( cast(ICompletionProposalExtension)p ) {
+                    ICompletionProposalExtension e= cast(ICompletionProposalExtension) p;
+                    position= e.getContextInformationPosition();
+                } else {
+                    if (selection is null)
+                        selection= fViewer.getSelectedRange();
+                    position= selection.x + selection.y;
+                }
+
+                fContentAssistant.showContextInformation(info, position);
+            }
+
+            fContentAssistant.fireProposalChosen(p);
+
+        } finally {
+            if (target !is null)
+                target.endCompoundChange();
+
+            if (registry !is null)
+                registry.unregister(fModificationEditingSupport);
+
+            fInserting= false;
+        }
+    }
+
+    /**
+     * Returns whether this popup has the focus.
+     *
+     * @return <code>true</code> if the popup has the focus
+     */
+    public bool hasFocus() {
+        if (Helper2.okToUse(fProposalShell))
+            return (fProposalShell.isFocusControl() || fProposalTable.isFocusControl());
+
+        return false;
+    }
+
+    /**
+     * Hides this popup.
+     */
+    public void hide() {
+
+        unregister();
+
+        if ( cast(IEditingSupportRegistry)fViewer ) {
+            IEditingSupportRegistry registry= cast(IEditingSupportRegistry) fViewer;
+            registry.unregister(fFocusEditingSupport);
+        }
+
+        if (Helper2.okToUse(fProposalShell)) {
+            fContentAssistant.removeContentAssistListener(this, ContentAssistant2.PROPOSAL_SELECTOR);
+
+            fPopupCloser.uninstall();
+            // see bug 47511: setVisible may run the event loop on GTK
+            // and trigger a rentrant call - have to make sure we don't
+            // dispose another shell that was already brought up in a
+            // reentrant call when calling setVisible()
+            Shell tempShell= fProposalShell;
+            fProposalShell= null;
+            tempShell.setVisible(false);
+            tempShell.dispose();
+        }
+    }
+
+    private void unregister() {
+        if (fDocumentListener !is null) {
+            IDocument document= fViewer.getDocument();
+            if (document !is null)
+                document.removeDocumentListener(fDocumentListener);
+            fDocumentListener= null;
+        }
+        fDocumentEvents.clear();
+
+        StyledText styledText= fViewer.getTextWidget();
+        if (fKeyListener !is null && styledText !is null && !styledText.isDisposed())
+            styledText.removeKeyListener(fKeyListener);
+
+        if (fLastProposal !is null) {
+            if ( cast(ICompletionProposalExtension2)fLastProposal ) {
+                ICompletionProposalExtension2 extension= cast(ICompletionProposalExtension2) fLastProposal;
+                extension.unselected(fViewer);
+            }
+
+            fLastProposal= null;
+        }
+
+        fFilteredProposals= null;
+
+        fContentAssistant.possibleCompletionsClosed_package();
+    }
+
+    /**
+     *Returns whether this popup is active. It is active if the propsal selector is visible.
+     *
+     * @return <code>true</code> if this popup is active
+     */
+    public bool isActive() {
+        return fProposalShell !is null && !fProposalShell.isDisposed();
+    }
+
+    /**
+     * Initializes the proposal selector with these given proposals.
+     *
+     * @param proposals the proposals
+     */
+    private void setProposals(ICompletionProposal[] proposals) {
+        if (Helper2.okToUse(fProposalTable)) {
+
+            ICompletionProposal oldProposal= getSelectedProposal();
+            if ( cast(ICompletionProposalExtension2)oldProposal )
+                (cast(ICompletionProposalExtension2) oldProposal).unselected(fViewer);
+
+            fFilteredProposals= proposals;
+
+            fProposalTable.setRedraw(false);
+            fProposalTable.removeAll();
+
+            Point selection= fViewer.getSelectedRange();
+            int endOffset;
+            endOffset= selection.x + selection.y;
+            IDocument document= fViewer.getDocument();
+            bool validate= false;
+            if (selection.y !is 0 && document !is null) validate= true;
+            int selectionIndex= 0;
+
+            TableItem item;
+            ICompletionProposal p;
+            for (int i= 0; i < proposals.length; i++) {
+                p= proposals[i];
+                item= new TableItem(fProposalTable, SWT.NULL);
+                if (p.getImage() !is null)
+                    item.setImage(p.getImage());
+
+                String displayString;
+                StyleRange[] styleRanges= null;
+                if (fIsColoredLabelsSupportEnabled && cast(ICompletionProposalExtension6)p ) {
+                    StyledString styledString= (cast(ICompletionProposalExtension6)p).getStyledDisplayString();
+                    displayString= styledString.getString();
+                    styleRanges= styledString.getStyleRanges();
+                } else
+                    displayString= p.getDisplayString();
+
+                item.setText(displayString);
+                if (fIsColoredLabelsSupportEnabled)
+                    TableOwnerDrawSupport.storeStyleRanges(item, 0, styleRanges);
+
+                item.setData(cast(Object)p);
+
+                if (validate && validateProposal(document, p, endOffset, null)) {
+                    selectionIndex= i;
+                    validate= false;
+                }
+            }
+
+            resizeProposalSelector(false);
+
+            selectProposal(selectionIndex, false);
+            fProposalTable.setRedraw(true);
+        }
+    }
+
+    private void resizeProposalSelector(bool adjustWidth) {
+        // in order to fill in the table items so size computation works correctly
+        // will cause flicker, though
+        fProposalTable.setRedraw(true);
+
+        int width= adjustWidth ? SWT.DEFAULT : (cast(GridData)fProposalTable.getLayoutData()).widthHint;
+        Point size= fProposalTable.computeSize(width, SWT.DEFAULT, true);
+
+        GridData data= new GridData(GridData.FILL_BOTH);
+        data.widthHint= adjustWidth ? Math.min(size.x, 300) : width;
+        data.heightHint= Math.min(getTableHeightHint(fProposalTable, fProposalTable.getItemCount()), getTableHeightHint(fProposalTable, 10));
+        fProposalTable.setLayoutData(data);
+
+        fProposalShell.layout(true);
+        fProposalShell.pack();
+
+        if (adjustWidth) {
+            fProposalShell.setLocation(getLocation());
+        }
+    }
+
+    /**
+     * Computes the table hight hint for <code>table</code>.
+     *
+     * @param table the table to compute the height for
+     * @param rows the number of rows to compute the height for
+     * @return the height hint for <code>table</code>
+     */
+    private int getTableHeightHint(Table table, int rows) {
+        if (table.getFont().opEquals(JFaceResources.getDefaultFont()))
+            table.setFont(JFaceResources.getDialogFont());
+        int result= table.getItemHeight() * rows;
+        if (table.getLinesVisible())
+            result+= table.getGridLineWidth() * (rows - 1);
+
+        // TODO adjust to correct size. +4 works on windows, but not others
+//      return result + 4;
+        return result;
+    }
+
+    private bool validateProposal(IDocument document, ICompletionProposal p, int offset, DocumentEvent event) {
+        // detect selected
+        if ( cast(ICompletionProposalExtension2)p ) {
+            ICompletionProposalExtension2 e= cast(ICompletionProposalExtension2) p;
+            if (e.validate(document, offset, event))
+                return true;
+        } else if ( cast(ICompletionProposalExtension)p ) {
+            ICompletionProposalExtension e= cast(ICompletionProposalExtension) p;
+            if (e.isValidFor(document, offset))
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the graphical location at which this popup should be made visible.
+     *
+     * @return the location of this popup
+     */
+    private Point getLocation() {
+        StyledText text= fViewer.getTextWidget();
+        Point selection= text.getSelection();
+        Point p= text.getLocationAtOffset(selection.x);
+        p.x -= fProposalShell.getBorderWidth();
+        if (p.x < 0) p.x= 0;
+        if (p.y < 0) p.y= 0;
+        p= new Point(p.x, p.y + text.getLineHeight(selection.x));
+        p= text.toDisplay(p);
+        return p;
+    }
+
+    /**
+     *Displays this popup and install the additional info controller, so that additional info
+     * is displayed when a proposal is selected and additional info is available.
+     */
+    private void displayProposals() {
+        if (fContentAssistant.addContentAssistListener(this, ContentAssistant2.PROPOSAL_SELECTOR)) {
+
+            if (fDocumentListener is null)
+                fDocumentListener=  new class()   IDocumentListener {
+                    public void documentAboutToBeChanged(DocumentEvent event) {
+                        if (!fInserting)
+                            fDocumentEvents.add(event);
+                    }
+
+                    public void documentChanged(DocumentEvent event) {
+                        if (!fInserting)
+                            filterProposals();
+                    }
+                };
+            IDocument document= fViewer.getDocument();
+            if (document !is null)
+                document.addDocumentListener(fDocumentListener);
+
+
+            if ( cast(IEditingSupportRegistry)fViewer ) {
+                IEditingSupportRegistry registry= cast(IEditingSupportRegistry) fViewer;
+                registry.register(fFocusEditingSupport);
+            }
+
+            fProposalShell.setVisible(true);
+            // see bug 47511: setVisible may run the event loop on GTK
+            // and trigger a rentrant call - have to check whether we are still
+            // visible
+            if (!Helper2.okToUse(fProposalShell))
+                return;
+
+
+            if (fAdditionalInfoController !is null) {
+                fAdditionalInfoController.install(fProposalTable);
+                fAdditionalInfoController.handleTableSelectionChanged();
+            }
+        }
+    }
+
+        /*
+         * @see IContentAssistListener#verifyKey(VerifyEvent)
+         */
+        public bool verifyKey(VerifyEvent e) {
+            if (!Helper2.okToUse(fProposalShell))
+                return true;
+
+            char key= e.character;
+            if (key is 0) {
+                int newSelection= fProposalTable.getSelectionIndex();
+                int visibleRows= (fProposalTable.getSize().y / fProposalTable.getItemHeight()) - 1;
+                bool smartToggle= false;
+                switch (e.keyCode) {
+
+                    case SWT.ARROW_LEFT :
+                    case SWT.ARROW_RIGHT :
+                        filterProposals();
+                        return true;
+
+                    case SWT.ARROW_UP :
+                        newSelection -= 1;
+                        if (newSelection < 0)
+                            newSelection= fProposalTable.getItemCount() - 1;
+                        break;
+
+                    case SWT.ARROW_DOWN :
+                        newSelection += 1;
+                        if (newSelection > fProposalTable.getItemCount() - 1)
+                            newSelection= 0;
+                        break;
+
+                    case SWT.PAGE_DOWN :
+                        newSelection += visibleRows;
+                        if (newSelection >= fProposalTable.getItemCount())
+                            newSelection= fProposalTable.getItemCount() - 1;
+                        break;
+
+                    case SWT.PAGE_UP :
+                        newSelection -= visibleRows;
+                        if (newSelection < 0)
+                            newSelection= 0;
+                        break;
+
+                    case SWT.HOME :
+                        newSelection= 0;
+                        break;
+
+                    case SWT.END :
+                        newSelection= fProposalTable.getItemCount() - 1;
+                        break;
+
+                    default :
+                        if (e.keyCode !is SWT.MOD1 && e.keyCode !is SWT.MOD2 && e.keyCode !is SWT.MOD3 && e.keyCode !is SWT.MOD4)
+                            hide();
+                        return true;
+                }
+
+                selectProposal(newSelection, smartToggle);
+
+                e.doit= false;
+                return false;
+
+            }
+
+            // key !is 0
+            switch (key) {
+                case 0x1B: // Esc
+                    e.doit= false;
+                    hide();
+                    break;
+
+                case '\n': // Ctrl-Enter on w2k
+                case '\r': // Enter
+                    if ((e.stateMask & SWT.CTRL) is 0) {
+                        e.doit= false;
+                        selectProposalWithMask(e.stateMask);
+                    }
+                    break;
+
+                    // in linked mode: hide popup
+                    // plus: don't invalidate the event in order to give LinkedUI a chance to handle it
+                case '\t':
+//                  hide();
+                    break;
+
+                default:
+                    ICompletionProposal p= getSelectedProposal();
+                if ( cast(ICompletionProposalExtension)p ) {
+                    ICompletionProposalExtension t= cast(ICompletionProposalExtension) p;
+                    char[] triggers= t.getTriggerCharacters();
+                    if (contains(triggers, key)) {
+                        hide();
+                        if (key is ';') {
+                            e.doit= true;
+                            insertProposal(p, cast(wchar) 0, e.stateMask, fViewer.getSelectedRange().x);
+                        } else {
+                            e.doit= false;
+                            insertProposal(p, key, e.stateMask, fViewer.getSelectedRange().x);
+                        }
+                    }
+                }
+            }
+
+            return true;
+        }
+
+    /**
+     * Selects the entry with the given index in the proposal selector and feeds
+     * the selection to the additional info controller.
+     *
+     * @param index the index in the list
+     * @param smartToggle <code>true</code> if the smart toogle key has been pressed
+     * @since 2.1
+     */
+    private void selectProposal(int index, bool smartToggle) {
+
+        ICompletionProposal oldProposal= getSelectedProposal();
+        if ( cast(ICompletionProposalExtension2)oldProposal )
+            (cast(ICompletionProposalExtension2) oldProposal).unselected(fViewer);
+
+        ICompletionProposal proposal= fFilteredProposals[index];
+        if ( cast(ICompletionProposalExtension2)proposal )
+            (cast(ICompletionProposalExtension2) proposal).selected(fViewer, smartToggle);
+
+        fLastProposal= proposal;
+
+        fProposalTable.setSelection(index);
+        fProposalTable.showSelection();
+        if (fAdditionalInfoController !is null)
+            fAdditionalInfoController.handleTableSelectionChanged();
+    }
+
+    /**
+     * Returns whether the given character is contained in the given array of
+     * characters.
+     *
+     * @param characters the list of characters
+     * @param c the character to look for in the list
+     * @return <code>true</code> if character belongs to the list
+     * @since 2.0
+     */
+    private bool contains(char[] characters, char c) {
+
+        if (characters is null)
+            return false;
+
+        for (int i= 0; i < characters.length; i++) {
+            if (c is characters[i])
+                return true;
+        }
+
+        return false;
+    }
+
+    /*
+     * @see IEventConsumer#processEvent(VerifyEvent)
+     */
+    public void processEvent(VerifyEvent e) {
+    }
+
+    /**
+     * Filters the displayed proposal based on the given cursor position and the
+     * offset of the original invocation of the content assistant.
+     */
+    private void filterProposals() {
+        ++ fInvocationCounter;
+        Control control= fViewer.getTextWidget();
+        control.getDisplay().asyncExec(dgRunnable( (long fInvocationCounter_) {
+            long fCounter= fInvocationCounter_;
+
+            if (fCounter !is fInvocationCounter) return;
+
+            int offset= fViewer.getSelectedRange().x;
+            ICompletionProposal[] proposals= null;
+            try  {
+                if (offset > -1) {
+                    DocumentEvent event= TextUtilities.mergeProcessedDocumentEvents(fDocumentEvents);
+                    proposals= computeFilteredProposals(offset, event);
+                }
+            } catch (BadLocationException x)  {
+            } finally  {
+                fDocumentEvents.clear();
+            }
+            fFilterOffset= offset;
+
+            if (proposals !is null && proposals.length > 0)
+                setProposals(proposals);
+            else
+                hide();
+        }, fInvocationCounter));
+    }
+
+    /**
+     * Computes the subset of already computed propsals that are still valid for
+     * the given offset.
+     *
+     * @param offset the offset
+     * @param event the merged document event
+     * @return the set of filtered proposals
+     * @since 2.0
+     */
+    private ICompletionProposal[] computeFilteredProposals(int offset, DocumentEvent event) {
+
+        if (offset is fInvocationOffset && event is null)
+            return fComputedProposals;
+
+        if (offset < fInvocationOffset) {
+            return null;
+        }
+
+        ICompletionProposal[] proposals= fComputedProposals;
+        if (offset > fFilterOffset)
+            proposals= fFilteredProposals;
+
+        if (proposals is null)
+            return null;
+
+        IDocument document= fViewer.getDocument();
+        int length= proposals.length;
+        List filtered= new ArrayList(length);
+        for (int i= 0; i < length; i++) {
+
+            if (cast(ICompletionProposalExtension2)proposals[i] ) {
+
+                ICompletionProposalExtension2 p= cast(ICompletionProposalExtension2) proposals[i];
+                if (p.validate(document, offset, event))
+                    filtered.add(cast(Object)p);
+
+            } else if (cast(ICompletionProposalExtension)proposals[i] ) {
+
+                ICompletionProposalExtension p= cast(ICompletionProposalExtension) proposals[i];
+                if (p.isValidFor(document, offset))
+                    filtered.add(cast(Object)p);
+
+            } else {
+                // restore original behavior
+                fInvocationOffset= offset;
+                fComputedProposals= computeProposals(fInvocationOffset);
+                return fComputedProposals;
+            }
+        }
+
+        return arraycast!(ICompletionProposal)(filtered.toArray());
+    }
+
+    /**
+     * Requests the proposal shell to take focus.
+     *
+     * @since 3.0
+     */
+    public void setFocus() {
+        if (Helper2.okToUse(fProposalShell))
+            fProposalShell.setFocus();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/ContentAssistMessages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.internal.text.link.contentassist.ContentAssistMessages;
+
+
+import org.eclipse.jface.internal.text.link.contentassist.IProposalListener; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.LineBreakingReader; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.CompletionProposalPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContextInformationPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.Helper2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.PopupCloser2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.IContentAssistListener2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistant2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.AdditionalInfoController2; // packageimport
+
+import java.lang.all;
+
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+import java.text.MessageFormat;
+
+
+/**
+ * Helper class to get NLSed messages.
+ *
+ * @since 3.0
+ */
+class ContentAssistMessages {
+
+//     private static const String RESOURCE_BUNDLE= ContentAssistMessages.classinfo.getName();
+
+    private static ResourceBundle fgResourceBundle;//= ResourceBundle.getBundle(RESOURCE_BUNDLE);
+
+    static this() {
+        fgResourceBundle = ResourceBundle.getBundle(
+            getImportData!("org.eclipse.jface.internal.text.link.contentassist.ContentAssistMessages.properties"));
+    }
+
+    private this() {
+    }
+
+    /**
+     * Gets a string from the resource bundle.
+     *
+     * @param key the string used to get the bundle value, must not be null
+     * @return the string from the resource bundle
+     */
+    public static String getString(String key) {
+        try {
+            return fgResourceBundle.getString(key);
+        } catch (MissingResourceException e) {
+            return "!" ~ key ~ "!";//$NON-NLS-2$ //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Gets a string from the resource bundle and formats it with the given arguments.
+     *
+     * @param key the string used to get the bundle value, must not be null
+     * @param args the arguments used to format the string
+     * @return the formatted string
+     */
+    public static String getFormattedString(String key, Object[] args... ) {
+        String format= null;
+        try {
+            format= fgResourceBundle.getString(key);
+        } catch (MissingResourceException e) {
+            return "!" ~ key ~ "!";//$NON-NLS-2$ //$NON-NLS-1$
+        }
+        return MessageFormat.format(format, args);
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/ContentAssistant2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1629 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.internal.text.link.contentassist.ContentAssistant2;
+
+import org.eclipse.jface.internal.text.link.contentassist.IProposalListener; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.LineBreakingReader; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.CompletionProposalPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContextInformationPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistMessages; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.Helper2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.PopupCloser2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.IContentAssistListener2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.AdditionalInfoController2; // packageimport
+
+import java.lang.all;
+import java.util.Stack;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.lang.JThread;
+import tango.core.sync.Mutex;
+import tango.core.sync.Condition;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.custom.VerifyKeyListener;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DefaultInformationControl;
+import org.eclipse.jface.text.IEventConsumer;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension;
+import org.eclipse.jface.text.IViewportListener;
+import org.eclipse.jface.text.IWidgetTokenKeeper;
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension;
+import org.eclipse.jface.text.IWidgetTokenOwner;
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.contentassist.CompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6;
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
+import org.eclipse.jface.text.contentassist.IContentAssistant;
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter;
+import org.eclipse.jface.text.contentassist.IContextInformationValidator;
+
+
+/**
+ * A custom implementation of the <code>IContentAssistant</code> interface.
+ * This implementation is used by the linked mode UI. This is internal and subject
+ * to change without notice.
+ */
+public class ContentAssistant2 : IContentAssistant, IContentAssistantExtension, IWidgetTokenKeeper, IWidgetTokenKeeperExtension {
+
+    /**
+     * A generic closer class used to monitor various
+     * interface events in order to determine whether
+     * content-assist should be terminated and all
+     * associated windows closed.
+     */
+    class Closer : ControlListener, MouseListener, FocusListener, DisposeListener, IViewportListener {
+
+        /** The shell on which we add listeners. */
+        private Shell fShell;
+        private long fViewportListenerStartTime;
+
+        /**
+         * Installs this closer on it's viewer's text widget.
+         */
+        protected void install() {
+            Control w= fViewer.getTextWidget();
+            if (Helper2.okToUse(w)) {
+
+                Shell shell= w.getShell();
+                fShell= shell;
+                shell.addControlListener(this);
+
+                w.addMouseListener(this);
+                w.addFocusListener(this);
+
+                /*
+                 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal Errors
+                 */
+                w.addDisposeListener(this);
+            }
+
+            fViewer.addViewportListener(this);
+            fViewportListenerStartTime= System.currentTimeMillis() + 500;
+        }
+
+        /**
+         * Uninstalls this closer from the viewer's text widget.
+         */
+        protected void uninstall() {
+            Shell shell= fShell;
+            fShell= null;
+            if (Helper2.okToUse(shell))
+                shell.removeControlListener(this);
+
+            Control w= fViewer.getTextWidget();
+            if (Helper2.okToUse(w)) {
+
+                w.removeMouseListener(this);
+                w.removeFocusListener(this);
+
+                /*
+                 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal Errors
+                 */
+                w.removeDisposeListener(this);
+            }
+
+            fViewer.removeViewportListener(this);
+        }
+
+        /*
+         * @see ControlListener#controlResized(ControlEvent)
+         */
+        public void controlResized(ControlEvent e) {
+            hide();
+        }
+
+        /*
+         * @see ControlListener#controlMoved(ControlEvent)
+         */
+        public void controlMoved(ControlEvent e) {
+            hide();
+        }
+
+        /*
+         * @see MouseListener#mouseDown(MouseEvent)
+         */
+        public void mouseDown(MouseEvent e) {
+            hide();
+        }
+
+        /*
+         * @see MouseListener#mouseUp(MouseEvent)
+         */
+        public void mouseUp(MouseEvent e) {
+        }
+
+        /*
+         * @see MouseListener#mouseDoubleClick(MouseEvent)
+         */
+        public void mouseDoubleClick(MouseEvent e) {
+            hide();
+        }
+
+        /*
+         * @see FocusListener#focusGained(FocusEvent)
+         */
+        public void focusGained(FocusEvent e) {
+        }
+
+        /*
+         * @see FocusListener#focusLost(FocusEvent)
+         */
+        public void focusLost(FocusEvent e) {
+            if (fViewer !is null) {
+                Control control= fViewer.getTextWidget();
+                if (control !is null) {
+                    Display d= control.getDisplay();
+                    if (d !is null) {
+                        d.asyncExec(new class()  Runnable {
+                            public void run() {
+                                if (!hasFocus())
+                                    hide();
+                            }
+                        });
+                    }
+                }
+            }
+        }
+
+        /*
+         * @seeDisposeListener#widgetDisposed(DisposeEvent)
+         */
+        public void widgetDisposed(DisposeEvent e) {
+            /*
+             * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal Errors
+             */
+            hide();
+        }
+
+        /*
+         * @see IViewportListener#viewportChanged(int)
+         */
+        public void viewportChanged(int topIndex) {
+            if (System.currentTimeMillis() > fViewportListenerStartTime)
+                hide();
+        }
+    }
+
+    /**
+     * An implementation of <code>IContentAssistListener</code>, this class is
+     * used to monitor key events in support of automatic activation
+     * of the content assistant. If enabled, the implementation utilizes a
+     * thread to watch for input characters matching the activation
+     * characters specified by the content assist processor, and if
+     * detected, will wait the indicated delay interval before
+     * activating the content assistant.
+     */
+    class AutoAssistListener : VerifyKeyListener, Runnable {
+
+        private JThread fThread;
+        private bool fIsReset= false;
+        private Mutex     fMutex;
+        private Condition fMutex_cond;
+        private int fShowStyle;
+
+        private const static int SHOW_PROPOSALS= 1;
+        private const static int SHOW_CONTEXT_INFO= 2;
+
+        protected this() {
+            fMutex = new Mutex();
+            fMutex_cond = new Condition(fMutex);
+        }
+
+        protected void start(int showStyle) {
+            fShowStyle= showStyle;
+            fThread= new JThread(this);
+            fThread.setName( ContentAssistMessages.getString("ContentAssistant.assist_delay_timer_name")); //$NON-NLS-1$
+            fThread.start();
+        }
+
+        public override void run() {
+            try {
+                while (true) {
+                    synchronized (fMutex) {
+                        if (fAutoActivationDelay !is 0)
+                            fMutex_cond.wait(fAutoActivationDelay);
+                        if (fIsReset) {
+                            fIsReset= false;
+                            continue;
+                        }
+                    }
+                    showAssist(fShowStyle);
+                    break;
+                }
+            } catch (InterruptedException e) {
+            }
+            fThread= null;
+        }
+
+        protected void reset(int showStyle) {
+            synchronized (fMutex) {
+                fShowStyle= showStyle;
+                fIsReset= true;
+                fMutex_cond.notifyAll();
+            }
+        }
+
+        protected void stop() {
+            JThread threadToStop= fThread;
+            if (threadToStop !is null)
+                threadToStop.interrupt();
+        }
+
+        private bool contains(char[] characters, char character) {
+            if (characters !is null) {
+                for (int i= 0; i < characters.length; i++) {
+                    if (character is characters[i])
+                        return true;
+                }
+            }
+            return false;
+        }
+
+        public void verifyKey(VerifyEvent e) {
+            // Only act on typed characters and ignore modifier-only events
+            if (e.character is 0 && (e.keyCode & SWT.KEYCODE_BIT) is 0)
+                return;
+
+            if (e.character !is 0 && (e.stateMask is SWT.ALT))
+                return;
+
+            int showStyle;
+            int pos= fViewer.getSelectedRange().x;
+            char[] activation= getCompletionProposalAutoActivationCharacters(fViewer, pos);
+
+            if (contains(activation, e.character) && !fProposalPopup.isActive())
+                showStyle= SHOW_PROPOSALS;
+            else {
+                activation= getContextInformationAutoActivationCharacters(fViewer, pos);
+                if (contains(activation, e.character) && !fContextInfoPopup.isActive())
+                    showStyle= SHOW_CONTEXT_INFO;
+                else {
+                    if (fThread !is null && fThread.isAlive())
+                        stop();
+                    return;
+                }
+            }
+
+            if (fThread !is null && fThread.isAlive())
+                reset(showStyle);
+            else
+                start(showStyle);
+        }
+
+        protected void showAssist(int showStyle) {
+            Control control= fViewer.getTextWidget();
+            Display d= control.getDisplay();
+            if (d !is null) {
+                try {
+                    d.syncExec(new class()  Runnable {
+                        public void run() {
+                            if (showStyle is SHOW_PROPOSALS)
+                                fProposalPopup.showProposals(true);
+                            else if (showStyle is SHOW_CONTEXT_INFO)
+                                fContextInfoPopup.showContextProposals(true);
+                        }
+                    });
+                } catch (SWTError e) {
+                }
+            }
+        }
+    }
+
+    /**
+     * The layout manager layouts the various
+     * windows associated with the content assistant based on the
+     * settings of the content assistant.
+     */
+    class LayoutManager : Listener {
+
+        // Presentation types.
+        /** proposal selector */
+        public const static int LAYOUT_PROPOSAL_SELECTOR= 0;
+        /** context selector */
+        public const static int LAYOUT_CONTEXT_SELECTOR= 1;
+        /** context info */
+        public const static int LAYOUT_CONTEXT_INFO_POPUP= 2;
+
+        int fContextType= LAYOUT_CONTEXT_SELECTOR;
+        Shell[] fShells;
+        Object[] fPopups;
+
+        this(){
+            fShells= new Shell[3];
+            fPopups= new Object[3];
+        }
+
+        protected void add(Object popup, Shell shell, int type, int offset) {
+            Assert.isNotNull(popup);
+            Assert.isTrue(shell !is null && !shell.isDisposed());
+            checkType(type);
+
+            if (fShells[type] !is shell) {
+                if (fShells[type] !is null)
+                    fShells[type].removeListener(SWT.Dispose, this);
+                shell.addListener(SWT.Dispose, this);
+                fShells[type]= shell;
+            }
+
+            fPopups[type]= popup;
+            if (type is LAYOUT_CONTEXT_SELECTOR || type is LAYOUT_CONTEXT_INFO_POPUP)
+                fContextType= type;
+
+            layout(type, offset);
+            adjustListeners(type);
+        }
+
+        protected void checkType(int type) {
+            Assert.isTrue(type is LAYOUT_PROPOSAL_SELECTOR ||
+                type is LAYOUT_CONTEXT_SELECTOR || type is LAYOUT_CONTEXT_INFO_POPUP);
+        }
+
+        public void handleEvent(Event event) {
+            Widget source= event.widget;
+            source.removeListener(SWT.Dispose, this);
+
+            int type= getShellType(source);
+            checkType(type);
+            fShells[type]= null;
+
+            switch (type) {
+                case LAYOUT_PROPOSAL_SELECTOR:
+                    if (fContextType is LAYOUT_CONTEXT_SELECTOR &&
+                            Helper2.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
+                        // Restore event notification to the tip popup.
+                        addContentAssistListener(cast(IContentAssistListener2) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
+                    }
+                    break;
+
+                case LAYOUT_CONTEXT_SELECTOR:
+                    if (Helper2.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
+                        if (fProposalPopupOrientation is PROPOSAL_STACKED)
+                            layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
+                        // Restore event notification to the proposal popup.
+                        addContentAssistListener(cast(IContentAssistListener2) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
+                    }
+                    fContextType= LAYOUT_CONTEXT_INFO_POPUP;
+                    break;
+
+                case LAYOUT_CONTEXT_INFO_POPUP:
+                    if (Helper2.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
+                        if (fContextInfoPopupOrientation is CONTEXT_INFO_BELOW)
+                            layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
+                    }
+                    fContextType= LAYOUT_CONTEXT_SELECTOR;
+                    break;
+                default:
+            }
+        }
+
+        protected int getShellType(Widget shell) {
+            for (int i=0; i<fShells.length; i++) {
+                if (fShells[i] is shell)
+                    return i;
+            }
+            return -1;
+        }
+
+        protected void layout(int type, int offset) {
+            switch (type) {
+                case LAYOUT_PROPOSAL_SELECTOR:
+                    layoutProposalSelector(offset);
+                    break;
+                case LAYOUT_CONTEXT_SELECTOR:
+                    layoutContextSelector(offset);
+                    break;
+                case LAYOUT_CONTEXT_INFO_POPUP:
+                    layoutContextInfoPopup(offset);
+                    break;
+                default:
+            }
+        }
+
+        protected void layoutProposalSelector(int offset) {
+            if (fContextType is LAYOUT_CONTEXT_INFO_POPUP &&
+                    fContextInfoPopupOrientation is CONTEXT_INFO_BELOW &&
+                    Helper2.okToUse(fShells[LAYOUT_CONTEXT_INFO_POPUP])) {
+                // Stack proposal selector beneath the tip box.
+                Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+                Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
+                shell.setLocation(getStackedLocation(shell, parent));
+            } else if (fContextType !is LAYOUT_CONTEXT_SELECTOR ||
+                        !Helper2.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
+                // There are no other presentations to be concerned with,
+                // so place the proposal selector beneath the cursor line.
+                Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+                shell.setLocation(getBelowLocation(shell, offset));
+            } else {
+                switch (fProposalPopupOrientation) {
+                    case PROPOSAL_REMOVE: {
+                        // Remove the tip selector and place the
+                        // proposal selector beneath the cursor line.
+                        fShells[LAYOUT_CONTEXT_SELECTOR].dispose();
+                        Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+                        shell.setLocation(getBelowLocation(shell, offset));
+                        break;
+                    }
+                    case PROPOSAL_OVERLAY: {
+                        // Overlay the tip selector with the proposal selector.
+                        Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+                        shell.setLocation(getBelowLocation(shell, offset));
+                        break;
+                    }
+                    case PROPOSAL_STACKED: {
+                        // Stack the proposal selector beneath the tip selector.
+                        Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+                        Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
+                        shell.setLocation(getStackedLocation(shell, parent));
+                        break;
+                    }
+                    default:
+                }
+            }
+        }
+
+        protected void layoutContextSelector(int offset) {
+            // Always place the context selector beneath the cursor line.
+            Shell shell= fShells[LAYOUT_CONTEXT_SELECTOR];
+            shell.setLocation(getBelowLocation(shell, offset));
+
+            if (Helper2.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
+                switch (fProposalPopupOrientation) {
+                    case PROPOSAL_REMOVE:
+                        // Remove the proposal selector.
+                        fShells[LAYOUT_PROPOSAL_SELECTOR].dispose();
+                        break;
+
+                    case PROPOSAL_OVERLAY:
+                        // The proposal selector has been overlaid by the tip selector.
+                        break;
+
+                    case PROPOSAL_STACKED: {
+                        // Stack the proposal selector beneath the tip selector.
+                        shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+                        Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
+                        shell.setLocation(getStackedLocation(shell, parent));
+                        break;
+                    }
+                    default:
+                }
+            }
+        }
+
+        protected void layoutContextInfoPopup(int offset) {
+            switch (fContextInfoPopupOrientation) {
+                case CONTEXT_INFO_ABOVE: {
+                    // Place the popup above the cursor line.
+                    Shell shell= fShells[LAYOUT_CONTEXT_INFO_POPUP];
+                    shell.setLocation(getAboveLocation(shell, offset));
+                    break;
+                }
+                case CONTEXT_INFO_BELOW: {
+                    // Place the popup beneath the cursor line.
+                    Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
+                    parent.setLocation(getBelowLocation(parent, offset));
+                    if (Helper2.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
+                        // Stack the proposal selector beneath the context info popup.
+                        Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+                        shell.setLocation(getStackedLocation(shell, parent));
+                    }
+                    break;
+                }
+                default:
+            }
+        }
+
+        protected void shiftHorizontalLocation(Point location, Rectangle shellBounds, Rectangle displayBounds) {
+            if (location.x + shellBounds.width > displayBounds.width)
+                location.x= displayBounds.width - shellBounds.width;
+
+            if (location.x < displayBounds.x)
+                location.x= displayBounds.x;
+        }
+
+        protected void shiftVerticalLocation(Point location, Rectangle shellBounds, Rectangle displayBounds) {
+            if (location.y + shellBounds.height > displayBounds.height)
+                location.y= displayBounds.height - shellBounds.height;
+
+            if (location.y < displayBounds.y)
+                location.y= displayBounds.y;
+        }
+
+        protected Point getAboveLocation(Shell shell, int offset) {
+            StyledText text= fViewer.getTextWidget();
+            Point location= text.getLocationAtOffset(offset);
+            location= text.toDisplay(location);
+
+            Rectangle shellBounds= shell.getBounds();
+            Rectangle displayBounds= shell.getDisplay().getClientArea();
+
+            location.y=location.y - shellBounds.height;
+
+            shiftHorizontalLocation(location, shellBounds, displayBounds);
+            shiftVerticalLocation(location, shellBounds, displayBounds);
+
+            return location;
+        }
+
+        protected Point getBelowLocation(Shell shell, int offset) {
+            StyledText text= fViewer.getTextWidget();
+            Point location= text.getLocationAtOffset(offset);
+            if (location.x < 0) location.x= 0;
+            if (location.y < 0) location.y= 0;
+            location= text.toDisplay(location);
+
+            Rectangle shellBounds= shell.getBounds();
+            Rectangle displayBounds= shell.getDisplay().getClientArea();
+
+            location.y= location.y + text.getLineHeight(offset);
+            shiftHorizontalLocation(location, shellBounds, displayBounds);
+            shiftVerticalLocation(location, shellBounds, displayBounds);
+
+            return location;
+        }
+
+        protected Point getStackedLocation(Shell shell, Shell parent) {
+            Point p= parent.getLocation();
+            Point size= parent.getSize();
+            p.x += size.x / 4;
+            p.y += size.y;
+
+            p= parent.toDisplay(p);
+
+            Rectangle shellBounds= shell.getBounds();
+            Rectangle displayBounds= shell.getDisplay().getClientArea();
+            shiftHorizontalLocation(p, shellBounds, displayBounds);
+            shiftVerticalLocation(p, shellBounds, displayBounds);
+
+            return p;
+        }
+
+        protected void adjustListeners(int type) {
+            switch (type) {
+                case LAYOUT_PROPOSAL_SELECTOR:
+                    if (fContextType is LAYOUT_CONTEXT_SELECTOR &&
+                            Helper2.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR]))
+                        // Disable event notification to the tip selector.
+                        removeContentAssistListener(cast(IContentAssistListener2) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
+                    break;
+                case LAYOUT_CONTEXT_SELECTOR:
+                    if (Helper2.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR]))
+                        // Disable event notification to the proposal selector.
+                        removeContentAssistListener(cast(IContentAssistListener2) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
+                    break;
+                case LAYOUT_CONTEXT_INFO_POPUP:
+                    break;
+                default:
+            }
+        }
+    }
+
+    /**
+     * Internal key listener and event consumer.
+     */
+    class InternalListener : VerifyKeyListener, IEventConsumer {
+
+        /**
+         * Verifies key events by notifying the registered listeners.
+         * Each listener is allowed to indicate that the event has been
+         * handled and should not be further processed.
+         *
+         * @param e the verify event
+         * @see VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
+         */
+        public void verifyKey(VerifyEvent e) {
+            IContentAssistListener2[] listeners= arraycast!(IContentAssistListener2)( fListeners.dup );
+            for (int i= 0; i < listeners.length; i++) {
+                if (listeners[i] !is null) {
+                    if (!listeners[i].verifyKey(e) || !e.doit)
+                        return;
+                }
+            }
+        }
+
+        /*
+         * @see IEventConsumer#processEvent
+         */
+        public void processEvent(VerifyEvent event) {
+
+            installKeyListener();
+
+            IContentAssistListener2[] listeners= arraycast!(IContentAssistListener2)( fListeners.dup );
+            for (int i= 0; i < listeners.length; i++) {
+                if (listeners[i] !is null) {
+                    listeners[i].processEvent(event);
+                    if (!event.doit)
+                        return;
+                }
+            }
+        }
+    }
+
+
+    // Content-Assist Listener types
+    const static int CONTEXT_SELECTOR= 0;
+    const static int PROPOSAL_SELECTOR= 1;
+    const static int CONTEXT_INFO_POPUP= 2;
+
+    /**
+     * The popup priority: &gt; info pop-ups, &lt; standard content assist.
+     * Default value: <code>10</code>.
+     *
+     * @since 3.0
+     */
+    public static const int WIDGET_PRIORITY= 10;
+
+
+    private static const int DEFAULT_AUTO_ACTIVATION_DELAY= 500;
+
+    private IInformationControlCreator fInformationControlCreator;
+    private int fAutoActivationDelay= DEFAULT_AUTO_ACTIVATION_DELAY;
+    private bool fIsAutoActivated= false;
+    private bool fIsAutoInserting= false;
+    private int fProposalPopupOrientation= PROPOSAL_OVERLAY;
+    private int fContextInfoPopupOrientation= CONTEXT_INFO_ABOVE;
+    private Map fProcessors;
+    private String fPartitioning;
+
+    private Color fContextInfoPopupBackground;
+    private Color fContextInfoPopupForeground;
+    private Color fContextSelectorBackground;
+    private Color fContextSelectorForeground;
+
+    private ITextViewer fViewer;
+    private String fLastErrorMessage;
+
+    private Closer fCloser;
+    private LayoutManager fLayoutManager;
+    private AutoAssistListener fAutoAssistListener;
+    private InternalListener fInternalListener;
+    private CompletionProposalPopup2 fProposalPopup;
+    private ContextInformationPopup2 fContextInfoPopup;
+
+    private bool fKeyListenerHooked= false;
+    private IContentAssistListener2[] fListeners;
+    private int fCompletionPosition;
+    private String[] fProposalStrings;
+    private ICompletionProposal[] fProposals;
+    private const List fProposalListeners;
+
+    /**
+     * Tells whether colored label support is enabled.
+     * @since 3.4
+     */
+    private bool fIsColoredLabelsSupportEnabled= false;
+
+
+    /**
+     * Creates a new content assistant. The content assistant is not automatically activated,
+     * overlays the completion proposals with context information list if necessary, and
+     * shows the context information above the location at which it was activated. If auto
+     * activation will be enabled, without further configuration steps, this content assistant
+     * is activated after a 500 ms delay. It uses the default partitioning.
+     */
+    public this() {
+        fListeners= new IContentAssistListener2[4];
+        fProposalListeners= new ArrayList();
+
+        setContextInformationPopupOrientation(CONTEXT_INFO_ABOVE);
+        setInformationControlCreator(getInformationControlCreator());
+
+//      JavaTextTools textTools= JavaPlugin.getDefault().getJavaTextTools();
+//      IColorManager manager= textTools.getColorManager();
+//
+//      IPreferenceStore store=  JavaPlugin.getDefault().getPreferenceStore();
+//
+//      Color c= getColor(store, PreferenceConstants.CODEASSIST_PROPOSALS_FOREGROUND, manager);
+//      setProposalSelectorForeground(c);
+//
+//      c= getColor(store, PreferenceConstants.CODEASSIST_PROPOSALS_BACKGROUND, manager);
+//      setProposalSelectorBackground(c);
+    }
+
+    /**
+     * Creates an <code>IInformationControlCreator</code> to be used to display context information.
+     *
+     * @return an <code>IInformationControlCreator</code> to be used to display context information
+     */
+    private IInformationControlCreator getInformationControlCreator() {
+        return new class()  IInformationControlCreator {
+            public IInformationControl createInformationControl(Shell parent) {
+                return new DefaultInformationControl(parent, false);
+            }
+        };
+    }
+
+    /**
+     * Sets the document partitioning this content assistant is using.
+     *
+     * @param partitioning the document partitioning for this content assistant
+     */
+    public void setDocumentPartitioning(String partitioning) {
+        Assert.isNotNull(partitioning);
+        fPartitioning= partitioning;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension#getDocumentPartitioning()
+     * @since 3.0
+     */
+    public String getDocumentPartitioning() {
+        return fPartitioning;
+    }
+
+    /**
+     * Registers a given content assist processor for a particular content type.
+     * If there is already a processor registered for this type, the new processor
+     * is registered instead of the old one.
+     *
+     * @param processor the content assist processor to register, or <code>null</code> to remove an existing one
+     * @param contentType the content type under which to register
+     */
+     public void setContentAssistProcessor(IContentAssistProcessor processor, String contentType) {
+
+        Assert.isNotNull(contentType);
+
+        if (fProcessors is null)
+            fProcessors= new HashMap();
+
+        if (processor is null)
+            fProcessors.remove(contentType);
+        else
+            fProcessors.put(contentType, cast(Object)processor);
+    }
+
+    /*
+     * @see IContentAssistant#getContentAssistProcessor
+     */
+    public IContentAssistProcessor getContentAssistProcessor(String contentType) {
+        if (fProcessors is null)
+            return null;
+
+        return cast(IContentAssistProcessor) fProcessors.get(contentType);
+    }
+
+    /**
+     * Enables the content assistant's auto activation mode.
+     *
+     * @param enabled indicates whether auto activation is enabled or not
+     */
+    public void enableAutoActivation(bool enabled) {
+        fIsAutoActivated= enabled;
+        manageAutoActivation(fIsAutoActivated);
+    }
+
+    /**
+     * Enables the content assistant's auto insertion mode. If enabled,
+     * the content assistant inserts a proposal automatically if it is
+     * the only proposal. In the case of ambiguities, the user must
+     * make the choice.
+     *
+     * @param enabled indicates whether auto insertion is enabled or not
+     * @since 2.0
+     */
+    public void enableAutoInsert(bool enabled) {
+        fIsAutoInserting= enabled;
+    }
+
+    /**
+     * Returns whether this content assistant is in the auto insertion
+     * mode or not.
+     *
+     * @return <code>true</code> if in auto insertion mode
+     * @since 2.0
+     */
+    bool isAutoInserting() {
+        return fIsAutoInserting;
+    }
+
+    /**
+     * Installs and uninstall the listeners needed for auto-activation.
+     * @param start <code>true</code> if listeners must be installed,
+     *  <code>false</code> if they must be removed
+     * @since 2.0
+     */
+    private void manageAutoActivation(bool start) {
+        if (start) {
+
+            if (fViewer !is null && fAutoAssistListener is null) {
+                fAutoAssistListener= new AutoAssistListener();
+                if ( cast(ITextViewerExtension)fViewer ) {
+                    ITextViewerExtension extension= cast(ITextViewerExtension) fViewer;
+                    extension.appendVerifyKeyListener(fAutoAssistListener);
+                } else {
+                    StyledText textWidget= fViewer.getTextWidget();
+                    if (Helper2.okToUse(textWidget))
+                        textWidget.addVerifyKeyListener(fAutoAssistListener);
+                }
+            }
+
+        } else if (fAutoAssistListener !is null) {
+
+            if ( cast(ITextViewerExtension)fViewer ) {
+                ITextViewerExtension extension= cast(ITextViewerExtension) fViewer;
+                extension.removeVerifyKeyListener(fAutoAssistListener);
+            } else {
+                StyledText textWidget= fViewer.getTextWidget();
+                if (Helper2.okToUse(textWidget))
+                    textWidget.removeVerifyKeyListener(fAutoAssistListener);
+            }
+
+            fAutoAssistListener= null;
+        }
+    }
+
+    /**
+     * Sets the delay after which the content assistant is automatically invoked
+     * if the cursor is behind an auto activation character.
+     *
+     * @param delay the auto activation delay
+     */
+    public void setAutoActivationDelay(int delay) {
+        fAutoActivationDelay= delay;
+    }
+
+    /**
+     * Sets the proposal pop-ups' orientation.
+     * The following values may be used:
+     * <ul>
+     *   <li>PROPOSAL_OVERLAY<p>
+     *     proposal popup windows should overlay each other
+     *   </li>
+     *   <li>PROPOSAL_REMOVE<p>
+     *     any currently shown proposal popup should be closed
+     *   </li>
+     *   <li>PROPOSAL_STACKED<p>
+     *     proposal popup windows should be vertical stacked, with no overlap,
+     *     beneath the line containing the current cursor location
+     *   </li>
+     * </ul>
+     *
+     * @param orientation the popup's orientation
+     */
+    public void setProposalPopupOrientation(int orientation) {
+        fProposalPopupOrientation= orientation;
+    }
+
+    /**
+     * Sets the context information popup's orientation.
+     * The following values may be used:
+     * <ul>
+     *   <li>CONTEXT_ABOVE<p>
+     *     context information popup should always appear above the line containing
+     *     the current cursor location
+     *   </li>
+     *   <li>CONTEXT_BELOW<p>
+     *     context information popup should always appear below the line containing
+     *     the current cursor location
+     *   </li>
+     * </ul>
+     *
+     * @param orientation the popup's orientation
+     */
+    public void setContextInformationPopupOrientation(int orientation) {
+        fContextInfoPopupOrientation= orientation;
+    }
+
+    /**
+     * Sets the context information popup's background color.
+     *
+     * @param background the background color
+     */
+    public void setContextInformationPopupBackground(Color background) {
+        fContextInfoPopupBackground= background;
+    }
+
+    /**
+     * Returns the background of the context information popup.
+     *
+     * @return the background of the context information popup
+     * @since 2.0
+     */
+    Color getContextInformationPopupBackground() {
+        return fContextInfoPopupBackground;
+    }
+
+    /**
+     * Sets the context information popup's foreground color.
+     *
+     * @param foreground the foreground color
+     * @since 2.0
+     */
+    public void setContextInformationPopupForeground(Color foreground) {
+        fContextInfoPopupForeground= foreground;
+    }
+
+    /**
+     * Returns the foreground of the context information popup.
+     *
+     * @return the foreground of the context information popup
+     * @since 2.0
+     */
+    Color getContextInformationPopupForeground() {
+        return fContextInfoPopupForeground;
+    }
+
+    /**
+     * Sets the context selector's background color.
+     *
+     * @param background the background color
+     * @since 2.0
+     */
+    public void setContextSelectorBackground(Color background) {
+        fContextSelectorBackground= background;
+    }
+
+    /**
+     * Returns the background of the context selector.
+     *
+     * @return the background of the context selector
+     * @since 2.0
+     */
+    Color getContextSelectorBackground() {
+        return fContextSelectorBackground;
+    }
+
+    /**
+     * Sets the context selector's foreground color.
+     *
+     * @param foreground the foreground color
+     * @since 2.0
+     */
+    public void setContextSelectorForeground(Color foreground) {
+        fContextSelectorForeground= foreground;
+    }
+
+    /**
+     * Returns the foreground of the context selector.
+     *
+     * @return the foreground of the context selector
+     * @since 2.0
+     */
+    Color getContextSelectorForeground() {
+        return fContextSelectorForeground;
+    }
+
+    /**
+     * Sets the information control creator for the additional information control.
+     *
+     * @param creator the information control creator for the additional information control
+     * @since 2.0
+     */
+    public void setInformationControlCreator(IInformationControlCreator creator) {
+        fInformationControlCreator= creator;
+    }
+
+    /*
+     * @see IContentAssist#install
+     */
+    public void install(ITextViewer textViewer) {
+        Assert.isNotNull(cast(Object)textViewer);
+
+        fViewer= textViewer;
+
+        fLayoutManager= new LayoutManager();
+        fInternalListener= new InternalListener();
+
+        AdditionalInfoController2 controller= null;
+        if (fInformationControlCreator !is null) {
+            int delay= fAutoActivationDelay;
+            if (delay is 0)
+                delay= DEFAULT_AUTO_ACTIVATION_DELAY;
+            delay= cast(int)Math.round(delay * 1.5f);
+            controller= new AdditionalInfoController2(fInformationControlCreator, delay);
+        }
+        fContextInfoPopup= new ContextInformationPopup2(this, fViewer);
+        fProposalPopup= new CompletionProposalPopup2(this, fViewer, controller);
+
+        manageAutoActivation(fIsAutoActivated);
+    }
+
+    /*
+     * @see IContentAssist#uninstall
+     */
+    public void uninstall() {
+
+        if (fProposalPopup !is null)
+            fProposalPopup.hide();
+
+        if (fContextInfoPopup !is null)
+            fContextInfoPopup.hide();
+
+        manageAutoActivation(false);
+
+        if (fCloser !is null) {
+            fCloser.uninstall();
+            fCloser= null;
+        }
+
+        fViewer= null;
+    }
+
+    /**
+     * Adds the given shell of the specified type to the layout.
+     * Valid types are defined by <code>LayoutManager</code>.
+     *
+     * @param popup a content assist popup
+     * @param shell the shell of the content-assist popup
+     * @param type the type of popup
+     * @param visibleOffset the offset at which to layout the popup relative to the offset of the viewer's visible region
+     * @since 2.0
+     */
+    void addToLayout(Object popup, Shell shell, int type, int visibleOffset) {
+        fLayoutManager.add(popup, shell, type, visibleOffset);
+    }
+
+    /**
+     * Layouts the registered popup of the given type relative to the
+     * given offset. The offset is relative to the offset of the viewer's visible region.
+     * Valid types are defined by <code>LayoutManager</code>.
+     *
+     * @param type the type of popup to layout
+     * @param visibleOffset the offset at which to layout relative to the offset of the viewer's visible region
+     * @since 2.0
+     */
+    void layout(int type, int visibleOffset) {
+        fLayoutManager.layout(type, visibleOffset);
+    }
+
+    /**
+     * Notifies the controller that a popup has lost focus.
+     *
+     * @param e the focus event
+     */
+    void popupFocusLost(FocusEvent e) {
+        fCloser.focusLost(e);
+    }
+
+    /**
+     * Returns the offset of the selection relative to the offset of the visible region.
+     *
+     * @return the offset of the selection relative to the offset of the visible region
+     * @since 2.0
+     */
+    int getSelectionOffset() {
+        StyledText text= fViewer.getTextWidget();
+        return text.getSelectionRange().x;
+    }
+
+    /**
+     * Returns whether the widget token could be acquired.
+     * The following are valid listener types:
+     * <ul>
+     *   <li>AUTO_ASSIST
+     *   <li>CONTEXT_SELECTOR
+     *   <li>PROPOSAL_SELECTOR
+     *   <li>CONTEXT_INFO_POPUP
+     * <ul>
+     * @param type the listener type for which to acquire
+     * @return <code>true</code> if the widget token could be acquired
+     * @since 2.0
+     */
+    private bool acquireWidgetToken(int type) {
+        switch (type) {
+            case CONTEXT_SELECTOR:
+            case PROPOSAL_SELECTOR:
+                if ( cast(IWidgetTokenOwner)fViewer ) {
+                    IWidgetTokenOwner owner= cast(IWidgetTokenOwner) fViewer;
+                    return owner.requestWidgetToken(this);
+                } else if ( cast(IWidgetTokenOwnerExtension)fViewer )  {
+                    IWidgetTokenOwnerExtension extension= cast(IWidgetTokenOwnerExtension) fViewer;
+                    return extension.requestWidgetToken(this, WIDGET_PRIORITY);
+                }
+            default:
+        }
+        return true;
+    }
+
+    /**
+     * Registers a content assist listener.
+     * The following are valid listener types:
+     * <ul>
+     *   <li>AUTO_ASSIST
+     *   <li>CONTEXT_SELECTOR
+     *   <li>PROPOSAL_SELECTOR
+     *   <li>CONTEXT_INFO_POPUP
+     * <ul>
+     * Returns whether the listener could be added successfully. A listener
+     * can not be added if the widget token could not be acquired.
+     *
+     * @param listener the listener to register
+     * @param type the type of listener
+     * @return <code>true</code> if the listener could be added
+     */
+    bool addContentAssistListener(IContentAssistListener2 listener, int type) {
+
+        if (acquireWidgetToken(type)) {
+
+            fListeners[type]= listener;
+
+            if (getNumberOfListeners() is 1) {
+                fCloser= new Closer();
+                fCloser.install();
+                fViewer.setEventConsumer(fInternalListener);
+                installKeyListener();
+            }
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Installs a key listener on the text viewer's widget.
+     */
+    private void installKeyListener() {
+        if (!fKeyListenerHooked) {
+            StyledText text= fViewer.getTextWidget();
+            if (Helper2.okToUse(text)) {
+
+                if ( cast(ITextViewerExtension)fViewer ) {
+                    ITextViewerExtension e= cast(ITextViewerExtension) fViewer;
+                    e.prependVerifyKeyListener(fInternalListener);
+                } else {
+                    text.addVerifyKeyListener(fInternalListener);
+                }
+
+                fKeyListenerHooked= true;
+            }
+        }
+    }
+
+    /**
+     * Releases the previously acquired widget token if the token
+     * is no longer necessary.
+     * The following are valid listener types:
+     * <ul>
+     *   <li>AUTO_ASSIST
+     *   <li>CONTEXT_SELECTOR
+     *   <li>PROPOSAL_SELECTOR
+     *   <li>CONTEXT_INFO_POPUP
+     * <ul>
+     *
+     * @param type the listener type
+     * @since 2.0
+     */
+    private void releaseWidgetToken(int type) {
+        if (fListeners[CONTEXT_SELECTOR] is null && fListeners[PROPOSAL_SELECTOR] is null) {
+            if ( cast(IWidgetTokenOwner)fViewer ) {
+                IWidgetTokenOwner owner= cast(IWidgetTokenOwner) fViewer;
+                owner.releaseWidgetToken(this);
+            }
+        }
+    }
+
+    /**
+     * Unregisters a content assist listener.
+     *
+     * @param listener the listener to unregister
+     * @param type the type of listener
+     *
+     * @see #addContentAssistListener
+     */
+    void removeContentAssistListener(IContentAssistListener2 listener, int type) {
+        fListeners[type]= null;
+
+        if (getNumberOfListeners() is 0) {
+
+            if (fCloser !is null) {
+                fCloser.uninstall();
+                fCloser= null;
+            }
+
+            uninstallKeyListener();
+            fViewer.setEventConsumer(null);
+        }
+
+        releaseWidgetToken(type);
+    }
+
+    /**
+     * Uninstall the key listener from the text viewer's widget.
+     */
+    private void uninstallKeyListener() {
+        if (fKeyListenerHooked) {
+            StyledText text= fViewer.getTextWidget();
+            if (Helper2.okToUse(text)) {
+
+                if ( cast(ITextViewerExtension)fViewer ) {
+                    ITextViewerExtension e= cast(ITextViewerExtension) fViewer;
+                    e.removeVerifyKeyListener(fInternalListener);
+                } else {
+                    text.removeVerifyKeyListener(fInternalListener);
+                }
+
+                fKeyListenerHooked= false;
+            }
+        }
+    }
+
+    /**
+     * Returns the number of listeners.
+     *
+     * @return the number of listeners
+     * @since 2.0
+     */
+    private int getNumberOfListeners() {
+        int count= 0;
+        for (int i= 0; i <= CONTEXT_INFO_POPUP; i++) {
+            if (fListeners[i] !is null)
+                ++ count;
+        }
+        return count;
+    }
+
+    /*
+     * @see IContentAssist#showPossibleCompletions
+     */
+    public String showPossibleCompletions() {
+        return fProposalPopup.showProposals(false);
+    }
+
+    /**
+     * Hides the proposal popup.
+     */
+    public void hidePossibleCompletions() {
+        if (fProposalPopup !is null)
+            fProposalPopup.hide();
+    }
+
+    /**
+     * Hides any open pop-ups.
+     */
+    protected void hide() {
+        if (fProposalPopup !is null)
+            fProposalPopup.hide();
+        if (fContextInfoPopup !is null)
+            fContextInfoPopup.hide();
+    }
+    package void hide_package() {
+        hide();
+    }
+
+    /**
+     * Callback to signal this content assistant that the presentation of the possible completions has been stopped.
+     * @since 2.1
+     */
+    protected void possibleCompletionsClosed() {
+    }
+    package void possibleCompletionsClosed_package(){
+        possibleCompletionsClosed();
+    }
+    /*
+     * @see IContentAssist#showContextInformation
+     */
+    public String showContextInformation() {
+        return fContextInfoPopup.showContextProposals(false);
+    }
+
+
+    /**
+     * Callback to signal this content assistant that the presentation of the context information has been stopped.
+     * @since 2.1
+     */
+    protected void contextInformationClosed() {
+    }
+    package void contextInformationClosed_package() {
+        contextInformationClosed();
+    }
+
+    /**
+     * Requests that the specified context information to be shown.
+     *
+     * @param contextInformation the context information to be shown
+     * @param position the position to which the context information refers to
+     * @since 2.0
+     */
+    void showContextInformation(IContextInformation contextInformation, int position) {
+        fContextInfoPopup.showContextInformation(contextInformation, position);
+    }
+
+    /**
+     * Returns the current content assist error message.
+     *
+     * @return an error message or <code>null</code> if no error has occurred
+     */
+    String getErrorMessage() {
+        return fLastErrorMessage;
+    }
+
+    /**
+     * Returns the content assist processor for the content
+     * type of the specified document position.
+     *
+     * @param viewer the text viewer
+     * @param offset a offset within the document
+     * @return a content-assist processor or <code>null</code> if none exists
+     */
+    private IContentAssistProcessor getProcessor(ITextViewer viewer, int offset) {
+        try {
+            String type= TextUtilities.getContentType(viewer.getDocument(), getDocumentPartitioning(), offset, true);
+            return getContentAssistProcessor(type);
+        } catch (BadLocationException x) {
+        }
+        return null;
+    }
+
+    /**
+     * Returns an array of completion proposals computed based on
+     * the specified document position. The position is used to
+     * determine the appropriate content assist processor to invoke.
+     *
+     * @param viewer the viewer for which to compute the proposals
+     * @param position a document position
+     * @return an array of completion proposals
+     *
+     * @see IContentAssistProcessor#computeCompletionProposals
+     */
+    ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int position) {
+        if (fProposals !is null) {
+            return fProposals;
+        } else if (fProposalStrings !is null) {
+            ICompletionProposal[] result= new ICompletionProposal[fProposalStrings.length];
+            for (int i= 0; i < fProposalStrings.length; i++) {
+                result[i]= new CompletionProposal(fProposalStrings[i], position, fProposalStrings[i].length(), fProposalStrings[i].length());
+            }
+            return result;
+        } else return null;
+    }
+
+    /**
+     * Returns an array of context information objects computed based
+     * on the specified document position. The position is used to determine
+     * the appropriate content assist processor to invoke.
+     *
+     * @param viewer the viewer for which to compute the context information
+     * @param position a document position
+     * @return an array of context information objects
+     *
+     * @see IContentAssistProcessor#computeContextInformation
+     */
+    IContextInformation[] computeContextInformation(ITextViewer viewer, int position) {
+        fLastErrorMessage= null;
+
+        IContextInformation[] result= null;
+
+        IContentAssistProcessor p= getProcessor(viewer, position);
+        if (p !is null) {
+            result= p.computeContextInformation(viewer, position);
+            fLastErrorMessage= p.getErrorMessage();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns the context information validator that should be used to
+     * determine when the currently displayed context information should
+     * be dismissed. The position is used to determine the appropriate
+     * content assist processor to invoke.
+     *
+     * @param textViewer the text viewer
+     * @param offset a document offset
+     * @return an validator
+     *
+     * @see IContentAssistProcessor#getContextInformationValidator
+     */
+    IContextInformationValidator getContextInformationValidator(ITextViewer textViewer, int offset) {
+        IContentAssistProcessor p= getProcessor(textViewer, offset);
+        return p !is null ? p.getContextInformationValidator() : null;
+    }
+
+    /**
+     * Returns the context information presenter that should be used to
+     * display context information. The position is used to determine the appropriate
+     * content assist processor to invoke.
+     *
+     * @param textViewer the text viewer
+     * @param offset a document offset
+     * @return a presenter
+     * @since 2.0
+     */
+    IContextInformationPresenter getContextInformationPresenter(ITextViewer textViewer, int offset) {
+        IContextInformationValidator validator= getContextInformationValidator(textViewer, offset);
+        if ( cast(IContextInformationPresenter)validator )
+            return cast(IContextInformationPresenter) validator;
+        return null;
+    }
+
+    /**
+     * Returns the characters which when typed by the user should automatically
+     * initiate proposing completions. The position is used to determine the
+     * appropriate content assist processor to invoke.
+     *
+     * @param textViewer the text viewer
+     * @param offset a document offset
+     * @return the auto activation characters
+     *
+     * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters
+     */
+    private char[] getCompletionProposalAutoActivationCharacters(ITextViewer textViewer, int offset) {
+        IContentAssistProcessor p= getProcessor(textViewer, offset);
+        return p !is null ? p.getCompletionProposalAutoActivationCharacters() : null;
+    }
+
+    /**
+     * Returns the characters which when typed by the user should automatically
+     * initiate the presentation of context information. The position is used
+     * to determine the appropriate content assist processor to invoke.
+     *
+     * @param textViewer the text viewer
+     * @param offset a document offset
+     * @return the auto activation characters
+     *
+     * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters
+     */
+    private char[] getContextInformationAutoActivationCharacters(ITextViewer textViewer, int offset) {
+        IContentAssistProcessor p= getProcessor(textViewer, offset);
+        return p !is null ? p.getContextInformationAutoActivationCharacters() : null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner)
+     * @since 2.0
+     */
+    public bool requestWidgetToken(IWidgetTokenOwner owner) {
+        hidePossibleCompletions();
+        return true;
+    }
+
+    /**
+     * @param completionPosition
+     */
+    public void setCompletionPosition(int completionPosition) {
+        fCompletionPosition= completionPosition;
+    }
+
+    /**
+     * @return the completion position
+     */
+    public int getCompletionPosition() {
+        return fCompletionPosition;
+    }
+
+    /**
+     * @param proposals
+     */
+    public void setCompletions(String[] proposals) {
+        fProposalStrings= proposals;
+    }
+
+    /**
+     * @param proposals
+     */
+    public void setCompletions(ICompletionProposal[] proposals) {
+        fProposals= proposals;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(org.eclipse.jface.text.IWidgetTokenOwner, int)
+     * @since 3.0
+     */
+    public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) {
+        if (priority > WIDGET_PRIORITY) {
+            hidePossibleCompletions();
+            return true;
+        }
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#setFocus(org.eclipse.jface.text.IWidgetTokenOwner)
+     * @since 3.0
+     */
+    public bool setFocus(IWidgetTokenOwner owner) {
+        if (fProposalPopup !is null) {
+            fProposalPopup.setFocus();
+            return fProposalPopup.hasFocus();
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether any popups controlled by the receiver have the input focus.
+     *
+     * @return <code>true</code> if any of the managed popups have the focus, <code>false</code> otherwise
+     */
+    public bool hasFocus() {
+        return (fProposalPopup !is null && fProposalPopup.hasFocus())
+                || (fContextInfoPopup !is null && fContextInfoPopup.hasFocus());
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension#completePrefix()
+     */
+    public String completePrefix() {
+        return null;
+    }
+
+    /**
+     * @param proposal
+     */
+    public void fireProposalChosen(ICompletionProposal proposal) {
+        List list= new ArrayList(fProposalListeners);
+        for (Iterator it= list.iterator(); it.hasNext();) {
+            IProposalListener listener= cast(IProposalListener) it.next();
+            listener.proposalChosen(proposal);
+        }
+
+    }
+
+    /**
+     * @param listener
+     */
+    public void removeProposalListener(IProposalListener listener) {
+        fProposalListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * @param listener
+     */
+    public void addProposalListener(IProposalListener listener) {
+        fProposalListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Tells whether the support for colored labels is enabled.
+     *
+     * @return <code>true</code> if the support for colored labels is enabled, <code>false</code> otherwise
+     * @since 3.4
+     */
+    bool isColoredLabelsSupportEnabled() {
+        return fIsColoredLabelsSupportEnabled;
+    }
+
+    /**
+     * Enables the support for colored labels in the proposal popup.
+     * <p>Completion proposals can implement {@link ICompletionProposalExtension6}
+     * to provide colored proposal labels.</p>
+     *
+     * @param isEnabled if <code>true</code> the support for colored labels is enabled in the proposal popup
+     * @since 3.4
+     */
+    public void enableColoredLabels(bool isEnabled) {
+        fIsColoredLabelsSupportEnabled= isEnabled;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/ContextInformationPopup2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,648 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.internal.text.link.contentassist.ContextInformationPopup2;
+
+import org.eclipse.jface.internal.text.link.contentassist.IProposalListener; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.LineBreakingReader; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.CompletionProposalPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistMessages; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.Helper2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.PopupCloser2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.IContentAssistListener2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistant2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.AdditionalInfoController2; // packageimport
+
+import java.lang.all;
+import java.util.Stack;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.contentassist.IContextInformationExtension;
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter;
+import org.eclipse.jface.text.contentassist.IContextInformationValidator;
+
+
+/**
+ * This class is used to present context information to the user.
+ * If multiple contexts are valid at the current cursor location,
+ * a list is presented from which the user may choose one context.
+ * Once the user makes their choice, or if there was only a single
+ * possible context, the context information is shown in a tooltip like popup. <p>
+ * If the tooltip is visible and the user wants to see context information of
+ * a context embedded into the one for which context information is displayed,
+ * context information for the embedded context is shown. As soon as the
+ * cursor leaves the embedded context area, the context information for
+ * the embedding context is shown again.
+ *
+ * @see IContextInformation
+ * @see IContextInformationValidator
+ */
+class ContextInformationPopup2 : IContentAssistListener2 {
+
+
+
+    /**
+     * Represents the state necessary for embedding contexts.
+     * @since 2.0
+     */
+    static class ContextFrame {
+        public int fBeginOffset;
+        public int fOffset;
+        public int fVisibleOffset;
+        public IContextInformation fInformation;
+        public IContextInformationValidator fValidator;
+        public IContextInformationPresenter fPresenter;
+    }
+
+    private ITextViewer fViewer;
+    private ContentAssistant2 fContentAssistant;
+
+    private PopupCloser2 fPopupCloser;
+    private Shell fContextSelectorShell;
+    private Table fContextSelectorTable;
+    private IContextInformation[] fContextSelectorInput;
+    private String fLineDelimiter= null;
+
+    private Shell fContextInfoPopup;
+    private StyledText fContextInfoText;
+    private TextPresentation fTextPresentation;
+
+    private Stack fContextFrameStack;
+
+
+    /**
+     * Creates a new context information popup.
+     *
+     * @param contentAssistant the content assist for computing the context information
+     * @param viewer the viewer on top of which the context information is shown
+     */
+    public this(ContentAssistant2 contentAssistant, ITextViewer viewer) {
+        fPopupCloser= new PopupCloser2();
+        fContextFrameStack= new Stack();
+        fContentAssistant= contentAssistant;
+        fViewer= viewer;
+    }
+
+    /**
+     * Shows all possible contexts for the given cursor position of the viewer.
+     *
+     * @param autoActivated <code>true</code>  if auto activated
+     * @return  a potential error message or <code>null</code> in case of no error
+     */
+    public String showContextProposals(bool autoActivated) {
+        final StyledText styledText= fViewer.getTextWidget();
+        BusyIndicator.showWhile(styledText.getDisplay(), dgRunnable( (bool autoActivated_, StyledText styledText_ ) {
+            int position= fViewer.getSelectedRange().x;
+
+            IContextInformation[] contexts= computeContextInformation(position);
+            int count = (contexts is null ? 0 : contexts.length);
+            if (count is 1) {
+
+                // Show context information directly
+                internalShowContextInfo(contexts[0], position);
+
+            } else if (count > 0) {
+                // Precise context must be selected
+
+                if (fLineDelimiter is null)
+                    fLineDelimiter= styledText_.getLineDelimiter();
+
+                createContextSelector();
+                setContexts(contexts);
+                displayContextSelector();
+                hideContextInfoPopup();
+
+            } else if (!autoActivated_) {
+                styledText_.getDisplay().beep();
+            }
+        }, autoActivated, styledText ));
+
+        return getErrorMessage();
+    }
+
+    /**
+     * Displays the given context information for the given offset.
+     *
+     * @param info the context information
+     * @param position the offset
+     * @since 2.0
+     */
+    public void showContextInformation(IContextInformation info, int position) {
+        Control control= fViewer.getTextWidget();
+        BusyIndicator.showWhile(control.getDisplay(), dgRunnable( (IContextInformation info_, int position_) {
+            internalShowContextInfo(info_, position_);
+            hideContextSelector();
+        }, info, position));
+    }
+
+    /**
+     * Displays the given context information for the given offset.
+     *
+     * @param information the context information
+     * @param offset the offset
+     * @since 2.0
+     */
+
+    private void internalShowContextInfo(IContextInformation information, int offset) {
+
+        IContextInformationValidator validator= fContentAssistant.getContextInformationValidator(fViewer, offset);
+
+        if (validator !is null) {
+            ContextFrame current= new ContextFrame();
+            current.fInformation= information;
+            current.fBeginOffset= ( cast(IContextInformationExtension)information ) ? (cast(IContextInformationExtension) information).getContextInformationPosition() : offset;
+            if (current.fBeginOffset is -1) current.fBeginOffset= offset;
+            current.fOffset= offset;
+            current.fVisibleOffset= fViewer.getTextWidget().getSelectionRange().x - (offset - current.fBeginOffset);
+            current.fValidator= validator;
+            current.fPresenter= fContentAssistant.getContextInformationPresenter(fViewer, offset);
+
+            fContextFrameStack.push(current);
+
+            internalShowContextFrame(current, fContextFrameStack.size() is 1);
+        }
+    }
+
+    /**
+     * Shows the given context frame.
+     *
+     * @param frame the frane to display
+     * @param initial <code>true</code> if this is the first frame to be displayed
+     * @since 2.0
+     */
+    private void internalShowContextFrame(ContextFrame frame, bool initial) {
+
+        frame.fValidator.install(frame.fInformation, fViewer, frame.fOffset);
+
+        if (frame.fPresenter !is null) {
+            if (fTextPresentation is null)
+                fTextPresentation= new TextPresentation();
+            frame.fPresenter.install(frame.fInformation, fViewer, frame.fBeginOffset);
+            frame.fPresenter.updatePresentation(frame.fOffset, fTextPresentation);
+        }
+
+        createContextInfoPopup();
+
+        fContextInfoText.setText(frame.fInformation.getInformationDisplayString());
+        if (fTextPresentation !is null)
+            TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText);
+        resize();
+
+        if (initial) {
+            if (fContentAssistant.addContentAssistListener(this, ContentAssistant2.CONTEXT_INFO_POPUP)) {
+                fContentAssistant.addToLayout(this, fContextInfoPopup, ContentAssistant2.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset);
+                fContextInfoPopup.setVisible(true);
+            }
+        } else {
+            fContentAssistant.layout(ContentAssistant2.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset);
+        }
+    }
+
+    /**
+     * Computes all possible context information for the given offset.
+     *
+     * @param position the offset
+     * @return all possible context information for the given offset
+     * @since 2.0
+     */
+    private IContextInformation[] computeContextInformation(int position) {
+        return fContentAssistant.computeContextInformation(fViewer, position);
+    }
+
+    /**
+     *Returns the error message generated while computing context information.
+     *
+     * @return the error message
+     */
+    private String getErrorMessage() {
+        return fContentAssistant.getErrorMessage();
+    }
+
+    /**
+     * Creates the context information popup. This is the tooltip like overlay window.
+     */
+    private void createContextInfoPopup() {
+        if (Helper2.okToUse(fContextInfoPopup))
+            return;
+
+        Control control= fViewer.getTextWidget();
+        Display display= control.getDisplay();
+
+        fContextInfoPopup= new Shell(control.getShell(), SWT.NO_TRIM | SWT.ON_TOP);
+        fContextInfoPopup.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
+
+        fContextInfoText= new StyledText(fContextInfoPopup, SWT.MULTI | SWT.READ_ONLY);
+
+        Color c= fContentAssistant.getContextInformationPopupBackground();
+        if (c is null)
+            c= display.getSystemColor(SWT.COLOR_INFO_BACKGROUND);
+        fContextInfoText.setBackground(c);
+
+        c= fContentAssistant.getContextInformationPopupForeground();
+        if (c is null)
+            c= display.getSystemColor(SWT.COLOR_INFO_FOREGROUND);
+        fContextInfoText.setForeground(c);
+    }
+
+    /**
+     * Resizes the context information popup.
+     * @since 2.0
+     */
+    private void resize() {
+        Point size= fContextInfoText.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+        size.x += 3;
+        fContextInfoText.setSize(size);
+        fContextInfoText.setLocation(1,1);
+        size.x += 2;
+        size.y += 2;
+        fContextInfoPopup.setSize(size);
+    }
+
+    /**
+     *Hides the context information popup.
+     */
+    private void hideContextInfoPopup() {
+
+        if (Helper2.okToUse(fContextInfoPopup)) {
+
+            int size= fContextFrameStack.size();
+            if (size > 0) {
+                fContextFrameStack.pop();
+                -- size;
+            }
+
+            if (size > 0) {
+                ContextFrame current= cast(ContextFrame) fContextFrameStack.peek();
+                internalShowContextFrame(current, false);
+            } else {
+
+                fContentAssistant.removeContentAssistListener(this, ContentAssistant2.CONTEXT_INFO_POPUP);
+
+                fContextInfoPopup.setVisible(false);
+                fContextInfoPopup.dispose();
+                fContextInfoPopup= null;
+
+                if (fTextPresentation !is null) {
+                    fTextPresentation.clear();
+                    fTextPresentation= null;
+                }
+            }
+        }
+
+        if (fContextInfoPopup is null)
+            fContentAssistant.contextInformationClosed_package();
+    }
+
+    /**
+     * Creates the context selector in case the user has the choice between multiple valid contexts
+     * at a given offset.
+     */
+    private void createContextSelector() {
+        if (Helper2.okToUse(fContextSelectorShell))
+            return;
+
+        Control control= fViewer.getTextWidget();
+        fContextSelectorShell= new Shell(control.getShell(), SWT.NO_TRIM | SWT.ON_TOP);
+        GridLayout layout= new GridLayout();
+        layout.marginWidth= 0;
+        layout.marginHeight= 0;
+        fContextSelectorShell.setLayout(layout);
+        fContextSelectorShell.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_BLACK));
+
+
+        fContextSelectorTable= new Table(fContextSelectorShell, SWT.H_SCROLL | SWT.V_SCROLL);
+        fContextSelectorTable.setLocation(1, 1);
+        GridData gd= new GridData(GridData.FILL_BOTH);
+        gd.heightHint= fContextSelectorTable.getItemHeight() * 10;
+        gd.widthHint= 300;
+        fContextSelectorTable.setLayoutData(gd);
+
+        fContextSelectorShell.pack(true);
+
+        Color c= fContentAssistant.getContextSelectorBackground();
+        if (c is null)
+            c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
+        fContextSelectorTable.setBackground(c);
+
+        c= fContentAssistant.getContextSelectorForeground();
+        if (c is null)
+            c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
+        fContextSelectorTable.setForeground(c);
+
+        fContextSelectorTable.addSelectionListener(new class()  SelectionListener {
+            public void widgetSelected(SelectionEvent e) {
+            }
+
+            public void widgetDefaultSelected(SelectionEvent e) {
+                insertSelectedContext();
+                hideContextSelector();
+            }
+        });
+
+        fPopupCloser.install(fContentAssistant, fContextSelectorTable);
+
+        fContextSelectorTable.setHeaderVisible(false);
+        fContentAssistant.addToLayout(this, fContextSelectorShell, ContentAssistant2.LayoutManager.LAYOUT_CONTEXT_SELECTOR, fContentAssistant.getSelectionOffset());
+    }
+
+    /**
+     * Causes the context information of the context selected in the context selector
+     * to be displayed in the context information popup.
+     */
+    private void insertSelectedContext() {
+        int i= fContextSelectorTable.getSelectionIndex();
+
+        if (i < 0 || i >= fContextSelectorInput.length)
+            return;
+
+        int position= fViewer.getSelectedRange().x;
+        internalShowContextInfo(fContextSelectorInput[i], position);
+    }
+
+    /**
+     * Sets the contexts in the context selector to the given set.
+     *
+     * @param contexts the possible contexts
+     */
+    private void setContexts(IContextInformation[] contexts) {
+        if (Helper2.okToUse(fContextSelectorTable)) {
+
+            fContextSelectorInput= contexts;
+
+            fContextSelectorTable.setRedraw(false);
+            fContextSelectorTable.removeAll();
+
+            TableItem item;
+            IContextInformation t;
+            for (int i= 0; i < contexts.length; i++) {
+                t= contexts[i];
+                item= new TableItem(fContextSelectorTable, SWT.NULL);
+                if (t.getImage() !is null)
+                    item.setImage(t.getImage());
+                item.setText(t.getContextDisplayString());
+            }
+
+            fContextSelectorTable.select(0);
+            fContextSelectorTable.setRedraw(true);
+        }
+    }
+
+    /**
+     * Displays the context selector.
+     */
+    private void displayContextSelector() {
+        if (fContentAssistant.addContentAssistListener(this, ContentAssistant2.CONTEXT_SELECTOR))
+            fContextSelectorShell.setVisible(true);
+    }
+
+    /**
+     * Hodes the context selector.
+     */
+    private void hideContextSelector() {
+        if (Helper2.okToUse(fContextSelectorShell)) {
+            fContentAssistant.removeContentAssistListener(this, ContentAssistant2.CONTEXT_SELECTOR);
+
+            fPopupCloser.uninstall();
+            fContextSelectorShell.setVisible(false);
+            fContextSelectorShell.dispose();
+            fContextSelectorShell= null;
+        }
+
+        if (!Helper2.okToUse(fContextInfoPopup))
+            fContentAssistant.contextInformationClosed_package();
+    }
+
+    /**
+     *Returns whether the context selector has the focus.
+     *
+     * @return <code>true</code> if teh context selector has the focus
+     */
+    public bool hasFocus() {
+        if (Helper2.okToUse(fContextSelectorShell))
+            return (fContextSelectorShell.isFocusControl() || fContextSelectorTable.isFocusControl());
+
+        return false;
+    }
+
+    /**
+     * Hides context selector and context information popup.
+     */
+    public void hide() {
+        hideContextSelector();
+        hideContextInfoPopup();
+    }
+
+    /**
+     * Returns whether this context information popup is active. I.e., either
+     * a context selector or context information is displayed.
+     *
+     * @return <code>true</code> if the context selector is active
+     */
+    public bool isActive() {
+        return (Helper2.okToUse(fContextInfoPopup) || Helper2.okToUse(fContextSelectorShell));
+    }
+
+    /*
+     * @see IContentAssistListener#verifyKey(VerifyEvent)
+     */
+    public bool verifyKey(VerifyEvent e) {
+        if (Helper2.okToUse(fContextSelectorShell))
+            return contextSelectorKeyPressed(e);
+        if (Helper2.okToUse(fContextInfoPopup))
+            return contextInfoPopupKeyPressed(e);
+        return true;
+    }
+
+    /**
+     * Processes a key stroke in the context selector.
+     *
+     * @param e the verify event describing the key stroke
+     * @return <code>true</code> if processing can be stopped
+     */
+    private bool contextSelectorKeyPressed(VerifyEvent e) {
+
+        char key= e.character;
+        if (key is 0) {
+
+            int change;
+            int visibleRows= (fContextSelectorTable.getSize().y / fContextSelectorTable.getItemHeight()) - 1;
+            int selection= fContextSelectorTable.getSelectionIndex();
+
+            switch (e.keyCode) {
+
+                case SWT.ARROW_UP:
+                    change= (fContextSelectorTable.getSelectionIndex() > 0 ? -1 : 0);
+                    break;
+
+                case SWT.ARROW_DOWN:
+                    change= (fContextSelectorTable.getSelectionIndex() < fContextSelectorTable.getItemCount() - 1 ? 1 : 0);
+                    break;
+
+                case SWT.PAGE_DOWN :
+                    change= visibleRows;
+                    if (selection + change >= fContextSelectorTable.getItemCount())
+                        change= fContextSelectorTable.getItemCount() - selection;
+                    break;
+
+                case SWT.PAGE_UP :
+                    change= -visibleRows;
+                    if (selection + change < 0)
+                        change= -selection;
+                    break;
+
+                case SWT.HOME :
+                    change= -selection;
+                    break;
+
+                case SWT.END :
+                    change= fContextSelectorTable.getItemCount() - selection;
+                    break;
+
+                default:
+                    if (e.keyCode !is SWT.MOD1 && e.keyCode !is SWT.MOD2 && e.keyCode !is SWT.MOD3 && e.keyCode !is SWT.MOD4)
+                        hideContextSelector();
+                    return true;
+            }
+
+            fContextSelectorTable.setSelection(selection + change);
+            fContextSelectorTable.showSelection();
+            e.doit= false;
+            return false;
+
+        } else if ('\t' is key) {
+            // switch focus to selector shell
+            e.doit= false;
+            fContextSelectorShell.setFocus();
+            return false;
+        } else if (key is SWT.ESC) {
+            e.doit= false;
+            hideContextSelector();
+        }
+
+        return true;
+    }
+
+    /**
+     * Processes a key stroke while the info popup is up.
+     *
+     * @param e the verify event describing the key stroke
+     * @return <code>true</code> if processing can be stopped
+     */
+    private bool contextInfoPopupKeyPressed(KeyEvent e) {
+
+        char key= e.character;
+        if (key is 0) {
+
+            switch (e.keyCode) {
+                case SWT.ARROW_LEFT:
+                case SWT.ARROW_RIGHT:
+                    validateContextInformation();
+                    break;
+                default:
+                    if (e.keyCode !is SWT.MOD1 && e.keyCode !is SWT.MOD2 && e.keyCode !is SWT.MOD3 && e.keyCode !is SWT.MOD4)
+                        hideContextInfoPopup();
+                    break;
+            }
+
+        } else if (key is SWT.ESC) {
+            e.doit= false;
+            hideContextInfoPopup();
+        } else {
+            validateContextInformation();
+        }
+        return true;
+    }
+
+    /*
+     * @see IEventConsumer#processEvent(VerifyEvent)
+     */
+    public void processEvent(VerifyEvent event) {
+        if (Helper2.okToUse(fContextSelectorShell))
+            contextSelectorProcessEvent(event);
+        if (Helper2.okToUse(fContextInfoPopup))
+            contextInfoPopupProcessEvent(event);
+    }
+
+    /**
+     * Processes a key stroke in the context selector.
+     *
+     * @param e the verify event describing the key stroke
+     */
+    private void contextSelectorProcessEvent(VerifyEvent e) {
+
+        if (e.start is e.end && e.text !is null && e.text.equals(fLineDelimiter)) {
+            e.doit= false;
+            insertSelectedContext();
+        }
+
+        hideContextSelector();
+    }
+
+    /**
+     * Processes a key stroke while the info popup is up.
+     *
+     * @param e the verify event describing the key stroke
+     */
+    private void contextInfoPopupProcessEvent(VerifyEvent e) {
+        if (e.start !is e.end && (e.text is null || e.text.length() is 0))
+            validateContextInformation();
+    }
+
+    /**
+     * Validates the context information for the viewer's actual cursor position.
+     */
+    private void validateContextInformation() {
+        /*
+         * Post the code in the event queue in order to ensure that the
+         * action described by this verify key event has already beed executed.
+         * Otherwise, we'd validate the context information based on the
+         * pre-key-stroke state.
+         */
+        fContextInfoPopup.getDisplay().asyncExec(dgRunnable( (ContextFrame fFrame_) {
+
+            ContextFrame fFrame= fFrame_;
+
+            if (Helper2.okToUse(fContextInfoPopup) && fFrame is fContextFrameStack.peek()) {
+                int offset= fViewer.getSelectedRange().x;
+                if (fFrame.fValidator is null || !fFrame.fValidator.isContextInformationValid(offset)) {
+                    hideContextInfoPopup();
+                } else if (fFrame.fPresenter !is null && fFrame.fPresenter.updatePresentation(offset, fTextPresentation)) {
+                    TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText);
+                    resize();
+                }
+            }
+        }, cast(ContextFrame) fContextFrameStack.peek()));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/Helper2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.internal.text.link.contentassist.Helper2;
+
+import org.eclipse.jface.internal.text.link.contentassist.IProposalListener; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.LineBreakingReader; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.CompletionProposalPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContextInformationPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistMessages; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.PopupCloser2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.IContentAssistListener2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistant2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.AdditionalInfoController2; // packageimport
+
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.widgets.Widget;
+
+
+/**
+ * Helper class for testing widget state.
+ */
+class Helper2 {
+
+    /**
+     * Returns whether the widget is <code>null</code> or disposed.
+     *
+     * @param widget the widget to check
+     * @return <code>true</code> if the widget is neither <code>null</code> nor disposed
+     */
+    public static bool okToUse(Widget widget) {
+        return (widget !is null && !widget.isDisposed());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/IContentAssistListener2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.internal.text.link.contentassist.IContentAssistListener2;
+
+import org.eclipse.jface.internal.text.link.contentassist.IProposalListener; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.LineBreakingReader; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.CompletionProposalPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContextInformationPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistMessages; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.Helper2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.PopupCloser2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistant2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.AdditionalInfoController2; // packageimport
+
+
+import java.lang.all;
+
+
+
+
+
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.jface.text.IEventConsumer;
+
+
+
+/**
+ * An interface whereby listeners can not only receive key events,
+ * but can also consume them to prevent subsequent listeners from
+ * processing the event.
+ */
+interface IContentAssistListener2 : IEventConsumer {
+
+    /**
+     * Verifies the key event.
+     *
+     * @param event the key event
+     * @return <code>true</code> if processing should be continued by additional listeners
+     * @see org.eclipse.swt.custom.VerifyKeyListener#verifyKey(VerifyEvent)
+     */
+    public bool verifyKey(VerifyEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/IProposalListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.internal.text.link.contentassist.IProposalListener;
+
+import org.eclipse.jface.internal.text.link.contentassist.LineBreakingReader; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.CompletionProposalPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContextInformationPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistMessages; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.Helper2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.PopupCloser2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.IContentAssistListener2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistant2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.AdditionalInfoController2; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+
+
+/**
+ *
+ */
+public interface IProposalListener {
+
+    /**
+     * @param proposal
+     */
+    void proposalChosen(ICompletionProposal proposal);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/LineBreakingReader.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.internal.text.link.contentassist.LineBreakingReader;
+
+import org.eclipse.jface.internal.text.link.contentassist.IProposalListener; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.CompletionProposalPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContextInformationPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistMessages; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.Helper2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.PopupCloser2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.IContentAssistListener2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistant2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.AdditionalInfoController2; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+import org.eclipse.dwtxhelper.BufferedReader;
+import org.eclipse.dwtxhelper.mangoicu.UBreakIterator;
+
+import org.eclipse.swt.graphics.GC;
+
+/*
+ * Not a real reader. Could change if requested
+ */
+public class LineBreakingReader {
+
+    private BufferedReader fReader;
+    private GC fGC;
+    private int fMaxWidth;
+
+    private String fLine;
+    private int fOffset;
+
+    private UBreakIterator fLineBreakIterator;
+    private bool fBreakWords;
+
+    /**
+     * Creates a reader that breaks an input text to fit in a given width.
+     *
+     * @param reader Reader of the input text
+     * @param gc The graphic context that defines the currently used font sizes
+     * @param maxLineWidth The max width (pixels) where the text has to fit in
+     */
+    public this(Reader reader, GC gc, int maxLineWidth) {
+        fReader= new BufferedReader(reader);
+        fGC= gc;
+        fMaxWidth= maxLineWidth;
+        fOffset= 0;
+        fLine= null;
+        fLineBreakIterator= UBreakIterator.openLineIterator( ULocale.Default);
+        fBreakWords= true;
+    }
+
+    public bool isFormattedLine() {
+        return fLine !is null;
+    }
+
+    /**
+     * Reads the next line. The lengths of the line will not exceed the given maximum
+     * width.
+     *
+     * @return the next line
+     * @throws IOException
+     */
+    public String readLine()  {
+        if (fLine is null) {
+            String line= fReader.readLine();
+            if (line is null)
+                return null;
+
+            int lineLen= fGC.textExtent(line).x;
+            if (lineLen < fMaxWidth) {
+                return line;
+            }
+            fLine= line;
+            fLineBreakIterator.setText(line);
+            fOffset= 0;
+        }
+        int breakOffset= findNextBreakOffset(fOffset);
+        String res;
+        if (breakOffset !is UBreakIterator.DONE) {
+            res= fLine.substring(fOffset, breakOffset);
+            fOffset= findWordBegin(breakOffset);
+            if (fOffset is fLine.length()) {
+                fLine= null;
+            }
+        } else {
+            res= fLine.substring(fOffset);
+            fLine= null;
+        }
+        return res;
+    }
+
+    private int findNextBreakOffset(int currOffset) {
+        int currWidth= 0;
+        int nextOffset= fLineBreakIterator.following(currOffset);
+        while (nextOffset !is UBreakIterator.DONE) {
+            String word= fLine.substring(currOffset, nextOffset);
+            int wordWidth= fGC.textExtent(word).x;
+            int nextWidth= wordWidth + currWidth;
+            if (nextWidth > fMaxWidth) {
+                if (currWidth > 0)
+                    return currOffset;
+
+                if (!fBreakWords)
+                    return nextOffset;
+
+                // need to fit into fMaxWidth
+                int length= word.length();
+                while (length >= 0) {
+                    length--;
+                    word= word.substring(0, length);
+                    wordWidth= fGC.textExtent(word).x;
+                    if (wordWidth + currWidth < fMaxWidth)
+                        return currOffset + length;
+                }
+                return nextOffset;
+            }
+            currWidth= nextWidth;
+            currOffset= nextOffset;
+            nextOffset= fLineBreakIterator.next();
+        }
+        return nextOffset;
+    }
+
+    private int findWordBegin(int idx) {
+        while (idx < fLine.length() && Character.isWhitespace(fLine.charAt(idx))) {
+            idx++;
+        }
+        return idx;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/PopupCloser2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.internal.text.link.contentassist.PopupCloser2;
+
+import org.eclipse.jface.internal.text.link.contentassist.IProposalListener; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.LineBreakingReader; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.CompletionProposalPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContextInformationPopup2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistMessages; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.Helper2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.IContentAssistListener2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistant2; // packageimport
+import org.eclipse.jface.internal.text.link.contentassist.AdditionalInfoController2; // packageimport
+
+
+import java.lang.all;
+
+
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+
+
+/**
+ * A generic closer class used to monitor various
+ * interface events in order to determine whether
+ * a content assistant should be terminated and all
+ * associated windows be closed.
+ */
+class PopupCloser2 : ShellAdapter , FocusListener, SelectionListener {
+
+    /** The content assistant to be monitored */
+    private ContentAssistant2 fContentAssistant;
+    /** The table of a selector popup opened by the content assistant */
+    private Table fTable;
+    /** The scrollbar of the table for the selector popup */
+    private ScrollBar fScrollbar;
+    /** Indicates whether the scrollbar thumb has been grabbed. */
+    private bool fScrollbarClicked= false;
+    /** The shell on which some listeners are registered. */
+    private Shell fShell;
+
+
+    /**
+     * Installs this closer on the given table opened by the given content assistant.
+     *
+     * @param contentAssistant the content assistant
+     * @param table the table to be tracked
+     */
+    public void install(ContentAssistant2 contentAssistant, Table table) {
+        fContentAssistant= contentAssistant;
+        fTable= table;
+        if (Helper2.okToUse(fTable)) {
+            Shell shell= fTable.getShell();
+            if (Helper2.okToUse(shell)) {
+                fShell= shell;
+                fShell.addShellListener(this);
+            }
+            fTable.addFocusListener(this);
+            fScrollbar= fTable.getVerticalBar();
+            if (fScrollbar !is null)
+                fScrollbar.addSelectionListener(this);
+        }
+    }
+
+    /**
+     * Uninstalls this closer if previously installed.
+     */
+    public void uninstall() {
+        fContentAssistant= null;
+        if (Helper2.okToUse(fShell))
+            fShell.removeShellListener(this);
+        fShell= null;
+        if (Helper2.okToUse(fScrollbar))
+            fScrollbar.removeSelectionListener(this);
+        if (Helper2.okToUse(fTable))
+            fTable.removeFocusListener(this);
+    }
+
+    /*
+     * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+     */
+    public void widgetSelected(SelectionEvent e) {
+        fScrollbarClicked= true;
+    }
+
+    /*
+     * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
+     */
+    public void widgetDefaultSelected(SelectionEvent e) {
+        fScrollbarClicked= true;
+    }
+
+    /*
+     * @see org.eclipse.swt.events.FocusListener#focusGained(org.eclipse.swt.events.FocusEvent)
+     */
+    public void focusGained(FocusEvent e) {
+    }
+
+    /*
+     * @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent)
+     */
+    public void focusLost(FocusEvent e) {
+        fScrollbarClicked= false;
+        Display d= fTable.getDisplay();
+        d.asyncExec(dgRunnable((FocusEvent e_) {
+            if (Helper2.okToUse(fTable) && !fTable.isFocusControl() && !fScrollbarClicked && fContentAssistant !is null)
+                fContentAssistant.popupFocusLost(e_);
+        }, e ));
+    }
+
+    /*
+     * @see org.eclipse.swt.events.ShellAdapter#shellDeactivated(org.eclipse.swt.events.ShellEvent)
+     * @since 3.1
+     */
+    public void shellDeactivated(ShellEvent e) {
+        if (fContentAssistant !is null)
+            fContentAssistant.hide_package();
+    }
+
+
+    /*
+     * @see org.eclipse.swt.events.ShellAdapter#shellClosed(org.eclipse.swt.events.ShellEvent)
+     * @since 3.1
+     */
+    public void shellClosed(ShellEvent e) {
+        if (fContentAssistant !is null)
+            fContentAssistant.hide_package();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/ChangeRegion.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.internal.text.revisions.ChangeRegion;
+
+import org.eclipse.jface.internal.text.revisions.HunkComputer; // packageimport
+import org.eclipse.jface.internal.text.revisions.LineIndexOutOfBoundsException; // packageimport
+import org.eclipse.jface.internal.text.revisions.Hunk; // packageimport
+import org.eclipse.jface.internal.text.revisions.Colors; // packageimport
+import org.eclipse.jface.internal.text.revisions.Range; // packageimport
+import org.eclipse.jface.internal.text.revisions.RevisionPainter; // packageimport
+import org.eclipse.jface.internal.text.revisions.RevisionSelectionProvider; // packageimport
+
+import java.lang.all;
+import java.util.ListIterator;
+import java.util.List;
+import java.util.LinkedList;
+import tango.text.convert.Format;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.revisions.Revision;
+import org.eclipse.jface.text.source.ILineRange;
+import org.eclipse.jface.text.source.LineRange;
+
+/**
+ * A change region describes a contiguous range of lines that was changed in the same revision of a
+ * document. Once it is adjusted to diff information, the originally contiguous range may be split
+ * into several ranges or even be empty.
+ *
+ * @since 3.2
+ */
+public final class ChangeRegion {
+    private const Revision fRevision;
+    private const ILineRange fLines;
+    private const List fAdjusted;
+
+    /**
+     * Creates a new change region for the given revision and line range.
+     *
+     * @param revision the revision of the new region
+     * @param lines the line range of the new region
+     * @throws IndexOutOfBoundsException if the line range is not well-formed
+     */
+    public this(Revision revision, ILineRange lines)  {
+        fAdjusted= new LinkedList();
+        Assert.isLegal(revision !is null);
+        Assert.isLegal(lines !is null);
+        fLines= Range.copy(lines);
+        fRevision=revision;
+        clearDiff();
+    }
+
+    /**
+     * Returns the revision that this region belongs to.
+     *
+     * @return the revision that this region belongs to
+     */
+    public Revision getRevision() {
+        return fRevision;
+    }
+
+    /**
+     * Returns the original (before applying diff information) line range of this change region.
+     *
+     * @return the original (before applying diff information) line range of this change region
+     */
+    public ILineRange getOriginalRange() {
+        return fLines;
+    }
+
+    /**
+     * Returns the list of {@link ILineRange}s of this change region for which the revision
+     * information is still valid.
+     *
+     * @return the list of adjusted line ranges
+     */
+    public List getAdjustedRanges() {
+        return fAdjusted;
+    }
+
+    /**
+     * Returns the line coverage of the adjusted ranges, an empty range if the coverage is empty.
+     *
+     * @return the line coverage of the adjusted ranges
+     */
+    public ILineRange getAdjustedCoverage() {
+        if (fAdjusted.isEmpty())
+            return new LineRange(fLines.getStartLine(), 0);
+
+        Range first= cast(Range) fAdjusted.get(0);
+        Range last= cast(Range) fAdjusted.get(fAdjusted.size() - 1);
+
+        return Range.createAbsolute(first.start(), last.end());
+    }
+
+    /**
+     * Clears any adjusted ranges, restoring the original range.
+     */
+    public void clearDiff() {
+        fAdjusted.clear();
+        fAdjusted.add(Range.copy(fLines));
+    }
+
+    /**
+     * Adjusts this change region to a diff hunk. This will change the adjusted ranges.
+     *
+     * @param hunk the diff hunk to adjust to
+     */
+    public void adjustTo(Hunk hunk) {
+        for (ListIterator it= fAdjusted.listIterator(); it.hasNext();) {
+            Range range= cast(Range) it.next();
+
+            // do we need a split?
+            int unchanged= getUnchanged(hunk, range.start());
+            if (unchanged > 0) {
+                if (unchanged >= range.length())
+                    continue;
+                range= range.split(unchanged);
+                it.add(range);
+                it.previous(); it.next(); // needed so we can remove below
+            }
+
+            int line= range.start();
+            Assert.isTrue(hunk.line <= line);
+
+            // by how much do we shrink?
+            int overlap= getOverlap(hunk, line);
+            if (overlap >= range.length()) {
+                it.remove();
+                continue;
+            }
+
+            // by how much do we move?
+            range.moveBy(hunk.delta + overlap);
+            range.resizeBy(-overlap);
+        }
+
+    }
+
+    private int getUnchanged(Hunk hunk, int line) {
+        return Math.max(0, hunk.line - line);
+    }
+
+    /*
+     * Returns the number of lines after line that the hunk reports as changed.
+     */
+    private int getOverlap(Hunk hunk, int line) {
+
+        int deltaLine= hunk.line + hunk.changed;
+        if (hunk.delta >= 0) {
+            if (deltaLine <= line)
+                return 0;
+            return deltaLine - line;
+        }
+
+        // hunk.delta < 0
+        int hunkEnd= deltaLine - hunk.delta;
+        int cutCount= hunkEnd - line;
+        return Math.max(0, cutCount);
+    }
+
+    /*
+     * @see java.lang.Object#toString()
+     */
+    public override String toString() {
+        return Format("ChangeRegion [{},[{}+{})]", fRevision.toString(), fLines.getStartLine(), fLines.getNumberOfLines() ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/Colors.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,281 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.internal.text.revisions.Colors;
+
+import org.eclipse.jface.internal.text.revisions.HunkComputer; // packageimport
+import org.eclipse.jface.internal.text.revisions.LineIndexOutOfBoundsException; // packageimport
+import org.eclipse.jface.internal.text.revisions.Hunk; // packageimport
+import org.eclipse.jface.internal.text.revisions.ChangeRegion; // packageimport
+import org.eclipse.jface.internal.text.revisions.Range; // packageimport
+import org.eclipse.jface.internal.text.revisions.RevisionPainter; // packageimport
+import org.eclipse.jface.internal.text.revisions.RevisionSelectionProvider; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * Utility for color operations.
+ *
+ * @since 3.3
+ */
+public final class Colors {
+    /*
+     * Implementation note: Color computation assumes sRGB, which is probably not true, and does not
+     * always give good results. CIE based algorithms would be better, see
+     * http://www.w3.org/TR/PNG-ColorAppendix.html and http://en.wikipedia.org/wiki/Lab_color_space
+     */
+
+    /**
+     * Returns the human-perceived brightness of a color as float in [0.0, 1.0]. The used RGB
+     * weights come from http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html#RTFToC9.
+     *
+     * @param rgb the color
+     * @return the gray-scale value
+     */
+    public static float brightness(RGB rgb) {
+        return Math.min(1f, (0.2126f * rgb.red + 0.7152f * rgb.green + 0.0722f * rgb.blue + 0.5f) / 255f);
+    }
+
+    /**
+     * Normalizes a color in its perceived brightness. Yellows are darkened, while blues and reds
+     * are lightened. Depending on the hue, the brightness range within the RGB gamut may be
+     * different, outside values are clipped. Note that this is an approximation; the returned RGB
+     * is not guaranteed to have the requested {@link #brightness(RGB) brightness}.
+     *
+     * @param color the color to normalize
+     * @param brightness the requested brightness, in [0,&nbsp;1]
+     * @return a normalized version of <code>color</code>
+     * @see #brightness(RGB)
+     */
+    public static RGB adjustBrightness(RGB color, float brightness_) {
+        float[] hsi= toHSI(color);
+        float psychoFactor= brightness_ - brightness(color);
+        float weight= 0.5f; // found by trial and error
+        hsi[2]= Math.max(0, Math.min(1.0f, hsi[2] + psychoFactor * weight));
+        color= fromHSI(hsi);
+        return color;
+    }
+
+    /**
+     * Converts an {@link RGB} to an <a href="http://en.wikipedia.org/wiki/HSL_color_space">HSI</a>
+     * triplet.
+     *
+     * @param color the color to convert
+     * @return the HSI float array of length 3
+     */
+    private static float[] toHSI(RGB color) {
+        float r = color.red / 255f;
+        float g = color.green / 255f;
+        float b = color.blue / 255f;
+        float max = Math.max(Math.max(r, g), b);
+        float min = Math.min(Math.min(r, g), b);
+        float delta = max - min;
+        float maxPlusMin= max + min;
+        float intensity = maxPlusMin / 2;
+        float saturation= intensity < 0.5 ? delta / maxPlusMin : delta / (2 - maxPlusMin);
+
+        float hue = 0;
+        if (delta !is 0) {
+            if (r is max) {
+                hue = (g  - b) / delta;
+            } else {
+                if (g is max) {
+                    hue = 2 + (b - r) / delta;
+                } else {
+                    hue = 4 + (r - g) / delta;
+                }
+            }
+            hue *= 60;
+            if (hue < 0) hue += 360;
+        }
+        return [hue, saturation, intensity];
+    }
+
+    /**
+     * Converts a <a href="http://en.wikipedia.org/wiki/HSL_color_space">HSI</a> triplet to an RGB.
+     *
+     * @param hsi the HSI values
+     * @return the RGB corresponding to the HSI spec
+     */
+    private static RGB fromHSI(float[] hsi) {
+        float r, g, b;
+        float hue= hsi[0];
+        float saturation= hsi[1];
+        float intensity= hsi[2];
+        if (saturation is 0) {
+            r = g = b = intensity;
+        } else {
+            float temp2= intensity < 0.5f ? intensity * (1.0f + saturation) : (intensity + saturation) - (intensity * saturation);
+            float temp1= 2f * intensity - temp2;
+            if (hue is 360) hue = 0;
+            hue /= 360;
+
+            r= hue2RGB(temp1, temp2, hue + 1f/3f);
+            g= hue2RGB(temp1, temp2, hue);
+            b= hue2RGB(temp1, temp2, hue - 1f/3f);
+        }
+
+        int red = cast(int)(r * 255 + 0.5);
+        int green = cast(int)(g * 255 + 0.5);
+        int blue = cast(int)(b * 255 + 0.5);
+        return new RGB(red, green, blue);
+    }
+
+    private static float hue2RGB(float t1, float t2, float hue) {
+        if (hue < 0)
+            hue += 1;
+        else if (hue > 1)
+            hue -= 1;
+        if (6f * hue < 1)
+            return t1 +(t2 - t1) * 6f * hue;
+        if (2f * hue < 1)
+            return t2;
+        if (3f * hue < 2)
+            return t1 + (t2 - t1) * (2f/3f - hue) * 6f;
+        return t1;
+    }
+
+    /**
+     * Returns an RGB that lies between the given foreground and background
+     * colors using the given mixing factor. A <code>factor</code> of 1.0 will produce a
+     * color equal to <code>fg</code>, while a <code>factor</code> of 0.0 will produce one
+     * equal to <code>bg</code>.
+     * @param bg the background color
+     * @param fg the foreground color
+     * @param factor the mixing factor, must be in [0,&nbsp;1]
+     *
+     * @return the interpolated color
+     */
+    public static RGB blend(RGB bg, RGB fg, float factor) {
+        Assert.isLegal(bg !is null);
+        Assert.isLegal(fg !is null);
+        Assert.isLegal(factor >= 0f && factor <= 1f);
+
+        float complement= 1f - factor;
+        return new RGB(
+                cast(int) (complement * bg.red + factor * fg.red),
+                cast(int) (complement * bg.green + factor * fg.green),
+                cast(int) (complement * bg.blue + factor * fg.blue)
+        );
+    }
+
+    /**
+     * Returns an array of colors in a smooth palette from <code>start</code> to <code>end</code>.
+     * <p>
+     * The returned array has size <code>steps</code>, and the color at index 0 is <code>start</code>, the color
+     * at index <code>steps&nbsp;-&nbsp;1</code> is <code>end</code>.
+     *
+     * @param start the start color of the palette
+     * @param end the end color of the palette
+     * @param steps the requested size, must be &gt; 0
+     * @return an array of <code>steps</code> colors in the palette from <code>start</code> to <code>end</code>
+     */
+    public static RGB[] palette(RGB start, RGB end, int steps) {
+        Assert.isLegal(start !is null);
+        Assert.isLegal(end !is null);
+        Assert.isLegal(steps > 0);
+
+        if (steps is 1)
+            return [ start ];
+
+        float step= 1.0f / (steps - 1);
+        RGB[] gradient= new RGB[steps];
+        for (int i= 0; i < steps; i++)
+            gradient[i]= blend(start, end, step * i);
+
+        return gradient;
+    }
+
+    /**
+     * Returns an array of colors with hues evenly distributed on the hue wheel defined by the <a
+     * href="http://en.wikipedia.org/wiki/HSV_color_space">HSB color space</a>. The returned array
+     * has size <code>steps</code>. The distance <var>d</var> between two successive colors is
+     * in [120&#176;,&nbsp;180&#176;].
+     * <p>
+     * The color at a given <code>index</code> has the hue returned by
+     * {@linkplain #computeHue(int) computeHue(index)}; i.e. the computed hues are not equidistant,
+     * but adaptively distributed on the color wheel.
+     * </p>
+     * <p>
+     * The first six colors returned correspond to the following {@link SWT} color constants:
+     * {@link SWT#COLOR_RED red}, {@link SWT#COLOR_GREEN green}, {@link SWT#COLOR_BLUE blue},
+     * {@link SWT#COLOR_YELLOW yellow}, {@link SWT#COLOR_CYAN cyan},
+     * {@link SWT#COLOR_MAGENTA magenta}.
+     * </p>
+     *
+     * @param steps the requested size, must be &gt;= 2
+     * @return an array of <code>steps</code> colors evenly distributed on the color wheel
+     */
+    public static RGB[] rainbow(int steps) {
+        Assert.isLegal(steps >= 2);
+
+        RGB[] rainbow= new RGB[steps];
+        for (int i= 0; i < steps; i++)
+            rainbow[i]= new RGB(computeHue(i), 1f, 1f);
+
+        return rainbow;
+    }
+
+    /**
+     * Returns an indexed hue in [0&#176;,&nbsp;360&#176;), distributing the hues evenly on the hue wheel
+     * defined by the <a href="http://en.wikipedia.org/wiki/HSV_color_space">HSB (or HSV) color
+     * space</a>. The distance <var>d</var> between two successive colors is in [120&#176;,&nbsp;180&#176;].
+     * <p>
+     * The first six colors returned correspond to the following {@link SWT} color constants:
+     * {@link SWT#COLOR_RED red}, {@link SWT#COLOR_GREEN green}, {@link SWT#COLOR_BLUE blue},
+     * {@link SWT#COLOR_YELLOW yellow}, {@link SWT#COLOR_CYAN cyan},
+     * {@link SWT#COLOR_MAGENTA magenta}.
+     * </p>
+     *
+     * @param index the index of the color, must be &gt;= 0
+     * @return a color hue in [0&#176;,&nbsp;360&#176;)
+     * @see RGB#RGB(float, float, float)
+     */
+    public static float computeHue(int index) {
+        Assert.isLegal(index >= 0);
+        /*
+         * Base 3 gives a nice partitioning for RGB colors with red, green, blue being the colors
+         * 0,1,2, and yellow, cyan, magenta colors 3,4,5.
+         */
+        final int base= 3;
+        final float range= 360f;
+
+        // partition the baseRange by using the least significant bit to select one half of the
+        // partitioning
+        int baseIndex= index / base;
+        float baseRange= range / base;
+        float baseOffset= 0f;
+        while (baseIndex > 0) {
+            baseRange /= 2;
+            int lsb= baseIndex % 2;
+            baseOffset += lsb * baseRange;
+            baseIndex >>= 1;
+        }
+
+        final int baseMod= index % base;
+        final float hue= baseOffset + baseMod * range / base;
+        Assert.isTrue(hue >= 0 && hue < 360);
+        return hue;
+    }
+
+    private this() {
+        // not instantiatable
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/Hunk.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.text.revisions.Hunk;
+
+import org.eclipse.jface.internal.text.revisions.HunkComputer; // packageimport
+import org.eclipse.jface.internal.text.revisions.LineIndexOutOfBoundsException; // packageimport
+import org.eclipse.jface.internal.text.revisions.Colors; // packageimport
+import org.eclipse.jface.internal.text.revisions.ChangeRegion; // packageimport
+import org.eclipse.jface.internal.text.revisions.Range; // packageimport
+import org.eclipse.jface.internal.text.revisions.RevisionPainter; // packageimport
+import org.eclipse.jface.internal.text.revisions.RevisionSelectionProvider; // packageimport
+
+import java.lang.all;
+import tango.text.convert.Format;
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * A hunk describes a contiguous range of changed, added or deleted lines. <code>Hunk</code>s are separated by
+ * one or more unchanged lines.
+ *
+ * @since 3.3
+ */
+public final class Hunk {
+    /**
+     * The line at which the hunk starts in the current document. Must be in
+     * <code>[0, numberOfLines]</code> &ndash; note the inclusive end; there may be a hunk with
+     * <code>line is numberOfLines</code> to describe deleted lines at then end of the document.
+     */
+    public const int line;
+    /**
+     * The difference in lines compared to the corresponding line range in the original. Positive
+     * for added lines, negative for deleted lines.
+     */
+    public const int delta;
+    /** The number of changed lines in this hunk, must be &gt;= 0. */
+    public const int changed;
+
+    /**
+     * Creates a new hunk.
+     *
+     * @param line the line at which the hunk starts, must be &gt;= 0
+     * @param delta the difference in lines compared to the original
+     * @param changed the number of changed lines in this hunk, must be &gt;= 0
+     */
+    public this(int line, int delta, int changed) {
+        Assert.isLegal(line >= 0);
+        Assert.isLegal(changed >= 0);
+        this.line= line;
+        this.delta= delta;
+        this.changed= changed;
+    }
+
+    /*
+     * @see java.lang.Object#toString()
+     */
+    public override String toString() {
+        return Format("Hunk [{}>{}{}{}]", line, changed, delta < 0 ? "-" : "+", Math.abs(delta) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+    }
+
+    /*
+     * @see java.lang.Object#hashCode()
+     */
+    public override hash_t toHash() {
+        final int prime= 31;
+        int result= 1;
+        result= prime * result + changed;
+        result= prime * result + delta;
+        result= prime * result + line;
+        return result;
+    }
+
+    /*
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public override int opEquals(Object obj) {
+        if (obj is this)
+            return true;
+        if ( Hunk other= cast(Hunk)obj ) {
+            return other.line is this.line && other.delta is this.delta && other.changed is this.changed;
+        }
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/HunkComputer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.text.revisions.HunkComputer;
+
+import org.eclipse.jface.internal.text.revisions.LineIndexOutOfBoundsException; // packageimport
+import org.eclipse.jface.internal.text.revisions.Hunk; // packageimport
+import org.eclipse.jface.internal.text.revisions.Colors; // packageimport
+import org.eclipse.jface.internal.text.revisions.ChangeRegion; // packageimport
+import org.eclipse.jface.internal.text.revisions.Range; // packageimport
+import org.eclipse.jface.internal.text.revisions.RevisionPainter; // packageimport
+import org.eclipse.jface.internal.text.revisions.RevisionSelectionProvider; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+
+
+
+import org.eclipse.jface.text.source.ILineDiffInfo;
+import org.eclipse.jface.text.source.ILineDiffer;
+
+
+/**
+ * Computes the diff hunks from an {@link ILineDiffer}.
+ *
+ * @since 3.3
+ */
+public final class HunkComputer {
+    /**
+     * Converts the line-based information of {@link ILineDiffer} into {@link Hunk}s, grouping
+     * contiguous blocks of lines that are changed (added, deleted).
+     *
+     * @param differ the line differ to query
+     * @param lines the number of lines to query
+     * @return the corresponding {@link Hunk} information
+     */
+    public static Hunk[] computeHunks(ILineDiffer differ, int lines) {
+        List hunks= new ArrayList(lines);
+
+        int added= 0;
+        int changed= 0;
+        ILineDiffInfo info= null;
+        for (int line= 0; line < lines; line++) {
+            info= differ.getLineInfo(line);
+            if (info is null)
+                continue;
+
+            int changeType= info.getChangeType();
+            switch (changeType) {
+                case ILineDiffInfo.ADDED:
+                    added++;
+                    continue;
+                case ILineDiffInfo.CHANGED:
+                    changed++;
+                    continue;
+                case ILineDiffInfo.UNCHANGED:
+                    added -= info.getRemovedLinesAbove();
+                    if (added !is 0 || changed !is 0) {
+                        hunks.add(new Hunk(line - changed - Math.max(0, added), added, changed));
+                        added= 0;
+                        changed= 0;
+                    }
+                default:
+            }
+        }
+
+        // last hunk
+        if (info !is null) {
+            added -= info.getRemovedLinesBelow();
+            if (added !is 0 || changed !is 0) {
+                hunks.add(new Hunk(lines - changed, added, changed));
+                added= 0;
+                changed= 0;
+            }
+        }
+
+        return arraycast!(Hunk)( hunks.toArray());
+    }
+    private this() {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/LineIndexOutOfBoundsException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.internal.text.revisions.LineIndexOutOfBoundsException;
+
+import org.eclipse.jface.internal.text.revisions.HunkComputer; // packageimport
+import org.eclipse.jface.internal.text.revisions.Hunk; // packageimport
+import org.eclipse.jface.internal.text.revisions.Colors; // packageimport
+import org.eclipse.jface.internal.text.revisions.ChangeRegion; // packageimport
+import org.eclipse.jface.internal.text.revisions.Range; // packageimport
+import org.eclipse.jface.internal.text.revisions.RevisionPainter; // packageimport
+import org.eclipse.jface.internal.text.revisions.RevisionSelectionProvider; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Thrown to indicate that an attempt to create or modify a {@link Range} failed because it would
+ * have resulted in an illegal range. A range is illegal if its length is &lt;= 0 or if its start
+ * line is &lt; 0.
+ *
+ * @since 3.2
+ */
+public final class LineIndexOutOfBoundsException : IndexOutOfBoundsException {
+    private static const long serialVersionUID= 1L;
+
+    /**
+     * Constructs an <code>LineIndexOutOfBoundsException</code> with no detail message.
+     */
+    public this() {
+        super();
+    }
+
+    /**
+     * Constructs an <code>LineIndexOutOfBoundsException</code> with the specified detail message.
+     *
+     * @param s the detail message.
+     */
+    public this(String s) {
+        super(s);
+    }
+
+    /**
+     * Constructs a new <code>LineIndexOutOfBoundsException</code>
+     * object with an argument indicating the illegal index.
+     *
+     * @param index the illegal index.
+     */
+    public this(int index) {
+        super("Line index out of range: " ~ Integer.toString(index)); //$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/Range.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,281 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.text.revisions.Range;
+
+import org.eclipse.jface.internal.text.revisions.HunkComputer; // packageimport
+import org.eclipse.jface.internal.text.revisions.LineIndexOutOfBoundsException; // packageimport
+import org.eclipse.jface.internal.text.revisions.Hunk; // packageimport
+import org.eclipse.jface.internal.text.revisions.Colors; // packageimport
+import org.eclipse.jface.internal.text.revisions.ChangeRegion; // packageimport
+import org.eclipse.jface.internal.text.revisions.RevisionPainter; // packageimport
+import org.eclipse.jface.internal.text.revisions.RevisionSelectionProvider; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+import tango.text.convert.Format;
+
+import org.eclipse.jface.text.source.ILineRange;
+
+/**
+ * A variable {@link org.eclipse.jface.text.source.ILineRange} with the following invariant:
+ * <ul>
+ * <li>{@link #start() start} &gt;= 0
+ * <li>{@link #length() length} &gt; 0, i.e. a range cannot be empty
+ * </ul>
+ * <p>
+ * Attempts to create or modify a <code>Range</code> such that this invariant would be violated
+ * result in a {@link LineIndexOutOfBoundsException} being
+ * thrown.
+ * </p>
+ *
+ * @since 3.2
+ */
+public final class Range : ILineRange, Cloneable {
+    /**
+     * Creates a new range with the same start and length as the passed line range.
+     *
+     * @param range the range to copy
+     * @return a <code>Range</code> with the same start and length as <code>range</code>
+     * @throws LineIndexOutOfBoundsException if the passed {@link ILineRange} does not adhere to the
+     *         contract of {@link Range}
+     */
+    public static Range copy(ILineRange range)  {
+        return createRelative(range.getStartLine(), range.getNumberOfLines());
+    }
+
+    /**
+     * Creates a new range equal to the passed line range.
+     *
+     * @param range the range to copy
+     * @return a <code>Range</code> equal to <code>range</code>
+     */
+    public static Range copy(Range range) {
+        return createRelative(range.start(), range.length());
+    }
+
+    /**
+     * Creates a new range with the given start offset and length.
+     *
+     * @param start the first line of the new range, must be &gt;= 0
+     * @param length the number of lines included in the new range, must be &gt; 0
+     * @return a <code>Range</code> with the given start and length
+     * @throws LineIndexOutOfBoundsException if the parameters violate the invariant of
+     *         {@link Range}
+     */
+    public static Range createRelative(int start, int length)  {
+        return new Range(start, length);
+    }
+
+    /**
+     * Creates a new range with the given start and end offsets.
+     *
+     * @param start the first line of the new range, must be &gt;= 0
+     * @param end the first line not in the range any more (exclusive), must be &gt; <code>start</code>
+     * @return a <code>Range</code> with the given start and end offsets
+     * @throws LineIndexOutOfBoundsException if the parameters violate the invariant of
+     *         {@link Range}
+     */
+    public static Range createAbsolute(int start, int end) {
+        return new Range(start, end - start);
+    }
+
+    private int fStart;
+    private int fLength;
+
+    /*
+     * Private constructor.
+     */
+    private this(int start, int length) {
+        moveTo(start);
+        setLength(length);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.ILineRange#getStartLine()
+     */
+    public int getStartLine() {
+        return start();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.ILineRange#getNumberOfLines()
+     */
+    public int getNumberOfLines() {
+        return length();
+    }
+
+    /**
+     * Returns the first line contained in this range. Short equivalent of {@link #getStartLine()}.
+     *
+     * @return the first line contained in this range
+     */
+    public int start() {
+        return fStart;
+    }
+
+    /**
+     * Returns the number of lines contained in this range. Short equivalent of {@link #getNumberOfLines()}.
+     *
+     * @return the number of lines contained in this range
+     */
+    public int length() {
+        return fLength;
+    }
+
+    /**
+     * Returns the first line after this range. Equivalent to {@linkplain #start() start} + {@linkplain #length() length}.
+     *
+     * @return the first line after this range
+     */
+    public int end() {
+        return start() + length();
+    }
+
+    /**
+     * Moves the receiver to <code>start</code>, keeping {@link #length()} constant.
+     *
+     * @param start the new start, must be &gt;= 0
+     * @throws LineIndexOutOfBoundsException if <code>start</code> &lt; 0
+     */
+    public void moveTo(int start)  {
+        if (!(start >= 0))
+            throw new LineIndexOutOfBoundsException(Format("Cannot set a negative start: {}", start)); //$NON-NLS-1$
+        fStart= start;
+    }
+
+    /**
+     * Moves this range such that the {@link #end()} is at <code>end</code>, keeping
+     * {@link #length()} constant.
+     *
+     * @param end the new end
+     * @throws LineIndexOutOfBoundsException if <code>end</code> &lt;= {@link #start()}
+     */
+    public void moveEndTo(int end)  {
+        moveTo(end - length());
+    }
+
+    /**
+     * Moves the range by <code>delta</code> lines, keeping {@link #length()} constant. The
+     * resulting start line must be &gt;= 0.
+     *
+     * @param delta the number of lines to shift the range
+     * @throws LineIndexOutOfBoundsException if <code>-delta</code> &gt; {@link #start()}
+     */
+    public void moveBy(int delta)  {
+        moveTo(start() + delta);
+    }
+
+    /**
+     * Moves the start offset to <code>start</code>, keeping {@link #end()} constant.
+     *
+     * @param start the new start, must be &gt;= 0 and &lt; {@link #end()}
+     * @throws LineIndexOutOfBoundsException if <code>start</code> &lt; 0 or &gt;= {@link #end()}
+     */
+    public void setStart(int start)  {
+        int end= end();
+        if (!(start >= 0 && start < end))
+            throw new LineIndexOutOfBoundsException(Format("Cannot set a negative start: {}", start)); //$NON-NLS-1$
+        moveTo(start);
+        setEnd(end);
+    }
+
+    /**
+     * Sets the end of this range, keeping {@link #start()} constant.
+     *
+     * @param end the new end, must be &gt; {@link #start()}
+     * @throws LineIndexOutOfBoundsException if <code>end</code> &lt;= {@link #start()}
+     */
+    public void setEnd(int end)  {
+        setLength(end - start());
+    }
+
+    /**
+     * Sets the length of this range, keeping {@link #start()} constant.
+     *
+     * @param length the new length, must be &gt; 0
+     * @throws LineIndexOutOfBoundsException if <code>length</code> &lt;= 0
+     */
+    public void setLength(int length)  {
+        if (!(length > 0))
+            throw new LineIndexOutOfBoundsException(Format("Cannot set length <= 0: {}", length)); //$NON-NLS-1$
+        fLength= length;
+    }
+
+    /**
+     * Sets the length of this range, keeping {@link #end()} constant.
+     *
+     * @param length the new length, must be &gt; 0 and &lt;= {@link #end()}
+     * @throws LineIndexOutOfBoundsException if <code>length</code> &lt;= 0
+     */
+    public void setLengthAndMove(int length)  {
+        setStart(end() - length);
+    }
+
+    /**
+     * Resizes the range by <code>delta</code> lines, keeping {@link #start()} constant.
+     *
+     * @param delta the number of lines to resize the range
+     * @throws LineIndexOutOfBoundsException if <code>-delta</code> &gt;= {@link #length()}
+     */
+    public void resizeBy(int delta)  {
+        setLength(length() + delta);
+    }
+
+    /**
+     * Resizes the range by <code>delta</code> lines by moving the start offset, {@link #end()} remains unchanged.
+     *
+     * @param delta the number of lines to resize the range
+     * @throws LineIndexOutOfBoundsException if <code>-delta</code> &gt;= {@link #length()}
+     */
+    public void resizeAndMoveBy(int delta)  {
+        setStart(start() + delta);
+    }
+
+    /**
+     * Splits a range off the end of the receiver. The receiver is shortened to only include
+     * <code>remaining</code> lines after the split.
+     *
+     * @param remaining the number of lines to remain in the receiver, must be in [1, {@link #length() length})
+     * @return the split off range
+     * @throws LineIndexOutOfBoundsException if <code>remaining</code>&gt;= {@link #length()} or <code>remaining</code>&ltt;= 0
+     */
+    public Range split(int remaining)  {
+        if (!(remaining < length())) // assert before modification
+            throw new LineIndexOutOfBoundsException(Format("Remaining must be less than length: {}", length())); //$NON-NLS-1$
+
+        int splitLength= length() - remaining;
+        setLength(remaining);
+        return new Range(end(), splitLength);
+    }
+
+    /**
+     * Returns <code>true</code> if the passed range has the same offset and length as the receiver.
+     *
+     * @param range another line range to compare the receiver to
+     * @return <code>true</code> if <code>range</code> has the same offset and length as the receiver
+     */
+    public bool equalRange(ILineRange range) {
+        if (range is this)
+            return true;
+        if (range is null)
+            return false;
+        return range.getStartLine() is start() && range.getNumberOfLines() is length();
+    }
+
+    /*
+     * @see java.lang.Object#clone()
+     */
+    public Object clone() {
+        return Range.copy(this);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/RevisionPainter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1615 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.internal.text.revisions.RevisionPainter;
+
+import org.eclipse.jface.internal.text.revisions.HunkComputer; // packageimport
+import org.eclipse.jface.internal.text.revisions.LineIndexOutOfBoundsException; // packageimport
+import org.eclipse.jface.internal.text.revisions.Hunk; // packageimport
+import org.eclipse.jface.internal.text.revisions.Colors; // packageimport
+import org.eclipse.jface.internal.text.revisions.ChangeRegion; // packageimport
+import org.eclipse.jface.internal.text.revisions.Range; // packageimport
+import org.eclipse.jface.internal.text.revisions.RevisionSelectionProvider; // packageimport
+
+import java.lang.all;
+import java.util.ListIterator;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import tango.text.convert.Format;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.internal.text.html.BrowserInformationControl;
+import org.eclipse.jface.internal.text.html.HTMLPrinter;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DefaultInformationControl;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.JFaceTextUtil;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.information.IInformationProviderExtension2;
+import org.eclipse.jface.text.revisions.IRevisionListener;
+import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension;
+import org.eclipse.jface.text.revisions.Revision;
+import org.eclipse.jface.text.revisions.RevisionEvent;
+import org.eclipse.jface.text.revisions.RevisionInformation;
+import org.eclipse.jface.text.revisions.RevisionRange;
+import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.CompositeRuler;
+import org.eclipse.jface.text.source.IAnnotationHover;
+import org.eclipse.jface.text.source.IAnnotationHoverExtension;
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.IAnnotationModelExtension;
+import org.eclipse.jface.text.source.IAnnotationModelListener;
+import org.eclipse.jface.text.source.IChangeRulerColumn;
+import org.eclipse.jface.text.source.ILineDiffer;
+import org.eclipse.jface.text.source.ILineRange;
+import org.eclipse.jface.text.source.ISharedTextColors;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.text.source.IVerticalRulerColumn;
+import org.eclipse.jface.text.source.LineRange;
+
+
+/**
+ * A strategy for painting the live annotate colors onto the vertical ruler column. It also manages
+ * the revision hover.
+ *
+ * @since 3.2
+ */
+public final class RevisionPainter {
+    /** Tells whether this class is in debug mode. */
+    private static bool DEBUG_init = false;
+    private static bool DEBUG_;
+    private static bool DEBUG(){
+        if( !DEBUG_init ){
+            DEBUG_init = true;
+            DEBUG_ = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.jface.text.source/debug/RevisionRulerColumn")); //$NON-NLS-1$//$NON-NLS-2$
+        }
+        return DEBUG_;
+    }
+
+    // RGBs provided by UI Designer
+    private static RGB BY_DATE_START_COLOR_;
+    private static RGB BY_DATE_START_COLOR(){
+        if( BY_DATE_START_COLOR_ is null ){
+            synchronized(RevisionPainter.classinfo){
+                if( BY_DATE_START_COLOR_ is null ){
+                    BY_DATE_START_COLOR_ = new RGB(199, 134, 57);
+                }
+            }
+        }
+        return BY_DATE_START_COLOR_;
+    }
+    private static RGB BY_DATE_END_COLOR_;
+    private static RGB BY_DATE_END_COLOR(){
+        if( BY_DATE_END_COLOR_ is null ){
+            synchronized(RevisionPainter.classinfo){
+                if( BY_DATE_END_COLOR_ is null ){
+                    BY_DATE_END_COLOR_ = new RGB(241, 225, 206);
+                }
+            }
+        }
+        return BY_DATE_END_COLOR_;
+    }
+
+
+    /**
+     * The annotations created to show a revision in the overview ruler.
+     */
+    private static final class RevisionAnnotation : Annotation {
+        public this(String text) {
+            super("org.eclipse.ui.workbench.texteditor.revisionAnnotation", false, text); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * The color tool manages revision colors and computes shaded colors based on the relative age
+     * and author of a revision.
+     */
+    private final class ColorTool {
+        /**
+         * The average perceived intensity of a base color. 0 means black, 1 means white. A base
+         * revision color perceived as light such as yellow will be darkened, while colors perceived
+         * as dark such as blue will be lightened up.
+         */
+        private static const float AVERAGE_INTENSITY= 0.5f;
+        /**
+         * The maximum shading in [0, 1] - this is the shade that the most recent revision will
+         * receive.
+         */
+        private static const float MAX_SHADING= 0.7f;
+        /**
+         * The minimum shading in [0, 1] - this is the shade that the oldest revision will receive.
+         */
+        private static const float MIN_SHADING= 0.2f;
+        /**
+         * The shade for the focus boxes.
+         */
+        private static const float FOCUS_COLOR_SHADING= 1f;
+
+
+        /**
+         * A list of {@link Long}, storing the age of each revision in a sorted list.
+         */
+        private List fRevisions;
+        /**
+         * The stored shaded colors.
+         */
+        private const Map fColors;
+        /**
+         * The stored focus colors.
+         */
+        private const Map fFocusColors;
+
+        this(){
+            fColors= new HashMap();
+            fFocusColors= new HashMap();
+        }
+        /**
+         * Sets the revision information, which is needed to compute the relative age of a revision.
+         *
+         * @param info the new revision info, <code>null</code> for none.
+         */
+        public void setInfo(RevisionInformation info) {
+            fRevisions= null;
+            fColors.clear();
+            fFocusColors.clear();
+
+            if (info is null)
+                return;
+            List revisions= new ArrayList();
+            for (Iterator it= info.getRevisions().iterator(); it.hasNext();) {
+                Revision revision= cast(Revision) it.next();
+                revisions.add(new Long(computeAge(revision)));
+            }
+            Collections.sort(revisions);
+            fRevisions= revisions;
+        }
+
+        private RGB adaptColor(Revision revision, bool focus) {
+            RGB rgb;
+            float scale;
+            if (fRenderingMode is IRevisionRulerColumnExtension_AGE) {
+                int index= computeAgeIndex(revision);
+                if (index is -1 || fRevisions.size() is 0) {
+                    rgb= getBackground().getRGB();
+                } else {
+                    // gradient from intense red for most recent to faint yellow for oldest
+                    RGB[] gradient= Colors.palette(BY_DATE_START_COLOR, BY_DATE_END_COLOR, fRevisions.size());
+                    rgb= gradient[gradient.length - index - 1];
+                }
+                scale= 0.99f;
+            } else if (fRenderingMode is IRevisionRulerColumnExtension_AUTHOR) {
+                rgb= revision.getColor();
+                rgb= Colors.adjustBrightness(rgb, AVERAGE_INTENSITY);
+                scale= 0.6f;
+            } else if (fRenderingMode is IRevisionRulerColumnExtension_AUTHOR_SHADED_BY_AGE) {
+                rgb= revision.getColor();
+                rgb= Colors.adjustBrightness(rgb, AVERAGE_INTENSITY);
+                int index= computeAgeIndex(revision);
+                int size= fRevisions.size();
+                // relative age: newest is 0, oldest is 1
+                // if there is only one revision, use an intermediate value to avoid extreme coloring
+                if (index is -1 || size < 2)
+                    scale= 0.5f;
+                else
+                    scale= cast(float) index / (size - 1);
+            } else {
+                Assert.isTrue(false);
+                return null; // dummy
+            }
+            rgb= getShadedColor(rgb, scale, focus);
+            return rgb;
+        }
+
+        private int computeAgeIndex(Revision revision) {
+            long age= computeAge(revision);
+            int index= fRevisions.indexOf(new Long(age));
+            return index;
+        }
+
+        private RGB getShadedColor(RGB color, float scale, bool focus) {
+            Assert.isLegal(scale >= 0.0);
+            Assert.isLegal(scale <= 1.0);
+            RGB background= getBackground().getRGB();
+
+            // normalize to lie within [MIN_SHADING, MAX_SHADING]
+            // use more intense colors if the ruler is narrow (i.e. not showing line numbers)
+            bool makeIntense= getWidth() <= 15;
+            float intensityShift= makeIntense ? 0.3f : 0f;
+            float max= MAX_SHADING + intensityShift;
+            float min= MIN_SHADING + intensityShift;
+            scale= (max - min) * scale + min;
+
+            // focus coloring
+            if (focus) {
+                scale += FOCUS_COLOR_SHADING;
+                if (scale > 1) {
+                    background= new RGB(255 - background.red, 255 - background.green, 255 - background.blue);
+                    scale= 2 - scale;
+                }
+            }
+
+            return Colors.blend(background, color, scale);
+        }
+
+        private long computeAge(Revision revision) {
+            return revision.getDate().getTime();
+        }
+
+        /**
+         * Returns the color for a revision based on relative age and author.
+         *
+         * @param revision the revision
+         * @param focus <code>true</code> to return the focus color
+         * @return the color for a revision
+         */
+        public RGB getColor(Revision revision, bool focus) {
+            Map map= focus ? fFocusColors : fColors;
+            RGB color= cast(RGB) map.get(revision);
+            if (color !is null)
+                return color;
+
+            color= adaptColor(revision, focus);
+            map.put(revision, color);
+            return color;
+        }
+    }
+
+    /**
+     * Handles all the mouse interaction in this line number ruler column.
+     */
+    private class MouseHandler : MouseMoveListener, MouseTrackListener, Listener {
+
+        private RevisionRange fMouseDownRegion;
+
+        private void handleMouseUp(Event e) {
+            if (e.button is 1) {
+                RevisionRange upRegion= fFocusRange;
+                RevisionRange downRegion= fMouseDownRegion;
+                fMouseDownRegion= null;
+
+                if (upRegion is downRegion) {
+                    Revision revision= upRegion is null ? null : upRegion.getRevision();
+                    if (revision is fSelectedRevision)
+                        revision= null; // deselect already selected revision
+                    handleRevisionSelected(revision);
+                }
+            }
+        }
+
+        private void handleMouseDown(Event e) {
+            if (e.button is 3)
+                updateFocusRevision(null); // kill any focus as the ctx menu is going to show
+            if (e.button is 1) {
+                fMouseDownRegion= fFocusRange;
+                postRedraw();
+            }
+        }
+
+        /*
+         * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+         */
+        public void handleEvent(Event event) {
+            switch (event.type) {
+                case SWT.MouseWheel:
+                    handleMouseWheel(event);
+                    break;
+                case SWT.MouseDown:
+                    handleMouseDown(event);
+                    break;
+                case SWT.MouseUp:
+                    handleMouseUp(event);
+                    break;
+                default:
+                    Assert.isLegal(false);
+            }
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseTrackListener#mouseEnter(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseEnter(MouseEvent e) {
+            updateFocusLine(toDocumentLineNumber(e.y));
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseTrackListener#mouseExit(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseExit(MouseEvent e) {
+            updateFocusLine(-1);
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseTrackListener#mouseHover(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseHover(MouseEvent e) {
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseMove(MouseEvent e) {
+            updateFocusLine(toDocumentLineNumber(e.y));
+        }
+    }
+
+    /**
+     * Internal listener class that will update the ruler when the underlying model changes.
+     */
+    private class AnnotationListener : IAnnotationModelListener {
+        /*
+         * @see org.eclipse.jface.text.source.IAnnotationModelListener#modelChanged(org.eclipse.jface.text.source.IAnnotationModel)
+         */
+        public void modelChanged(IAnnotationModel model) {
+            clearRangeCache();
+            postRedraw();
+        }
+
+    }
+
+    /**
+     * The information control creator.
+     */
+    private static final class HoverInformationControlCreator : AbstractReusableInformationControlCreator {
+        private bool fIsFocusable;
+
+        public this(bool isFocusable) {
+            fIsFocusable= isFocusable;
+        }
+
+        /*
+         * @see org.eclipse.jface.internal.text.revisions.AbstractReusableInformationControlCreator#doCreateInformationControl(org.eclipse.swt.widgets.Shell)
+         */
+        protected IInformationControl doCreateInformationControl(Shell parent) {
+            if (BrowserInformationControl.isAvailable(parent)) {
+                return new class(parent, JFaceResources.DIALOG_FONT, fIsFocusable)  BrowserInformationControl {
+                    public this(Shell parent, String symbolicFontName, bool resizable){
+                        super(parent, symbolicFontName,resizable);
+                    }
+                    /*
+                     * @see org.eclipse.jface.internal.text.html.BrowserInformationControl#setInformation(java.lang.String)
+                     * @since 3.3
+                     */
+                    public void setInformation(String content) {
+                        content= addCSSToHTMLFragment(content);
+                        super.setInformation(content);
+                    }
+
+                    /**
+                     * Adds a HTML header and CSS info if <code>html</code> is only an HTML fragment (has no
+                     * &lt;html&gt; section).
+                     *
+                     * @param html the html / text produced by a revision
+                     * @return modified html
+                     */
+                    private String addCSSToHTMLFragment(String html) {
+                        int max= Math.min(100, html.length());
+                        if (html.substring(0, max).indexOf("<html>") !is -1) //$NON-NLS-1$
+                            // there is already a header
+                            return html;
+
+                        StringBuffer info= new StringBuffer(512 + html.length());
+                        HTMLPrinter.insertPageProlog(info, 0, fgStyleSheet);
+                        info.append(html);
+                        HTMLPrinter.addPageEpilog(info);
+                        return info.toString();
+                    }
+
+                };
+            }
+            return new DefaultInformationControl(parent, fIsFocusable);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.AbstractReusableInformationControlCreator#canReplace(org.eclipse.jface.text.IInformationControlCreator)
+         */
+        public bool canReplace(IInformationControlCreator creator) {
+            return creator.classinfo is this.classinfo
+                    && (cast(HoverInformationControlCreator) creator).fIsFocusable is fIsFocusable;
+        }
+    }
+
+    private static final String fgStyleSheet= "/* Font definitions */\n" ~ //$NON-NLS-1$
+        "body, h1, h2, h3, h4, h5, h6, p, table, td, caption, th, ul, ol, dl, li, dd, dt {font-family: sans-serif; font-size: 9pt }\n" ~ //$NON-NLS-1$
+        "pre                { font-family: monospace; font-size: 9pt }\n" ~ //$NON-NLS-1$
+        "\n" ~ //$NON-NLS-1$
+        "/* Margins */\n" ~ //$NON-NLS-1$
+        "body        { overflow: auto; margin-top: 0; margin-bottom: 4; margin-left: 3; margin-right: 0 }\n" ~ //$NON-NLS-1$
+        "h1           { margin-top: 5; margin-bottom: 1 }   \n" ~ //$NON-NLS-1$
+        "h2           { margin-top: 25; margin-bottom: 3 }\n" ~ //$NON-NLS-1$
+        "h3           { margin-top: 20; margin-bottom: 3 }\n" ~ //$NON-NLS-1$
+        "h4           { margin-top: 20; margin-bottom: 3 }\n" ~ //$NON-NLS-1$
+        "h5           { margin-top: 0; margin-bottom: 0 }\n" ~ //$NON-NLS-1$
+        "p            { margin-top: 10px; margin-bottom: 10px }\n" ~ //$NON-NLS-1$
+        "pre             { margin-left: 6 }\n" ~ //$NON-NLS-1$
+        "ul          { margin-top: 0; margin-bottom: 10 }\n" ~ //$NON-NLS-1$
+        "li          { margin-top: 0; margin-bottom: 0 } \n" ~ //$NON-NLS-1$
+        "li p        { margin-top: 0; margin-bottom: 0 } \n" ~ //$NON-NLS-1$
+        "ol          { margin-top: 0; margin-bottom: 10 }\n" ~ //$NON-NLS-1$
+        "dl          { margin-top: 0; margin-bottom: 10 }\n" ~ //$NON-NLS-1$
+        "dt          { margin-top: 0; margin-bottom: 0; font-weight: bold }\n" ~ //$NON-NLS-1$
+        "dd          { margin-top: 0; margin-bottom: 0 }\n" ~ //$NON-NLS-1$
+        "\n" ~ //$NON-NLS-1$
+        "/* Styles and colors */\n" ~ //$NON-NLS-1$
+        "a:link      { color: #0000FF }\n" ~ //$NON-NLS-1$
+        "a:hover         { color: #000080 }\n" ~ //$NON-NLS-1$
+        "a:visited    { text-decoration: underline }\n" ~ //$NON-NLS-1$
+        "h4           { font-style: italic }\n" ~ //$NON-NLS-1$
+        "strong      { font-weight: bold }\n" ~ //$NON-NLS-1$
+        "em          { font-style: italic }\n" ~ //$NON-NLS-1$
+        "var             { font-style: italic }\n" ~ //$NON-NLS-1$
+        "th          { font-weight: bold }\n" ~ //$NON-NLS-1$
+        ""; //$NON-NLS-1$
+
+    /**
+     * The revision hover displays information about the currently selected revision.
+     */
+    private final class RevisionHover : IAnnotationHover, IAnnotationHoverExtension, IAnnotationHoverExtension2, IInformationProviderExtension2 {
+
+        /*
+         * @see org.eclipse.jface.text.source.IAnnotationHover#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer,
+         *      int)
+         */
+        public String getHoverInfo(ISourceViewer sourceViewer, int lineNumber) {
+            Object info= getHoverInfo(sourceViewer, getHoverLineRange(sourceViewer, lineNumber), 0);
+            return info is null ? null : info.toString();
+        }
+
+        /*
+         * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverControlCreator()
+         */
+        public IInformationControlCreator getHoverControlCreator() {
+            RevisionInformation revisionInfo= fRevisionInfo;
+            if (revisionInfo !is null) {
+                IInformationControlCreator creator= revisionInfo.getHoverControlCreator();
+                if (creator !is null)
+                    return creator;
+            }
+            return new HoverInformationControlCreator(false);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#canHandleMouseCursor()
+         */
+        public bool canHandleMouseCursor() {
+            return false;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.source.IAnnotationHoverExtension2#canHandleMouseWheel()
+         */
+        public bool canHandleMouseWheel() {
+            return true;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer,
+         *      org.eclipse.jface.text.source.ILineRange, int)
+         */
+        public Object getHoverInfo(ISourceViewer sourceViewer, ILineRange lineRange, int visibleNumberOfLines) {
+            RevisionRange range= getRange(lineRange.getStartLine());
+            Object info= range is null ? null : range.getRevision().getHoverInfo();
+            return info;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverLineRange(org.eclipse.jface.text.source.ISourceViewer,
+         *      int)
+         */
+        public ILineRange getHoverLineRange(ISourceViewer viewer, int lineNumber) {
+            RevisionRange range= getRange(lineNumber);
+            return range is null ? null : new LineRange(lineNumber, 1);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.information.IInformationProviderExtension2#getInformationPresenterControlCreator()
+         */
+        public IInformationControlCreator getInformationPresenterControlCreator() {
+            RevisionInformation revisionInfo= fRevisionInfo;
+            if (revisionInfo !is null) {
+                IInformationControlCreator creator= revisionInfo.getInformationPresenterControlCreator();
+                if (creator !is null)
+                    return creator;
+            }
+            return new HoverInformationControlCreator(true);
+        }
+    }
+
+    /* Listeners and helpers. */
+
+    /** The shared color provider. */
+    private const ISharedTextColors fSharedColors;
+    /** The color tool. */
+    private const ColorTool fColorTool;
+    /** The mouse handler. */
+    private const MouseHandler fMouseHandler;
+    /** The hover. */
+    private const RevisionHover fHover;
+    /** The annotation listener. */
+    private const AnnotationListener fAnnotationListener;
+    /** The selection provider. */
+    private const RevisionSelectionProvider fRevisionSelectionProvider;
+    /**
+     * The list of revision listeners.
+     * @since 3.3.
+     */
+    private const ListenerList fRevisionListeners;
+
+    /* The context - column and viewer we are connected to. */
+
+    /** The vertical ruler column that delegates painting to this painter. */
+    private const IVerticalRulerColumn fColumn;
+    /** The parent ruler. */
+    private CompositeRuler fParentRuler;
+    /** The column's control, typically a {@link Canvas}, possibly <code>null</code>. */
+    private Control fControl;
+    /** The text viewer that the column is attached to. */
+    private ITextViewer fViewer;
+    /** The viewer's text widget. */
+    private StyledText fWidget;
+
+    /* The models we operate on. */
+
+    /** The revision model object. */
+    private RevisionInformation fRevisionInfo;
+    /** The line differ. */
+    private ILineDiffer fLineDiffer= null;
+    /** The annotation model. */
+    private IAnnotationModel fAnnotationModel= null;
+    /** The background color, possibly <code>null</code>. */
+    private Color fBackground;
+
+    /* Cache. */
+
+    /** The cached list of ranges adapted to quick diff. */
+    private List fRevisionRanges= null;
+    /** The annotations created for the overview ruler temporary display. */
+    private List fAnnotations;
+
+    /* State */
+
+    /** The current focus line, -1 for none. */
+    private int fFocusLine= -1;
+    /** The current focus region, <code>null</code> if none. */
+    private RevisionRange fFocusRange= null;
+    /** The current focus revision, <code>null</code> if none. */
+    private Revision fFocusRevision= null;
+    /**
+     * The currently selected revision, <code>null</code> if none. The difference between
+     * {@link #fFocusRevision} and {@link #fSelectedRevision} may not be obvious: the focus revision
+     * is the one focused by the mouse (by hovering over a block of the revision), while the
+     * selected revision is sticky, i.e. is not removed when the mouse leaves the ruler.
+     *
+     * @since 3.3
+     */
+    private Revision fSelectedRevision= null;
+    /** <code>true</code> if the mouse wheel handler is installed, <code>false</code> otherwise. */
+    private bool fWheelHandlerInstalled= false;
+    /**
+     * The revision rendering mode.
+     */
+    private IRevisionRulerColumnExtension_RenderingMode fRenderingMode;
+    /**
+     * The required with in characters.
+     * @since 3.3
+     */
+    private int fRequiredWidth= -1;
+    /**
+     * The width of the revision field in chars to compute {@link #fAuthorInset} from.
+     * @since 3.3
+     */
+    private int fRevisionIdChars= 0;
+    /**
+     * <code>true</code> to show revision ids, <code>false</code> otherwise.
+     * @since 3.3
+     */
+    private bool fShowRevision= false;
+    /**
+     * <code>true</code> to show the author, <code>false</code> otherwise.
+     * @since 3.3
+     */
+    private bool fShowAuthor= false;
+    /**
+     * The author inset in pixels for when author *and* revision id are shown.
+     * @since 3.3
+     */
+    private int fAuthorInset;
+    /**
+     * The remembered ruler width (as changing the ruler width triggers recomputation of the colors.
+     * @since 3.3
+     */
+    private int fLastWidth= -1;
+
+    /**
+     * Creates a new revision painter for a vertical ruler column.
+     *
+     * @param column the column that will delegate{@link #paint(GC, ILineRange) painting} to the
+     *        newly created painter.
+     * @param sharedColors a shared colors object to store shaded colors in
+     */
+    public this(IVerticalRulerColumn column, ISharedTextColors sharedColors) {
+
+        fColorTool= new ColorTool();
+        fMouseHandler= new MouseHandler();
+        fHover= new RevisionHover();
+        fAnnotationListener= new AnnotationListener();
+        fRevisionSelectionProvider= new RevisionSelectionProvider(this);
+        fRevisionListeners= new ListenerList(ListenerList.IDENTITY);
+        fAnnotations= new ArrayList();
+        fRenderingMode= IRevisionRulerColumnExtension_AUTHOR_SHADED_BY_AGE;
+
+        Assert.isLegal(column !is null);
+        Assert.isLegal(sharedColors !is null);
+        fColumn= column;
+        fSharedColors= sharedColors;
+    }
+
+    /**
+     * Sets the revision information to be drawn and triggers a redraw.
+     *
+     * @param info the revision information to show, <code>null</code> to draw none
+     */
+    public void setRevisionInformation(RevisionInformation info) {
+        if (fRevisionInfo !is info) {
+            fRequiredWidth= -1;
+            fRevisionIdChars= 0;
+            fRevisionInfo= info;
+            clearRangeCache();
+            updateFocusRange(null);
+            handleRevisionSelected(cast(Revision) null);
+            fColorTool.setInfo(info);
+            postRedraw();
+            informListeners();
+        }
+    }
+
+    /**
+     * Changes the rendering mode and triggers redrawing if needed.
+     *
+     * @param renderingMode the rendering mode
+     * @since 3.3
+     */
+    public void setRenderingMode(IRevisionRulerColumnExtension_RenderingMode renderingMode) {
+        Assert.isLegal(renderingMode !is null);
+        if (fRenderingMode !is renderingMode) {
+            fRenderingMode= renderingMode;
+            fColorTool.setInfo(fRevisionInfo);
+            postRedraw();
+        }
+    }
+
+    /**
+     * Sets the background color.
+     *
+     * @param background the background color, <code>null</code> for the platform's list
+     *        background
+     */
+    public void setBackground(Color background) {
+        fBackground= background;
+    }
+
+    /**
+     * Sets the parent ruler - the delegating column must call this method as soon as it creates its
+     * control.
+     *
+     * @param parentRuler the parent ruler
+     */
+    public void setParentRuler(CompositeRuler parentRuler) {
+        fParentRuler= parentRuler;
+    }
+
+    /**
+     * Delegates the painting of the quick diff colors to this painter. The painter will draw the
+     * color boxes onto the passed {@link GC} for all model (document) lines in
+     * <code>visibleModelLines</code>.
+     *
+     * @param gc the {@link GC} to draw onto
+     * @param visibleLines the lines (in document offsets) that are currently (perhaps only
+     *        partially) visible
+     */
+    public void paint(GC gc, ILineRange visibleLines) {
+        connectIfNeeded();
+        if (!isConnected())
+            return;
+
+        // compute the horizontal indent of the author for the case that we show revision
+        // and author
+        if (fShowAuthor && fShowRevision) {
+            char[] string= new char[fRevisionIdChars + 1];
+            Arrays.fill(string, '9');
+            if (string.length > 1) {
+                string[0]= '.';
+                string[1]= ' ';
+            }
+            fAuthorInset= gc.stringExtent(new_String(string)).x;
+        }
+
+        // recompute colors (show intense colors if ruler is narrow)
+        int width= getWidth();
+        if (width !is fLastWidth) {
+            fColorTool.setInfo(fRevisionInfo);
+            fLastWidth= width;
+        }
+
+        // draw change regions
+        List/* <RevisionRange> */ranges= getRanges(visibleLines);
+        for (Iterator it= ranges.iterator(); it.hasNext();) {
+            RevisionRange region= cast(RevisionRange) it.next();
+            paintRange(region, gc);
+        }
+    }
+
+    /**
+     * Ensures that the column is fully instantiated, i.e. has a control, and that the viewer is
+     * visible.
+     */
+    private void connectIfNeeded() {
+        if (isConnected() || fParentRuler is null)
+            return;
+
+        fViewer= fParentRuler.getTextViewer();
+        if (fViewer is null)
+            return;
+
+        fWidget= fViewer.getTextWidget();
+        if (fWidget is null)
+            return;
+
+        fControl= fColumn.getControl();
+        if (fControl is null)
+            return;
+
+        fControl.addMouseTrackListener(fMouseHandler);
+        fControl.addMouseMoveListener(fMouseHandler);
+        fControl.addListener(SWT.MouseUp, fMouseHandler);
+        fControl.addListener(SWT.MouseDown, fMouseHandler);
+        fControl.addDisposeListener(new class()  DisposeListener {
+            /*
+             * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
+             */
+            public void widgetDisposed(DisposeEvent e) {
+                handleDispose();
+            }
+        });
+
+        fRevisionSelectionProvider.install(fViewer);
+    }
+
+    /**
+     * Returns <code>true</code> if the column is fully connected.
+     *
+     * @return <code>true</code> if the column is fully connected, false otherwise
+     */
+    private bool isConnected() {
+        return fControl !is null;
+    }
+
+    /**
+     * Sets the annotation model.
+     *
+     * @param model the annotation model, possibly <code>null</code>
+     * @see IVerticalRulerColumn#setModel(IAnnotationModel)
+     */
+    public void setModel(IAnnotationModel model) {
+        IAnnotationModel diffModel;
+        if ( cast(IAnnotationModelExtension)model )
+            diffModel= (cast(IAnnotationModelExtension) model).getAnnotationModel(stringcast(IChangeRulerColumn.QUICK_DIFF_MODEL_ID));
+        else
+            diffModel= model;
+
+        setDiffer(diffModel);
+        setAnnotationModel(model);
+    }
+
+    /**
+     * Sets the annotation model.
+     *
+     * @param model the annotation model.
+     */
+    private void setAnnotationModel(IAnnotationModel model) {
+        if (fAnnotationModel !is model)
+            fAnnotationModel= model;
+    }
+
+    /**
+     * Sets the line differ.
+     *
+     * @param differ the line differ or <code>null</code> if none
+     */
+    private void setDiffer(IAnnotationModel differ) {
+        if ( cast(ILineDiffer)differ  || differ is null) {
+            if ( cast(Object)fLineDiffer !is cast(Object)differ) {
+                if (fLineDiffer !is null)
+                    (cast(IAnnotationModel) fLineDiffer).removeAnnotationModelListener(fAnnotationListener);
+                fLineDiffer= cast(ILineDiffer) differ;
+                if (fLineDiffer !is null)
+                    (cast(IAnnotationModel) fLineDiffer).addAnnotationModelListener(fAnnotationListener);
+            }
+        }
+    }
+
+    /**
+     * Disposes of the painter's resources.
+     */
+    private void handleDispose() {
+        updateFocusLine(-1);
+
+        if (fLineDiffer !is null) {
+            (cast(IAnnotationModel) fLineDiffer).removeAnnotationModelListener(fAnnotationListener);
+            fLineDiffer= null;
+        }
+
+        fRevisionSelectionProvider.uninstall();
+    }
+
+    /**
+     * Paints a single change region onto <code>gc</code>.
+     *
+     * @param range the range to paint
+     * @param gc the {@link GC} to paint on
+     */
+    private void paintRange(RevisionRange range, GC gc) {
+        ILineRange widgetRange= modelLinesToWidgetLines(range);
+        if (widgetRange is null)
+            return;
+
+        Revision revision= range.getRevision();
+        bool drawArmedFocus= range is fMouseHandler.fMouseDownRegion;
+        bool drawSelection= !drawArmedFocus && revision is fSelectedRevision;
+        bool drawFocus= !drawSelection && !drawArmedFocus && revision is fFocusRevision;
+        Rectangle box= computeBoxBounds(widgetRange);
+
+        gc.setBackground(lookupColor(revision, false));
+        if (drawArmedFocus) {
+            Color foreground= gc.getForeground();
+            Color focusColor= lookupColor(revision, true);
+            gc.setForeground(focusColor);
+            gc.fillRectangle(box);
+            gc.drawRectangle(box.x, box.y, box.width - 1, box.height - 1); // highlight box
+            gc.drawRectangle(box.x + 1, box.y + 1, box.width - 3, box.height - 3); // inner highlight box
+            gc.setForeground(foreground);
+        } else if (drawFocus || drawSelection) {
+            Color foreground= gc.getForeground();
+            Color focusColor= lookupColor(revision, true);
+            gc.setForeground(focusColor);
+            gc.fillRectangle(box);
+            gc.drawRectangle(box.x, box.y, box.width - 1, box.height - 1); // highlight box
+            gc.setForeground(foreground);
+        } else {
+            gc.fillRectangle(box);
+        }
+
+        if ((fShowAuthor || fShowRevision)) {
+            int indentation= 1;
+            int baselineBias= getBaselineBias(gc, widgetRange.getStartLine());
+            if (fShowAuthor && fShowRevision) {
+                gc.drawString(revision.getId(), indentation, box.y + baselineBias, true);
+                gc.drawString(revision.getAuthor(), fAuthorInset, box.y + baselineBias, true);
+            } else if (fShowAuthor) {
+                gc.drawString(revision.getAuthor(), indentation, box.y + baselineBias, true);
+            } else if (fShowRevision) {
+                gc.drawString(revision.getId(), indentation, box.y + baselineBias, true);
+            }
+        }
+    }
+
+    /**
+     * Returns the difference between the baseline of the widget and the
+     * baseline as specified by the font for <code>gc</code>. When drawing
+     * line numbers, the returned bias should be added to obtain text lined up
+     * on the correct base line of the text widget.
+     *
+     * @param gc the <code>GC</code> to get the font metrics from
+     * @param widgetLine the widget line
+     * @return the baseline bias to use when drawing text that is lined up with
+     *         <code>fCachedTextWidget</code>
+     * @since 3.3
+     */
+    private int getBaselineBias(GC gc, int widgetLine) {
+        /*
+         * https://bugs.eclipse.org/bugs/show_bug.cgi?id=62951
+         * widget line height may be more than the font height used for the
+         * line numbers, since font styles (bold, italics...) can have larger
+         * font metrics than the simple font used for the numbers.
+         */
+        int offset= fWidget.getOffsetAtLine(widgetLine);
+        int widgetBaseline= fWidget.getBaseline(offset);
+
+        FontMetrics fm = gc.getFontMetrics();
+        int fontBaseline = fm.getAscent() + fm.getLeading();
+        int baselineBias= widgetBaseline - fontBaseline;
+        return Math.max(0, baselineBias);
+    }
+
+    /**
+     * Looks up the color for a certain revision.
+     *
+     * @param revision the revision to get the color for
+     * @param focus <code>true</code> if it is the focus revision
+     * @return the color for the revision
+     */
+    private Color lookupColor(Revision revision, bool focus) {
+        return fSharedColors.getColor(fColorTool.getColor(revision, focus));
+    }
+
+    /**
+     * Returns the revision range that contains the given line, or
+     * <code>null</code> if there is none.
+     *
+     * @param line the line of interest
+     * @return the corresponding <code>RevisionRange</code> or <code>null</code>
+     */
+    private RevisionRange getRange(int line) {
+        List ranges= getRangeCache();
+
+        if (ranges.isEmpty() || line is -1)
+            return null;
+
+        for (Iterator it= ranges.iterator(); it.hasNext();) {
+            RevisionRange range= cast(RevisionRange) it.next();
+            if (contains(range, line))
+                return range;
+        }
+
+        // line may be right after the last region
+        RevisionRange lastRegion= cast(RevisionRange) ranges.get(ranges.size() - 1);
+        if (line is end(lastRegion))
+            return lastRegion;
+        return null;
+    }
+
+    /**
+     * Returns the sublist of all <code>RevisionRange</code>s that intersect with the given lines.
+     *
+     * @param lines the model based lines of interest
+     * @return elementType: RevisionRange
+     */
+    private List getRanges(ILineRange lines) {
+        List ranges= getRangeCache();
+
+        // return the interesting subset
+        int end_= end(lines);
+        int first= -1, last= -1;
+        for (int i= 0; i < ranges.size(); i++) {
+            RevisionRange range= cast(RevisionRange) ranges.get(i);
+            int rangeEnd= end(range);
+            if (first is -1 && rangeEnd > lines.getStartLine())
+                first= i;
+            if (first !is -1 && rangeEnd > end_) {
+                last= i;
+                break;
+            }
+        }
+        if (first is -1)
+            return Collections.EMPTY_LIST;
+        if (last is -1)
+            last= ranges.size() - 1; // bottom index may be one too much
+
+        return ranges.subList(first, last + 1);
+    }
+
+    /**
+     * Gets all change ranges of the revisions in the revision model and adapts them to the current
+     * quick diff information. The list is cached.
+     *
+     * @return the list of all change regions, with diff information applied
+     */
+    private List getRangeCache() {
+        if (fRevisionRanges is null) {
+            if (fRevisionInfo is null) {
+                fRevisionRanges= Collections.EMPTY_LIST;
+            } else {
+                Hunk[] hunks= HunkComputer.computeHunks(fLineDiffer, fViewer.getDocument().getNumberOfLines());
+                fRevisionInfo.applyDiff(hunks);
+                fRevisionRanges= fRevisionInfo.getRanges();
+                updateOverviewAnnotations();
+                informListeners();
+            }
+        }
+
+        return fRevisionRanges;
+    }
+
+    /**
+     * Clears the range cache.
+     *
+     * @since 3.3
+     */
+    private void clearRangeCache() {
+        fRevisionRanges= null;
+    }
+
+    /**
+     * Returns <code>true</code> if <code>range</code> contains <code>line</code>. A line is
+     * not contained in a range if it is the range's exclusive end line.
+     *
+     * @param range the range to check whether it contains <code>line</code>
+     * @param line the line the line
+     * @return <code>true</code> if <code>range</code> contains <code>line</code>,
+     *         <code>false</code> if not
+     */
+    private static bool contains(ILineRange range, int line) {
+        return range.getStartLine() <= line && end(range) > line;
+    }
+
+    /**
+     * Computes the end index of a line range.
+     *
+     * @param range a line range
+     * @return the last line (exclusive) of <code>range</code>
+     */
+    private static int end(ILineRange range) {
+        return range.getStartLine() + range.getNumberOfLines();
+    }
+
+    /**
+     * Returns the visible extent of a document line range in widget lines.
+     *
+     * @param range the document line range
+     * @return the visible extent of <code>range</code> in widget lines
+     */
+    private ILineRange modelLinesToWidgetLines(ILineRange range) {
+        int widgetStartLine= -1;
+        int widgetEndLine= -1;
+        if ( cast(ITextViewerExtension5)fViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) fViewer;
+            int modelEndLine= end(range);
+            for (int modelLine= range.getStartLine(); modelLine < modelEndLine; modelLine++) {
+                int widgetLine= extension.modelLine2WidgetLine(modelLine);
+                if (widgetLine !is -1) {
+                    if (widgetStartLine is -1)
+                        widgetStartLine= widgetLine;
+                    widgetEndLine= widgetLine;
+                }
+            }
+        } else {
+            IRegion region= fViewer.getVisibleRegion();
+            IDocument document= fViewer.getDocument();
+            try {
+                int visibleStartLine= document.getLineOfOffset(region.getOffset());
+                int visibleEndLine= document.getLineOfOffset(region.getOffset() + region.getLength());
+                widgetStartLine= Math.max(0, range.getStartLine() - visibleStartLine);
+                widgetEndLine= Math.min(visibleEndLine, end(range) - 1);
+            } catch (BadLocationException x) {
+                ExceptionPrintStackTrace(x);
+                // ignore and return null
+            }
+        }
+        if (widgetStartLine is -1 || widgetEndLine is -1)
+            return null;
+        return new LineRange(widgetStartLine, widgetEndLine - widgetStartLine + 1);
+    }
+
+    /**
+     * Returns the revision hover.
+     *
+     * @return the revision hover
+     */
+    public IAnnotationHover getHover() {
+        return fHover;
+    }
+
+    /**
+     * Computes and returns the bounds of the rectangle corresponding to a widget line range. The
+     * rectangle is in pixel coordinates relative to the text widget's
+     * {@link StyledText#getClientArea() client area} and has the width of the ruler.
+     *
+     * @param range the widget line range
+     * @return the box bounds corresponding to <code>range</code>
+     */
+    private Rectangle computeBoxBounds(ILineRange range) {
+        int y1= fWidget.getLinePixel(range.getStartLine());
+        int y2= fWidget.getLinePixel(range.getStartLine() + range.getNumberOfLines());
+
+        return new Rectangle(0, y1, getWidth(), y2 - y1 - 1);
+    }
+
+    /**
+     * Shows (or hides) the overview annotations.
+     */
+    private void updateOverviewAnnotations() {
+        if (fAnnotationModel is null)
+            return;
+
+        Revision revision= fFocusRevision !is null ? fFocusRevision : fSelectedRevision;
+
+        Map added= null;
+        if (revision !is null) {
+            added= new HashMap();
+            for (Iterator it= revision.getRegions().iterator(); it.hasNext();) {
+                RevisionRange range= cast(RevisionRange) it.next();
+                try {
+                    IRegion charRegion= toCharRegion(range);
+                    Position position= new Position(charRegion.getOffset(), charRegion.getLength());
+                    Annotation annotation= new RevisionAnnotation(revision.getId());
+                    added.put(annotation, position);
+                } catch (BadLocationException x) {
+                    // ignore - document was changed, show no annotations
+                }
+            }
+        }
+
+        if ( cast(IAnnotationModelExtension)fAnnotationModel ) {
+            IAnnotationModelExtension ext= cast(IAnnotationModelExtension) fAnnotationModel;
+            ext.replaceAnnotations(arraycast!(Annotation)( fAnnotations.toArray()), added);
+        } else {
+            for (Iterator it= fAnnotations.iterator(); it.hasNext();) {
+                Annotation annotation= cast(Annotation) it.next();
+                fAnnotationModel.removeAnnotation(annotation);
+            }
+            if (added !is null) {
+                for (Iterator it= added.entrySet().iterator(); it.hasNext();) {
+                    Map.Entry entry= cast(Map.Entry) it.next();
+                    fAnnotationModel.addAnnotation(cast(Annotation) entry.getKey(), cast(Position) entry.getValue());
+                }
+            }
+        }
+        fAnnotations.clear();
+        if (added !is null)
+            fAnnotations.addAll(added.keySet());
+
+    }
+
+    /**
+     * Returns the character offset based region of a line range.
+     *
+     * @param lines the line range to convert
+     * @return the character offset range corresponding to <code>range</code>
+     * @throws BadLocationException if the line range is not within the document bounds
+     */
+    private IRegion toCharRegion(ILineRange lines)  {
+        IDocument document= fViewer.getDocument();
+        int offset= document.getLineOffset(lines.getStartLine());
+        int nextLine= end(lines);
+        int endOffset;
+        if (nextLine >= document.getNumberOfLines())
+            endOffset= document.getLength();
+        else
+            endOffset= document.getLineOffset(nextLine);
+        return new Region(offset, endOffset - offset);
+    }
+
+    /**
+     * Handles the selection of a revision and informs listeners.
+     *
+     * @param revision the selected revision, <code>null</code> for none
+     */
+    void handleRevisionSelected(Revision revision) {
+        fSelectedRevision= revision;
+        fRevisionSelectionProvider.revisionSelected(revision);
+        updateOverviewAnnotations();
+        postRedraw();
+    }
+
+    /**
+     * Handles the selection of a revision id and informs listeners
+     *
+     * @param id the selected revision id
+     */
+    void handleRevisionSelected(String id) {
+        Assert.isLegal(id !is null);
+        if (fRevisionInfo is null)
+            return;
+
+        for (Iterator it= fRevisionInfo.getRevisions().iterator(); it.hasNext();) {
+            Revision revision= cast(Revision) it.next();
+            if (id.equals(revision.getId())) {
+                handleRevisionSelected(revision);
+                return;
+            }
+        }
+
+        // clear selection if it does not exist
+        handleRevisionSelected(cast(Revision) null);
+    }
+
+    /**
+     * Returns the selection provider.
+     *
+     * @return the selection provider
+     */
+    public RevisionSelectionProvider getRevisionSelectionProvider() {
+        return fRevisionSelectionProvider;
+    }
+
+    /**
+     * Updates the focus line with a new line.
+     *
+     * @param line the new focus line, -1 for no focus
+     */
+    private void updateFocusLine(int line) {
+        if (fFocusLine !is line)
+            onFocusLineChanged(fFocusLine, line);
+    }
+
+    /**
+     * Handles a changing focus line.
+     *
+     * @param previousLine the old focus line (-1 for no focus)
+     * @param nextLine the new focus line (-1 for no focus)
+     */
+    private void onFocusLineChanged(int previousLine, int nextLine) {
+        if (DEBUG)
+            System.out_.println(Format("line: {} > {}", previousLine, nextLine)); //$NON-NLS-1$ //$NON-NLS-2$
+        fFocusLine= nextLine;
+        RevisionRange region= getRange(nextLine);
+        updateFocusRange(region);
+    }
+
+    /**
+     * Updates the focus range.
+     *
+     * @param range the new focus range, <code>null</code> for no focus
+     */
+    private void updateFocusRange(RevisionRange range) {
+        if (range !is fFocusRange)
+            onFocusRangeChanged(fFocusRange, range);
+    }
+
+    /**
+     * Handles a changing focus range.
+     *
+     * @param previousRange the old focus range (<code>null</code> for no focus)
+     * @param nextRange the new focus range (<code>null</code> for no focus)
+     */
+    private void onFocusRangeChanged(RevisionRange previousRange, RevisionRange nextRange) {
+        if (DEBUG)
+            System.out_.println(Format("range: {} > {}", previousRange, nextRange)); //$NON-NLS-1$ //$NON-NLS-2$
+        fFocusRange= nextRange;
+        Revision revision= nextRange is null ? null : nextRange.getRevision();
+        updateFocusRevision(revision);
+    }
+
+    private void updateFocusRevision(Revision revision) {
+        if (fFocusRevision !is revision)
+            onFocusRevisionChanged(fFocusRevision, revision);
+    }
+
+    /**
+     * Handles a changing focus revision.
+     *
+     * @param previousRevision the old focus revision (<code>null</code> for no focus)
+     * @param nextRevision the new focus revision (<code>null</code> for no focus)
+     */
+    private void onFocusRevisionChanged(Revision previousRevision, Revision nextRevision) {
+        if (DEBUG)
+            System.out_.println(Format("revision: {} > {}", previousRevision, nextRevision)); //$NON-NLS-1$ //$NON-NLS-2$
+        fFocusRevision= nextRevision;
+        uninstallWheelHandler();
+        installWheelHandler();
+        updateOverviewAnnotations();
+        redraw(); // pick up new highlights
+    }
+
+    /**
+     * Uninstalls the mouse wheel handler.
+     */
+    private void uninstallWheelHandler() {
+        fControl.removeListener(SWT.MouseWheel, cast(Listener)fMouseHandler);
+        fWheelHandlerInstalled= false;
+    }
+
+    /**
+     * Installs the mouse wheel handler.
+     */
+    private void installWheelHandler() {
+        if (fFocusRevision !is null && !fWheelHandlerInstalled) {
+            //FIXME: does not work on Windows, because Canvas cannot get focus and therefore does not send out mouse wheel events:
+            //https://bugs.eclipse.org/bugs/show_bug.cgi?id=81189
+            //see also https://bugs.eclipse.org/bugs/show_bug.cgi?id=75766
+            fControl.addListener(SWT.MouseWheel, fMouseHandler);
+            fWheelHandlerInstalled= true;
+        }
+    }
+
+    /**
+     * Handles a mouse wheel event.
+     *
+     * @param event the mouse wheel event
+     */
+    private void handleMouseWheel(Event event) {
+        bool up= event.count > 0;
+        int documentHoverLine= fFocusLine;
+
+        ILineRange nextWidgetRange= null;
+        ILineRange last= null;
+        List ranges= fFocusRevision.getRegions();
+        if (up) {
+            for (Iterator it= ranges.iterator(); it.hasNext();) {
+                RevisionRange range= cast(RevisionRange) it.next();
+                ILineRange widgetRange= modelLinesToWidgetLines(range);
+                if (contains(range, documentHoverLine)) {
+                    nextWidgetRange= last;
+                    break;
+                }
+                if (widgetRange !is null)
+                    last= widgetRange;
+            }
+        } else {
+            for (ListIterator it= ranges.listIterator(ranges.size()); it.hasPrevious();) {
+                RevisionRange range= cast(RevisionRange) it.previous();
+                ILineRange widgetRange= modelLinesToWidgetLines(range);
+                if (contains(range, documentHoverLine)) {
+                    nextWidgetRange= last;
+                    break;
+                }
+                if (widgetRange !is null)
+                    last= widgetRange;
+            }
+        }
+
+        if (nextWidgetRange is null)
+            return;
+
+        int widgetCurrentFocusLine= modelLinesToWidgetLines(new LineRange(documentHoverLine, 1)).getStartLine();
+        int widgetNextFocusLine= nextWidgetRange.getStartLine();
+        int newTopPixel= fWidget.getTopPixel() + JFaceTextUtil.computeLineHeight(fWidget, widgetCurrentFocusLine, widgetNextFocusLine, widgetNextFocusLine - widgetCurrentFocusLine);
+        fWidget.setTopPixel(newTopPixel);
+        if (newTopPixel < 0) {
+            Point cursorLocation= fWidget.getDisplay().getCursorLocation();
+            cursorLocation.y+= newTopPixel;
+            fWidget.getDisplay().setCursorLocation(cursorLocation);
+        } else {
+            int topPixel= fWidget.getTopPixel();
+            if (topPixel < newTopPixel) {
+                Point cursorLocation= fWidget.getDisplay().getCursorLocation();
+                cursorLocation.y+= newTopPixel - topPixel;
+                fWidget.getDisplay().setCursorLocation(cursorLocation);
+            }
+        }
+        updateFocusLine(toDocumentLineNumber(fWidget.toControl(fWidget.getDisplay().getCursorLocation()).y));
+        immediateUpdate();
+    }
+
+    /**
+     * Triggers a redraw in the display thread.
+     */
+    private final void postRedraw() {
+        if (isConnected() && !fControl.isDisposed()) {
+            Display d= fControl.getDisplay();
+            if (d !is null) {
+                d.asyncExec(new class()  Runnable {
+                    public void run() {
+                        redraw();
+                    }
+                });
+            }
+        }
+    }
+
+    /**
+     * Translates a y coordinate in the pixel coordinates of the column's control to a document line
+     * number.
+     *
+     * @param y the y coordinate
+     * @return the corresponding document line, -1 for no line
+     * @see CompositeRuler#toDocumentLineNumber(int)
+     */
+    private int toDocumentLineNumber(int y) {
+        return fParentRuler.toDocumentLineNumber(y);
+    }
+
+    /**
+     * Triggers redrawing of the column.
+     */
+    private void redraw() {
+        fColumn.redraw();
+    }
+
+    /**
+     * Triggers immediate redrawing of the entire column - use with care.
+     */
+    private void immediateUpdate() {
+        fParentRuler.immediateUpdate();
+    }
+
+    /**
+     * Returns the width of the column.
+     *
+     * @return the width of the column
+     */
+    private int getWidth() {
+        return fColumn.getWidth();
+    }
+
+    /**
+     * Returns the System background color for list widgets.
+     *
+     * @return the System background color for list widgets
+     */
+    private Color getBackground() {
+        if (fBackground is null)
+            return fWidget.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
+        return fBackground;
+    }
+
+    /**
+     * Sets the hover later returned by {@link #getHover()}.
+     *
+     * @param hover the hover
+     */
+    public void setHover(IAnnotationHover hover) {
+        // TODO ignore for now - must make revision hover settable from outside
+    }
+
+    /**
+     * Returns <code>true</code> if the receiver can provide a hover for a certain document line.
+     *
+     * @param activeLine the document line of interest
+     * @return <code>true</code> if the receiver can provide a hover
+     */
+    public bool hasHover(int activeLine) {
+        return cast(ISourceViewer)fViewer && fHover.getHoverLineRange(cast(ISourceViewer) fViewer, activeLine) !is null;
+    }
+
+    /**
+     * Returns the revision at a certain document offset, or <code>null</code> for none.
+     *
+     * @param offset the document offset
+     * @return the revision at offset, or <code>null</code> for none
+     */
+    Revision getRevision(int offset) {
+        IDocument document= fViewer.getDocument();
+        int line;
+        try {
+            line= document.getLineOfOffset(offset);
+        } catch (BadLocationException x) {
+            return null;
+        }
+        if (line !is -1) {
+            RevisionRange range= getRange(line);
+            if (range !is null)
+                return range.getRevision();
+        }
+        return null;
+    }
+
+    /**
+     * Returns <code>true</code> if a revision model has been set, <code>false</code> otherwise.
+     *
+     * @return <code>true</code> if a revision model has been set, <code>false</code> otherwise
+     */
+    public bool hasInformation() {
+        return fRevisionInfo !is null;
+    }
+
+    /**
+     * Returns the width in chars required to display information.
+     *
+     * @return the width in chars required to display information
+     * @since 3.3
+     */
+    public int getRequiredWidth() {
+        if (fRequiredWidth is -1) {
+            if (hasInformation() && (fShowRevision || fShowAuthor)) {
+                int revisionWidth= 0;
+                int authorWidth= 0;
+                for (Iterator it= fRevisionInfo.getRevisions().iterator(); it.hasNext();) {
+                    Revision revision= cast(Revision) it.next();
+                    revisionWidth= Math.max(revisionWidth, revision.getId().length());
+                    authorWidth= Math.max(authorWidth, revision.getAuthor().length());
+                }
+                fRevisionIdChars= revisionWidth + 1;
+                if (fShowAuthor && fShowRevision)
+                    fRequiredWidth= revisionWidth + authorWidth + 2;
+                else if (fShowAuthor)
+                    fRequiredWidth= authorWidth + 1;
+                else
+                    fRequiredWidth= revisionWidth + 1;
+            } else {
+                fRequiredWidth= 0;
+            }
+        }
+        return fRequiredWidth;
+    }
+
+    /**
+     * Enables showing the revision id.
+     *
+     * @param show <code>true</code> to show the revision, <code>false</code> to hide it
+     */
+    public void showRevisionId(bool show) {
+        if (fShowRevision !is show) {
+            fRequiredWidth= -1;
+            fRevisionIdChars= 0;
+            fShowRevision= show;
+            postRedraw();
+        }
+    }
+
+    /**
+     * Enables showing the revision author.
+     *
+     * @param show <code>true</code> to show the author, <code>false</code> to hide it
+     */
+    public void showRevisionAuthor(bool show) {
+        if (fShowAuthor !is show) {
+            fRequiredWidth= -1;
+            fRevisionIdChars= 0;
+            fShowAuthor= show;
+            postRedraw();
+        }
+    }
+
+    /**
+     * Adds a revision listener.
+     *
+     * @param listener the listener
+     * @since 3.3
+     */
+    public void addRevisionListener(IRevisionListener listener) {
+        fRevisionListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes a revision listener.
+     *
+     * @param listener the listener
+     * @since 3.3
+     */
+    public void removeRevisionListener(IRevisionListener listener) {
+        fRevisionListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Informs the revision listeners about a change.
+     *
+     * @since 3.3
+     */
+    private void informListeners() {
+        if (fRevisionInfo is null || fRevisionListeners.isEmpty())
+            return;
+
+        RevisionEvent event= new RevisionEvent(fRevisionInfo);
+        Object[] listeners= fRevisionListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++) {
+            IRevisionListener listener= cast(IRevisionListener) listeners[i];
+            listener.revisionInformationChanged(event);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/revisions/RevisionSelectionProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.text.revisions.RevisionSelectionProvider;
+
+import org.eclipse.jface.internal.text.revisions.HunkComputer; // packageimport
+import org.eclipse.jface.internal.text.revisions.LineIndexOutOfBoundsException; // packageimport
+import org.eclipse.jface.internal.text.revisions.Hunk; // packageimport
+import org.eclipse.jface.internal.text.revisions.Colors; // packageimport
+import org.eclipse.jface.internal.text.revisions.ChangeRegion; // packageimport
+import org.eclipse.jface.internal.text.revisions.Range; // packageimport
+import org.eclipse.jface.internal.text.revisions.RevisionPainter; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.revisions.Revision;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+
+/**
+ * A selection provider for annotate revisions. Selections of a revision can currently happen in
+ * following ways - note that this list may be changed in the future:
+ * <ul>
+ * <li>when the user clicks the revision ruler with the mouse</li>
+ * <li>when the caret is moved to a revision's line (only on post-selection)</li>
+ * </ul>
+ * <p>
+ * Calling {@link #setSelection(ISelection)} will set the current sticky revision on the ruler.
+ * </p>
+ *
+ * @since 3.2
+ */
+public final class RevisionSelectionProvider : ISelectionProvider {
+
+    /**
+     * Post selection listener on the viewer that remembers the selection provider it is registered
+     * with.
+     */
+    private final class PostSelectionListener : ISelectionChangedListener {
+        private const IPostSelectionProvider fPostProvider;
+
+        public this(IPostSelectionProvider postProvider) {
+            postProvider.addPostSelectionChangedListener(this);
+            fPostProvider= postProvider;
+        }
+
+        public void selectionChanged(SelectionChangedEvent event) {
+            ISelection selection= event.getSelection();
+            if ( cast(ITextSelection)selection ) {
+                ITextSelection ts= cast(ITextSelection) selection;
+                int offset= ts.getOffset();
+                setSelectedRevision(fPainter.getRevision(offset));
+            }
+
+        }
+
+        public void dispose() {
+            fPostProvider.removePostSelectionChangedListener(this);
+        }
+    }
+
+    private const RevisionPainter fPainter;
+    private const ListenerList fListeners;
+
+    /**
+     * The text viewer once we are installed, <code>null</code> if not installed.
+     */
+    private ITextViewer fViewer;
+    /**
+     * The selection listener on the viewer, or <code>null</code>.
+     */
+    private PostSelectionListener fSelectionListener;
+    /**
+     * The last selection, or <code>null</code>.
+     */
+    private Revision fSelection;
+    /**
+     * Incoming selection changes are ignored while sending out events.
+     *
+     * @since 3.3
+     */
+    private bool fIgnoreEvents= false;
+
+    /**
+     * Creates a new selection provider.
+     *
+     * @param painter the painter that the created provider interacts with
+     */
+    this(RevisionPainter painter) {
+        fListeners= new ListenerList();
+        fPainter= painter;
+    }
+
+    /*
+     * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
+     */
+    public void addSelectionChangedListener(ISelectionChangedListener listener) {
+        fListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
+     */
+    public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+        fListeners.remove(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
+     */
+    public ISelection getSelection() {
+        if (fSelection is null)
+            return StructuredSelection.EMPTY;
+        return new StructuredSelection(fSelection);
+    }
+
+    /*
+     * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)
+     */
+    public void setSelection(ISelection selection) {
+        if (fIgnoreEvents)
+            return;
+        if ( cast(IStructuredSelection)selection ) {
+            Object first= (cast(IStructuredSelection) selection).getFirstElement();
+            if ( cast(Revision)first )
+                fPainter.handleRevisionSelected(cast(Revision) first);
+            else if ( auto str = cast(ArrayWrapperString)first )
+                fPainter.handleRevisionSelected(str.array);
+            else if (selection.isEmpty())
+                fPainter.handleRevisionSelected(cast(Revision) null);
+        }
+    }
+
+    /**
+     * Installs the selection provider on the viewer.
+     *
+     * @param viewer the viewer on which we listen to for post selection events
+     */
+    void install(ITextViewer viewer) {
+        uninstall();
+        fViewer= viewer;
+        if (fViewer !is null) {
+            ISelectionProvider provider= fViewer.getSelectionProvider();
+            if ( cast(IPostSelectionProvider)provider ) {
+                IPostSelectionProvider postProvider= cast(IPostSelectionProvider) provider;
+                fSelectionListener= new PostSelectionListener(postProvider);
+            }
+        }
+    }
+
+    /**
+     * Uninstalls the selection provider.
+     */
+    void uninstall() {
+        fViewer= null;
+        if (fSelectionListener !is null) {
+            fSelectionListener.dispose();
+            fSelectionListener= null;
+        }
+    }
+
+    /**
+     * Private protocol used by {@link RevisionPainter} to signal selection of a revision.
+     *
+     * @param revision the selected revision, or <code>null</code> for none
+     */
+    void revisionSelected(Revision revision) {
+        setSelectedRevision(revision);
+    }
+
+    /**
+     * Updates the currently selected revision and sends out an event if it changed.
+     *
+     * @param revision the newly selected revision or <code>null</code> for none
+     */
+    private void setSelectedRevision(Revision revision) {
+        if (revision !is fSelection) {
+            fSelection= revision;
+            fireSelectionEvent();
+        }
+    }
+
+    private void fireSelectionEvent() {
+        fIgnoreEvents= true;
+        try {
+            ISelection selection= getSelection();
+            SelectionChangedEvent event= new SelectionChangedEvent(this, selection);
+
+            Object[] listeners= fListeners.getListeners();
+            for (int i= 0; i < listeners.length; i++)
+                (cast(ISelectionChangedListener) listeners[i]).selectionChanged(event);
+        } finally {
+            fIgnoreEvents= false;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/source/DiffPainter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,542 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.text.source.DiffPainter;
+
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.JFaceTextUtil;
+import org.eclipse.jface.text.source.CompositeRuler;
+import org.eclipse.jface.text.source.IAnnotationHover;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.IAnnotationModelExtension;
+import org.eclipse.jface.text.source.IAnnotationModelListener;
+import org.eclipse.jface.text.source.IChangeRulerColumn;
+import org.eclipse.jface.text.source.ILineDiffInfo;
+import org.eclipse.jface.text.source.ILineDiffer;
+import org.eclipse.jface.text.source.ILineDifferExtension2;
+import org.eclipse.jface.text.source.ILineRange;
+import org.eclipse.jface.text.source.ISharedTextColors;
+import org.eclipse.jface.text.source.IVerticalRulerColumn;
+
+
+/**
+ * A strategy for painting the quick diff colors onto the vertical ruler column. It also manages the
+ * quick diff hover.
+ *
+ * @since 3.2
+ */
+public final class DiffPainter {
+    /**
+     * Internal listener class that will update the ruler when the underlying model changes.
+     */
+    private class AnnotationListener : IAnnotationModelListener {
+        /*
+         * @see org.eclipse.jface.text.source.IAnnotationModelListener#modelChanged(org.eclipse.jface.text.source.IAnnotationModel)
+         */
+        public void modelChanged(IAnnotationModel model) {
+            postRedraw();
+        }
+    }
+
+    /** The vertical ruler column that delegates painting to this painter. */
+    private const IVerticalRulerColumn fColumn;
+    /** The parent ruler. */
+    private CompositeRuler fParentRuler;
+    /** The column's control, typically a {@link Canvas}, possibly <code>null</code>. */
+    private Control fControl;
+    /** The text viewer that the column is attached to. */
+    private ITextViewer fViewer;
+    /** The viewer's text widget. */
+    private StyledText fWidget;
+    /** The line differ extracted from the annotation model. */
+    private ILineDiffer fLineDiffer= null;
+    /** Color for changed lines */
+    private Color fAddedColor;
+    /** Color for added lines */
+    private Color fChangedColor;
+    /** Color for the deleted line indicator */
+    private Color fDeletedColor;
+    /** The background color. */
+    private Color fBackground;
+    /** The ruler's hover */
+    private IAnnotationHover fHover;
+    /** The internal listener */
+    private const AnnotationListener fAnnotationListener;
+    /** The shared color provider, possibly <code>null</code>. */
+    private const ISharedTextColors fSharedColors;
+
+    /**
+     * Creates a new diff painter for a vertical ruler column.
+     *
+     * @param column the column that will delegate{@link #paint(GC, ILineRange) painting} to the
+     *        newly created painter.
+     * @param sharedColors a shared colors object to store shaded colors in, may be
+     *        <code>null</code>
+     */
+    public this(IVerticalRulerColumn column, ISharedTextColors sharedColors) {
+        fAnnotationListener= new AnnotationListener();
+        Assert.isLegal(column !is null);
+        fColumn= column;
+        fSharedColors= sharedColors;
+    }
+
+    /**
+     * Sets the parent ruler - the delegating column must call this method as soon as it creates its
+     * control.
+     *
+     * @param parentRuler the parent ruler
+     */
+    public void setParentRuler(CompositeRuler parentRuler) {
+        fParentRuler= parentRuler;
+    }
+
+    /**
+     * Sets the quick diff hover later returned by {@link #getHover()}.
+     *
+     * @param hover the hover
+     */
+    public void setHover(IAnnotationHover hover) {
+        fHover= hover;
+    }
+
+    /**
+     * Returns the quick diff hover set by {@link #setHover(IAnnotationHover)}.
+     *
+     * @return the quick diff hover set by {@link #setHover(IAnnotationHover)}
+     */
+    public IAnnotationHover getHover() {
+        return fHover;
+    }
+
+    /**
+     * Sets the background color.
+     *
+     * @param background the background color, <code>null</code> to use the platform's list background
+     */
+    public void setBackground(Color background) {
+        fBackground= background;
+    }
+
+    /**
+     * Delegates the painting of the quick diff colors to this painter. The painter will draw the
+     * color boxes onto the passed {@link GC} for all model (document) lines in
+     * <code>visibleModelLines</code>.
+     *
+     * @param gc the {@link GC} to draw onto
+     * @param visibleModelLines the lines (in document offsets) that are currently (perhaps only
+     *        partially) visible
+     */
+    public void paint(GC gc, ILineRange visibleModelLines) {
+        connectIfNeeded();
+        if (!isConnected())
+            return;
+
+        // draw diff info
+        final int lastLine= end(visibleModelLines);
+        final int width= getWidth();
+        final Color deletionColor= getDeletionColor();
+        for (int line= visibleModelLines.getStartLine(); line < lastLine; line++) {
+            paintLine(line, gc, width, deletionColor);
+        }
+    }
+
+    /**
+     * Ensures that the column is fully instantiated, i.e. has a control, and that the viewer is
+     * visible.
+     */
+    private void connectIfNeeded() {
+        if (isConnected() || fParentRuler is null)
+            return;
+
+        fViewer= fParentRuler.getTextViewer();
+        if (fViewer is null)
+            return;
+
+        fWidget= fViewer.getTextWidget();
+        if (fWidget is null)
+            return;
+
+        fControl= fColumn.getControl();
+        if (fControl is null)
+            return;
+
+        fControl.addDisposeListener(new class()  DisposeListener {
+            /*
+             * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
+             */
+            public void widgetDisposed(DisposeEvent e) {
+                handleDispose();
+            }
+        });
+    }
+
+    /**
+     * Returns <code>true</code> if the column is fully connected.
+     *
+     * @return <code>true</code> if the column is fully connected, false otherwise
+     */
+    private bool isConnected() {
+        return fControl !is null;
+    }
+
+    /**
+     * Disposes of this painter and releases any resources.
+     */
+    private void handleDispose() {
+        if (fLineDiffer !is null) {
+            (cast(IAnnotationModel) fLineDiffer).removeAnnotationModelListener(fAnnotationListener);
+            fLineDiffer= null;
+        }
+    }
+
+    /**
+     * Paints a single model line onto <code>gc</code>.
+     *
+     * @param line the model line to paint
+     * @param gc the {@link GC} to paint onto
+     * @param width the width of the column
+     * @param deletionColor the background color used to indicate deletions
+     */
+    private void paintLine(int line, GC gc, int width, Color deletionColor) {
+        int widgetLine= JFaceTextUtil.modelLineToWidgetLine(fViewer, line);
+        if (widgetLine is -1)
+            return;
+
+        ILineDiffInfo info= getDiffInfo(line);
+
+        if (info !is null) {
+            int y= fWidget.getLinePixel(widgetLine);
+            int lineHeight= fWidget.getLineHeight(fWidget.getOffsetAtLine(widgetLine));
+
+            // draw background color if special
+            if (hasSpecialColor(info)) {
+                gc.setBackground(getColor(info));
+                gc.fillRectangle(0, y, width, lineHeight);
+            }
+
+            /* Deletion Indicator: Simply a horizontal line */
+            int delBefore= info.getRemovedLinesAbove();
+            int delBelow= info.getRemovedLinesBelow();
+            if (delBefore > 0 || delBelow > 0) {
+                gc.setForeground(deletionColor);
+                if (delBefore > 0)
+                    gc.drawLine(0, y, width, y);
+                if (delBelow > 0)
+                    gc.drawLine(0, y + lineHeight - 1, width, y + lineHeight - 1);
+            }
+        }
+    }
+
+    /**
+     * Returns whether the line background differs from the default.
+     *
+     * @param info the info being queried
+     * @return <code>true</code> if <code>info</code> describes either a changed or an added
+     *         line.
+     */
+    private bool hasSpecialColor(ILineDiffInfo info) {
+        return info.getChangeType() is ILineDiffInfo.ADDED || info.getChangeType() is ILineDiffInfo.CHANGED;
+    }
+
+    /**
+     * Retrieves the <code>ILineDiffInfo</code> for <code>line</code> from the model. There are
+     * optimizations for direct access and sequential access patterns.
+     *
+     * @param line the line we want the info for.
+     * @return the <code>ILineDiffInfo</code> for <code>line</code>, or <code>null</code>.
+     */
+    private ILineDiffInfo getDiffInfo(int line) {
+        if (fLineDiffer !is null)
+            return fLineDiffer.getLineInfo(line);
+
+        return null;
+    }
+
+    /**
+     * Returns the color for deleted lines.
+     *
+     * @return the color to be used for the deletion indicator
+     */
+    private Color getDeletionColor() {
+        return fDeletedColor is null ? getBackground() : fDeletedColor;
+    }
+
+    /**
+     * Returns the color for the given line diff info.
+     *
+     * @param info the <code>ILineDiffInfo</code> being queried
+     * @return the correct background color for the line type being described by <code>info</code>
+     */
+    private Color getColor(ILineDiffInfo info) {
+        Assert.isTrue(info !is null && info.getChangeType() !is ILineDiffInfo.UNCHANGED);
+        Color ret= null;
+        switch (info.getChangeType()) {
+            case ILineDiffInfo.CHANGED:
+                ret= getShadedColor(fChangedColor);
+                break;
+            case ILineDiffInfo.ADDED:
+                ret= getShadedColor(fAddedColor);
+                break;
+            default:
+        }
+        return ret is null ? getBackground() : ret;
+    }
+
+    /**
+     * Sets the background color for changed lines.
+     *
+     * @param color the new color to be used for the changed lines background
+     * @return the shaded color
+     */
+    private Color getShadedColor(Color color) {
+        if (color is null)
+            return null;
+
+        if (fSharedColors is null)
+            return color;
+
+        RGB baseRGB= color.getRGB();
+        RGB background= getBackground().getRGB();
+
+        bool darkBase= isDark(baseRGB);
+        bool darkBackground= isDark(background);
+        if (darkBase && darkBackground)
+            background= new RGB(255, 255, 255);
+        else if (!darkBase && !darkBackground)
+            background= new RGB(0, 0, 0);
+
+        return fSharedColors.getColor(interpolate(baseRGB, background, 0.6));
+    }
+
+    /**
+     * Sets the annotation model.
+     *
+     * @param model the annotation model, possibly <code>null</code>
+     * @see IVerticalRulerColumn#setModel(IAnnotationModel)
+     */
+    public void setModel(IAnnotationModel model) {
+        IAnnotationModel newModel;
+        if ( cast(IAnnotationModelExtension)model )
+            newModel= (cast(IAnnotationModelExtension) model).getAnnotationModel(stringcast(IChangeRulerColumn.QUICK_DIFF_MODEL_ID));
+        else
+            newModel= model;
+
+        setDiffer(newModel);
+    }
+
+    /**
+     * Sets the line differ.
+     *
+     * @param differ the line differ
+     */
+    private void setDiffer(IAnnotationModel differ) {
+        if ( cast(ILineDiffer)differ ) {
+            if ( cast(Object)fLineDiffer !is cast(Object)differ) {
+                if (fLineDiffer !is null)
+                    (cast(IAnnotationModel) fLineDiffer).removeAnnotationModelListener(fAnnotationListener);
+                fLineDiffer= cast(ILineDiffer) differ;
+                if (fLineDiffer !is null)
+                    (cast(IAnnotationModel) fLineDiffer).addAnnotationModelListener(fAnnotationListener);
+            }
+        }
+    }
+
+    /**
+     * Triggers a redraw in the display thread.
+     */
+    private final void postRedraw() {
+        if (isConnected() && !fControl.isDisposed()) {
+            Display d= fControl.getDisplay();
+            if (d !is null) {
+                d.asyncExec(new class()  Runnable {
+                    public void run() {
+                        redraw();
+                    }
+                });
+            }
+        }
+    }
+
+    /**
+     * Triggers redrawing of the column.
+     */
+    private void redraw() {
+        fColumn.redraw();
+    }
+
+    /**
+     * Returns the width of the column.
+     *
+     * @return the width of the column
+     */
+    private int getWidth() {
+        return fColumn.getWidth();
+    }
+
+    /**
+     * Computes the end index of a line range.
+     *
+     * @param range a line range
+     * @return the last line (exclusive) of <code>range</code>
+     */
+    private static int end(ILineRange range) {
+        return range.getStartLine() + range.getNumberOfLines();
+    }
+
+    /**
+     * Returns the System background color for list widgets or the set background.
+     *
+     * @return the System background color for list widgets
+     */
+    private Color getBackground() {
+        if (fBackground is null)
+            return fWidget.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
+        return fBackground;
+    }
+
+    /**
+     * Sets the color for added lines.
+     *
+     * @param addedColor the color for added lines
+     * @see org.eclipse.jface.text.source.IChangeRulerColumn#setAddedColor(org.eclipse.swt.graphics.Color)
+     */
+    public void setAddedColor(Color addedColor) {
+        fAddedColor= addedColor;
+    }
+
+    /**
+     * Sets the color for changed lines.
+     *
+     * @param changedColor the color for changed lines
+     * @see org.eclipse.jface.text.source.IChangeRulerColumn#setChangedColor(org.eclipse.swt.graphics.Color)
+     */
+    public void setChangedColor(Color changedColor) {
+        fChangedColor= changedColor;
+    }
+
+    /**
+     * Sets the color for deleted lines.
+     *
+     * @param deletedColor the color for deleted lines
+     * @see org.eclipse.jface.text.source.IChangeRulerColumn#setDeletedColor(org.eclipse.swt.graphics.Color)
+     */
+    public void setDeletedColor(Color deletedColor) {
+        fDeletedColor= deletedColor;
+    }
+
+    /**
+     * Returns <code>true</code> if the receiver can provide a hover for a certain document line.
+     *
+     * @param activeLine the document line of interest
+     * @return <code>true</code> if the receiver can provide a hover
+     */
+    public bool hasHover(int activeLine) {
+        return true;
+    }
+
+    /**
+     * Returns the display character for the accessibility mode for a certain model line.
+     *
+     * @param line the document line of interest
+     * @return the display character for <code>line</code>
+     */
+    public String getDisplayCharacter(int line) {
+        return getDisplayCharacter(getDiffInfo(line));
+    }
+
+    /**
+     * Returns the character to display in character display mode for the given
+     * <code>ILineDiffInfo</code>
+     *
+     * @param info the <code>ILineDiffInfo</code> being queried
+     * @return the character indication for <code>info</code>
+     */
+    private String getDisplayCharacter(ILineDiffInfo info) {
+        if (info is null)
+            return " "; //$NON-NLS-1$
+        switch (info.getChangeType()) {
+            case ILineDiffInfo.CHANGED:
+                return "~"; //$NON-NLS-1$
+            case ILineDiffInfo.ADDED:
+                return "+"; //$NON-NLS-1$
+            default:
+        }
+        return " "; //$NON-NLS-1$
+    }
+
+    /**
+     * Returns a specification of a color that lies between the given foreground and background
+     * color using the given scale factor.
+     *
+     * @param fg the foreground color
+     * @param bg the background color
+     * @param scale the scale factor
+     * @return the interpolated color
+     */
+    private static RGB interpolate(RGB fg, RGB bg, double scale) {
+        return new RGB(cast(int) ((1.0 - scale) * fg.red + scale * bg.red), cast(int) ((1.0 - scale) * fg.green + scale * bg.green), cast(int) ((1.0 - scale) * fg.blue + scale * bg.blue));
+    }
+
+    /**
+     * Returns the grey value in which the given color would be drawn in grey-scale.
+     *
+     * @param rgb the color
+     * @return the grey-scale value
+     */
+    private static double greyLevel(RGB rgb) {
+        if (rgb.red is rgb.green && rgb.green is rgb.blue)
+            return rgb.red;
+        return (0.299 * rgb.red + 0.587 * rgb.green + 0.114 * rgb.blue + 0.5);
+    }
+
+    /**
+     * Returns whether the given color is dark or light depending on the colors grey-scale level.
+     *
+     * @param rgb the color
+     * @return <code>true</code> if the color is dark, <code>false</code> if it is light
+     */
+    private static bool isDark(RGB rgb) {
+        return greyLevel(rgb) > 128;
+    }
+
+    /**
+     * Returns <code>true</code> if diff information is being displayed, <code>false</code> otherwise.
+     *
+     * @return <code>true</code> if diff information is being displayed, <code>false</code> otherwise
+     * @since 3.3
+     */
+    public bool hasInformation() {
+        if ( cast(ILineDifferExtension2)fLineDiffer )
+            return !(cast(ILineDifferExtension2) fLineDiffer).isSuspended();
+        return fLineDiffer !is null;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractHoverInformationControlManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1140 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.AbstractHoverInformationControlManager;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+import tango.text.convert.Format;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Scrollable;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.internal.text.DelayedInputChangeListener;
+import org.eclipse.jface.internal.text.InformationControlReplacer;
+import org.eclipse.jface.internal.text.InternalAccessor;
+import org.eclipse.jface.text.ITextViewerExtension8;
+import org.eclipse.jface.text.source.AnnotationBarHoverManager;
+import org.eclipse.jface.util.Geometry;
+
+
+/**
+ * An information control manager that shows information in response to mouse
+ * hover events. The mouse hover events are caught by registering a
+ * {@link org.eclipse.swt.events.MouseTrackListener} on the manager's subject
+ * control. The manager has by default an information control closer that closes
+ * the information control as soon as the mouse pointer leaves the subject area,
+ * the user presses a key, or the subject control is resized, moved, or
+ * deactivated.
+ * <p>
+ * When being activated by a mouse hover event, the manager disables itself,
+ * until the mouse leaves the subject area. Thus, the manager is usually still
+ * disabled, when the information control has already been closed by the closer.
+ *
+ * @see org.eclipse.swt.events.MouseTrackListener
+ * @since 2.0
+ */
+abstract public class AbstractHoverInformationControlManager : AbstractInformationControlManager {
+
+    /**
+     * The  information control closer for the hover information. Closes the information control as
+     * soon as the mouse pointer leaves the subject area (unless "move into hover" is enabled),
+     * a mouse button is pressed, the user presses a key, or the subject control is resized, moved, or loses focus.
+     */
+    class Closer : IInformationControlCloser, MouseListener, MouseMoveListener, ControlListener, KeyListener, SelectionListener, Listener {
+
+        /** The closer's subject control */
+        private Control fSubjectControl;
+        /** The subject area */
+        private Rectangle fSubjectArea;
+        /** Indicates whether this closer is active */
+        private bool fIsActive= false;
+        /**
+         * The cached display.
+         * @since 3.1
+         */
+        private Display fDisplay;
+
+
+        /**
+         * Creates a new information control closer.
+         */
+        public this() {
+        }
+
+        /*
+         * @see IInformationControlCloser#setSubjectControl(Control)
+         */
+        public void setSubjectControl(Control control) {
+            fSubjectControl= control;
+        }
+
+        /*
+         * @see IInformationControlCloser#setHoverControl(IHoverControl)
+         */
+        public void setInformationControl(IInformationControl control) {
+            // NOTE: we use getCurrentInformationControl() from the outer class
+        }
+
+        /*
+         * @see IInformationControlCloser#start(Rectangle)
+         */
+        public void start(Rectangle subjectArea) {
+
+            if (fIsActive)
+                return;
+            fIsActive= true;
+            fWaitForMouseUp= false;
+
+            fSubjectArea= subjectArea;
+
+            if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
+                fSubjectControl.addMouseListener(this);
+                fSubjectControl.addMouseMoveListener(this);
+                fSubjectControl.addControlListener(this);
+                fSubjectControl.addKeyListener(this);
+                if ( cast(Scrollable)fSubjectControl ) {
+                    Scrollable scrollable= cast(Scrollable) fSubjectControl;
+                    ScrollBar vBar= scrollable.getVerticalBar();
+                    if (vBar !is null)
+                        vBar.addSelectionListener(this);
+                    ScrollBar hBar= scrollable.getHorizontalBar();
+                    if (hBar !is null)
+                        hBar.addSelectionListener(this);
+                }
+
+                fDisplay= fSubjectControl.getDisplay();
+                if (!fDisplay.isDisposed()) {
+                    fDisplay.addFilter(SWT.Activate, this);
+                    fDisplay.addFilter(SWT.MouseWheel, this);
+
+                    fDisplay.addFilter(SWT.FocusOut, this);
+
+                    fDisplay.addFilter(SWT.MouseDown, this);
+                    fDisplay.addFilter(SWT.MouseUp, this);
+
+                    fDisplay.addFilter(SWT.MouseMove, this);
+                    fDisplay.addFilter(SWT.MouseEnter, this);
+                    fDisplay.addFilter(SWT.MouseExit, this);
+                }
+            }
+        }
+
+        /*
+         * @see IInformationControlCloser#stop()
+         */
+        public void stop() {
+            if (!fIsActive)
+                return;
+
+            fIsActive= false;
+
+            if (DEBUG)
+                System.out_.println("AbstractHoverInformationControlManager.Closer stopped"); //$NON-NLS-1$
+
+            if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
+                fSubjectControl.removeMouseListener(this);
+                fSubjectControl.removeMouseMoveListener(this);
+                fSubjectControl.removeControlListener(this);
+                fSubjectControl.removeKeyListener(this);
+                if ( cast(Scrollable)fSubjectControl ) {
+                    Scrollable scrollable= cast(Scrollable) fSubjectControl;
+                    ScrollBar vBar= scrollable.getVerticalBar();
+                    if (vBar !is null)
+                        vBar.removeSelectionListener(this);
+                    ScrollBar hBar= scrollable.getHorizontalBar();
+                    if (hBar !is null)
+                        hBar.removeSelectionListener(this);
+                }
+            }
+
+            if (fDisplay !is null && !fDisplay.isDisposed()) {
+                fDisplay.removeFilter(SWT.Activate, this);
+                fDisplay.removeFilter(SWT.MouseWheel, this);
+
+                fDisplay.removeFilter(SWT.FocusOut, this);
+
+                fDisplay.removeFilter(SWT.MouseDown, this);
+                fDisplay.removeFilter(SWT.MouseUp, this);
+
+                fDisplay.removeFilter(SWT.MouseMove, this);
+                fDisplay.removeFilter(SWT.MouseEnter, this);
+                fDisplay.removeFilter(SWT.MouseExit, this);
+            }
+            fDisplay= null;
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseMove(MouseEvent event) {
+            if (!hasInformationControlReplacer() || !canMoveIntoInformationControl(getCurrentInformationControl())) {
+                if (!fSubjectArea.contains(event.x, event.y)) {
+                    hideInformationControl();
+                }
+
+            } else if (getCurrentInformationControl() !is null && !getCurrentInformationControl().isFocusControl()) {
+                if (!inKeepUpZone(event.x, event.y, fSubjectControl, fSubjectArea, true)) {
+                    hideInformationControl();
+                }
+            }
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseUp(MouseEvent event) {
+        }
+
+        /*
+         * @see MouseListener#mouseDown(MouseEvent)
+         */
+        public void mouseDown(MouseEvent event) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see MouseListener#mouseDoubleClick(MouseEvent)
+         */
+        public void mouseDoubleClick(MouseEvent event) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see ControlListener#controlResized(ControlEvent)
+         */
+        public void controlResized(ControlEvent event) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see ControlListener#controlMoved(ControlEvent)
+         */
+        public void controlMoved(ControlEvent event) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see KeyListener#keyReleased(KeyEvent)
+         */
+        public void keyReleased(KeyEvent event) {
+        }
+
+        /*
+         * @see KeyListener#keyPressed(KeyEvent)
+         */
+        public void keyPressed(KeyEvent event) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+         */
+        public void widgetSelected(SelectionEvent e) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
+         */
+        public void widgetDefaultSelected(SelectionEvent e) {
+        }
+
+        /*
+         * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+         * @since 3.1
+         */
+        public void handleEvent(Event event) {
+            switch (event.type) {
+                case SWT.Activate:
+                case SWT.MouseWheel:
+                    if (!hasInformationControlReplacer())
+                        hideInformationControl();
+                    else if (!isReplaceInProgress()) {
+                        IInformationControl infoControl= getCurrentInformationControl();
+                        // During isReplaceInProgress(), events can come from the replacing information control
+                        if ( cast(Control)event.widget  && cast(IInformationControlExtension5)infoControl ) {
+                            Control control= cast(Control) event.widget;
+                            IInformationControlExtension5 iControl5= cast(IInformationControlExtension5) infoControl;
+                            if (!(iControl5.containsControl(control)))
+                                hideInformationControl();
+                            else if (event.type is SWT.MouseWheel && cancelReplacingDelay())
+                                replaceInformationControl(false);
+                        } else if (infoControl !is null && infoControl.isFocusControl() && cancelReplacingDelay()) {
+                            replaceInformationControl(true);
+                        }
+                    }
+                    break;
+
+                case SWT.MouseUp:
+                case SWT.MouseDown:
+                    if (!hasInformationControlReplacer())
+                        hideInformationControl();
+                    else if (!isReplaceInProgress()) {
+                        IInformationControl infoControl= getCurrentInformationControl();
+                        if ( cast(Control)event.widget  && cast(IInformationControlExtension5)infoControl ) {
+                            Control control= cast(Control) event.widget;
+                            final IInformationControlExtension5 iControl5= cast(IInformationControlExtension5) infoControl;
+                            if (!(iControl5.containsControl(control))) {
+                                hideInformationControl();
+                            } else if (cancelReplacingDelay()) {
+                                if (event.type is SWT.MouseUp) {
+                                    stop(); // avoid that someone else replaces the info control before the async is exec'd
+                                    if ( cast(IDelayedInputChangeProvider)infoControl ) {
+                                        final IDelayedInputChangeProvider delayedICP= cast(IDelayedInputChangeProvider) infoControl;
+                                        final IInputChangedListener inputChangeListener= new DelayedInputChangeListener(delayedICP, getInformationControlReplacer());
+                                        delayedICP.setDelayedInputChangeListener(inputChangeListener);
+                                        // cancel automatic input updating after a small timeout:
+                                        control.getShell().getDisplay().timerExec(1000, new class()  Runnable {
+                                            public void run() {
+                                                delayedICP.setDelayedInputChangeListener(null);
+                                            }
+                                        });
+                                    }
+
+                                    // XXX: workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=212392 :
+                                    control.getShell().getDisplay().asyncExec(new class()  Runnable {
+                                        public void run() {
+                                            replaceInformationControl(true);
+                                        }
+                                    });
+                                } else {
+                                    fWaitForMouseUp= true;
+                                }
+                            }
+                        } else {
+                            handleMouseMove(event);
+                        }
+                    }
+                    break;
+
+                case SWT.FocusOut:
+                    IInformationControl iControl= getCurrentInformationControl();
+                    if (iControl !is null && ! iControl.isFocusControl())
+                        hideInformationControl();
+                    break;
+
+                case SWT.MouseMove:
+                case SWT.MouseEnter:
+                case SWT.MouseExit:
+                    handleMouseMove(event);
+                    break;
+                default:
+            }
+        }
+
+        /**
+         * Handle mouse movement events.
+         *
+         * @param event the event
+         * @since 3.4
+         */
+        private void handleMouseMove(Event event) {
+//          if (DEBUG)
+//              System.out_.println("AbstractHoverInformationControl.Closer.handleMouseMove():" + event); //$NON-NLS-1$
+
+            if (!( cast(Control)event.widget ))
+                return;
+            Control eventControl= cast(Control) event.widget;
+
+            //transform coordinates to subject control:
+            Point mouseLoc= event.display.map(eventControl, fSubjectControl, event.x, event.y);
+
+            if (fSubjectArea.contains(mouseLoc))
+                return;
+
+            IInformationControl iControl= getCurrentInformationControl();
+            if (!hasInformationControlReplacer() || !canMoveIntoInformationControl(iControl)) {
+                if ( cast(AnnotationBarHoverManager)this.outer ) {
+                    if (getInternalAccessor().getAllowMouseExit())
+                        return;
+                }
+                hideInformationControl();
+                return;
+            }
+
+            IInformationControlExtension3 iControl3= cast(IInformationControlExtension3) iControl;
+            Rectangle controlBounds= iControl3.getBounds();
+            if (controlBounds !is null) {
+                Rectangle tooltipBounds= event.display.map(null, eventControl, controlBounds);
+                if (tooltipBounds.contains(event.x, event.y)) {
+                    if (!isReplaceInProgress() && event.type !is SWT.MouseExit)
+                        startReplaceInformationControl(event.display);
+                    return;
+                }
+                cancelReplacingDelay();
+            }
+
+            if (!fSubjectControl.getBounds().contains(mouseLoc)) {
+                /*
+                 *  Use inKeepUpZone() to make sure it also works when the hover is
+                 *  completely outside of the subject control.
+                 */
+                if (!inKeepUpZone(mouseLoc.x, mouseLoc.y, fSubjectControl, fSubjectArea, true)) {
+                    hideInformationControl();
+                    return;
+                }
+            }
+        }
+    }
+
+    /**
+     * To be installed on the manager's subject control.  Serves two different purposes:
+     * <ul>
+     * <li> start function: initiates the computation of the information to be presented. This happens on
+     *      receipt of a mouse hover event and disables the information control manager,
+     * <li> restart function: tracks mouse move and shell activation event to determine when the information
+     *      control manager needs to be reactivated.
+     * </ul>
+     */
+    class MouseTracker : ShellAdapter , MouseTrackListener, MouseMoveListener {
+
+        /** Margin around the original hover event location for computing the hover area. */
+        private const static int EPSILON= 3;
+
+        /** The area in which the original hover event occurred. */
+        private Rectangle fHoverArea;
+        /** The area for which is computed information is valid. */
+        private Rectangle fSubjectArea;
+        /** The tracker's subject control. */
+        private Control fSubjectControl;
+
+        /** Indicates whether the tracker is in restart mode ignoring hover events. */
+        private bool fIsInRestartMode= false;
+        /** Indicates whether the tracker is computing the information to be presented. */
+        private bool fIsComputing= false;
+        /** Indicates whether the mouse has been lost. */
+        private bool fMouseLostWhileComputing= false;
+        /** Indicates whether the subject control's shell has been deactivated. */
+        private bool fShellDeactivatedWhileComputing= false;
+
+        /**
+         * Creates a new mouse tracker.
+         */
+        public this() {
+        }
+
+        /**
+         * Sets this mouse tracker's subject area, the area to be tracked in order
+         * to re-enable the information control manager.
+         *
+         * @param subjectArea the subject area
+         */
+        public void setSubjectArea(Rectangle subjectArea) {
+            Assert.isNotNull(subjectArea);
+            fSubjectArea= subjectArea;
+        }
+
+        /**
+         * Starts this mouse tracker. The given control becomes this tracker's subject control.
+         * Installs itself as mouse track listener on the subject control.
+         *
+         * @param subjectControl the subject control
+         */
+        public void start(Control subjectControl) {
+            fSubjectControl= subjectControl;
+            if (fSubjectControl !is null && !fSubjectControl.isDisposed())
+                fSubjectControl.addMouseTrackListener(this);
+
+            fIsInRestartMode= false;
+            fIsComputing= false;
+            fMouseLostWhileComputing= false;
+            fShellDeactivatedWhileComputing= false;
+        }
+
+        /**
+         * Stops this mouse tracker. Removes itself  as mouse track, mouse move, and
+         * shell listener from the subject control.
+         */
+        public void stop() {
+            if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
+                fSubjectControl.removeMouseTrackListener(this);
+                fSubjectControl.removeMouseMoveListener(this);
+                fSubjectControl.getShell().removeShellListener(this);
+            }
+        }
+
+        /**
+         * Initiates the computation of the information to be presented. Sets the initial hover area
+         * to a small rectangle around the hover event location. Adds mouse move and shell activation listeners
+         * to track whether the computed information is, after completion, useful for presentation and to
+         * implement the restart function.
+         *
+         * @param event the mouse hover event
+         */
+        public void mouseHover(MouseEvent event) {
+            if (fIsComputing || fIsInRestartMode ||
+                    (fSubjectControl !is null && !fSubjectControl.isDisposed() && fSubjectControl.getShell() !is fSubjectControl.getShell().getDisplay().getActiveShell())) {
+                if (DEBUG)
+                    System.out_.println(Format("AbstractHoverInformationControlManager...mouseHover: @ {}/{} : hover cancelled: fIsComputing= {}, fIsInRestartMode= {}", event.x, event.y, fIsComputing, fIsInRestartMode)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+                return;
+            }
+
+            fIsInRestartMode= true;
+            fIsComputing= true;
+            fMouseLostWhileComputing= false;
+            fShellDeactivatedWhileComputing= false;
+
+            fHoverEventStateMask= event.stateMask;
+            fHoverEvent= event;
+            fHoverArea= new Rectangle(event.x - EPSILON, event.y - EPSILON, 2 * EPSILON, 2 * EPSILON );
+            if (fHoverArea.x < 0)
+                fHoverArea.x= 0;
+            if (fHoverArea.y < 0)
+                fHoverArea.y= 0;
+            setSubjectArea(fHoverArea);
+
+            if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
+                fSubjectControl.addMouseMoveListener(this);
+                fSubjectControl.getShell().addShellListener(this);
+            }
+            doShowInformation();
+        }
+
+        /**
+         * Deactivates this tracker's restart function and enables the information control
+         * manager. Does not have any effect if the tracker is still executing the start function (i.e.
+         * computing the information to be presented.
+         */
+        protected void deactivate() {
+            if (fIsComputing)
+                return;
+
+            fIsInRestartMode= false;
+            if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
+                fSubjectControl.removeMouseMoveListener(this);
+                fSubjectControl.getShell().removeShellListener(this);
+            }
+        }
+
+        /*
+         * @see MouseTrackListener#mouseEnter(MouseEvent)
+         */
+        public void mouseEnter(MouseEvent e) {
+        }
+
+        /*
+         * @see MouseTrackListener#mouseExit(MouseEvent)
+         */
+        public void mouseExit(MouseEvent e) {
+            if (!hasInformationControlReplacer() || !canMoveIntoInformationControl(getCurrentInformationControl()) || !inKeepUpZone(e.x, e.y, fSubjectControl, fSubjectArea, false)) {
+                fMouseLostWhileComputing= true;
+                deactivate();
+            }
+        }
+
+        /*
+         * @see MouseMoveListener#mouseMove(MouseEvent)
+         */
+        public void mouseMove(MouseEvent event) {
+            if (!hasInformationControlReplacer() || !canMoveIntoInformationControl(getCurrentInformationControl())) {
+                if (!fSubjectArea.contains(event.x, event.y))
+                    deactivate();
+            } else {
+                if (!inKeepUpZone(event.x, event.y, fSubjectControl, fSubjectArea, false))
+                    deactivate();
+            }
+        }
+
+        /*
+         * @see ShellListener#shellDeactivated(ShellEvent)
+         */
+        public void shellDeactivated(ShellEvent e) {
+            fShellDeactivatedWhileComputing= true;
+            deactivate();
+        }
+
+        /*
+         * @see ShellListener#shellIconified(ShellEvent)
+         */
+        public void shellIconified(ShellEvent e) {
+            fShellDeactivatedWhileComputing= true;
+            deactivate();
+        }
+
+        /**
+         * Tells this tracker that the start function processing has been completed.
+         */
+        public void computationCompleted() {
+            fIsComputing= false;
+            fMouseLostWhileComputing= false;
+            fShellDeactivatedWhileComputing= false;
+        }
+
+        /**
+         * Determines whether the computed information is still useful for presentation.
+         * This is not the case, if the shell of the subject control has been deactivated, the mouse
+         * left the subject control, or the mouse moved on, so that it is no longer in the subject
+         * area.
+         *
+         * @return <code>true</code> if information is still useful for presentation, <code>false</code> otherwise
+         */
+        public bool isMouseLost() {
+
+            if (fMouseLostWhileComputing || fShellDeactivatedWhileComputing)
+                return true;
+
+            if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
+                Display display= fSubjectControl.getDisplay();
+                Point p= display.getCursorLocation();
+                p= fSubjectControl.toControl(p);
+                if (!fSubjectArea.contains(p) && !fHoverArea.contains(p))
+                    return true;
+            }
+
+            return false;
+        }
+    }
+
+    /**
+     * The delay in {@link ITextViewerExtension8.EnrichMode#AFTER_DELAY} mode after which
+     * the hover is enriched when the mouse has stopped moving inside the hover.
+     * @since 3.4
+     */
+    private static const long HOVER_AUTO_REPLACING_DELAY= 200;
+
+    /** The mouse tracker on the subject control */
+    private MouseTracker fMouseTracker;
+    /**
+     * The remembered hover event.
+     * @since 3.0
+     */
+    private MouseEvent fHoverEvent= null;
+    /** The remembered hover event state mask of the keyboard modifiers */
+    private int fHoverEventStateMask= 0;
+    /**
+     * The thread that delays replacing of the hover information control.
+     * To be accessed in the UI thread only!
+     *
+     * @since 3.4
+     */
+    private Job fReplacingDelayJob;
+
+    /**
+     * The {@link ITextViewerExtension8.EnrichMode}, may be <code>null</code>.
+     * @since 3.4
+     */
+    private ITextViewerExtension8_EnrichMode fEnrichMode;
+
+    /**
+     * Indicates whether we have received a MouseDown event and are waiting for a MouseUp
+     * (and don't replace the information control until that happened).
+     * @since 3.4
+     */
+    private bool fWaitForMouseUp= false;
+
+    /**
+     * Creates a new hover information control manager using the given information control creator.
+     * By default a <code>Closer</code> instance is set as this manager's closer.
+     *
+     * @param creator the information control creator
+     */
+    protected this(IInformationControlCreator creator) {
+        fMouseTracker= new MouseTracker();
+        super(creator);
+        setCloser(new Closer());
+        setHoverEnrichMode(ITextViewerExtension8_EnrichMode.AFTER_DELAY);
+    }
+
+    /**
+     * Tests whether a given mouse location is within the keep-up zone.
+     * The hover should not be hidden as long as the mouse stays inside this zone.
+     *
+     * @param x the x coordinate, relative to the <em>subject control</em>
+     * @param y the y coordinate, relative to the <em>subject control</em>
+     * @param subjectControl the subject control
+     * @param subjectArea the area for which the presented information is valid
+     * @param blowUp If <code>true</code>, then calculate for the closer, i.e. blow up the keepUp area.
+     *        If <code>false</code>, then use tight bounds for hover detection.
+     *
+     * @return <code>true</code> iff the mouse event occurred in the keep-up zone
+     * @since 3.4
+     */
+    private bool inKeepUpZone(int x, int y, Control subjectControl, Rectangle subjectArea, bool blowUp) {
+        if (subjectArea.contains(x, y))
+            return true;
+
+        IInformationControl iControl= getCurrentInformationControl();
+        if (( cast(IInformationControlExtension5)iControl  && !(cast(IInformationControlExtension5) iControl).isVisible())) {
+            iControl= null;
+            if (getInformationControlReplacer() !is null) {
+                iControl= getInformationControlReplacer().getCurrentInformationControl2();
+                if (( cast(IInformationControlExtension5)iControl  && !(cast(IInformationControlExtension5) iControl).isVisible())) {
+                    return false;
+                }
+            }
+        }
+        if ( cast(IInformationControlExtension3)iControl ) {
+            IInformationControlExtension3 iControl3= cast(IInformationControlExtension3) iControl;
+
+            Rectangle iControlBounds= subjectControl.getDisplay().map(null, subjectControl, iControl3.getBounds());
+            Rectangle totalBounds= Geometry.copy(iControlBounds);
+            if (blowUp && isReplaceInProgress()) {
+                //Problem: blown up iControl overlaps rest of subjectArea's line
+                // solution for now: only blow up for keep up (closer), but not for further hover detection
+                int margin= getInformationControlReplacer().getKeepUpMargin();
+                Geometry.expand(totalBounds, margin, margin, margin, margin);
+            }
+
+            if (!blowUp) {
+                if (iControlBounds.contains(x, y))
+                    return true;
+
+                if (subjectArea.y + subjectArea.height < iControlBounds.y) {
+                    // special case for hover events: subjectArea totally above iControl:
+                    //  +-----------+
+                    //  |subjectArea|
+                    //  +-----------+
+                    //  |also keepUp|
+                    // ++-----------+-------+
+                    // | InformationControl |
+                    // +--------------------+
+                    if (subjectArea.y + subjectArea.height <= y && y <= totalBounds.y) {
+                        // is vertically between subject area and iControl
+                        if (subjectArea.x <= x && x <= subjectArea.x + subjectArea.width) {
+                            // is below subject area (in a vertical projection)
+                            return true;
+                        }
+                        // FIXME: cases when subjectArea extends to left or right of iControl?
+                    }
+                    return false;
+
+                } else if (iControlBounds.x + iControlBounds.width < subjectArea.x) {
+                    // special case for hover events (e.g. in overview ruler): iControl totally left of subjectArea
+                    // +--------------------+-----------+
+                    // |                    |           +-----------+
+                    // | InformationControl |also keepUp|subjectArea|
+                    // |                    |           +-----------+
+                    // +--------------------+-----------+
+                    if (iControlBounds.x + iControlBounds.width <= x && x <= subjectArea.x) {
+                        // is horizontally between iControl and subject area
+                        if (iControlBounds.y <= y && y <= iControlBounds.y + iControlBounds.height) {
+                            // is to the right of iControl (in a horizontal projection)
+                            return true;
+                        }
+                    }
+                    return false;
+
+                } else if (subjectArea.x + subjectArea.width < iControlBounds.x) {
+                    // special case for hover events (e.g. in annotation ruler): subjectArea totally left of iControl
+                    //             +-----------+--------------------+
+                    // +-----------+           |                    |
+                    // |subjectArea|also keepUp| InformationControl |
+                    // +-----------+           |                    |
+                    //             +-----------+--------------------+
+                    if (subjectArea.x + subjectArea.width <= x && x <= iControlBounds.x) {
+                        // is horizontally between subject area and iControl
+                        if (iControlBounds.y <= y && y <= iControlBounds.y + iControlBounds.height) {
+                            // is to the left of iControl (in a horizontal projection)
+                            return true;
+                        }
+                    }
+                    return false;
+                }
+            }
+
+            // FIXME: should maybe use convex hull, not bounding box
+            totalBounds.add(subjectArea);
+            if (totalBounds.contains(x, y))
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Tests whether the given information control allows the mouse to be moved
+     * into it.
+     *
+     * @param iControl information control or <code>null</code> if none
+     * @return <code>true</code> if information control allows mouse move into
+     *         control, <code>false</code> otherwise
+     */
+    bool canMoveIntoInformationControl(IInformationControl iControl) {
+        return fEnrichMode !is null && canReplace(iControl);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#hideInformationControl()
+     */
+    protected void hideInformationControl() {
+        cancelReplacingDelay();
+        super.hideInformationControl();
+    }
+
+    /**
+     * Sets the hover enrich mode. Only applicable when an information
+     * control replacer has been set with
+     * {@link #setInformationControlReplacer(InformationControlReplacer)} .
+     *
+     * @param mode the enrich mode
+     * @since 3.4
+     * @see ITextViewerExtension8#setHoverEnrichMode(org.eclipse.jface.text.ITextViewerExtension8.EnrichMode)
+     */
+    void setHoverEnrichMode(ITextViewerExtension8_EnrichMode mode) {
+        fEnrichMode= mode;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#replaceInformationControl(bool)
+     */
+    void replaceInformationControl(bool takeFocus) {
+        fWaitForMouseUp= false;
+        super.replaceInformationControl(takeFocus);
+    }
+
+    /**
+     * Cancels the replacing delay job.
+     * @return <code>true</code> iff canceling was successful, <code>false</code> if replacing has already started
+     */
+    bool cancelReplacingDelay() {
+        fWaitForMouseUp= false;
+        if (fReplacingDelayJob !is null && fReplacingDelayJob.getState() !is Job.RUNNING) {
+            bool cancelled= fReplacingDelayJob.cancel();
+            fReplacingDelayJob= null;
+//          if (DEBUG)
+//              System.out_.println("AbstractHoverInformationControlManager.cancelReplacingDelay(): cancelled=" + cancelled); //$NON-NLS-1$
+            return cancelled;
+        }
+//      if (DEBUG)
+//          System.out_.println("AbstractHoverInformationControlManager.cancelReplacingDelay(): not delayed"); //$NON-NLS-1$
+        return true;
+    }
+
+    /**
+     * Starts replacing the information control, considering the current
+     * {@link ITextViewerExtension8.EnrichMode}.
+     * If set to {@link ITextViewerExtension8.EnrichMode#AFTER_DELAY}, this
+     * method cancels previous requests and restarts the delay timer.
+     *
+     * @param display the display to be used for the call to
+     *        {@link #replaceInformationControl(bool)} in the UI thread
+     */
+    private void startReplaceInformationControl(Display display) {
+        if (fEnrichMode is ITextViewerExtension8_EnrichMode.ON_CLICK)
+            return;
+
+        if (fReplacingDelayJob !is null) {
+            if (fReplacingDelayJob.getState() !is Job.RUNNING) {
+                if (fReplacingDelayJob.cancel()) {
+                    if (fEnrichMode is ITextViewerExtension8_EnrichMode.IMMEDIATELY) {
+                        fReplacingDelayJob= null;
+                        if (! fWaitForMouseUp)
+                            replaceInformationControl(false);
+                    } else {
+//                      if (DEBUG)
+//                          System.out_.println("AbstractHoverInformationControlManager.startReplaceInformationControl(): rescheduled"); //$NON-NLS-1$
+                        fReplacingDelayJob.schedule(HOVER_AUTO_REPLACING_DELAY);
+                    }
+                }
+            }
+            return;
+        }
+
+        fReplacingDelayJob= new class("AbstractHoverInformationControlManager Replace Delayer", display) Job { //$NON-NLS-1$
+            Display display_;
+            this( String str, Display b){
+                super(str);
+                display_=b;
+            }
+            public IStatus run(IProgressMonitor monitor) {
+                if (monitor.isCanceled() || display_.isDisposed()) {
+                    return Status.CANCEL_STATUS;
+                }
+                display_.syncExec(dgRunnable( (IProgressMonitor monitor_) {
+                    fReplacingDelayJob= null;
+                    if (monitor_.isCanceled())
+                        return;
+                    if (! fWaitForMouseUp)
+                        replaceInformationControl(false);
+                }, monitor ));
+                return Status.OK_STATUS;
+            }
+        };
+        fReplacingDelayJob.setSystem(true);
+        fReplacingDelayJob.setPriority(Job.INTERACTIVE);
+//      if (DEBUG)
+//          System.out_.println("AbstractHoverInformationControlManager.startReplaceInformationControl(): scheduled"); //$NON-NLS-1$
+        fReplacingDelayJob.schedule(HOVER_AUTO_REPLACING_DELAY);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#presentInformation()
+     */
+    protected void presentInformation() {
+        if (fMouseTracker is null) {
+            super.presentInformation();
+            return;
+        }
+
+        Rectangle area= getSubjectArea();
+        if (area !is null)
+            fMouseTracker.setSubjectArea(area);
+
+        if (fMouseTracker.isMouseLost()) {
+            fMouseTracker.computationCompleted();
+            fMouseTracker.deactivate();
+        } else {
+            fMouseTracker.computationCompleted();
+            super.presentInformation();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @deprecated visibility will be changed to protected
+     */
+    public void setEnabled(bool enabled) {
+
+        bool was= isEnabled();
+        super.setEnabled(enabled);
+        bool is_= isEnabled();
+
+        if (was !is is_ && fMouseTracker !is null) {
+            if (is_)
+                fMouseTracker.start(getSubjectControl());
+            else
+                fMouseTracker.stop();
+        }
+    }
+
+    /**
+     * Disposes this manager's information control.
+     */
+    public void dispose() {
+        if (fMouseTracker !is null) {
+            fMouseTracker.stop();
+            fMouseTracker.fSubjectControl= null;
+            fMouseTracker= null;
+        }
+        super.dispose();
+    }
+
+    /**
+     * Returns the location at which the most recent mouse hover event
+     * has been issued.
+     *
+     * @return the location of the most recent mouse hover event
+     */
+    protected Point getHoverEventLocation() {
+        return fHoverEvent !is null ? new Point(fHoverEvent.x, fHoverEvent.y) : new Point(-1, -1);
+    }
+    package Point getHoverEventLocation_package() {
+        return getHoverEventLocation();
+    }
+
+    /**
+     * Returns the most recent mouse hover event.
+     *
+     * @return the most recent mouse hover event or <code>null</code>
+     * @since 3.0
+     */
+    protected MouseEvent getHoverEvent() {
+        return fHoverEvent;
+    }
+
+    /**
+     * Returns the SWT event state of the most recent mouse hover event.
+     *
+     * @return the SWT event state of the most recent mouse hover event
+     */
+    protected int getHoverEventStateMask() {
+        return fHoverEventStateMask;
+    }
+
+    /**
+     * Returns an adapter that gives access to internal methods.
+     * <p>
+     * <strong>Note:</strong> This method is not intended to be referenced or overridden by clients.</p>
+     *
+     * @return the replaceable information control accessor
+     * @since 3.4
+     * @noreference This method is not intended to be referenced by clients.
+     * @nooverride This method is not intended to be re-implemented or extended by clients.
+     */
+    public InternalAccessor getInternalAccessor() {
+        return new MyInternalAccessor2(this);
+    }
+        static class MyInternalAccessor2 : MyInternalAccessor {
+            AbstractHoverInformationControlManager outer_;
+            this( AbstractHoverInformationControlManager a ){
+                outer_=a;
+                super(a);
+            }
+            public void setHoverEnrichMode(ITextViewerExtension8_EnrichMode mode) {
+                outer_.setHoverEnrichMode(mode);
+            }
+        }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractInformationControl.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,901 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.AbstractInformationControl;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+// import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Slider;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.Geometry;
+
+
+/**
+ * An abstract information control that can show content inside a shell.
+ * The information control can be created in two styles:
+ * <ul>
+ *  <li>non-resizable tooltip with optional status</li>
+ *  <li>resizable tooltip with optional tool bar</li>
+ * </ul>
+ * Additionally it can present either a status line containing a status text or
+ * a toolbar containing toolbar buttons.
+ * <p>
+ * Subclasses must either override {@link IInformationControl#setInformation(String)}
+ * or implement {@link IInformationControlExtension2}.
+ * They should also extend {@link #computeTrim()} if they create a content area
+ * with additional trim (e.g. scrollbars) and override {@link #getInformationPresenterControlCreator()}.
+ * </p>
+ *
+ * @since 3.4
+ */
+public abstract class AbstractInformationControl : IInformationControl, IInformationControlExtension, IInformationControlExtension3, IInformationControlExtension4, IInformationControlExtension5 {
+
+    /** The information control's shell. */
+    private const Shell fShell;
+    /** Composite containing the content created by subclasses. */
+    private const Composite fContentComposite;
+    /** Whether the information control is resizable. */
+    private const bool fResizable;
+
+    /** Composite containing the status line content or <code>null</code> if none. */
+    private Composite fStatusComposite;
+    /** Separator between content and status line or <code>null</code> if none. */
+    private Label fSeparator;
+    /** Label in the status line or <code>null</code> if none. */
+    private Label fStatusLabel;
+    /** The toolbar manager used by the toolbar or <code>null</code> if none. */
+    private const ToolBarManager fToolBarManager;
+    /** Status line toolbar or <code>null</code> if none. */
+    private ToolBar fToolBar;
+
+    /** Listener for shell activation and deactivation. */
+    private Listener fShellListener;
+    /** All focus listeners registered to this information control. */
+    private ListenerList fFocusListeners;
+
+    /** Size constraints, x is the maxWidth and y is the maxHeight, or <code>null</code> if not set. */
+    private Point fSizeConstraints;
+    /** The size of the resize handle if already set, -1 otherwise */
+    private int fResizeHandleSize;
+
+    /**
+     * Creates an abstract information control with the given shell as parent.
+     * The control will not be resizable and optionally show a status line with
+     * the given status field text.
+     * <p>
+     * <em>Important: Subclasses are required to call {@link #create()} at the end of their constructor.</em>
+     * </p>
+     *
+     * @param parentShell the parent of this control's shell
+     * @param statusFieldText the text to be used in the status field or <code>null</code> to hide the status field
+     */
+    public this(Shell parentShell, String statusFieldText) {
+        this(parentShell, SWT.TOOL | SWT.ON_TOP, statusFieldText, null);
+    }
+
+    /**
+     * Creates an abstract information control with the given shell as parent.
+     * The control will be resizable and optionally show a tool bar managed by
+     * the given tool bar manager.
+     * <p>
+     * <em>Important: Subclasses are required to call {@link #create()} at the end of their constructor.</em>
+     * </p>
+     *
+     * @param parentShell the parent of this control's shell
+     * @param toolBarManager the manager or <code>null</code> if toolbar is not desired
+     */
+    public this(Shell parentShell, ToolBarManager toolBarManager) {
+        this(parentShell, SWT.TOOL | SWT.ON_TOP | SWT.RESIZE, null, toolBarManager);
+    }
+
+    /**
+     * Creates an abstract information control with the given shell as parent.
+     * <p>
+     * <em>Important: Subclasses are required to call {@link #create()} at the end of their constructor.</em>
+     * </p>
+     *
+     * @param parentShell the parent of this control's shell
+     * @param isResizable <code>true</code> if the control should be resizable
+     */
+    public this(Shell parentShell, bool isResizable) {
+        this(parentShell, SWT.TOOL | SWT.ON_TOP | (isResizable ? SWT.RESIZE : 0), null, null);
+    }
+
+    /**
+     * Creates an abstract information control with the given shell as parent.
+     * The given shell style is used for the shell (NO_TRIM will be removed to make sure there's a border).
+     * <p>
+     * The control will optionally show either a status line or a tool bar.
+     * At most one of <code>toolBarManager</code> or <code>statusFieldText</code> can be non-null.
+     * </p>
+     * <p>
+     * <strong>Important:</strong>: Subclasses are required to call {@link #create()} at the end of their constructor.
+     * </p>
+     *
+     * @param parentShell the parent of this control's shell
+     * @param shellStyle style of this control's shell
+     * @param statusFieldText the text to be used in the status field or <code>null</code> to hide the status field
+     * @param toolBarManager the manager or <code>null</code> if toolbar is not desired
+     *
+     * @deprecated clients should use one of the public constructors
+     */
+    this(Shell parentShell, int shellStyle, String statusFieldText, ToolBarManager toolBarManager) {
+
+        fFocusListeners= new ListenerList(ListenerList.IDENTITY);
+
+        Assert.isTrue(statusFieldText is null || toolBarManager is null);
+        fResizeHandleSize= -1;
+        fToolBarManager= toolBarManager;
+
+        if ((shellStyle & SWT.NO_TRIM) !is 0)
+            shellStyle&= ~(SWT.NO_TRIM | SWT.SHELL_TRIM); // make sure we get the OS border but no other trims
+
+        fResizable= (shellStyle & SWT.RESIZE) !is 0; // on GTK, Shell removes SWT.RESIZE if SWT.ON_TOP is set
+        fShell= new Shell(parentShell, shellStyle);
+        Display display= fShell.getDisplay();
+        Color foreground= display.getSystemColor(SWT.COLOR_INFO_FOREGROUND);
+        Color background= display.getSystemColor(SWT.COLOR_INFO_BACKGROUND);
+        setColor(fShell, foreground, background);
+
+        GridLayout layout= new GridLayout(1, false);
+        layout.marginHeight= 0;
+        layout.marginWidth= 0;
+        layout.verticalSpacing= 0;
+        fShell.setLayout(layout);
+
+        fContentComposite= new Composite(fShell, SWT.NONE);
+        fContentComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+        fContentComposite.setLayout(new FillLayout());
+        setColor(fContentComposite, foreground, background);
+
+        createStatusComposite(statusFieldText, toolBarManager, foreground, background);
+    }
+
+    private void createStatusComposite(String statusFieldText, ToolBarManager toolBarManager, Color foreground, Color background) {
+        if (toolBarManager is null && statusFieldText is null)
+            return;
+
+        fStatusComposite= new Composite(fShell, SWT.NONE);
+        GridData gridData= new GridData(SWT.FILL, SWT.BOTTOM, true, false);
+        fStatusComposite.setLayoutData(gridData);
+        GridLayout statusLayout= new GridLayout(1, false);
+        statusLayout.marginHeight= 0;
+        statusLayout.marginWidth= 0;
+        statusLayout.verticalSpacing= 1;
+        fStatusComposite.setLayout(statusLayout);
+
+        fSeparator= new Label(fStatusComposite, SWT.SEPARATOR | SWT.HORIZONTAL);
+        fSeparator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+        if (statusFieldText !is null) {
+            createStatusLabel(statusFieldText, foreground, background);
+        } else {
+            createToolBar(toolBarManager);
+        }
+    }
+
+    private void createStatusLabel(String statusFieldText, Color foreground, Color background) {
+        fStatusLabel= new Label(fStatusComposite, SWT.RIGHT);
+        fStatusLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+        fStatusLabel.setText(statusFieldText);
+
+        FontData[] fontDatas= JFaceResources.getDialogFont().getFontData();
+        for (int i= 0; i < fontDatas.length; i++) {
+            fontDatas[i].setHeight(fontDatas[i].getHeight() * 9 / 10);
+        }
+        fStatusLabel.setFont(new Font(fStatusLabel.getDisplay(), fontDatas));
+
+        fStatusLabel.setForeground(fStatusLabel.getDisplay().getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW));
+        fStatusLabel.setBackground(background);
+        setColor(fStatusComposite, foreground, background);
+    }
+
+    private void createToolBar(ToolBarManager toolBarManager) {
+        Composite bars= new Composite(fStatusComposite, SWT.NONE);
+        bars.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+
+        GridLayout layout= new GridLayout(3, false);
+        layout.marginHeight= 0;
+        layout.marginWidth= 0;
+        layout.horizontalSpacing= 0;
+        layout.verticalSpacing= 0;
+        bars.setLayout(layout);
+
+        fToolBar= toolBarManager.createControl(bars);
+        GridData gd= new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false);
+        fToolBar.setLayoutData(gd);
+
+        Composite spacer= new Composite(bars, SWT.NONE);
+        gd= new GridData(SWT.FILL, SWT.FILL, true, true);
+        gd.widthHint= 0;
+        gd.heightHint= 0;
+        spacer.setLayoutData(gd);
+
+        addMoveSupport(spacer);
+        addResizeSupportIfNecessary(bars);
+    }
+
+    private void addResizeSupportIfNecessary(Composite bars) {
+        // XXX: workarounds for
+        // - https://bugs.eclipse.org/bugs/show_bug.cgi?id=219139 : API to add resize grip / grow box in lower right corner of shell
+        // - https://bugs.eclipse.org/bugs/show_bug.cgi?id=23980 : platform specific shell resize behavior
+        String platform= SWT.getPlatform();
+        bool isWin= platform.equals("win32"); //$NON-NLS-1$
+        if (!isWin && !platform.equals("gtk")) //$NON-NLS-1$
+            return;
+
+        Canvas resizer= new Canvas(bars, SWT.NONE);
+
+        int size= getResizeHandleSize(bars);
+
+        GridData data= new GridData(SWT.END, SWT.END, false, true);
+        data.widthHint= size;
+        data.heightHint= size;
+        resizer.setLayoutData(data);
+        resizer.addPaintListener(new class(isWin,resizer)  PaintListener {
+            bool isWin_;
+            Canvas resizer_;
+            this(bool a, Canvas b ){
+                isWin_=a;
+                resizer_=b;
+            }
+            public void paintControl(PaintEvent e) {
+                Point s= resizer_.getSize();
+                int x= s.x - 2;
+                int y= s.y - 2;
+                int min= Math.min(x, y);
+                if (isWin_) {
+                    // draw dots
+                    e.gc.setBackground(resizer_.getDisplay().getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
+                    int end= min - 1;
+                    for (int i= 0; i <= 2; i++)
+                        for (int j= 0; j <= 2 - i; j++)
+                            e.gc.fillRectangle(end - 4 * i, end - 4 * j, 2, 2);
+                    end--;
+                    e.gc.setBackground(resizer_.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
+                    for (int i= 0; i <= 2; i++)
+                        for (int j= 0; j <= 2 - i; j++)
+                            e.gc.fillRectangle(end - 4 * i, end - 4 * j, 2, 2);
+
+                } else {
+                    // draw diagonal lines
+                    e.gc.setForeground(resizer_.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
+                    for (int i= 1; i < min; i+= 4) {
+                        e.gc.drawLine(i, y, x, i);
+                    }
+                    e.gc.setForeground(resizer_.getDisplay().getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
+                    for (int i= 2; i < min; i+= 4) {
+                        e.gc.drawLine(i, y, x, i);
+                    }
+                }
+            }
+        });
+
+        resizer.setCursor(new Cursor(resizer.getDisplay(), SWT.CURSOR_SIZESE));
+        MouseAdapter resizeSupport= new class(resizer)  MouseAdapter {
+            Canvas resizer_;
+            this(Canvas a){
+                resizer_=a;
+            }
+            private MouseMoveListener fResizeListener;
+
+            public void mouseDown(MouseEvent e) {
+                Point shellSize= fShell.getSize();
+                int shellX= shellSize.x;
+                int shellY= shellSize.y;
+                Point mouseLoc= resizer_.toDisplay(e.x, e.y);
+                int mouseX= mouseLoc.x;
+                int mouseY= mouseLoc.y;
+                fResizeListener= new class(shellX,shellY,mouseX,mouseY)  MouseMoveListener {
+                    int shellX_;
+                    int shellY_;
+                    int mouseX_;
+                    int mouseY_;
+                    this(int a, int b, int c, int d ){
+                        shellX_=a;
+                        shellY_=b;
+                        mouseX_=c;
+                        mouseY_=d;
+                    }
+                    public void mouseMove(MouseEvent e2) {
+                        Point mouseLoc2= resizer_.toDisplay(e2.x, e2.y);
+                        int dx= mouseLoc2.x - mouseX_;
+                        int dy= mouseLoc2.y - mouseY_;
+                        setSize(shellX_ + dx, shellY_ + dy);
+                    }
+                };
+                resizer_.addMouseMoveListener(fResizeListener);
+            }
+
+            public void mouseUp(MouseEvent e) {
+                resizer_.removeMouseMoveListener(fResizeListener);
+                fResizeListener= null;
+            }
+        };
+        resizer.addMouseListener(resizeSupport);
+    }
+
+    private int getResizeHandleSize(Composite parent) {
+        if (fResizeHandleSize is -1) {
+            Slider sliderV= new Slider(parent, SWT.VERTICAL);
+            Slider sliderH= new Slider(parent, SWT.HORIZONTAL);
+            int width= sliderV.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
+            int height= sliderH.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
+            sliderV.dispose();
+            sliderH.dispose();
+            fResizeHandleSize= Math.min(width, height);
+        }
+
+        return fResizeHandleSize;
+    }
+
+    /**
+     * Adds support to move the shell by dragging the given control.
+     *
+     * @param control the control that can be used to move the shell
+     */
+    private void addMoveSupport(Control control) {
+        MouseAdapter moveSupport= new class(control)  MouseAdapter {
+            private MouseMoveListener fMoveListener;
+            Control control_;
+            this(Control a){
+                control_=a;
+            }
+            public void mouseDown(MouseEvent e) {
+                Point shellLoc= fShell.getLocation();
+                final int shellX= shellLoc.x;
+                final int shellY= shellLoc.y;
+                Point mouseLoc= control_.toDisplay(e.x, e.y);
+                final int mouseX= mouseLoc.x;
+                final int mouseY= mouseLoc.y;
+                fMoveListener= new class()  MouseMoveListener {
+                    public void mouseMove(MouseEvent e2) {
+                        Point mouseLoc2= control_.toDisplay(e2.x, e2.y);
+                        int dx= mouseLoc2.x - mouseX;
+                        int dy= mouseLoc2.y - mouseY;
+                        fShell.setLocation(shellX + dx, shellY + dy);
+                    }
+                };
+                control_.addMouseMoveListener(fMoveListener);
+            }
+
+            public void mouseUp(MouseEvent e) {
+                control_.removeMouseMoveListener(fMoveListener);
+                fMoveListener= null;
+            }
+        };
+        control.addMouseListener(moveSupport);
+    }
+
+    /**
+     * Utility to set the foreground and the background color of the given
+     * control
+     *
+     * @param control the control to modify
+     * @param foreground the color to use for the foreground
+     * @param background the color to use for the background
+     */
+    private static void setColor(Control control, Color foreground, Color background) {
+        control.setForeground(foreground);
+        control.setBackground(background);
+    }
+
+    /**
+     * The shell of the popup window.
+     *
+     * @return the shell used for the popup window
+     */
+    protected final Shell getShell() {
+        return fShell;
+    }
+
+    /**
+     * The toolbar manager used to manage the toolbar, or <code>null</code> if
+     * no toolbar is shown.
+     *
+     * @return the tool bar manager or <code>null</code>
+     */
+    protected final ToolBarManager getToolBarManager() {
+        return fToolBarManager;
+    }
+
+    /**
+     * Creates the content of this information control. Subclasses must call
+     * this method at the end of their constructor(s).
+     */
+    protected final void create() {
+        createContent(fContentComposite);
+    }
+
+    /**
+     * Creates the content of the popup window.
+     * <p>
+     * Implementors will usually take over {@link Composite#getBackground()} and
+     * {@link Composite#getForeground()} from <code>parent</code>.
+     * </p>
+     * <p>
+     * Implementors are expected to consider {@link #isResizable()}: If
+     * <code>true</code>, they should show scrollbars if their content may
+     * exceed the size of the information control. If <code>false</code>,
+     * they should never show scrollbars.
+     * </p>
+     * <p>
+     * The given <code>parent</code> comes with a {@link FillLayout}.
+     * Subclasses may set a different layout.
+     * </p>
+     *
+     * @param parent the container of the content
+     */
+    protected abstract void createContent(Composite parent);
+
+    /**
+     * Sets the information to be presented by this information control.
+     * <p>
+     * The default implementation does nothing. Subclasses must either override this method
+     * or implement {@link IInformationControlExtension2}.
+     *
+     * @param information the information to be presented
+     *
+     * @see org.eclipse.jface.text.IInformationControl#setInformation(java.lang.String)
+     */
+    public void setInformation(String information) {
+
+    }
+
+    /**
+     * Returns whether the information control is resizable.
+     *
+     * @return <code>true</code> if the information control is resizable,
+     *         <code>false</code> if it is not resizable.
+     */
+    public bool isResizable() {
+        return fResizable;
+    }
+
+    /*
+     * @see IInformationControl#setVisible(bool)
+     */
+    public void setVisible(bool visible) {
+        if (fShell.isVisible() is visible)
+            return;
+
+        fShell.setVisible(visible);
+    }
+
+    /*
+     * @see IInformationControl#dispose()
+     */
+    public void dispose() {
+        if (fShell !is null && !fShell.isDisposed())
+            fShell.dispose();
+    }
+
+    /*
+     * @see IInformationControl#setSize(int, int)
+     */
+    public void setSize(int width, int height) {
+        fShell.setSize(width, height);
+    }
+
+    /*
+     * @see IInformationControl#setLocation(Point)
+     */
+    public void setLocation(Point location) {
+        fShell.setLocation(location);
+    }
+
+    /*
+     * @see IInformationControl#setSizeConstraints(int, int)
+     */
+    public void setSizeConstraints(int maxWidth, int maxHeight) {
+        fSizeConstraints= new Point(maxWidth, maxHeight);
+    }
+
+    /**
+     * Returns the size constraints.
+     *
+     * @return the size constraints or <code>null</code> if not set
+     * @see #setSizeConstraints(int, int)
+     */
+    protected final Point getSizeConstraints() {
+        return fSizeConstraints !is null ? Geometry.copy(fSizeConstraints) : null;
+    }
+
+    /*
+     * @see IInformationControl#computeSizeHint()
+     */
+    public Point computeSizeHint() {
+        // XXX: Verify whether this is a good default implementation. If yes, document it.
+        Point constrains= getSizeConstraints();
+        if (constrains is null)
+            return fShell.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+
+        return fShell.computeSize(constrains.x, constrains.y, true);
+    }
+
+    /**
+     * Computes the trim (status text and tool bar are considered as trim).
+     * Subclasses can extend this method to add additional trim (e.g. scroll
+     * bars for resizable information controls).
+     *
+     * @see org.eclipse.jface.text.IInformationControlExtension3#computeTrim()
+     */
+    public Rectangle computeTrim() {
+        Rectangle trim= fShell.computeTrim(0, 0, 0, 0);
+
+        if (fStatusComposite !is null)
+            trim.height+= fStatusComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
+
+        return trim;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlExtension3#getBounds()
+     */
+    public Rectangle getBounds() {
+        return fShell.getBounds();
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * The default implementation always returns <code>false</code>.
+     * </p>
+     * @see org.eclipse.jface.text.IInformationControlExtension3#restoresLocation()
+     */
+    public bool restoresLocation() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * The default implementation always returns <code>false</code>.
+     * </p>
+     * @see org.eclipse.jface.text.IInformationControlExtension3#restoresSize()
+     */
+    public bool restoresSize() {
+        return false;
+    }
+
+    /*
+     * @see IInformationControl#addDisposeListener(DisposeListener)
+     */
+    public void addDisposeListener(DisposeListener listener) {
+        fShell.addDisposeListener(listener);
+    }
+
+    /*
+     * @see IInformationControl#removeDisposeListener(DisposeListener)
+     */
+    public void removeDisposeListener(DisposeListener listener) {
+        fShell.removeDisposeListener(listener);
+    }
+
+    /*
+     * @see IInformationControl#setForegroundColor(Color)
+     */
+    public void setForegroundColor(Color foreground) {
+        fContentComposite.setForeground(foreground);
+    }
+
+    /*
+     * @see IInformationControl#setBackgroundColor(Color)
+     */
+    public void setBackgroundColor(Color background) {
+        fContentComposite.setBackground(background);
+    }
+
+    /**
+     * {@inheritDoc}
+     * This method is not intended to be overridden by subclasses.
+     */
+    public bool isFocusControl() {
+        return fShell.getDisplay().getActiveShell() is fShell;
+    }
+
+    /**
+     * This default implementation sets the focus on the popup shell.
+     * Subclasses can override or extend.
+     *
+     * @see IInformationControl#setFocus()
+     */
+    public void setFocus() {
+        bool focusTaken= fShell.setFocus();
+        if (!focusTaken)
+            fShell.forceFocus();
+    }
+
+    /**
+     * {@inheritDoc}
+     * This method is not intended to be overridden by subclasses.
+     */
+    public void addFocusListener(FocusListener listener) {
+        if (fFocusListeners.isEmpty()) {
+            fShellListener= new class()  Listener {
+
+                public void handleEvent(Event event) {
+                    Object[] listeners= fFocusListeners.getListeners();
+                    for (int i= 0; i < listeners.length; i++) {
+                        FocusListener focusListener= cast(FocusListener)listeners[i];
+                        if (event.type is SWT.Activate) {
+                            focusListener.focusGained(new FocusEvent(event));
+                        } else {
+                            focusListener.focusLost(new FocusEvent(event));
+                        }
+                    }
+                }
+            };
+            fShell.addListener(SWT.Deactivate, fShellListener);
+            fShell.addListener(SWT.Activate, fShellListener);
+        }
+        fFocusListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * {@inheritDoc}
+     * This method is not intended to be overridden by subclasses.
+     */
+    public void removeFocusListener(FocusListener listener) {
+        fFocusListeners.remove(cast(Object)listener);
+        if (fFocusListeners.isEmpty()) {
+            fShell.removeListener(SWT.Activate, fShellListener);
+            fShell.removeListener(SWT.Deactivate, fShellListener);
+            fShellListener= null;
+        }
+    }
+
+    /**
+     * Sets the text of the status field.
+     * <p>
+     * The default implementation currently only updates the status field when
+     * the popup shell is not visible. The status field can currently only be
+     * shown if the information control has been created with a non-null status
+     * field text.
+     * </p>
+     *
+     * @param statusFieldText the text to be used in the optional status field
+     *        or <code>null</code> if the status field should be hidden
+     *
+     * @see org.eclipse.jface.text.IInformationControlExtension4#setStatusText(java.lang.String)
+     */
+    public void setStatusText(String statusFieldText) {
+        if (fStatusLabel !is null && ! getShell().isVisible()) {
+            if (statusFieldText is null ) {
+                fStatusComposite.setVisible(false);
+            } else {
+                fStatusLabel.setText(statusFieldText);
+                fStatusComposite.setVisible(true);
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlExtension5#containsControl(org.eclipse.swt.widgets.Control)
+     */
+    public bool containsControl(Control control) {
+        do {
+            if (control is fShell)
+                return true;
+            if (cast(Shell)control )
+                return false;
+            control= control.getParent();
+        } while (control !is null);
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlExtension5#isVisible()
+     */
+    public bool isVisible() {
+        return fShell !is null && !fShell.isDisposed() && fShell.isVisible();
+    }
+
+    /**
+     * {@inheritDoc}
+     * This default implementation returns <code>null</code>. Subclasses may override.
+     */
+    public IInformationControlCreator getInformationPresenterControlCreator() {
+        return null;
+    }
+
+    /**
+     * Computes the size constraints based on the
+     * {@link JFaceResources#getDialogFont() dialog font}. Subclasses can
+     * override or extend.
+     *
+     * @see org.eclipse.jface.text.IInformationControlExtension5#computeSizeConstraints(int, int)
+     */
+    public Point computeSizeConstraints(int widthInChars, int heightInChars) {
+        GC gc= new GC(fContentComposite);
+        gc.setFont(JFaceResources.getDialogFont());
+        int width= gc.getFontMetrics().getAverageCharWidth();
+        int height= gc.getFontMetrics().getHeight();
+        gc.dispose();
+
+        return new Point(widthInChars * width, heightInChars * height);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractInformationControlManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1603 @@
+/*******************************************************************************
+ * 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
+ *     Sean Montgomery, sean_montgomery@comcast.net - https://bugs.eclipse.org/bugs/show_bug.cgi?id=45095
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.AbstractInformationControlManager;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+// import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Monitor;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.internal.text.InformationControlReplacer;
+import org.eclipse.jface.internal.text.InternalAccessor;
+import org.eclipse.jface.text.ITextViewerExtension8;
+import org.eclipse.jface.util.Geometry;
+
+
+/**
+ * Manages the life cycle, visibility, layout, and contents of an
+ * {@link org.eclipse.jface.text.IInformationControl}. This manager can be
+ * installed on and removed from a control, referred to as the subject control,
+ * i.e. the one from which the subject of the information to be shown is
+ * retrieved. Also a manager can be enabled or disabled. An installed and
+ * enabled manager can be forced to show information in its information control
+ * using <code>showInformation</code>. An information control manager uses an
+ * <code>IInformationControlCloser</code> to define the behavior when a
+ * presented information control must be closed. The disposal of the subject and
+ * the information control are internally handled by the information control
+ * manager and are not the responsibility of the information control closer.
+ *
+ * @see org.eclipse.jface.text.IInformationControl
+ * @since 2.0
+ */
+abstract public class AbstractInformationControlManager {
+
+    /**
+     * An internal class that gives access to internal methods.
+     *
+     * @since 3.4
+     */
+    public static class MyInternalAccessor : InternalAccessor {
+        AbstractInformationControlManager outer_;
+        this( AbstractInformationControlManager a ){
+            outer_=a;
+        }
+        public IInformationControl getCurrentInformationControl() {
+            return outer_.getCurrentInformationControl();
+        }
+
+        public void setInformationControlReplacer(InformationControlReplacer replacer) {
+            outer_.setInformationControlReplacer(replacer);
+        }
+
+        public InformationControlReplacer getInformationControlReplacer() {
+            return outer_.getInformationControlReplacer();
+        }
+
+        public bool canReplace(IInformationControl control) {
+            return outer_.canReplace(control);
+        }
+
+        public bool isReplaceInProgress() {
+            return outer_.isReplaceInProgress();
+        }
+
+        public void replaceInformationControl(bool takeFocus) {
+            outer_.replaceInformationControl(takeFocus);
+        }
+
+        public void cropToClosestMonitor(Rectangle bounds) {
+            outer_.cropToClosestMonitor(bounds);
+        }
+
+        public void setHoverEnrichMode(EnrichMode mode) {
+            throw new UnsupportedOperationException("only implemented in AbstractHoverInformationControlManager"); //$NON-NLS-1$
+        }
+
+        public bool getAllowMouseExit() {
+            throw new UnsupportedOperationException("only implemented in AnnotationBarHoverManager"); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Interface of an information control closer. An information control closer
+     * monitors its information control and its subject control and closes the
+     * information control if necessary.
+     * <p>
+     * Clients must implement this interface in order to equip an information
+     * control manager accordingly.
+     */
+    public interface IInformationControlCloser {
+
+        /**
+         * Sets the closer's subject control. This is the control that parents
+         * the information control and from which the subject of the information
+         * to be shown is retrieved. <p>
+         * Must be called before <code>start</code>. May again be called
+         * between <code>start</code> and <code>stop</code>.
+         *
+         * @param subject the subject control
+         */
+        public void setSubjectControl(Control subject);
+
+        /**
+         * Sets the closer's information control, the one to close if necessary. <p>
+         * Must be called before <code>start</code>. May again be called
+         * between <code>start</code> and <code>stop</code>.
+         *
+         * @param control the information control
+         */
+        public void setInformationControl(IInformationControl control);
+
+        /**
+         * Tells this closer to start monitoring the subject and the information
+         * control. The presented information is considered valid for the given
+         * area of the subject control's display.
+         *
+         * @param subjectArea the area for which the presented information is valid
+         */
+        public void start(Rectangle subjectArea);
+
+        /**
+         * Tells this closer to stop monitoring the subject and the information control.
+         */
+        public void stop();
+    }
+
+
+
+    /**
+     * Constitutes entities to enumerate anchors for the layout of the information control.
+     */
+    public static final class Anchor {
+        private const int fFlag;
+        private this(int flag) {
+            fFlag= flag;
+        }
+        /**
+         * Returns the SWT direction flag. One of {@link SWT#BOTTOM}, {@link SWT#TOP},
+         * {@link SWT#LEFT}, {@link SWT#RIGHT}, {@link SWT#CENTER},
+         *
+         * @return the SWT direction flag
+         * @since 3.3
+         */
+        int getSWTFlag() {
+            return fFlag;
+        }
+
+        public override String toString() {
+            switch (fFlag) {
+                case SWT.BOTTOM: return "BOTTOM"; //$NON-NLS-1$
+                case SWT.TOP: return "TOP"; //$NON-NLS-1$
+                case SWT.LEFT: return "LEFT"; //$NON-NLS-1$
+                case SWT.RIGHT: return "RIGHT"; //$NON-NLS-1$
+                case SWT.CENTER: return "CENTER"; //$NON-NLS-1$
+                default: return Integer.toHexString(fFlag);
+            }
+        }
+    }
+
+    /** Internal anchor list. */
+    private static Anchor[] ANCHORS_;
+    private static Anchor[] ANCHORS() {
+        if( ANCHORS_ is null ) ANCHORS_= [ new Anchor(SWT.TOP), new Anchor(SWT.BOTTOM), new Anchor(SWT.LEFT), new Anchor(SWT.RIGHT) ];
+        return ANCHORS_;
+    }
+
+
+    /** Anchor representing the top of the information area */
+    public static Anchor ANCHOR_TOP() { return ANCHORS()[0]; }
+    /** Anchor representing the bottom of the information area */
+    public static Anchor ANCHOR_BOTTOM() { return ANCHORS()[1]; }
+    /** Anchor representing the left side of the information area */
+    public static Anchor ANCHOR_LEFT() { return ANCHORS()[2]; }
+    /** Anchor representing the right side of the information area */
+    public static Anchor ANCHOR_RIGHT() { return ANCHORS()[3]; }
+    /**
+     * Anchor representing the middle of the subject control
+     * @since 2.1
+     */
+    public static Anchor ANCHOR_GLOBAL_;
+    public static Anchor ANCHOR_GLOBAL(){
+        if( ANCHOR_GLOBAL_ is null ) ANCHOR_GLOBAL_ = new Anchor(SWT.CENTER);
+        return ANCHOR_GLOBAL_;
+    }
+
+    /**
+     * Dialog store constant for the location's x-coordinate.
+     * @since 3.0
+     */
+    public static const String STORE_LOCATION_X= "location.x"; //$NON-NLS-1$
+    /**
+     * Dialog store constant for the location's y-coordinate.
+     * @since 3.0
+     */
+    public static const String STORE_LOCATION_Y= "location.y"; //$NON-NLS-1$
+    /**
+     * Dialog store constant for the size's width.
+     * @since 3.0
+     */
+    public static const String STORE_SIZE_WIDTH= "size.width"; //$NON-NLS-1$
+    /**
+     * Dialog store constant for the size's height.
+     * @since 3.0
+     */
+    public static const String STORE_SIZE_HEIGHT= "size.height"; //$NON-NLS-1$
+
+    /**
+     * Tells whether this class and its subclasses are in debug mode.
+     * <p>
+     * Subclasses may use this.
+     * </p>
+     * @since 3.4
+     */
+    private static bool DEBUG_;
+    private static bool DEBUG_init = false;
+    protected static bool DEBUG(){
+        if( !DEBUG_init ){
+            DEBUG_init = true;
+            DEBUG_ = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.jface.text/debug/AbstractInformationControlManager"));  //$NON-NLS-1$//$NON-NLS-2$
+        }
+        return DEBUG_;
+    }
+
+
+    /** The subject control of the information control */
+    private Control  fSubjectControl;
+
+    /** The display area for which the information to be presented is valid */
+    private Rectangle fSubjectArea;
+
+    /** The information to be presented */
+    private Object fInformation;
+
+    /** Indicates whether the information control takes focus when visible */
+    private bool fTakesFocusWhenVisible= false;
+
+    /**
+     * The information control.
+     *
+     * <p>This field should not be referenced by subclasses. It is <code>protected</code> for API
+     * compatibility reasons.
+     */
+    protected IInformationControl fInformationControl;
+
+    /**
+     * The information control creator.
+     *
+     * <p>This field should not be referenced by subclasses. It is <code>protected</code> for API
+     * compatibility reasons.
+     */
+    protected IInformationControlCreator fInformationControlCreator;
+
+    /**
+     * The information control closer.
+     *
+     * <p>This field should not be referenced by subclasses. It is <code>protected</code> for API
+     * compatibility reasons.
+     */
+    protected IInformationControlCloser fInformationControlCloser;
+
+    /**
+     * Indicates that the information control has been disposed.
+     *
+     * <p>This field should not be referenced by subclasses. It is <code>protected</code> for API
+     * compatibility reasons.
+     */
+    protected bool fDisposed= false;
+
+    /**
+     * The information control replacer to be used when this information control
+     * needs to be replaced with another information control.
+     *
+     * @since 3.4
+     */
+    private InformationControlReplacer fInformationControlReplacer;
+
+    /** Indicates the enable state of this manager */
+    private bool fEnabled= false;
+
+    /** Cached, computed size constraints of the information control in points */
+    private Point fSizeConstraints;
+
+    /** The vertical margin when laying out the information control */
+    private int fMarginY= 5;
+
+    /** The horizontal margin when laying out the information control */
+    private int fMarginX= 5;
+
+    /** The width constraint of the information control in characters */
+    private int fWidthConstraint= 60;
+
+    /** The height constraint of the information control  in characters */
+    private int fHeightConstraint= 6;
+
+    /** Indicates whether the size constraints should be enforced as minimal control size */
+    private bool fEnforceAsMinimalSize= false;
+
+    /** Indicates whether the size constraints should be enforced as maximal control size */
+    private bool fEnforceAsMaximalSize= false;
+
+    /** The anchor for laying out the information control in relation to the subject control */
+    private Anchor fAnchor;
+
+    /**
+     * The anchor sequence used to layout the information control if the original anchor
+     * can not be used because the information control would not fit in the display client area.
+     * <p>
+     * The fallback anchor for a given anchor is the one that comes directly after the given anchor or
+     * is the first one in the sequence if the given anchor is the last one in the sequence.
+     * <p>
+     * </p>
+     * Note: This sequence is ignored if the original anchor is not contained in this sequence.
+     * </p>
+     *
+     * @see #fAnchor
+     */
+    private Anchor[] fFallbackAnchors;
+    /**
+     * The custom information control creator.
+     * @since 3.0
+     */
+    private /+volatile+/ IInformationControlCreator fCustomInformationControlCreator;
+
+    /**
+     * Tells whether a custom information control is in use.
+     * @since 3.0
+     */
+    private bool fIsCustomInformationControl= false;
+
+    /**
+     * The dialog settings for the control's bounds.
+     * @since 3.0
+     */
+    private IDialogSettings fDialogSettings;
+
+    /**
+     * Tells whether the control's location should be read
+     * from the dialog settings and whether the last
+     * valid control's size is stored back into the  settings.
+     *
+     * @since 3.0
+     */
+    private bool fIsRestoringLocation;
+
+    /**
+     * Tells whether the control's size should be read
+     * from the dialog settings and whether the last
+     * valid control's size is stored back into the  settings.
+     *
+     * @since 3.0
+     */
+    private bool fIsRestoringSize;
+
+    /**
+     * The dispose listener on the subject control.
+     *
+     * @since 3.1
+     */
+    private DisposeListener fSubjectControlDisposeListener;
+
+
+    /**
+     * Creates a new information control manager using the given information control creator.
+     * By default the following configuration is given:
+     * <ul>
+     * <li> enabled is false
+     * <li> horizontal margin is 5 points
+     * <li> vertical margin is 5 points
+     * <li> width constraint is 60 characters
+     * <li> height constraint is 6 characters
+     * <li> enforce constraints as minimal size is false
+     * <li> enforce constraints as maximal size is false
+     * <li> layout anchor is ANCHOR_BOTTOM
+     * <li> fall back anchors is { ANCHOR_TOP, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_RIGHT, ANCHOR_GLOBAL }
+     * <li> takes focus when visible is false
+     * </ul>
+     *
+     * @param creator the information control creator
+     */
+    protected this(IInformationControlCreator creator) {
+        fAnchor= ANCHOR_BOTTOM();
+        fFallbackAnchors= ANCHORS();
+        Assert.isNotNull(cast(Object)creator);
+        fInformationControlCreator= creator;
+    }
+
+    /**
+     * Computes the information to be displayed and the area in which the computed
+     * information is valid. Implementation of this method must finish their computation
+     * by setting the computation results using <code>setInformation</code>.
+     */
+    abstract protected void computeInformation();
+
+    /**
+     * Sets the parameters of the information to be displayed. These are the information itself and
+     * the area for which the given information is valid. This so called subject area is a graphical
+     * region of the information control's subject control. This method calls <code>presentInformation()</code>
+     * to trigger the presentation of the computed information.
+     *
+     * @param information the information, or <code>null</code> if none is available
+     * @param subjectArea the subject area, or <code>null</code> if none is available
+     */
+    protected final void setInformation(String information, Rectangle subjectArea) {
+        setInformation(stringcast(information), subjectArea);
+    }
+
+    /**
+     * Sets the parameters of the information to be displayed. These are the information itself and
+     * the area for which the given information is valid. This so called subject area is a graphical
+     * region of the information control's subject control. This method calls <code>presentInformation()</code>
+     * to trigger the presentation of the computed information.
+     *
+     * @param information the information, or <code>null</code> if none is available
+     * @param subjectArea the subject area, or <code>null</code> if none is available
+     * @since  2.1
+     */
+    protected final void setInformation(Object information, Rectangle subjectArea) {
+        fInformation= information;
+        fSubjectArea= subjectArea;
+        presentInformation();
+    }
+
+    /**
+     * Sets the information control closer for this manager.
+     *
+     * @param closer the information control closer for this manager
+     */
+    protected void setCloser(IInformationControlCloser closer) {
+        fInformationControlCloser= closer;
+    }
+
+    /**
+     * Sets the information control replacer for this manager and disposes the
+     * old one if set.
+     *
+     * @param replacer the information control replacer for this manager, or
+     *            <code>null</code> if no information control replacing should
+     *            take place
+     * @since 3.4
+     */
+    void setInformationControlReplacer(InformationControlReplacer replacer) {
+        if (fInformationControlReplacer !is null)
+            fInformationControlReplacer.dispose();
+        fInformationControlReplacer= replacer;
+    }
+
+    /**
+     * Returns the current information control replacer or <code>null</code> if none has been installed.
+     *
+     * @return the current information control replacer or <code>null</code> if none has been installed
+     * @since 3.4
+     */
+    InformationControlReplacer getInformationControlReplacer() {
+        return fInformationControlReplacer;
+    }
+
+    /**
+     * Returns whether an information control replacer has been installed.
+     *
+     * @return whether an information control replacer has been installed
+     * @since 3.4
+     */
+    bool hasInformationControlReplacer() {
+        return fInformationControlReplacer !is null;
+    }
+
+    /**
+     * Tests whether the given information control is replaceable.
+     *
+     * @param iControl information control or <code>null</code> if none
+     * @return <code>true</code> if information control is replaceable, <code>false</code> otherwise
+     * @since 3.4
+     */
+    bool canReplace(IInformationControl iControl) {
+        return cast(IInformationControlExtension3)iControl
+                && cast(IInformationControlExtension5)iControl
+                && (cast(IInformationControlExtension5) iControl).getInformationPresenterControlCreator() !is null;
+    }
+
+    /**
+     * Returns the current information control, or <code>null</code> if none.
+     *
+     * @return the current information control, or <code>null</code> if none
+     * @since 3.4
+     */
+    IInformationControl getCurrentInformationControl() {
+        return fInformationControl;
+    }
+
+    /**
+     * Tells whether this manager's information control is currently being replaced.
+     *
+     * @return <code>true</code> if a replace is in progress
+     * @since 3.4
+     */
+    bool isReplaceInProgress() {
+        return fInformationControlReplacer !is null && fInformationControlReplacer.isReplacing();
+    }
+
+    /**
+     * Sets the horizontal and vertical margin to be used when laying out the
+     * information control relative to the subject control.
+     *
+     * @param xMargin the x-margin
+     * @param yMargin the y-Margin
+     */
+    public void setMargins(int xMargin, int yMargin) {
+        fMarginX= xMargin;
+        fMarginY= yMargin;
+    }
+
+    /**
+     * Sets the width- and height constraints of the information control.
+     *
+     * @param widthInChar the width constraint in number of characters
+     * @param heightInChar the height constrain in number of characters
+     * @param enforceAsMinimalSize indicates whether the constraints describe the minimal allowed size of the control
+     * @param enforceAsMaximalSize indicates whether the constraints describe the maximal allowed size of the control
+     */
+    public void setSizeConstraints(int widthInChar, int heightInChar, bool enforceAsMinimalSize, bool enforceAsMaximalSize) {
+        fSizeConstraints= null;
+        fWidthConstraint= widthInChar;
+        fHeightConstraint= heightInChar;
+        fEnforceAsMinimalSize= enforceAsMinimalSize;
+        fEnforceAsMaximalSize= enforceAsMaximalSize;
+
+    }
+
+    /**
+     * Tells this information control manager to open the information
+     * control with the values contained in the given dialog settings
+     * and to store the control's last valid size in the given dialog
+     * settings.
+     * <p>
+     * Note: This API is only valid if the information control implements
+     * {@link IInformationControlExtension3}. Not following this restriction
+     * will later result in an {@link UnsupportedOperationException}.
+     * </p>
+     * <p>
+     * The constants used to store the values are:
+     * <ul>
+     *  <li>{@link AbstractInformationControlManager#STORE_LOCATION_X}</li>
+     *  <li>{@link AbstractInformationControlManager#STORE_LOCATION_Y}</li>
+     *  <li>{@link AbstractInformationControlManager#STORE_SIZE_WIDTH}</li>
+     *  <li>{@link AbstractInformationControlManager#STORE_SIZE_HEIGHT}</li>
+     * </ul>
+     * </p>
+     *
+     * @param dialogSettings
+     * @param restoreLocation <code>true</code> iff the location is must be (re-)stored
+     * @param restoreSize <code>true</code>iff the size is (re-)stored
+     * @since 3.0
+     */
+    public void setRestoreInformationControlBounds(IDialogSettings dialogSettings, bool restoreLocation, bool restoreSize) {
+        Assert.isTrue(dialogSettings !is null && (restoreLocation || restoreSize));
+        fDialogSettings= dialogSettings;
+        fIsRestoringLocation= restoreLocation;
+        fIsRestoringSize= restoreSize;
+    }
+
+    /**
+     * Sets the anchor used for laying out the information control relative to the
+     * subject control. E.g, using <code>ANCHOR_TOP</code> indicates that the
+     * information control is position above the area for which the information to
+     * be displayed is valid.
+     *
+     * @param anchor the layout anchor
+     */
+    public void setAnchor(Anchor anchor) {
+        fAnchor= anchor;
+    }
+
+    /**
+     * Sets the anchors fallback sequence used to layout the information control if the original
+     * anchor can not be used because the information control would not fit in the display client
+     * area.
+     * <p>
+     * The fallback anchor for a given anchor is the one that comes directly after the given anchor or
+     * is the first one in the sequence if the given anchor is the last one in the sequence.
+     * <p>
+     * </p>
+     * Note: This sequence is ignored if the original anchor is not contained in this list.
+     * </p>
+     *
+     * @param fallbackAnchors the array with the anchor fallback sequence
+     * @see #setAnchor(AbstractInformationControlManager.Anchor)
+     */
+    public void setFallbackAnchors(Anchor[] fallbackAnchors) {
+        if (fallbackAnchors !is null) {
+            fFallbackAnchors= new Anchor[fallbackAnchors.length];
+            System.arraycopy(fallbackAnchors, 0, fFallbackAnchors, 0, fallbackAnchors.length);
+        } else
+            fFallbackAnchors= null;
+    }
+
+    /**
+     * Sets the temporary custom control creator, overriding this manager's default information control creator.
+     *
+     * @param informationControlCreator the creator, possibly <code>null</code>
+     * @since 3.0
+     */
+    protected void setCustomInformationControlCreator(IInformationControlCreator informationControlCreator)  {
+        if (informationControlCreator !is null && cast(IInformationControlCreatorExtension)fCustomInformationControlCreator) {
+            IInformationControlCreatorExtension extension= cast(IInformationControlCreatorExtension) fCustomInformationControlCreator;
+            if (extension.canReplace(informationControlCreator))
+                return;
+        }
+        fCustomInformationControlCreator= informationControlCreator;
+    }
+
+    /**
+     * Tells the manager whether it should set the focus to the information control when made visible.
+     *
+     * @param takesFocus <code>true</code> if information control should take focus when made visible
+     */
+    public void takesFocusWhenVisible(bool takesFocus) {
+        fTakesFocusWhenVisible= takesFocus;
+    }
+
+    /**
+     * Handles the disposal of the subject control. By default, the information control
+     * is disposed by calling <code>disposeInformationControl</code>. Subclasses may extend
+     * this method.
+     */
+    protected void handleSubjectControlDisposed() {
+        disposeInformationControl();
+    }
+
+    /**
+     * Installs this manager on the given control. The control is now taking the role of
+     * the subject control. This implementation sets the control also as the information
+     * control closer's subject control and automatically enables this manager.
+     *
+     * @param subjectControl the subject control
+     */
+    public void install(Control subjectControl) {
+        if (fSubjectControl !is null && !fSubjectControl.isDisposed() && fSubjectControlDisposeListener !is null)
+            fSubjectControl.removeDisposeListener(fSubjectControlDisposeListener);
+
+        fSubjectControl= subjectControl;
+
+        if (fSubjectControl !is null)
+            fSubjectControl.addDisposeListener(getSubjectControlDisposeListener());
+
+        if (fInformationControlCloser !is null)
+            fInformationControlCloser.setSubjectControl(subjectControl);
+
+        setEnabled(true);
+        fDisposed= false;
+    }
+
+    /**
+     * Returns the dispose listener which gets added
+     * to the subject control.
+     *
+     * @return the dispose listener
+     * @since 3.1
+     */
+    private DisposeListener getSubjectControlDisposeListener() {
+        if (fSubjectControlDisposeListener is null) {
+            fSubjectControlDisposeListener= new class()  DisposeListener {
+                public void widgetDisposed(DisposeEvent e) {
+                    handleSubjectControlDisposed();
+                }
+            };
+        }
+        return fSubjectControlDisposeListener;
+    }
+
+    /**
+     * Returns the subject control of this manager/information control.
+     *
+     * @return the subject control
+     */
+    protected Control getSubjectControl() {
+        return fSubjectControl;
+    }
+
+    /**
+     * Returns the actual subject area.
+     *
+     * @return the actual subject area
+     */
+    protected Rectangle getSubjectArea() {
+        return fSubjectArea;
+    }
+
+    /**
+     * Sets the enable state of this manager.
+     *
+     * @param enabled the enable state
+     * @deprecated visibility will be changed to protected
+     */
+    public void setEnabled(bool enabled) {
+        fEnabled= enabled;
+    }
+
+    /**
+     * Returns whether this manager is enabled or not.
+     *
+     * @return <code>true</code> if this manager is enabled otherwise <code>false</code>
+     */
+    protected bool isEnabled() {
+        return fEnabled;
+    }
+
+    /**
+     * Computes the size constraints of the information control in points based on the
+     * default font of the given subject control as well as the size constraints in character
+     * width.
+     *
+     * @param subjectControl the subject control
+     * @param informationControl the information control whose size constraints are computed
+     * @return the computed size constraints in points
+     */
+    protected Point computeSizeConstraints(Control subjectControl, IInformationControl informationControl) {
+
+        if (fSizeConstraints is null) {
+            if ( cast(IInformationControlExtension5)informationControl ) {
+                IInformationControlExtension5 iControl5= cast(IInformationControlExtension5) informationControl;
+                fSizeConstraints= iControl5.computeSizeConstraints(fWidthConstraint, fHeightConstraint);
+                if (fSizeConstraints !is null)
+                    return Geometry.copy(fSizeConstraints);
+            }
+            if (subjectControl is null)
+                return null;
+
+            GC gc= new GC(subjectControl);
+            gc.setFont(subjectControl.getFont());
+            int width= gc.getFontMetrics().getAverageCharWidth();
+            int height = gc.getFontMetrics().getHeight();
+            gc.dispose();
+
+            fSizeConstraints= new Point (fWidthConstraint * width, fHeightConstraint * height);
+        }
+
+        return new Point(fSizeConstraints.x, fSizeConstraints.y);
+    }
+
+    /**
+     * Computes the size constraints of the information control in points.
+     *
+     * @param subjectControl the subject control
+     * @param subjectArea the subject area
+     * @param informationControl the information control whose size constraints are computed
+     * @return the computed size constraints in points
+     * @since 3.0
+     */
+    protected Point computeSizeConstraints(Control subjectControl, Rectangle subjectArea, IInformationControl informationControl) {
+        return computeSizeConstraints(subjectControl, informationControl);
+    }
+
+    /**
+     * Handles the disposal of the information control. By default, the information
+     * control closer is stopped.
+     */
+    protected void handleInformationControlDisposed() {
+
+        storeInformationControlBounds();
+
+        if ( cast(IInformationControlExtension5)fInformationControl )
+            fSizeConstraints= null;
+        fInformationControl= null;
+        if (fInformationControlCloser !is null) {
+            fInformationControlCloser.setInformationControl(null); //XXX: null is against the spec
+            fInformationControlCloser.stop();
+        }
+    }
+
+    /**
+     * Returns the information control. If the information control has not been created yet,
+     * it is automatically created.
+     *
+     * @return the information control
+     */
+    protected IInformationControl getInformationControl() {
+
+        if (fDisposed)
+            return fInformationControl;
+
+        IInformationControlCreator creator= null;
+
+        if (fCustomInformationControlCreator is null) {
+            creator= fInformationControlCreator;
+            if (fIsCustomInformationControl && fInformationControl !is null) {
+                if ( cast(IInformationControlExtension5)fInformationControl )
+                    fSizeConstraints= null;
+                fInformationControl.dispose();
+                fInformationControl= null;
+            }
+            fIsCustomInformationControl= false;
+
+        } else  {
+
+            creator= fCustomInformationControlCreator;
+            if ( cast(IInformationControlCreatorExtension)creator )  {
+                IInformationControlCreatorExtension extension= cast(IInformationControlCreatorExtension) creator;
+                if (fInformationControl !is null && extension.canReuse(fInformationControl))
+                    return fInformationControl;
+            }
+            if (fInformationControl !is null)  {
+                if ( cast(IInformationControlExtension5)fInformationControl )
+                    fSizeConstraints= null;
+                fInformationControl.dispose();
+                fInformationControl= null;
+            }
+            fIsCustomInformationControl= true;
+        }
+
+        if (fInformationControl is null) {
+            fInformationControl= creator.createInformationControl(fSubjectControl.getShell());
+            fInformationControl.addDisposeListener(new class()  DisposeListener {
+                public void widgetDisposed(DisposeEvent e) {
+                    handleInformationControlDisposed();
+                }
+            });
+
+            if (fInformationControlCloser !is null)
+                fInformationControlCloser.setInformationControl(fInformationControl);
+        }
+
+        return fInformationControl;
+    }
+
+    /**
+     * Computes the display location of the information control. The location is computed
+     * considering the given subject area, the anchor at the subject area, and the
+     * size of the information control. This method does not care about whether the information
+     * control would be completely visible when placed at the result location.
+     *
+     * @param subjectArea the subject area
+     * @param controlSize the size of the information control
+     * @param anchor the anchor at the subject area
+     * @return the display location of the information control
+     */
+    protected Point computeLocation(Rectangle subjectArea, Point controlSize, Anchor anchor) {
+        int xShift= 0;
+        int yShift= 0;
+
+        switch (anchor.getSWTFlag()) {
+            case SWT.CENTER:
+                Point subjectControlSize= fSubjectControl.getSize();
+                Point location= new Point(subjectControlSize.x / 2, subjectControlSize.y / 2);
+                location.x -= (controlSize.x / 2);
+                location.y -= (controlSize.y / 2);
+                return fSubjectControl.toDisplay(location);
+            case SWT.BOTTOM:
+                yShift= subjectArea.height + fMarginY;
+                break;
+            case SWT.RIGHT:
+                xShift= fMarginX + subjectArea.width;
+                break;
+            case SWT.TOP:
+                yShift= -controlSize.y - fMarginY;
+                break;
+            case SWT.LEFT:
+                xShift= -controlSize.x - fMarginX;
+                break;
+            default:
+        }
+
+        bool isRTL= fSubjectControl !is null && (fSubjectControl.getStyle() & SWT.RIGHT_TO_LEFT) !is 0;
+        if (isRTL)
+            xShift += controlSize.x;
+
+        return  fSubjectControl.toDisplay(new Point(subjectArea.x + xShift, subjectArea.y + yShift));
+    }
+
+    /**
+     * Computes the area available for an information control given an anchor and the subject area
+     * within <code>bounds</code>.
+     *
+     * @param subjectArea the subject area
+     * @param bounds the bounds
+     * @param anchor the anchor at the subject area
+     * @return the area available at the given anchor relative to the subject area, confined to the
+     *         monitor's client area
+     * @since 3.3
+     */
+    protected Rectangle computeAvailableArea(Rectangle subjectArea, Rectangle bounds, Anchor anchor) {
+        Rectangle area;
+        switch (anchor.getSWTFlag()) {
+            case SWT.CENTER:
+                area= bounds;
+                break;
+            case SWT.BOTTOM:
+                int y= subjectArea.y + subjectArea.height + fMarginY;
+                area= new Rectangle(bounds.x, y, bounds.width, bounds.y + bounds.height - y);
+                break;
+            case SWT.RIGHT:
+                int x= subjectArea.x + subjectArea.width + fMarginX;
+                area= new Rectangle(x, bounds.y, bounds.x + bounds.width - x, bounds.height);
+                break;
+            case SWT.TOP:
+                area= new Rectangle(bounds.x, bounds.y, bounds.width, subjectArea.y - bounds.y - fMarginY);
+                break;
+            case SWT.LEFT:
+                area= new Rectangle(bounds.x, bounds.y, subjectArea.x - bounds.x - fMarginX, bounds.height);
+                break;
+            default:
+                Assert.isLegal(false);
+                return null;
+        }
+
+        // Don't return negative areas if the subjectArea overlaps with the monitor bounds.
+        area.intersect(bounds);
+        return area;
+    }
+
+    /**
+     * Checks whether a control of the given size at the given location would be completely visible
+     * in the given display area when laid out by using the given anchor. If not, this method tries
+     * to shift the control orthogonal to the direction given by the anchor to make it visible. If possible
+     * it updates the location.<p>
+     * This method returns <code>true</code> if the potentially updated position results in a
+     * completely visible control, or <code>false</code> otherwise.
+     *
+     *
+     * @param location the location of the control
+     * @param size the size of the control
+     * @param displayArea the display area in which the control should be visible
+     * @param anchor anchor for lying out the control
+     * @return <code>true</code>if the updated location is useful
+     */
+    protected bool updateLocation(Point location, Point size, Rectangle displayArea, Anchor anchor) {
+
+        int displayLowerRightX= displayArea.x + displayArea.width;
+        int displayLowerRightY= displayArea.y + displayArea.height;
+        int lowerRightX= location.x + size.x;
+        int lowerRightY= location.y + size.y;
+
+        if (ANCHOR_BOTTOM is anchor || ANCHOR_TOP is anchor) {
+
+            if (ANCHOR_BOTTOM is anchor) {
+                if (lowerRightY > displayLowerRightY)
+                    return false;
+            } else {
+                if (location.y < displayArea.y)
+                    return false;
+            }
+
+            if (lowerRightX > displayLowerRightX)
+                location.x= location.x - (lowerRightX - displayLowerRightX);
+
+            return (location.x >= displayArea.x && location.y >= displayArea.y);
+
+        } else if (ANCHOR_RIGHT is anchor || ANCHOR_LEFT is anchor) {
+
+            if (ANCHOR_RIGHT is anchor) {
+                if (lowerRightX > displayLowerRightX)
+                    return false;
+            } else {
+                if (location.x < displayArea.x)
+                    return false;
+            }
+
+            if (lowerRightY > displayLowerRightY)
+                location.y= location.y - (lowerRightY - displayLowerRightY);
+
+            return (location.x >= displayArea.x && location.y >= displayArea.y);
+
+        } else if (ANCHOR_GLOBAL is anchor) {
+
+            if (lowerRightX > displayLowerRightX)
+                location.x= location.x - (lowerRightX - displayLowerRightX);
+
+            if (lowerRightY > displayLowerRightY)
+                location.y= location.y - (lowerRightY - displayLowerRightY);
+
+            return (location.x >= displayArea.x && location.y >= displayArea.y);
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the next fallback anchor as specified by this manager's
+     * fallback anchor sequence.
+     * <p>
+     * The fallback anchor for the given anchor is the one that comes directly after
+     * the given anchor or is the first one in the sequence if the given anchor is the
+     * last one in the sequence.
+     * </p>
+     * <p>
+     * Note: It is the callers responsibility to prevent an endless loop i.e. to test
+     * whether a given anchor has already been used once.
+     * then
+     * </p>
+     *
+     * @param anchor the current anchor
+     * @return the next fallback anchor or <code>null</code> if no fallback anchor is available
+     */
+    protected Anchor getNextFallbackAnchor(Anchor anchor) {
+
+        if (anchor is null || fFallbackAnchors is null)
+            return null;
+
+        for (int i= 0; i < fFallbackAnchors.length; i++) {
+            if (fFallbackAnchors[i] is anchor)
+                return fFallbackAnchors[i + 1 is fFallbackAnchors.length ? 0 : i + 1];
+        }
+
+        return null;
+    }
+
+    /**
+     * Computes the location of the information control depending on the
+     * subject area and the size of the information control. This method attempts
+     * to find a location at which the information control lies completely in the display's
+     * client area while honoring the manager's default anchor. If this isn't possible using the
+     * default anchor, the fallback anchors are tried out.
+     *
+     * @param subjectArea the information area
+     * @param controlSize the size of the information control
+     * @return the computed location of the information control
+     */
+    protected Point computeInformationControlLocation(Rectangle subjectArea, Point controlSize) {
+        Rectangle subjectAreaDisplayRelative= Geometry.toDisplay(fSubjectControl, subjectArea);
+
+        Point upperLeft;
+        Anchor testAnchor= fAnchor;
+        Rectangle bestBounds= null;
+        int bestArea= Integer.MIN_VALUE;
+        Anchor bestAnchor= null;
+        do {
+
+            upperLeft= computeLocation(subjectArea, controlSize, testAnchor);
+            org.eclipse.swt.widgets.Monitor.Monitor monitor= getClosestMonitor(subjectAreaDisplayRelative, testAnchor);
+            if (updateLocation(upperLeft, controlSize, monitor.getClientArea(), testAnchor))
+                return upperLeft;
+
+            // compute available area for this anchor and update if better than best
+            Rectangle available= computeAvailableArea(subjectAreaDisplayRelative, monitor.getClientArea(), testAnchor);
+            Rectangle proposed= new Rectangle(upperLeft.x, upperLeft.y, controlSize.x, controlSize.y);
+            available.intersect(proposed);
+            int area= available.width * available.height;
+            if (area > bestArea) {
+                bestArea= area;
+                bestBounds= available;
+                bestAnchor= testAnchor;
+            }
+
+            testAnchor= getNextFallbackAnchor(testAnchor);
+
+        } while (testAnchor !is fAnchor && testAnchor !is null);
+
+        // no anchor is perfect - select the one with larges area and set the size to not overlap with the subjectArea
+        if (bestAnchor !is ANCHOR_GLOBAL)
+            Geometry.set(controlSize, Geometry.getSize(bestBounds));
+        return Geometry.getLocation(bestBounds);
+    }
+
+    /**
+     * Gets the closest monitor given an anchor and the subject area.
+     *
+     * @param area the subject area
+     * @param anchor the anchor
+     * @return the monitor closest to the edge of <code>area</code> defined by
+     *         <code>anchor</code>
+     * @since 3.3
+     */
+    private org.eclipse.swt.widgets.Monitor.Monitor getClosestMonitor(Rectangle area, Anchor anchor) {
+        Point center;
+        if (ANCHOR_GLOBAL is anchor)
+            center= Geometry.centerPoint(area);
+        else
+            center= Geometry.centerPoint(Geometry.getExtrudedEdge(area, 0, anchor.getSWTFlag()));
+        return getClosestMonitor(fSubjectControl.getDisplay(), Geometry.createRectangle(center, new Point(0, 0)));
+    }
+
+    /**
+     * Copied from org.eclipse.jface.window.Window. Returns the monitor whose client area contains
+     * the given point. If no monitor contains the point, returns the monitor that is closest to the
+     * point. If this is ever made public, it should be moved into a separate utility class.
+     *
+     * @param display the display to search for monitors
+     * @param rectangle the rectangle to find the closest monitor for (display coordinates)
+     * @return the monitor closest to the given point
+     * @since 3.3
+     */
+    private org.eclipse.swt.widgets.Monitor.Monitor getClosestMonitor(Display display, Rectangle rectangle) {
+        int closest = Integer.MAX_VALUE;
+
+        Point toFind= Geometry.centerPoint(rectangle);
+        org.eclipse.swt.widgets.Monitor.Monitor[] monitors = display.getMonitors();
+        org.eclipse.swt.widgets.Monitor.Monitor result = monitors[0];
+
+        for (int idx = 0; idx < monitors.length; idx++) {
+            org.eclipse.swt.widgets.Monitor.Monitor current = monitors[idx];
+
+            Rectangle clientArea = current.getClientArea();
+
+            if (clientArea.contains(toFind)) {
+                return current;
+            }
+
+            int distance = Geometry.distanceSquared(Geometry.centerPoint(clientArea), toFind);
+            if (distance < closest) {
+                closest = distance;
+                result = current;
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Computes information to be displayed as well as the subject area
+     * and initiates that this information is presented in the information control.
+     * This happens only if this controller is enabled.
+     */
+    public void showInformation() {
+        if (fEnabled)
+            doShowInformation();
+    }
+
+    /**
+     * Computes information to be displayed as well as the subject area
+     * and initiates that this information is presented in the information control.
+     */
+    protected void doShowInformation() {
+        fSubjectArea= null;
+        fInformation= null;
+        computeInformation();
+    }
+
+    /**
+     * Presents the information in the information control or hides the information
+     * control if no information should be presented. The information has previously
+     * been set using <code>setInformation</code>.
+     */
+    protected void presentInformation() {
+        bool hasContents= false;
+        if ( stringcast(fInformation) )
+            hasContents= (stringcast(fInformation)).trim().length() > 0;
+        else
+            hasContents= (fInformation !is null);
+
+        if (fSubjectArea !is null && hasContents)
+            internalShowInformationControl(fSubjectArea, fInformation);
+        else
+            hideInformationControl();
+    }
+
+    /**
+     * Opens the information control with the given information and the specified
+     * subject area. It also activates the information control closer.
+     *
+     * @param subjectArea the information area
+     * @param information the information
+     */
+    private void internalShowInformationControl(Rectangle subjectArea, Object information) {
+        if ( cast(InformationControlReplacer)this ) {
+            (cast(InformationControlReplacer) this).showInformationControl(subjectArea, information);
+            return;
+        }
+
+        IInformationControl informationControl= getInformationControl();
+        if (informationControl !is null) {
+
+            Point sizeConstraints= computeSizeConstraints(fSubjectControl, fSubjectArea, informationControl);
+            if ( cast(IInformationControlExtension3)informationControl ) {
+                IInformationControlExtension3 iControl3= cast(IInformationControlExtension3) informationControl;
+                Rectangle trim= iControl3.computeTrim();
+                sizeConstraints.x += trim.width;
+                sizeConstraints.y += trim.height;
+            }
+            informationControl.setSizeConstraints(sizeConstraints.x, sizeConstraints.y);
+
+            if ( cast(IInformationControlExtension2)informationControl )
+                (cast(IInformationControlExtension2)informationControl).setInput(information);
+            else
+                informationControl.setInformation(information.toString());
+
+            if ( cast(IInformationControlExtension)informationControl ) {
+                IInformationControlExtension extension= cast(IInformationControlExtension)informationControl;
+                if (!extension.hasContents())
+                    return;
+            }
+
+            Point size= null;
+            Point location= null;
+            Rectangle bounds= restoreInformationControlBounds();
+
+            if (bounds !is null) {
+                if (bounds.x > -1 && bounds.y > -1)
+                    location= Geometry.getLocation(bounds);
+
+                if (bounds.width > -1 && bounds.height > -1)
+                    size= Geometry.getSize(bounds);
+            }
+
+            if (size is null)
+                size= informationControl.computeSizeHint();
+
+            if (fEnforceAsMinimalSize)
+                size= Geometry.max(size, sizeConstraints);
+            if (fEnforceAsMaximalSize)
+                size= Geometry.min(size, sizeConstraints);
+
+            if (location is null)
+                location= computeInformationControlLocation(subjectArea, size);
+
+            Rectangle controlBounds= Geometry.createRectangle(location, size);
+            cropToClosestMonitor(controlBounds);
+            location= Geometry.getLocation(controlBounds);
+            size= Geometry.getSize(controlBounds);
+            informationControl.setLocation(location);
+            informationControl.setSize(size.x, size.y);
+
+            showInformationControl(subjectArea);
+        }
+    }
+
+    /**
+     * Crops the given bounds such that they lie completely on the closest monitor.
+     *
+     * @param bounds shell bounds to crop
+     * @since 3.4
+     */
+    void cropToClosestMonitor(Rectangle bounds) {
+        Rectangle monitorBounds= getClosestMonitor(fSubjectControl.getDisplay(), bounds).getClientArea();
+        bounds.intersect(monitorBounds);
+    }
+
+    /**
+     * Hides the information control and stops the information control closer.
+     */
+    protected void hideInformationControl() {
+        if (fInformationControl !is null) {
+            storeInformationControlBounds();
+            fInformationControl.setVisible(false);
+            if (fInformationControlCloser !is null)
+                fInformationControlCloser.stop();
+        }
+    }
+
+    /**
+     * Shows the information control and starts the information control closer.
+     * This method may not be called by clients.
+     *
+     * @param subjectArea the information area
+     */
+    protected void showInformationControl(Rectangle subjectArea) {
+        fInformationControl.setVisible(true);
+
+        if (fTakesFocusWhenVisible)
+            fInformationControl.setFocus();
+
+        if (fInformationControlCloser !is null)
+            fInformationControlCloser.start(subjectArea);
+    }
+
+    /**
+     * Replaces this manager's information control as defined by
+     * the information control replacer.
+     * <strong>Must only be called when {@link #fInformationControl} instanceof {@link IInformationControlExtension3}!</strong>
+     *
+     * @param takeFocus <code>true</code> iff the replacing information control should take focus
+     *
+     * @since 3.4
+     */
+    void replaceInformationControl(bool takeFocus) {
+        if (fInformationControlReplacer !is null && canReplace(fInformationControl)) {
+            IInformationControlExtension3 iControl3= cast(IInformationControlExtension3) fInformationControl;
+            Rectangle b= iControl3.getBounds();
+            Rectangle t= iControl3.computeTrim();
+            Rectangle contentBounds= new Rectangle(b.x - t.x, b.y - t.y, b.width - t.width, b.height - t.height);
+            IInformationControlCreator informationPresenterControlCreator= (cast(IInformationControlExtension5) fInformationControl).getInformationPresenterControlCreator();
+            fInformationControlReplacer.replaceInformationControl(informationPresenterControlCreator, contentBounds, fInformation, fSubjectArea, takeFocus);
+        }
+        hideInformationControl();
+    }
+
+    /**
+     * Disposes this manager's information control.
+     */
+    public void disposeInformationControl() {
+        if (fInformationControl !is null) {
+            fInformationControl.dispose();
+            handleInformationControlDisposed();
+        }
+    }
+
+    /**
+     * Disposes this manager and if necessary all dependent parts such as
+     * the information control. For symmetry it first disables this manager.
+     */
+    public void dispose() {
+        if (!fDisposed) {
+
+            fDisposed= true;
+
+            setEnabled(false);
+            disposeInformationControl();
+
+            if (fInformationControlReplacer !is null) {
+                fInformationControlReplacer.dispose();
+                fInformationControlReplacer= null;
+            }
+
+            if (fSubjectControl !is null && !fSubjectControl.isDisposed() && fSubjectControlDisposeListener !is null)
+                fSubjectControl.removeDisposeListener(fSubjectControlDisposeListener);
+            fSubjectControl= null;
+            fSubjectControlDisposeListener= null;
+
+            fIsCustomInformationControl= false;
+            fCustomInformationControlCreator= null;
+            fInformationControlCreator= null;
+            fInformationControlCloser= null;
+        }
+    }
+
+    // ------ control's size handling dialog settings ------
+
+    /**
+     * Stores the information control's bounds.
+     *
+     * @since 3.0
+     */
+    protected void storeInformationControlBounds() {
+        if (fDialogSettings is null || fInformationControl is null || !(fIsRestoringLocation || fIsRestoringSize))
+            return;
+
+        if (!( cast(IInformationControlExtension3)fInformationControl ))
+            throw new UnsupportedOperationException();
+
+        bool controlRestoresSize= (cast(IInformationControlExtension3)fInformationControl).restoresSize();
+        bool controlRestoresLocation= (cast(IInformationControlExtension3)fInformationControl).restoresLocation();
+
+        Rectangle bounds= (cast(IInformationControlExtension3)fInformationControl).getBounds();
+        if (bounds is null)
+            return;
+
+        if (fIsRestoringSize && controlRestoresSize) {
+            fDialogSettings.put(STORE_SIZE_WIDTH, bounds.width);
+            fDialogSettings.put(STORE_SIZE_HEIGHT, bounds.height);
+        }
+        if (fIsRestoringLocation && controlRestoresLocation) {
+            fDialogSettings.put(STORE_LOCATION_X, bounds.x);
+            fDialogSettings.put(STORE_LOCATION_Y, bounds.y);
+        }
+    }
+    /**
+     * Restores the information control's bounds.
+     *
+     * @return the stored bounds
+     * @since 3.0
+     */
+    protected Rectangle restoreInformationControlBounds() {
+        if (fDialogSettings is null || !(fIsRestoringLocation || fIsRestoringSize))
+            return null;
+
+        if (!( cast(IInformationControlExtension3)fInformationControl ))
+            throw new UnsupportedOperationException();
+
+        bool controlRestoresSize= (cast(IInformationControlExtension3)fInformationControl).restoresSize();
+        bool controlRestoresLocation= (cast(IInformationControlExtension3)fInformationControl).restoresLocation();
+
+        Rectangle bounds= new Rectangle(-1, -1, -1, -1);
+
+        if (fIsRestoringSize && controlRestoresSize) {
+            try {
+                bounds.width= fDialogSettings.getInt(STORE_SIZE_WIDTH);
+                bounds.height= fDialogSettings.getInt(STORE_SIZE_HEIGHT);
+            } catch (NumberFormatException ex) {
+                bounds.width= -1;
+                bounds.height= -1;
+            }
+        }
+
+        if (fIsRestoringLocation && controlRestoresLocation) {
+            try {
+                bounds.x= fDialogSettings.getInt(STORE_LOCATION_X);
+                bounds.y= fDialogSettings.getInt(STORE_LOCATION_Y);
+            } catch (NumberFormatException ex) {
+                bounds.x= -1;
+                bounds.y= -1;
+            }
+        }
+
+        // sanity check
+        if (bounds.x is -1 && bounds.y is -1 && bounds.width is -1 && bounds.height is -1)
+            return null;
+
+        Rectangle maxBounds= null;
+        if (fSubjectControl !is null && !fSubjectControl.isDisposed())
+            maxBounds= fSubjectControl.getDisplay().getBounds();
+        else {
+            // fallback
+            Display display= Display.getCurrent();
+            if (display is null)
+                display= Display.getDefault();
+            if (display !is null && !display.isDisposed())
+                maxBounds= display.getBounds();
+        }
+
+
+        if (bounds.width > -1 && bounds.height > -1) {
+            if (maxBounds !is null) {
+                bounds.width= Math.min(bounds.width, maxBounds.width);
+                bounds.height= Math.min(bounds.height, maxBounds.height);
+            }
+
+            // Enforce an absolute minimal size
+            bounds.width= Math.max(bounds.width, 30);
+            bounds.height= Math.max(bounds.height, 30);
+        }
+
+        if (bounds.x > -1 && bounds.y > -1 && maxBounds !is null) {
+            bounds.x= Math.max(bounds.x, maxBounds.x);
+            bounds.y= Math.max(bounds.y, maxBounds.y);
+
+            if (bounds .width > -1 && bounds.height > -1) {
+                bounds.x= Math.min(bounds.x, maxBounds.width - bounds.width);
+                bounds.y= Math.min(bounds.y, maxBounds.height - bounds.height);
+            }
+        }
+
+        return bounds;
+    }
+
+    /**
+     * Returns an adapter that gives access to internal methods.
+     * <p>
+     * <strong>Note:</strong> This method is not intended to be referenced or overridden by clients.</p>
+     *
+     * @return the replaceable information control accessor
+     * @since 3.4
+     * @noreference This method is not intended to be referenced by clients.
+     * @nooverride This method is not intended to be re-implemented or extended by clients.
+     */
+    public InternalAccessor getInternalAccessor() {
+        return new MyInternalAccessor(this);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractReusableInformationControlCreator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,228 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.AbstractReusableInformationControlCreator;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+
+
+
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+
+
+/**
+ * Abstract class for a reusable information control creators.
+ *
+ * @since 3.3
+ */
+public abstract class AbstractReusableInformationControlCreator : IInformationControlCreator, IInformationControlCreatorExtension, DisposeListener {
+
+    private Map fInformationControls;
+
+    /**
+     * Creates the control.
+     *
+     * @param parent the parent shell
+     * @return the created information control
+     */
+    protected abstract IInformationControl doCreateInformationControl(Shell parent);
+
+    this(){
+        fInformationControls= new HashMap();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlCreator#createInformationControl(org.eclipse.swt.widgets.Shell)
+     */
+    public IInformationControl createInformationControl(Shell parent) {
+        IInformationControl control= cast(IInformationControl)fInformationControls.get(parent);
+        if (control is null) {
+            control= doCreateInformationControl(parent);
+            control.addDisposeListener(this);
+            fInformationControls.put(parent, cast(Object)control);
+        }
+        return control;
+    }
+
+    /*
+     * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
+     */
+    public void widgetDisposed(DisposeEvent e) {
+        Composite parent= null;
+        if (cast(Shell)e.widget )
+            parent= (cast(Shell)e.widget).getParent();
+        if ( cast(Shell)parent )
+            fInformationControls.remove(parent);
+    }
+
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlCreatorExtension#canReuse(org.eclipse.jface.text.IInformationControl)
+     */
+    public bool canReuse(IInformationControl control) {
+        return fInformationControls.containsValue(cast(Object)control);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlCreatorExtension#canReplace(org.eclipse.jface.text.IInformationControlCreator)
+     */
+    public bool canReplace(IInformationControlCreator creator) {
+        return creator.classinfo is this.classinfo;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/CursorLinePainter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,407 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.CursorLinePainter;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+import org.eclipse.swt.custom.LineBackgroundEvent;
+import org.eclipse.swt.custom.LineBackgroundListener;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+
+
+/**
+ * A painter the draws the background of the caret line in a configured color.
+ * <p>
+ * Clients usually instantiate and configure object of this class.</p>
+ * <p>
+ * This class is not intended to be subclassed.</p>
+ *
+ * @since 2.1
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class CursorLinePainter : IPainter, LineBackgroundListener {
+
+    /** The viewer the painter works on */
+    private const ITextViewer fViewer;
+    /** The cursor line back ground color */
+    private Color fHighlightColor;
+    /** The paint position manager for managing the line coordinates */
+    private IPaintPositionManager fPositionManager;
+
+    /** Keeps track of the line to be painted */
+    private Position fCurrentLine;
+    /** Keeps track of the line to be cleared */
+    private Position fLastLine;
+    /** Keeps track of the line number of the last painted line */
+    private int fLastLineNumber= -1;
+    /** Indicates whether this painter is active */
+    private bool fIsActive;
+
+    /**
+     * Creates a new painter for the given source viewer.
+     *
+     * @param textViewer the source viewer for which to create a painter
+     */
+    public this(ITextViewer textViewer) {
+        fCurrentLine= new Position(0, 0);
+        fLastLine= new Position(0, 0);
+        fViewer= textViewer;
+    }
+
+    /**
+     * Sets the color in which to draw the background of the cursor line.
+     *
+     * @param highlightColor the color in which to draw the background of the cursor line
+     */
+    public void setHighlightColor(Color highlightColor) {
+        fHighlightColor= highlightColor;
+    }
+
+    /*
+     * @see LineBackgroundListener#lineGetBackground(LineBackgroundEvent)
+     */
+    public void lineGetBackground(LineBackgroundEvent event) {
+        // don't use cached line information because of asynchronous painting
+
+        StyledText textWidget= fViewer.getTextWidget();
+        if (textWidget !is null) {
+
+            int caret= textWidget.getCaretOffset();
+            int length= event.lineText.length();
+
+            if (event.lineOffset <= caret && caret <= event.lineOffset + length)
+                event.lineBackground= fHighlightColor;
+            else
+                event.lineBackground= textWidget.getBackground();
+        }
+    }
+
+    /**
+     * Updates all the cached information about the lines to be painted and to be cleared. Returns <code>true</code>
+     * if the line number of the cursor line has changed.
+     *
+     * @return <code>true</code> if cursor line changed
+     */
+    private bool updateHighlightLine() {
+        try {
+
+            IDocument document= fViewer.getDocument();
+            int modelCaret= getModelCaret();
+            int lineNumber= document.getLineOfOffset(modelCaret);
+
+            // redraw if the current line number is different from the last line number we painted
+            // initially fLastLineNumber is -1
+            if (lineNumber !is fLastLineNumber || !fCurrentLine.overlapsWith(modelCaret, 0)) {
+
+                fLastLine.offset= fCurrentLine.offset;
+                fLastLine.length= fCurrentLine.length;
+                fLastLine.isDeleted_= fCurrentLine.isDeleted_;
+
+                if (fCurrentLine.isDeleted_) {
+                    fCurrentLine.isDeleted_= false;
+                    fPositionManager.managePosition(fCurrentLine);
+                }
+
+                fCurrentLine.offset= document.getLineOffset(lineNumber);
+                if (lineNumber is document.getNumberOfLines() - 1)
+                    fCurrentLine.length= document.getLength() - fCurrentLine.offset;
+                else
+                    fCurrentLine.length= document.getLineOffset(lineNumber + 1) - fCurrentLine.offset;
+
+                fLastLineNumber= lineNumber;
+                return true;
+
+            }
+
+        } catch (BadLocationException e) {
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the location of the caret as offset in the source viewer's
+     * input document.
+     *
+     * @return the caret location
+     */
+    private int getModelCaret() {
+        int widgetCaret= fViewer.getTextWidget().getCaretOffset();
+        if ( cast(ITextViewerExtension5)fViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) fViewer;
+            return extension.widgetOffset2ModelOffset(widgetCaret);
+        }
+        IRegion visible= fViewer.getVisibleRegion();
+        return widgetCaret + visible.getOffset();
+    }
+
+    /**
+     * Assumes the given position to specify offset and length of a line to be painted.
+     *
+     * @param position the specification of the line  to be painted
+     */
+    private void drawHighlightLine(Position position) {
+
+        // if the position that is about to be drawn was deleted then we can't
+        if (position.isDeleted())
+            return;
+
+        int widgetOffset= 0;
+        if ( cast(ITextViewerExtension5)fViewer ) {
+
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) fViewer;
+            widgetOffset= extension.modelOffset2WidgetOffset(position.getOffset());
+            if (widgetOffset is -1)
+                return;
+
+        } else {
+
+            IRegion visible= fViewer.getVisibleRegion();
+            widgetOffset= position.getOffset() - visible.getOffset();
+            if (widgetOffset < 0 || visible.getLength() < widgetOffset )
+                return;
+        }
+
+        StyledText textWidget= fViewer.getTextWidget();
+        // check for https://bugs.eclipse.org/bugs/show_bug.cgi?id=64898
+        // this is a guard against the symptoms but not the actual solution
+        if (0 <= widgetOffset && widgetOffset <= textWidget.getCharCount()) {
+            Point upperLeft= textWidget.getLocationAtOffset(widgetOffset);
+            int width= textWidget.getClientArea().width + textWidget.getHorizontalPixel();
+            int height= textWidget.getLineHeight(widgetOffset);
+            textWidget.redraw(0, upperLeft.y, width, height, false);
+        }
+    }
+
+    /*
+     * @see IPainter#deactivate(bool)
+     */
+    public void deactivate(bool redraw) {
+        if (fIsActive) {
+            fIsActive= false;
+
+            /* on turning off the feature one has to paint the currently
+             * highlighted line with the standard background color
+             */
+            if (redraw)
+                drawHighlightLine(fCurrentLine);
+
+            fViewer.getTextWidget().removeLineBackgroundListener(this);
+
+            if (fPositionManager !is null)
+                fPositionManager.unmanagePosition(fCurrentLine);
+
+            fLastLineNumber= -1;
+            fCurrentLine.offset= 0;
+            fCurrentLine.length= 0;
+        }
+    }
+
+    /*
+     * @see IPainter#dispose()
+     */
+    public void dispose() {
+    }
+
+    /*
+     * @see IPainter#paint(int)
+     */
+    public void paint(int reason) {
+        if (fViewer.getDocument() is null) {
+            deactivate(false);
+            return;
+        }
+
+        StyledText textWidget= fViewer.getTextWidget();
+
+        // check selection
+        Point selection= textWidget.getSelection();
+        int startLine= textWidget.getLineAtOffset(selection.x);
+        int endLine= textWidget.getLineAtOffset(selection.y);
+        if (startLine !is endLine) {
+            deactivate(true);
+            return;
+        }
+
+        // initialization
+        if (!fIsActive) {
+            textWidget.addLineBackgroundListener(this);
+            fPositionManager.managePosition(fCurrentLine);
+            fIsActive= true;
+        }
+
+        //redraw line highlight only if it hasn't been drawn yet on the respective line
+        if (updateHighlightLine()) {
+            // clear last line
+            drawHighlightLine(fLastLine);
+            // draw new line
+            drawHighlightLine(fCurrentLine);
+        }
+    }
+
+    /*
+     * @see IPainter#setPositionManager(IPaintPositionManager)
+     */
+    public void setPositionManager(IPaintPositionManager manager) {
+        fPositionManager = manager;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultAutoIndentStrategy.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.DefaultAutoIndentStrategy;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Default implementation of {@link org.eclipse.jface.text.IAutoIndentStrategy}.
+ * This strategy always copies the indentation of the previous line.
+ * <p>
+ * This class is not intended to be subclassed.</p>
+ *
+ * @deprecated since 3.1 use {@link org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy} instead
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class DefaultAutoIndentStrategy : DefaultIndentLineAutoEditStrategy , IAutoIndentStrategy {
+
+    /**
+     * Creates a new default auto indent strategy.
+     */
+    public this() {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultDocumentAdapter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,587 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.DefaultDocumentAdapter;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.TextChangeListener;
+import org.eclipse.swt.custom.TextChangedEvent;
+import org.eclipse.swt.custom.TextChangingEvent;
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * Default implementation of {@link org.eclipse.jface.text.IDocumentAdapter}.
+ * <p>
+ * <strong>Note:</strong> This adapter does not work if the widget auto-wraps the text.
+ * </p>
+ */
+class DefaultDocumentAdapter : IDocumentAdapter, IDocumentListener, IDocumentAdapterExtension {
+
+    /** The adapted document. */
+    private IDocument fDocument;
+    /** The document clone for the non-forwarding case. */
+    private IDocument fDocumentClone;
+    /** The original content */
+    private String fOriginalContent;
+    /** The original line delimiters */
+    private String[] fOriginalLineDelimiters;
+    /** The registered text change listeners */
+    private List fTextChangeListeners;
+    /**
+     * The remembered document event
+     * @since 2.0
+     */
+    private DocumentEvent fEvent;
+    /** The line delimiter */
+    private String fLineDelimiter= null;
+    /**
+     * Indicates whether this adapter is forwarding document changes
+     * @since 2.0
+     */
+    private bool fIsForwarding= true;
+    /**
+     * Length of document at receipt of <code>documentAboutToBeChanged</code>
+     * @since 2.1
+     */
+    private int fRememberedLengthOfDocument;
+    /**
+     * Length of first document line at receipt of <code>documentAboutToBeChanged</code>
+     * @since 2.1
+     */
+    private int fRememberedLengthOfFirstLine;
+    /**
+     * The data of the event at receipt of <code>documentAboutToBeChanged</code>
+     * @since 2.1
+     */
+    private  DocumentEvent fOriginalEvent;
+
+
+    /**
+     * Creates a new document adapter which is initially not connected to
+     * any document.
+     */
+    public this() {
+        fTextChangeListeners= new ArrayList(1);
+        fOriginalEvent= new DocumentEvent();
+    }
+
+    /**
+     * Sets the given document as the document to be adapted.
+     *
+     * @param document the document to be adapted or <code>null</code> if there is no document
+     */
+    public void setDocument(IDocument document) {
+
+        if (fDocument !is null)
+            fDocument.removePrenotifiedDocumentListener(this);
+
+        fDocument= document;
+        fLineDelimiter= null;
+
+        if (!fIsForwarding) {
+            fDocumentClone= null;
+            if (fDocument !is null) {
+                fOriginalContent= fDocument.get();
+                fOriginalLineDelimiters= fDocument.getLegalLineDelimiters();
+            } else {
+                fOriginalContent= null;
+                fOriginalLineDelimiters= null;
+            }
+        }
+
+        if (fDocument !is null)
+            fDocument.addPrenotifiedDocumentListener(this);
+    }
+
+    /*
+     * @see StyledTextContent#addTextChangeListener(TextChangeListener)
+     */
+    public void addTextChangeListener(TextChangeListener listener) {
+        Assert.isNotNull(cast(Object)listener);
+        if (!fTextChangeListeners.contains(cast(Object)listener))
+            fTextChangeListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * @see StyledTextContent#removeTextChangeListener(TextChangeListener)
+     */
+    public void removeTextChangeListener(TextChangeListener listener) {
+        Assert.isNotNull(cast(Object)listener);
+        fTextChangeListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Tries to repair the line information.
+     *
+     * @param document the document
+     * @see IRepairableDocument#repairLineInformation()
+     * @since 3.0
+     */
+    private void repairLineInformation(IDocument document) {
+        if ( cast(IRepairableDocument)document ) {
+            IRepairableDocument repairable= cast(IRepairableDocument) document;
+            repairable.repairLineInformation();
+        }
+    }
+
+    /**
+     * Returns the line for the given line number.
+     *
+     * @param document the document
+     * @param line the line number
+     * @return the content of the line of the given number in the given document
+     * @throws BadLocationException if the line number is invalid for the adapted document
+     * @since 3.0
+     */
+    private String doGetLine(IDocument document, int line)  {
+        IRegion r= document.getLineInformation(line);
+        return document.get(r.getOffset(), r.getLength());
+    }
+
+    private IDocument getDocumentForRead() {
+        if (!fIsForwarding) {
+            if (fDocumentClone is null) {
+                String content= fOriginalContent is null ? "" : fOriginalContent; //$NON-NLS-1$
+                String[] delims= fOriginalLineDelimiters is null ? DefaultLineTracker.DELIMITERS : fOriginalLineDelimiters;
+                fDocumentClone= new DocumentClone(content, delims);
+            }
+            return fDocumentClone;
+        }
+
+        return fDocument;
+    }
+
+    /*
+     * @see StyledTextContent#getLine(int)
+     */
+    public String getLine(int line) {
+
+        IDocument document= getDocumentForRead();
+        try {
+            return doGetLine(document, line);
+        } catch (BadLocationException x) {
+            repairLineInformation(document);
+            try {
+                return doGetLine(document, line);
+            } catch (BadLocationException x2) {
+            }
+        }
+
+        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+        return null;
+    }
+
+    /*
+     * @see StyledTextContent#getLineAtOffset(int)
+     */
+    public int getLineAtOffset(int offset) {
+        IDocument document= getDocumentForRead();
+        try {
+            return document.getLineOfOffset(offset);
+        } catch (BadLocationException x) {
+            repairLineInformation(document);
+            try {
+                return document.getLineOfOffset(offset);
+            } catch (BadLocationException x2) {
+            }
+        }
+
+        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+        return -1;
+    }
+
+    /*
+     * @see StyledTextContent#getLineCount()
+     */
+    public int getLineCount() {
+        return getDocumentForRead().getNumberOfLines();
+    }
+
+    /*
+     * @see StyledTextContent#getOffsetAtLine(int)
+     */
+    public int getOffsetAtLine(int line) {
+        IDocument document= getDocumentForRead();
+        try {
+            return document.getLineOffset(line);
+        } catch (BadLocationException x) {
+            repairLineInformation(document);
+            try {
+                return document.getLineOffset(line);
+            } catch (BadLocationException x2) {
+            }
+        }
+
+        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+        return -1;
+    }
+
+    /*
+     * @see StyledTextContent#getTextRange(int, int)
+     */
+    public String getTextRange(int offset, int length) {
+        try {
+            return getDocumentForRead().get(offset, length);
+        } catch (BadLocationException x) {
+            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+            return null;
+        }
+    }
+
+    /*
+     * @see StyledTextContent#replaceTextRange(int, int, String)
+     */
+    public void replaceTextRange(int pos, int length, String text) {
+        try {
+            fDocument.replace(pos, length, text);
+        } catch (BadLocationException x) {
+            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+        }
+    }
+
+    /*
+     * @see StyledTextContent#setText(String)
+     */
+    public void setText(String text) {
+        fDocument.set(text);
+    }
+
+    /*
+     * @see StyledTextContent#getCharCount()
+     */
+    public int getCharCount() {
+        return getDocumentForRead().getLength();
+    }
+
+    /*
+     * @see StyledTextContent#getLineDelimiter()
+     */
+    public String getLineDelimiter() {
+        if (fLineDelimiter is null)
+            fLineDelimiter= TextUtilities.getDefaultLineDelimiter(fDocument);
+        return fLineDelimiter;
+    }
+
+    /*
+     * @see IDocumentListener#documentChanged(DocumentEvent)
+     */
+    public void documentChanged(DocumentEvent event) {
+        // check whether the given event is the one which was remembered
+        if (fEvent is null || event !is fEvent)
+            return;
+
+        if (isPatchedEvent(event) || (event.getOffset() is 0 && event.getLength() is fRememberedLengthOfDocument)) {
+            fLineDelimiter= null;
+            fireTextSet();
+        } else {
+            if (event.getOffset() < fRememberedLengthOfFirstLine)
+                fLineDelimiter= null;
+            fireTextChanged();
+        }
+    }
+
+    /*
+     * @see IDocumentListener#documentAboutToBeChanged(DocumentEvent)
+     */
+    public void documentAboutToBeChanged(DocumentEvent event) {
+
+        fRememberedLengthOfDocument= fDocument.getLength();
+        try {
+            fRememberedLengthOfFirstLine= fDocument.getLineLength(0);
+        } catch (BadLocationException e) {
+            fRememberedLengthOfFirstLine= -1;
+        }
+
+        fEvent= event;
+        rememberEventData(fEvent);
+        fireTextChanging();
+    }
+
+    /**
+     * Checks whether this event has been changed between <code>documentAboutToBeChanged</code> and
+     * <code>documentChanged</code>.
+     *
+     * @param event the event to be checked
+     * @return <code>true</code> if the event has been changed, <code>false</code> otherwise
+     */
+    private bool isPatchedEvent(DocumentEvent event) {
+        return fOriginalEvent.fOffset !is event.fOffset || fOriginalEvent.fLength !is event.fLength || fOriginalEvent.fText !is event.fText;
+    }
+
+    /**
+     * Makes a copy of the given event and remembers it.
+     *
+     * @param event the event to be copied
+     */
+    private void rememberEventData(DocumentEvent event) {
+        fOriginalEvent.fOffset= event.fOffset;
+        fOriginalEvent.fLength= event.fLength;
+        fOriginalEvent.fText= event.fText;
+    }
+
+    /**
+     * Sends a text changed event to all registered listeners.
+     */
+    private void fireTextChanged() {
+
+        if (!fIsForwarding)
+            return;
+
+        TextChangedEvent event= new TextChangedEvent(this);
+
+        if (fTextChangeListeners !is null && fTextChangeListeners.size() > 0) {
+            Iterator e= (new ArrayList(fTextChangeListeners)).iterator();
+            while (e.hasNext())
+                (cast(TextChangeListener) e.next()).textChanged(event);
+        }
+    }
+
+    /**
+     * Sends a text set event to all registered listeners.
+     */
+    private void fireTextSet() {
+
+        if (!fIsForwarding)
+            return;
+
+        TextChangedEvent event = new TextChangedEvent(this);
+
+        if (fTextChangeListeners !is null && fTextChangeListeners.size() > 0) {
+            Iterator e= (new ArrayList(fTextChangeListeners)).iterator();
+            while (e.hasNext())
+                (cast(TextChangeListener) e.next()).textSet(event);
+        }
+    }
+
+    /**
+     * Sends the text changing event to all registered listeners.
+     */
+    private void fireTextChanging() {
+
+        if (!fIsForwarding)
+            return;
+
+        try {
+            IDocument document= fEvent.getDocument();
+            if (document is null)
+                return;
+
+            TextChangingEvent event= new TextChangingEvent(this);
+            event.start= fEvent.fOffset;
+            event.replaceCharCount= fEvent.fLength;
+            event.replaceLineCount= document.getNumberOfLines(fEvent.fOffset, fEvent.fLength) - 1;
+            event.newText= fEvent.fText;
+            event.newCharCount= (fEvent.fText is null ? 0 : fEvent.fText.length());
+            event.newLineCount= (fEvent.fText is null ? 0 : document.computeNumberOfLines(fEvent.fText));
+
+            if (fTextChangeListeners !is null && fTextChangeListeners.size() > 0) {
+                Iterator e= (new ArrayList(fTextChangeListeners)).iterator();
+                while (e.hasNext())
+                     (cast(TextChangeListener) e.next()).textChanging(event);
+            }
+
+        } catch (BadLocationException e) {
+        }
+    }
+
+    /*
+     * @see IDocumentAdapterExtension#resumeForwardingDocumentChanges()
+     * @since 2.0
+     */
+    public void resumeForwardingDocumentChanges() {
+        fIsForwarding= true;
+        fDocumentClone= null;
+        fOriginalContent= null;
+        fOriginalLineDelimiters= null;
+        fireTextSet();
+    }
+
+    /*
+     * @see IDocumentAdapterExtension#stopForwardingDocumentChanges()
+     * @since 2.0
+     */
+    public void stopForwardingDocumentChanges() {
+        fDocumentClone= null;
+        fOriginalContent= fDocument.get();
+        fOriginalLineDelimiters= fDocument.getLegalLineDelimiters();
+        fIsForwarding= false;
+    }
+
+    /++
+     + SWT extension
+     +/
+    public int utf8AdjustOffset( int offset ){
+        if (fDocument is null)
+            return offset;
+        if (offset is 0)
+            return offset;
+        if( offset >= fDocument.getLength() ){
+            return offset;
+        }
+        while( fDocument.getChar(offset) & 0xC0 is 0x80 && offset > 0 ){
+            offset--;
+        }
+        return offset;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultIndentLineAutoEditStrategy.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * This strategy always copies the indentation of the previous line.
+ * <p>
+ * This class is not intended to be subclassed.</p>
+ *
+ * @since 3.1
+ */
+public class DefaultIndentLineAutoEditStrategy : IAutoEditStrategy {
+
+    /**
+     * Creates a new indent line auto edit strategy which can be installed on
+     * text viewers.
+     */
+    public this() {
+    }
+
+    /**
+     * Returns the first offset greater than <code>offset</code> and smaller than
+     * <code>end</code> whose character is not a space or tab character. If no such
+     * offset is found, <code>end</code> is returned.
+     *
+     * @param document the document to search in
+     * @param offset the offset at which searching start
+     * @param end the offset at which searching stops
+     * @return the offset in the specified range whose character is not a space or tab
+     * @exception BadLocationException if position is an invalid range in the given document
+     */
+    protected int findEndOfWhiteSpace(IDocument document, int offset, int end)  {
+        while (offset < end) {
+            char c= document.getChar(offset);
+            if (c !is ' ' && c !is '\t') {
+                return offset;
+            }
+            offset++;
+        }
+        return end;
+    }
+
+    /**
+     * Copies the indentation of the previous line.
+     *
+     * @param d the document to work on
+     * @param c the command to deal with
+     */
+    private void autoIndentAfterNewLine(IDocument d, DocumentCommand c) {
+
+        if (c.offset is -1 || d.getLength() is 0)
+            return;
+
+        try {
+            // find start of line
+            int p= (c.offset is d.getLength() ? c.offset  - 1 : c.offset);
+            IRegion info= d.getLineInformationOfOffset(p);
+            int start= info.getOffset();
+
+            // find white spaces
+            int end= findEndOfWhiteSpace(d, start, c.offset);
+
+            StringBuffer buf= new StringBuffer(c.text);
+            if (end > start) {
+                // append to input
+                buf.append(d.get(start, end - start));
+            }
+
+            c.text= buf.toString();
+
+        } catch (BadLocationException excp) {
+            // stop work
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IAutoEditStrategy#customizeDocumentCommand(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.DocumentCommand)
+     */
+    public void customizeDocumentCommand(IDocument d, DocumentCommand c) {
+        if (c.length is 0 && c.text !is null && TextUtilities.endsWith(d.getLegalLineDelimiters(), c.text) !is -1)
+            autoIndentAfterNewLine(d, c);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultInformationControl.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,585 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.DefaultInformationControl;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Drawable;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.internal.text.html.HTMLTextPresenter;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.Geometry;
+
+    /**
+     * An information presenter determines the style presentation
+     * of information displayed in the default information control.
+     * The interface can be implemented by clients.
+     */
+    public interface IInformationPresenter {
+
+        /**
+         * Updates the given presentation of the given information and
+         * thereby may manipulate the information to be displayed. The manipulation
+         * could be the extraction of textual encoded style information etc. Returns the
+         * manipulated information.
+         * <p>
+         * <strong>Note:</strong> The given display must only be used for measuring.</p>
+         *
+         * @param display the display of the information control
+         * @param hoverInfo the information to be presented
+         * @param presentation the presentation to be updated
+         * @param maxWidth the maximal width in pixels
+         * @param maxHeight the maximal height in pixels
+         *
+         * @return the manipulated information
+         * @deprecated As of 3.2, replaced by {@link DefaultInformationControl.IInformationPresenterExtension#updatePresentation(Drawable, String, TextPresentation, int, int)}
+         */
+        String updatePresentation(Display display, String hoverInfo, TextPresentation presentation, int maxWidth, int maxHeight);
+    }
+    alias IInformationPresenter DefaultInformationControl_IInformationPresenter;
+
+    /**
+     * An information presenter determines the style presentation
+     * of information displayed in the default information control.
+     * The interface can be implemented by clients.
+     *
+     * @since 3.2
+     */
+    public interface IInformationPresenterExtension {
+
+        /**
+         * Updates the given presentation of the given information and
+         * thereby may manipulate the information to be displayed. The manipulation
+         * could be the extraction of textual encoded style information etc. Returns the
+         * manipulated information.
+         * <p>
+         * Replaces {@link DefaultInformationControl.IInformationPresenter#updatePresentation(Display, String, TextPresentation, int, int)}
+         * Implementations should use the font of the given <code>drawable</code> to calculate
+         * the size of the text to be presented.
+         * </p>
+         *
+         * @param drawable the drawable of the information control
+         * @param hoverInfo the information to be presented
+         * @param presentation the presentation to be updated
+         * @param maxWidth the maximal width in pixels
+         * @param maxHeight the maximal height in pixels
+         *
+         * @return the manipulated information
+         */
+        String updatePresentation(Drawable drawable, String hoverInfo, TextPresentation presentation, int maxWidth, int maxHeight);
+    }
+    alias IInformationPresenterExtension DefaultInformationControl_IInformationPresenterExtension;
+
+
+/**
+ * Default implementation of {@link org.eclipse.jface.text.IInformationControl}.
+ * <p>
+ * Displays textual information in a {@link org.eclipse.swt.custom.StyledText}
+ * widget. Before displaying, the information set to this information control is
+ * processed by an <code>IInformationPresenter</code>.
+ *
+ * @since 2.0
+ */
+public class DefaultInformationControl : AbstractInformationControl , DisposeListener {
+
+    /**
+     * Inner border thickness in pixels.
+     * @since 3.1
+     */
+    private static const int INNER_BORDER= 1;
+
+    /** The control's text widget */
+    private StyledText fText;
+    /** The information presenter, or <code>null</code> if none. */
+    private const IInformationPresenter fPresenter;
+    /** A cached text presentation */
+    private const TextPresentation fPresentation;
+
+    /**
+     * Additional styles to use for the text control.
+     * @since 3.4, previously called <code>fTextStyle</code>
+     */
+    private const int fAdditionalTextStyles;
+
+    /**
+     * Creates a default information control with the given shell as parent. An information
+     * presenter that can handle simple HTML is used to process the information to be displayed.
+     *
+     * @param parent the parent shell
+     * @param isResizeable <code>true</code> if the control should be resizable
+     * @since 3.4
+     */
+    public this(Shell parent, bool isResizeable) {
+        fPresentation= new TextPresentation();
+        super(parent, isResizeable);
+        fAdditionalTextStyles= isResizeable ? SWT.V_SCROLL | SWT.H_SCROLL : SWT.NONE;
+        fPresenter= new HTMLTextPresenter(!isResizeable);
+        create();
+    }
+
+    /**
+     * Creates a default information control with the given shell as parent. An information
+     * presenter that can handle simple HTML is used to process the information to be displayed.
+     *
+     * @param parent the parent shell
+     * @param statusFieldText the text to be used in the status field or <code>null</code> to hide the status field
+     * @since 3.4
+     */
+    public this(Shell parent, String statusFieldText) {
+        this(parent, statusFieldText, new HTMLTextPresenter(true));
+    }
+
+    /**
+     * Creates a default information control with the given shell as parent. The
+     * given information presenter is used to process the information to be
+     * displayed.
+     *
+     * @param parent the parent shell
+     * @param statusFieldText the text to be used in the status field or <code>null</code> to hide the status field
+     * @param presenter the presenter to be used, or <code>null</code> if no presenter should be used
+     * @since 3.4
+     */
+    public this(Shell parent, String statusFieldText, IInformationPresenter presenter) {
+        fPresentation= new TextPresentation();
+        super(parent, statusFieldText);
+        fAdditionalTextStyles= SWT.NONE;
+        fPresenter= presenter;
+        create();
+    }
+
+    /**
+     * Creates a resizable default information control with the given shell as parent. An
+     * information presenter that can handle simple HTML is used to process the information to be
+     * displayed.
+     *
+     * @param parent the parent shell
+     * @param toolBarManager the manager or <code>null</code> if toolbar is not desired
+     * @since 3.4
+     */
+    public this(Shell parent, ToolBarManager toolBarManager) {
+        this(parent, toolBarManager, new HTMLTextPresenter(false));
+    }
+
+    /**
+     * Creates a resizable default information control with the given shell as
+     * parent. The given information presenter is used to process the
+     * information to be displayed.
+     *
+     * @param parent the parent shell
+     * @param toolBarManager the manager or <code>null</code> if toolbar is not desired
+     * @param presenter the presenter to be used, or <code>null</code> if no presenter should be used
+     * @since 3.4
+     */
+    public this(Shell parent, ToolBarManager toolBarManager, IInformationPresenter presenter) {
+        fPresentation= new TextPresentation();
+        super(parent, toolBarManager);
+        fAdditionalTextStyles= SWT.V_SCROLL | SWT.H_SCROLL;
+        fPresenter= presenter;
+        create();
+    }
+
+    /**
+     * Creates a default information control with the given shell as parent.
+     * No information presenter is used to process the information
+     * to be displayed.
+     *
+     * @param parent the parent shell
+     */
+    public this(Shell parent) {
+        this(parent, cast(String)null, null);
+    }
+
+    /**
+     * Creates a default information control with the given shell as parent. The given
+     * information presenter is used to process the information to be displayed.
+     *
+     * @param parent the parent shell
+     * @param presenter the presenter to be used
+     */
+    public this(Shell parent, IInformationPresenter presenter) {
+        this(parent, cast(String)null, presenter);
+    }
+
+    /**
+     * Creates a default information control with the given shell as parent. The
+     * given information presenter is used to process the information to be
+     * displayed. The given styles are applied to the created styled text
+     * widget.
+     *
+     * @param parent the parent shell
+     * @param shellStyle the additional styles for the shell
+     * @param style the additional styles for the styled text widget
+     * @param presenter the presenter to be used
+     * @deprecated As of 3.4, replaced by simpler constructors
+     */
+    public this(Shell parent, int shellStyle, int style, IInformationPresenter presenter) {
+        this(parent, shellStyle, style, presenter, null);
+    }
+
+    /**
+     * Creates a default information control with the given shell as parent. The
+     * given information presenter is used to process the information to be
+     * displayed. The given styles are applied to the created styled text
+     * widget.
+     *
+     * @param parentShell the parent shell
+     * @param shellStyle the additional styles for the shell
+     * @param style the additional styles for the styled text widget
+     * @param presenter the presenter to be used
+     * @param statusFieldText the text to be used in the status field or <code>null</code> to hide the status field
+     * @since 3.0
+     * @deprecated As of 3.4, replaced by simpler constructors
+     */
+    public this(Shell parentShell, int shellStyle, int style, IInformationPresenter presenter, String statusFieldText) {
+        fPresentation= new TextPresentation();
+        super(parentShell, SWT.NO_FOCUS | SWT.ON_TOP | shellStyle, statusFieldText, null);
+        fAdditionalTextStyles= style;
+        fPresenter= presenter;
+        create();
+    }
+
+    /**
+     * Creates a default information control with the given shell as parent. The
+     * given information presenter is used to process the information to be
+     * displayed.
+     *
+     * @param parent the parent shell
+     * @param textStyles the additional styles for the styled text widget
+     * @param presenter the presenter to be used
+     * @deprecated As of 3.4, replaced by {@link #DefaultInformationControl(Shell, DefaultInformationControl.IInformationPresenter)}
+     */
+    public this(Shell parent, int textStyles, IInformationPresenter presenter) {
+        this(parent, textStyles, presenter, null);
+    }
+
+    /**
+     * Creates a default information control with the given shell as parent. The
+     * given information presenter is used to process the information to be
+     * displayed.
+     *
+     * @param parent the parent shell
+     * @param textStyles the additional styles for the styled text widget
+     * @param presenter the presenter to be used
+     * @param statusFieldText the text to be used in the status field or <code>null</code> to hide the status field
+     * @since 3.0
+     * @deprecated As of 3.4, replaced by {@link #DefaultInformationControl(Shell, String, DefaultInformationControl.IInformationPresenter)}
+     */
+    public this(Shell parent, int textStyles, IInformationPresenter presenter, String statusFieldText) {
+        fPresentation= new TextPresentation();
+        super(parent, statusFieldText);
+        fAdditionalTextStyles= textStyles;
+        fPresenter= presenter;
+        create();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControl#createContent(org.eclipse.swt.widgets.Composite)
+     */
+    protected void createContent(Composite parent) {
+        fText= new StyledText(parent, SWT.MULTI | SWT.READ_ONLY | fAdditionalTextStyles);
+        fText.setForeground(parent.getForeground());
+        fText.setBackground(parent.getBackground());
+        fText.setFont(JFaceResources.getDialogFont());
+        FillLayout layout= cast(FillLayout)parent.getLayout();
+        if (fText.getWordWrap()) {
+            // indent does not work for wrapping StyledText, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=56342 and https://bugs.eclipse.org/bugs/show_bug.cgi?id=115432
+            layout.marginHeight= INNER_BORDER;
+            layout.marginWidth= INNER_BORDER;
+        } else {
+            fText.setIndent(INNER_BORDER);
+        }
+    }
+
+    /*
+     * @see IInformationControl#setInformation(String)
+     */
+    public void setInformation(String content) {
+        if (fPresenter is null) {
+            fText.setText(content);
+        } else {
+            fPresentation.clear();
+
+            int maxWidth= -1;
+            int maxHeight= -1;
+            Point constraints= getSizeConstraints();
+            if (constraints !is null) {
+                maxWidth= constraints.x;
+                maxHeight= constraints.y;
+                if (fText.getWordWrap()) {
+                    maxWidth-= INNER_BORDER * 2;
+                    maxHeight-= INNER_BORDER * 2;
+                } else {
+                    maxWidth-= INNER_BORDER; // indent
+                }
+                Rectangle trim= computeTrim();
+                maxWidth-= trim.width;
+                maxHeight-= trim.height;
+                maxWidth-= fText.getCaret().getSize().x; // StyledText adds a border at the end of the line for the caret.
+            }
+            if (isResizable())
+                maxHeight= Integer.MAX_VALUE;
+
+            if ( cast(IInformationPresenterExtension)fPresenter )
+                content= (cast(IInformationPresenterExtension)fPresenter).updatePresentation(fText, content, fPresentation, maxWidth, maxHeight);
+            else
+                content= fPresenter.updatePresentation(getShell().getDisplay(), content, fPresentation, maxWidth, maxHeight);
+
+            if (content !is null) {
+                fText.setText(content);
+                TextPresentation.applyTextPresentation(fPresentation, fText);
+            } else {
+                fText.setText(""); //$NON-NLS-1$
+            }
+        }
+    }
+
+    /*
+     * @see IInformationControl#setVisible(bool)
+     */
+    public void setVisible(bool visible) {
+        if (visible) {
+            if (fText.getWordWrap()) {
+                Point currentSize= getShell().getSize();
+                getShell().pack(true);
+                Point newSize= getShell().getSize();
+                if (newSize.x > currentSize.x || newSize.y > currentSize.y)
+                    setSize(currentSize.x, currentSize.y); // restore previous size
+            }
+        }
+
+        super.setVisible(visible);
+    }
+
+    /*
+     * @see IInformationControl#computeSizeHint()
+     */
+    public Point computeSizeHint() {
+        // see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=117602
+        int widthHint= SWT.DEFAULT;
+        Point constraints= getSizeConstraints();
+        if (constraints !is null && fText.getWordWrap())
+            widthHint= constraints.x;
+
+        return getShell().computeSize(widthHint, SWT.DEFAULT, true);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControl#computeTrim()
+     */
+    public Rectangle computeTrim() {
+        return Geometry.add(super.computeTrim(), fText.computeTrim(0, 0, 0, 0));
+    }
+
+    /*
+     * @see IInformationControl#setForegroundColor(Color)
+     */
+    public void setForegroundColor(Color foreground) {
+        super.setForegroundColor(foreground);
+        fText.setForeground(foreground);
+    }
+
+    /*
+     * @see IInformationControl#setBackgroundColor(Color)
+     */
+    public void setBackgroundColor(Color background) {
+        super.setBackgroundColor(background);
+        fText.setBackground(background);
+    }
+
+    /*
+     * @see IInformationControlExtension#hasContents()
+     */
+    public bool hasContents() {
+        return fText.getCharCount() > 0;
+    }
+
+    /**
+     * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
+     * @since 3.0
+     * @deprecated As of 3.2, no longer used and called
+     */
+    public void widgetDisposed(DisposeEvent event) {
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IInformationControlExtension5#getInformationPresenterControlCreator()
+     * @since 3.4
+     */
+    public IInformationControlCreator getInformationPresenterControlCreator() {
+        return new class()  IInformationControlCreator {
+            /*
+             * @see org.eclipse.jface.text.IInformationControlCreator#createInformationControl(org.eclipse.swt.widgets.Shell)
+             */
+            public IInformationControl createInformationControl(Shell parent) {
+                return new DefaultInformationControl(parent, cast(ToolBarManager) null, fPresenter);
+            }
+        };
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultTextDoubleClickStrategy.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,373 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.DefaultTextDoubleClickStrategy;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.dwtxhelper.CharacterIterator;
+
+import org.eclipse.dwtxhelper.mangoicu.UBreakIterator;
+
+/**
+ * Standard implementation of
+ * {@link org.eclipse.jface.text.ITextDoubleClickStrategy}.
+ * <p>
+ * Selects words using <code>java.text.UBreakIterator</code> for the default
+ * locale.</p>
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ *
+ * @see java.text.UBreakIterator
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class DefaultTextDoubleClickStrategy : ITextDoubleClickStrategy {
+
+/++
+    /**
+     * Implements a character iterator that works directly on
+     * instances of <code>IDocument</code>. Used to collaborate with
+     * the break iterator.
+     *
+     * @see IDocument
+     * @since 2.0
+     */
+    static class DocumentCharacterIterator : CharacterIterator {
+
+        /** Document to iterate over. */
+        private IDocument fDocument;
+        /** Start offset of iteration. */
+        private int fOffset= -1;
+        /** End offset of iteration. */
+        private int fEndOffset= -1;
+        /** Current offset of iteration. */
+        private int fIndex= -1;
+
+        /** Creates a new document iterator. */
+        public this() {
+        }
+
+        /**
+         * Configures this document iterator with the document section to be visited.
+         *
+         * @param document the document to be iterated
+         * @param iteratorRange the range in the document to be iterated
+         */
+        public void setDocument(IDocument document, IRegion iteratorRange) {
+            fDocument= document;
+            fOffset= iteratorRange.getOffset();
+            fEndOffset= fOffset + iteratorRange.getLength();
+        }
+
+        /*
+         * @see CharacterIterator#first()
+         */
+        public char first() {
+            fIndex= fOffset;
+            return current();
+        }
+
+        /*
+         * @see CharacterIterator#last()
+         */
+        public char last() {
+            fIndex= fOffset < fEndOffset ? fEndOffset -1 : fEndOffset;
+            return current();
+        }
+
+        /*
+         * @see CharacterIterator#current()
+         */
+        public char current() {
+            if (fOffset <= fIndex && fIndex < fEndOffset) {
+                try {
+                    return fDocument.getChar(fIndex);
+                } catch (BadLocationException x) {
+                }
+            }
+            return DONE;
+        }
+
+        /*
+         * @see CharacterIterator#next()
+         */
+        public char next() {
+            ++fIndex;
+            int end= getEndIndex();
+            if (fIndex >= end) {
+                fIndex= end;
+                return DONE;
+            }
+            return current();
+        }
+
+        /*
+         * @see CharacterIterator#previous()
+         */
+        public char previous() {
+            if (fIndex is fOffset)
+                return DONE;
+
+            if (fIndex > fOffset)
+                -- fIndex;
+
+            return current();
+        }
+
+        /*
+         * @see CharacterIterator#setIndex(int)
+         */
+        public char setIndex(int index) {
+            fIndex= index;
+            return current();
+        }
+
+        /*
+         * @see CharacterIterator#getBeginIndex()
+         */
+        public int getBeginIndex() {
+            return fOffset;
+        }
+
+        /*
+         * @see CharacterIterator#getEndIndex()
+         */
+        public int getEndIndex() {
+            return fEndOffset;
+        }
+
+        /*
+         * @see CharacterIterator#getIndex()
+         */
+        public int getIndex() {
+            return fIndex;
+        }
+
+        /*
+         * @see CharacterIterator#clone()
+         */
+        public Object clone() {
+            DocumentCharacterIterator i= new DocumentCharacterIterator();
+            i.fDocument= fDocument;
+            i.fIndex= fIndex;
+            i.fOffset= fOffset;
+            i.fEndOffset= fEndOffset;
+            return i;
+        }
+    }
+++/
+
+    /**
+     * The document character iterator used by this strategy.
+     * @since 2.0
+     */
+//     private DocumentCharacterIterator fDocIter= new DocumentCharacterIterator();
+
+
+    /**
+     * Creates a new default text double click strategy.
+     */
+    public this() {
+//         super();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextDoubleClickStrategy#doubleClicked(org.eclipse.jface.text.ITextViewer)
+     */
+    public void doubleClicked(ITextViewer text) {
+
+        int position= text.getSelectedRange().x;
+
+        if (position < 0)
+            return;
+
+        try {
+
+            IDocument document= text.getDocument();
+            IRegion line= document.getLineInformationOfOffset(position);
+            if (position is line.getOffset() + line.getLength())
+                return;
+
+            //mangoicu
+//             fDocIter.setDocument(document, line);
+            String strLine = document.get( line.getOffset(), line.getLength() );
+            UBreakIterator breakIter= UBreakIterator.openWordIterator( ULocale.Default, strLine/+fDocIter+/ );
+
+
+            //int start= breakIter.preceding(position);
+            int start= breakIter.previous(position); // mangoicu
+            if (start is UBreakIterator.DONE)
+                start= line.getOffset();
+
+            int end= breakIter.following(position);
+            if (end is UBreakIterator.DONE)
+                end= line.getOffset() + line.getLength();
+
+            if (breakIter.isBoundary(position)) {
+                if (end - position > position- start)
+                    start= position;
+                else
+                    end= position;
+            }
+
+            if (start !is end)
+                text.setSelectedRange(start, end - start);
+
+        } catch (BadLocationException x) {
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultTextHover.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,284 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.DefaultTextHover;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.text.source.ISourceViewerExtension2;
+
+/**
+ * Standard implementation of {@link org.eclipse.jface.text.ITextHover}.
+ *
+ * @since 3.2
+ */
+public class DefaultTextHover : ITextHover {
+
+    /** This hover's source viewer */
+    private ISourceViewer fSourceViewer;
+
+    /**
+     * Creates a new annotation hover.
+     *
+     * @param sourceViewer this hover's annotation model
+     */
+    public this(ISourceViewer sourceViewer)  {
+        org.eclipse.core.runtime.Assert.Assert.isNotNull(cast(Object)sourceViewer);
+        fSourceViewer= sourceViewer;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextHover#getHoverInfo(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion)
+     */
+    public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) {
+        IAnnotationModel model= getAnnotationModel(fSourceViewer);
+        if (model is null)
+            return null;
+
+        Iterator e= model.getAnnotationIterator();
+        while (e.hasNext()) {
+            Annotation a= cast(Annotation) e.next();
+            if (isIncluded(a)) {
+                Position p= model.getPosition(a);
+                if (p !is null && p.overlapsWith(hoverRegion.getOffset(), hoverRegion.getLength())) {
+                    String msg= a.getText();
+                    if (msg !is null && msg.trim().length() > 0)
+                        return msg;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextHover#getHoverRegion(org.eclipse.jface.text.ITextViewer, int)
+     */
+    public IRegion getHoverRegion(ITextViewer textViewer, int offset) {
+        return findWord(textViewer.getDocument(), offset);
+    }
+
+    /**
+     * Tells whether the annotation should be included in
+     * the computation.
+     *
+     * @param annotation the annotation to test
+     * @return <code>true</code> if the annotation is included in the computation
+     */
+    protected bool isIncluded(Annotation annotation) {
+        return true;
+    }
+
+    private IAnnotationModel getAnnotationModel(ISourceViewer viewer) {
+        if (cast(ISourceViewerExtension2)viewer ) {
+            ISourceViewerExtension2 extension= cast(ISourceViewerExtension2) viewer;
+            return extension.getVisualAnnotationModel();
+        }
+        return viewer.getAnnotationModel();
+    }
+
+    private IRegion findWord(IDocument document, int offset) {
+        int start= -2;
+        int end= -1;
+
+implMissing(__FILE__,__LINE__);
+// SWT FIXME: unicode
+/+
+        try {
+            int pos= offset;
+            wchar c;
+
+            while (pos >= 0) {
+                c= document.getChar(pos);
+                if (!Character.isUnicodeIdentifierPart(c))
+                    break;
+                --pos;
+            }
+
+            start= pos;
+
+            pos= offset;
+            int length= document.getLength();
+
+            while (pos < length) {
+                c= document.getChar(pos);
+                if (!Character.isUnicodeIdentifierPart(c))
+                    break;
+                ++pos;
+            }
+
+            end= pos;
+
+        } catch (BadLocationException x) {
+        }
+
+        if (start >= -1 && end > -1) {
+            if (start is offset && end is offset)
+                return new Region(offset, 0);
+            else if (start is offset)
+                return new Region(start, end - start);
+            else
+                return new Region(start + 1, end - start - 1);
+        }
++/
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultUndoManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1486 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.DefaultUndoManager;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.operations.AbstractOperation;
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.commands.operations.IOperationHistoryListener;
+import org.eclipse.core.commands.operations.IUndoContext;
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.commands.operations.ObjectUndoContext;
+import org.eclipse.core.commands.operations.OperationHistoryEvent;
+import org.eclipse.core.commands.operations.OperationHistoryFactory;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.MessageDialog;
+
+
+/**
+ * Standard implementation of {@link org.eclipse.jface.text.IUndoManager}.
+ * <p>
+ * It registers with the connected text viewer as text input listener and
+ * document listener and logs all changes. It also monitors mouse and keyboard
+ * activities in order to partition the stream of text changes into undo-able
+ * edit commands.
+ * </p>
+ * <p>
+ * Since 3.1 this undo manager is a facade to the global operation history.
+ * </p>
+ * <p>
+ * The usage of {@link org.eclipse.core.runtime.IAdaptable} in the JFace
+ * layer has been approved by Platform UI, see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=87669#c9
+ * </p>
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.ITextViewer
+ * @see org.eclipse.jface.text.ITextInputListener
+ * @see org.eclipse.jface.text.IDocumentListener
+ * @see org.eclipse.core.commands.operations.IUndoableOperation
+ * @see org.eclipse.core.commands.operations.IOperationHistory
+ * @see MouseListener
+ * @see KeyListener
+ * @deprecated As of 3.2, replaced by {@link TextViewerUndoManager}
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class DefaultUndoManager : IUndoManager, IUndoManagerExtension {
+
+    /**
+     * Represents an undo-able edit command.
+     * <p>
+     * Since 3.1 this implements the interface for IUndoableOperation.
+     * </p>
+     */
+    class TextCommand : AbstractOperation {
+
+        /** The start index of the replaced text. */
+        protected int fStart= -1;
+        /** The end index of the replaced text. */
+        protected int fEnd= -1;
+        /** The newly inserted text. */
+        protected String fText;
+        /** The replaced text. */
+        protected String fPreservedText;
+
+        /** The undo modification stamp. */
+        protected long fUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+        /** The redo modification stamp. */
+        protected long fRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+
+        /**
+         * Creates a new text command.
+         *
+         * @param context the undo context for this command
+         * @since 3.1
+         */
+        this(IUndoContext context) {
+            super(JFaceTextMessages.getString("DefaultUndoManager.operationLabel")); //$NON-NLS-1$
+            addContext(context);
+        }
+
+        /**
+         * Re-initializes this text command.
+         */
+        protected void reinitialize() {
+            fStart= fEnd= -1;
+            fText= fPreservedText= null;
+            fUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+            fRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+        }
+
+        /**
+         * Sets the start and the end index of this command.
+         *
+         * @param start the start index
+         * @param end the end index
+         */
+        protected void set(int start, int end) {
+            fStart= start;
+            fEnd= end;
+            fText= null;
+            fPreservedText= null;
+        }
+
+        /*
+         * @see org.eclipse.core.commands.operations.IUndoableOperation#dispose()
+         * @since 3.1
+         */
+        public void dispose() {
+            reinitialize();
+        }
+
+        /**
+         * Undo the change described by this command.
+         *
+         * @since 2.0
+         */
+        protected void undoTextChange() {
+            try {
+                IDocument document= fTextViewer.getDocument();
+                if ( cast(IDocumentExtension4)document )
+                    (cast(IDocumentExtension4)document).replace(fStart, fText.length(), fPreservedText, fUndoModificationStamp);
+                else
+                    document.replace(fStart, fText.length(), fPreservedText);
+            } catch (BadLocationException x) {
+            }
+        }
+
+        /*
+         * @see org.eclipse.core.commands.operations.IUndoableOperation#canUndo()
+         * @since 3.1
+         */
+        public bool canUndo() {
+
+            if (isConnected() && isValid()) {
+                IDocument doc= fTextViewer.getDocument();
+                if ( cast(IDocumentExtension4)doc ) {
+                    long docStamp= (cast(IDocumentExtension4)doc).getModificationStamp();
+
+                    // Normal case: an undo is valid if its redo will restore document
+                    // to its current modification stamp
+                    bool canUndo= docStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP ||
+                        docStamp is getRedoModificationStamp();
+
+                    /* Special case to check if the answer is false.
+                     * If the last document change was empty, then the document's
+                     * modification stamp was incremented but nothing was committed.
+                     * The operation being queried has an older stamp.  In this case only,
+                     * the comparison is different.  A sequence of document changes that
+                     * include an empty change is handled correctly when a valid commit
+                     * follows the empty change, but when #canUndo() is queried just after
+                     * an empty change, we must special case the check.  The check is very
+                     * specific to prevent false positives.
+                     * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=98245
+                     */
+                    if (!canUndo &&
+                            this is fHistory.getUndoOperation(fUndoContext)  &&  // this is the latest operation
+                            this !is fCurrent && // there is a more current operation not on the stack
+                            !fCurrent.isValid() &&  // the current operation is not a valid document modification
+                            fCurrent.fUndoModificationStamp !is // the invalid current operation has a document stamp
+                                IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) {
+                        canUndo= fCurrent.fRedoModificationStamp is docStamp;
+                    }
+                    /*
+                     * When the composite is the current command, it may hold the timestamp
+                     * of a no-op change.  We check this here rather than in an override of
+                     * canUndo() in CompoundTextCommand simply to keep all the special case checks
+                     * in one place.
+                     */
+                    if (!canUndo &&
+                            this is fHistory.getUndoOperation(fUndoContext)  &&  // this is the latest operation
+                            null !is cast(CompoundTextCommand)this &&
+                            this is fCurrent && // this is the current operation
+                            this.fStart is -1 &&  // the current operation text is not valid
+                            fCurrent.fRedoModificationStamp !is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) {  // but it has a redo stamp
+                        canUndo= fCurrent.fRedoModificationStamp is docStamp;
+                    }
+
+                }
+                // if there is no timestamp to check, simply return true per the 3.0.1 behavior
+                return true;
+            }
+            return false;
+        }
+
+        /*
+         * @see org.eclipse.core.commands.operations.IUndoableOperation#canRedo()
+         * @since 3.1
+         */
+        public bool canRedo() {
+            if (isConnected() && isValid()) {
+                IDocument doc= fTextViewer.getDocument();
+                if ( cast(IDocumentExtension4)doc ) {
+                    long docStamp= (cast(IDocumentExtension4)doc).getModificationStamp();
+                    return docStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP ||
+                        docStamp is getUndoModificationStamp();
+                }
+                // if there is no timestamp to check, simply return true per the 3.0.1 behavior
+                return true;
+            }
+            return false;
+        }
+
+        /*
+         * @see org.eclipse.core.commands.operations.IUndoableOperation#canExecute()
+         * @since 3.1
+         */
+        public bool canExecute() {
+            return isConnected();
+        }
+
+        /*
+         * @see org.eclipse.core.commands.operations.IUndoableOperation#execute(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.core.runtime.IAdaptable)
+         * @since 3.1
+         */
+        public IStatus execute(IProgressMonitor monitor, IAdaptable uiInfo) {
+            // Text commands execute as they are typed, so executing one has no effect.
+            return Status.OK_STATUS;
+        }
+
+        /*
+         * Undo the change described by this command. Also selects and
+         * reveals the change.
+         */
+
+        /**
+         * Undo the change described by this command. Also selects and
+         * reveals the change.
+         *
+         * @param monitor   the progress monitor to use if necessary
+         * @param uiInfo    an adaptable that can provide UI info if needed
+         * @return the status
+         */
+        public IStatus undo(IProgressMonitor monitor, IAdaptable uiInfo) {
+            if (isValid()) {
+                undoTextChange();
+                selectAndReveal(fStart, fPreservedText is null ? 0 : fPreservedText.length());
+                resetProcessChangeSate();
+                return Status.OK_STATUS;
+            }
+            return IOperationHistory.OPERATION_INVALID_STATUS;
+        }
+
+        /**
+         * Re-applies the change described by this command.
+         *
+         * @since 2.0
+         */
+        protected void redoTextChange() {
+            try {
+                IDocument document= fTextViewer.getDocument();
+                if ( cast(IDocumentExtension4)document )
+                    (cast(IDocumentExtension4)document).replace(fStart, fEnd - fStart, fText, fRedoModificationStamp);
+                else
+                    fTextViewer.getDocument().replace(fStart, fEnd - fStart, fText);
+            } catch (BadLocationException x) {
+            }
+        }
+
+        /**
+         * Re-applies the change described by this command that previously been
+         * rolled back. Also selects and reveals the change.
+         *
+         * @param monitor   the progress monitor to use if necessary
+         * @param uiInfo    an adaptable that can provide UI info if needed
+         * @return the status
+         */
+        public IStatus redo(IProgressMonitor monitor, IAdaptable uiInfo) {
+            if (isValid()) {
+                redoTextChange();
+                resetProcessChangeSate();
+                selectAndReveal(fStart, fText is null ? 0 : fText.length());
+                return Status.OK_STATUS;
+            }
+            return IOperationHistory.OPERATION_INVALID_STATUS;
+        }
+
+        /**
+         * Update the command in response to a commit.
+         *
+         * @since 3.1
+         */
+
+        protected void updateCommand() {
+            fText= fTextBuffer.toString();
+            fTextBuffer.truncate(0);
+            fPreservedText= fPreservedTextBuffer.toString();
+            fPreservedTextBuffer.truncate(0);
+        }
+
+        /**
+         * Creates a new uncommitted text command depending on whether
+         * a compound change is currently being executed.
+         *
+         * @return a new, uncommitted text command or a compound text command
+         */
+        protected TextCommand createCurrent() {
+            return fFoldingIntoCompoundChange ? new CompoundTextCommand(fUndoContext) : new TextCommand(fUndoContext);
+        }
+
+        /**
+         * Commits the current change into this command.
+         */
+        protected void commit() {
+            if (fStart < 0) {
+                if (fFoldingIntoCompoundChange) {
+                    fCurrent= createCurrent();
+                } else {
+                    reinitialize();
+                }
+            } else {
+                updateCommand();
+                fCurrent= createCurrent();
+            }
+            resetProcessChangeSate();
+        }
+
+        /**
+         * Updates the text from the buffers without resetting
+         * the buffers or adding anything to the stack.
+         *
+         * @since 3.1
+         */
+        protected void pretendCommit() {
+            if (fStart > -1) {
+                fText= fTextBuffer.toString();
+                fPreservedText= fPreservedTextBuffer.toString();
+            }
+        }
+
+        /**
+         * Attempt a commit of this command and answer true if a new
+         * fCurrent was created as a result of the commit.
+         *
+         * @return true if the command was committed and created a
+         * new fCurrent, false if not.
+         * @since 3.1
+         */
+        protected bool attemptCommit() {
+            pretendCommit();
+            if (isValid()) {
+                this.outer.commit();
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Checks whether this text command is valid for undo or redo.
+         *
+         * @return <code>true</code> if the command is valid for undo or redo
+         * @since 3.1
+         */
+        protected bool isValid() {
+            return fStart > -1 &&
+                fEnd > -1 &&
+                fText !is null;
+        }
+
+        /*
+         * @see java.lang.Object#toString()
+         * @since 3.1
+         */
+        public override String toString() {
+            String delimiter= ", "; //$NON-NLS-1$
+            StringBuffer text= new StringBuffer(super.toString());
+            text.append("\n"); //$NON-NLS-1$
+            text.append(this.classinfo.name);
+            text.append(" undo modification stamp: "); //$NON-NLS-1$
+            text.append(fUndoModificationStamp);
+            text.append(" redo modification stamp: "); //$NON-NLS-1$
+            text.append(fRedoModificationStamp);
+            text.append(" start: "); //$NON-NLS-1$
+            text.append(fStart);
+            text.append(delimiter);
+            text.append("end: "); //$NON-NLS-1$
+            text.append(fEnd);
+            text.append(delimiter);
+            text.append("text: '"); //$NON-NLS-1$
+            text.append(fText);
+            text.append('\'');
+            text.append(delimiter);
+            text.append("preservedText: '"); //$NON-NLS-1$
+            text.append(fPreservedText);
+            text.append('\'');
+            return text.toString();
+        }
+
+        /**
+         * Return the undo modification stamp
+         *
+         * @return the undo modification stamp for this command
+         * @since 3.1
+         */
+        protected long getUndoModificationStamp() {
+            return fUndoModificationStamp;
+        }
+
+        /**
+         * Return the redo modification stamp
+         *
+         * @return the redo modification stamp for this command
+         * @since 3.1
+         */
+        protected long getRedoModificationStamp() {
+            return fRedoModificationStamp;
+        }
+    }
+
+    /**
+     * Represents an undo-able edit command consisting of several
+     * individual edit commands.
+     */
+    class CompoundTextCommand : TextCommand {
+
+        /** The list of individual commands */
+        private List fCommands;
+
+        /**
+         * Creates a new compound text command.
+         *
+         * @param context the undo context for this command
+         * @since 3.1
+         */
+        this(IUndoContext context) {
+            super(context);
+            fCommands= new ArrayList();
+        }
+
+        /**
+         * Adds a new individual command to this compound command.
+         *
+         * @param command the command to be added
+         */
+        protected void add(TextCommand command) {
+            fCommands.add(command);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.DefaultUndoManager.TextCommand#undo()
+         */
+        public IStatus undo(IProgressMonitor monitor, IAdaptable uiInfo) {
+            resetProcessChangeSate();
+
+            int size= fCommands.size();
+            if (size > 0) {
+
+                TextCommand c;
+
+                for (int i= size -1; i > 0;  --i) {
+                    c= cast(TextCommand) fCommands.get(i);
+                    c.undoTextChange();
+                }
+
+                c= cast(TextCommand) fCommands.get(0);
+                c.undo(monitor, uiInfo);
+            }
+
+            return Status.OK_STATUS;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.DefaultUndoManager.TextCommand#redo()
+         */
+        public IStatus redo(IProgressMonitor monitor, IAdaptable uiInfo) {
+            resetProcessChangeSate();
+
+            int size= fCommands.size();
+            if (size > 0) {
+
+                TextCommand c;
+
+                for (int i= 0; i < size -1;  ++i) {
+                    c= cast(TextCommand) fCommands.get(i);
+                    c.redoTextChange();
+                }
+
+                c= cast(TextCommand) fCommands.get(size -1);
+                c.redo(monitor, uiInfo);
+            }
+            return Status.OK_STATUS;
+        }
+
+        /*
+         * @see TextCommand#updateCommand
+
+         */
+
+        protected void updateCommand() {
+            // first gather the data from the buffers
+            super.updateCommand();
+
+            // the result of the command update is stored as a child command
+            TextCommand c= new TextCommand(fUndoContext);
+            c.fStart= fStart;
+            c.fEnd= fEnd;
+            c.fText= fText;
+            c.fPreservedText= fPreservedText;
+            c.fUndoModificationStamp= fUndoModificationStamp;
+            c.fRedoModificationStamp= fRedoModificationStamp;
+            add(c);
+
+            // clear out all indexes now that the child is added
+            reinitialize();
+        }
+
+        /*
+         * @see TextCommand#createCurrent
+         */
+        protected TextCommand createCurrent() {
+
+            if (!fFoldingIntoCompoundChange)
+                return new TextCommand(fUndoContext);
+
+            reinitialize();
+            return this;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.DefaultUndoManager.TextCommand#commit()
+         */
+        protected void commit() {
+            // if there is pending data, update the command
+            if (fStart > -1)
+                updateCommand();
+            fCurrent= createCurrent();
+            resetProcessChangeSate();
+        }
+
+        /**
+         * Checks whether the command is valid for undo or redo.
+         *
+         * @return true if the command is valid.
+         * @since 3.1
+         */
+        protected bool isValid() {
+            if (isConnected())
+                return (fStart > -1 || fCommands.size() > 0);
+            return false;
+        }
+
+        /**
+         * Returns the undo modification stamp.
+         *
+         * @return the undo modification stamp
+         * @since 3.1
+         */
+        protected long getUndoModificationStamp() {
+            if (fStart > -1)
+                return super.getUndoModificationStamp();
+            else if (fCommands.size() > 0)
+                return (cast(TextCommand)fCommands.get(0)).getUndoModificationStamp();
+
+            return fUndoModificationStamp;
+        }
+
+        /**
+         * Returns the redo modification stamp.
+         *
+         * @return the redo modification stamp
+         * @since 3.1
+         */
+        protected long getRedoModificationStamp() {
+            if (fStart > -1)
+                return super.getRedoModificationStamp();
+            else if (fCommands.size() > 0)
+                return (cast(TextCommand)fCommands.get(fCommands.size()-1)).getRedoModificationStamp();
+
+            return fRedoModificationStamp;
+        }
+    }
+
+    /**
+     * Internal listener to mouse and key events.
+     */
+    class KeyAndMouseListener : MouseListener, KeyListener {
+
+        /*
+         * @see MouseListener#mouseDoubleClick
+         */
+        public void mouseDoubleClick(MouseEvent e) {
+        }
+
+        /*
+         * If the right mouse button is pressed, the current editing command is closed
+         * @see MouseListener#mouseDown
+         */
+        public void mouseDown(MouseEvent e) {
+            if (e.button is 1)
+                commit();
+        }
+
+        /*
+         * @see MouseListener#mouseUp
+         */
+        public void mouseUp(MouseEvent e) {
+        }
+
+        /*
+         * @see KeyListener#keyPressed
+         */
+        public void keyReleased(KeyEvent e) {
+        }
+
+        /*
+         * On cursor keys, the current editing command is closed
+         * @see KeyListener#keyPressed
+         */
+        public void keyPressed(KeyEvent e) {
+            switch (e.keyCode) {
+                case SWT.ARROW_UP:
+                case SWT.ARROW_DOWN:
+                case SWT.ARROW_LEFT:
+                case SWT.ARROW_RIGHT:
+                    commit();
+                    break;
+                default:
+            }
+        }
+    }
+
+    /**
+     * Internal listener to document changes.
+     */
+    class DocumentListener : IDocumentListener {
+
+        private String fReplacedText;
+
+        /*
+         * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
+         */
+        public void documentAboutToBeChanged(DocumentEvent event) {
+            try {
+                fReplacedText= event.getDocument().get(event.getOffset(), event.getLength());
+                fPreservedUndoModificationStamp= event.getModificationStamp();
+            } catch (BadLocationException x) {
+                fReplacedText= null;
+            }
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
+         */
+        public void documentChanged(DocumentEvent event) {
+            fPreservedRedoModificationStamp= event.getModificationStamp();
+
+            // record the current valid state for the top operation in case it remains the
+            // top operation but changes state.
+            IUndoableOperation op= fHistory.getUndoOperation(fUndoContext);
+            bool wasValid= false;
+            if (op !is null)
+                wasValid= op.canUndo();
+            // Process the change, providing the before and after timestamps
+            processChange(event.getOffset(), event.getOffset() + event.getLength(), event.getText(), fReplacedText, fPreservedUndoModificationStamp, fPreservedRedoModificationStamp);
+
+            // now update fCurrent with the latest buffers from the document change.
+            fCurrent.pretendCommit();
+
+            if (op is fCurrent) {
+                // if the document change did not cause a new fCurrent to be created, then we should
+                // notify the history that the current operation changed if its validity has changed.
+                if (wasValid !is fCurrent.isValid())
+                    fHistory.operationChanged(op);
+            }
+            else {
+                // if the change created a new fCurrent that we did not yet add to the
+                // stack, do so if it's valid and we are not in the middle of a compound change.
+                if (fCurrent !is fLastAddedCommand && fCurrent.isValid()) {
+                    addToCommandStack(fCurrent);
+                }
+            }
+        }
+    }
+
+    /**
+     * Internal text input listener.
+     */
+    class TextInputListener : ITextInputListener {
+
+        /*
+         * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
+         */
+        public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+            if (oldInput !is null && fDocumentListener !is null) {
+                oldInput.removeDocumentListener(fDocumentListener);
+                commit();
+            }
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
+         */
+        public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
+            if (newInput !is null) {
+                if (fDocumentListener is null)
+                    fDocumentListener= new DocumentListener();
+                newInput.addDocumentListener(fDocumentListener);
+            }
+        }
+
+    }
+
+    /*
+     * @see IOperationHistoryListener
+     * @since 3.1
+     */
+    class HistoryListener : IOperationHistoryListener {
+        private IUndoableOperation fOperation;
+
+        public void historyNotification(OperationHistoryEvent event) {
+            int type= event.getEventType();
+            switch (type) {
+            case OperationHistoryEvent.ABOUT_TO_UNDO:
+            case OperationHistoryEvent.ABOUT_TO_REDO:
+                // if this is one of our operations
+                if (event.getOperation().hasContext(fUndoContext)) {
+                    fTextViewer.getTextWidget().getDisplay().syncExec(dgRunnable((OperationHistoryEvent event_, int type_ ) {
+                        // if we are undoing/redoing a command we generated, then ignore
+                        // the document changes associated with this undo or redo.
+                        if (event_.getOperation() ) {
+                            if ( cast(TextViewer)fTextViewer )
+                                (cast(TextViewer)fTextViewer).ignoreAutoEditStrategies_package(true);
+                            listenToTextChanges(false);
+
+                            // in the undo case only, make sure compounds are closed
+                            if (type_ is OperationHistoryEvent.ABOUT_TO_UNDO) {
+                                if (fFoldingIntoCompoundChange) {
+                                    endCompoundChange();
+                                }
+                            }
+                        } else {
+                            // the undo or redo has our context, but it is not one of
+                            // our commands.  We will listen to the changes, but will
+                            // reset the state that tracks the undo/redo history.
+                            commit();
+                            fLastAddedCommand= null;
+                        }
+                    }, event, type ));
+                    fOperation= event.getOperation();
+                }
+                break;
+            case OperationHistoryEvent.UNDONE:
+            case OperationHistoryEvent.REDONE:
+            case OperationHistoryEvent.OPERATION_NOT_OK:
+                if (event.getOperation() is fOperation) {
+                    fTextViewer.getTextWidget().getDisplay().syncExec(new class()  Runnable {
+                        public void run() {
+                            listenToTextChanges(true);
+                            fOperation= null;
+                            if ( cast(TextViewer)fTextViewer )
+                                (cast(TextViewer)fTextViewer).ignoreAutoEditStrategies_package(false);
+                         }
+                    });
+                }
+                break;
+            default:
+            }
+        }
+
+    }
+
+    /** Text buffer to collect text which is inserted into the viewer */
+    private StringBuffer fTextBuffer;
+    /** Text buffer to collect viewer content which has been replaced */
+    private StringBuffer fPreservedTextBuffer;
+    /** The document modification stamp for undo. */
+    protected long fPreservedUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+    /** The document modification stamp for redo. */
+    protected long fPreservedRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+    /** The internal key and mouse event listener */
+    private KeyAndMouseListener fKeyAndMouseListener;
+    /** The internal document listener */
+    private DocumentListener fDocumentListener;
+    /** The internal text input listener */
+    private TextInputListener fTextInputListener;
+
+
+    /** Indicates inserting state */
+    private bool fInserting= false;
+    /** Indicates overwriting state */
+    private bool fOverwriting= false;
+    /** Indicates whether the current change belongs to a compound change */
+    private bool fFoldingIntoCompoundChange= false;
+
+    /** The text viewer the undo manager is connected to */
+    private ITextViewer fTextViewer;
+
+    /** Supported undo level */
+    private int fUndoLevel;
+    /** The currently constructed edit command */
+    private TextCommand fCurrent;
+    /** The last delete edit command */
+    private TextCommand fPreviousDelete;
+
+    /**
+     * The undo context.
+     * @since 3.1
+     */
+    private IOperationHistory fHistory;
+    /**
+     * The operation history.
+     * @since 3.1
+     */
+    private IUndoContext fUndoContext;
+    /**
+     * The operation history listener used for managing undo and redo before
+     * and after the individual commands are performed.
+     * @since 3.1
+     */
+    private IOperationHistoryListener fHistoryListener;
+
+    /**
+     * The command last added to the operation history.  This must be tracked
+     * internally instead of asking the history, since outside parties may be placing
+     * items on our undo/redo history.
+     */
+    private TextCommand fLastAddedCommand= null;
+
+    /**
+     * Creates a new undo manager who remembers the specified number of edit commands.
+     *
+     * @param undoLevel the length of this manager's history
+     */
+    public this(int undoLevel) {
+        fTextBuffer= new StringBuffer();
+        fPreservedTextBuffer= new StringBuffer();
+
+        fHistoryListener= new HistoryListener();
+        fHistory= OperationHistoryFactory.getOperationHistory();
+        setMaximalUndoLevel(undoLevel);
+    }
+
+    /**
+     * Returns whether this undo manager is connected to a text viewer.
+     *
+     * @return <code>true</code> if connected, <code>false</code> otherwise
+     * @since 3.1
+     */
+    private bool isConnected() {
+        return fTextViewer !is null;
+    }
+
+    /*
+     * @see IUndoManager#beginCompoundChange
+     */
+    public void beginCompoundChange() {
+        if (isConnected()) {
+            fFoldingIntoCompoundChange= true;
+            commit();
+        }
+    }
+
+
+    /*
+     * @see IUndoManager#endCompoundChange
+     */
+    public void endCompoundChange() {
+        if (isConnected()) {
+            fFoldingIntoCompoundChange= false;
+            commit();
+        }
+    }
+
+    /**
+     * Registers all necessary listeners with the text viewer.
+     */
+    private void addListeners() {
+        StyledText text= fTextViewer.getTextWidget();
+        if (text !is null) {
+            fKeyAndMouseListener= new KeyAndMouseListener();
+            text.addMouseListener(fKeyAndMouseListener);
+            text.addKeyListener(fKeyAndMouseListener);
+            fTextInputListener= new TextInputListener();
+            fTextViewer.addTextInputListener(fTextInputListener);
+            fHistory.addOperationHistoryListener(fHistoryListener);
+            listenToTextChanges(true);
+        }
+    }
+
+    /**
+     * Unregister all previously installed listeners from the text viewer.
+     */
+    private void removeListeners() {
+        StyledText text= fTextViewer.getTextWidget();
+        if (text !is null) {
+            if (fKeyAndMouseListener !is null) {
+                text.removeMouseListener(fKeyAndMouseListener);
+                text.removeKeyListener(fKeyAndMouseListener);
+                fKeyAndMouseListener= null;
+            }
+            if (fTextInputListener !is null) {
+                fTextViewer.removeTextInputListener(fTextInputListener);
+                fTextInputListener= null;
+            }
+            listenToTextChanges(false);
+            fHistory.removeOperationHistoryListener(fHistoryListener);
+        }
+    }
+
+    /**
+     * Adds the given command to the operation history if it is not part of
+     * a compound change.
+     *
+     * @param command the command to be added
+     * @since 3.1
+     */
+    private void addToCommandStack(TextCommand command){
+        if (!fFoldingIntoCompoundChange || cast(CompoundTextCommand)command ) {
+            fHistory.add(command);
+            fLastAddedCommand= command;
+        }
+    }
+
+    /**
+     * Disposes the command stack.
+     *
+     * @since 3.1
+     */
+    private void disposeCommandStack() {
+        fHistory.dispose(fUndoContext, true, true, true);
+    }
+
+    /**
+     * Initializes the command stack.
+     *
+     * @since 3.1
+     */
+    private void initializeCommandStack() {
+        if (fHistory !is null && fUndoContext !is null)
+            fHistory.dispose(fUndoContext, true, true, false);
+
+    }
+
+    /**
+     * Switches the state of whether there is a text listener or not.
+     *
+     * @param listen the state which should be established
+     */
+    private void listenToTextChanges(bool listen) {
+        if (listen) {
+            if (fDocumentListener is null && fTextViewer.getDocument() !is null) {
+                fDocumentListener= new DocumentListener();
+                fTextViewer.getDocument().addDocumentListener(fDocumentListener);
+            }
+        } else if (!listen) {
+            if (fDocumentListener !is null && fTextViewer.getDocument() !is null) {
+                fTextViewer.getDocument().removeDocumentListener(fDocumentListener);
+                fDocumentListener= null;
+            }
+        }
+    }
+
+    /**
+     * Closes the current editing command and opens a new one.
+     */
+    private void commit() {
+        // if fCurrent has never been placed on the command stack, do so now.
+        // this can happen when there are multiple programmatically commits in a single
+        // document change.
+        if (fLastAddedCommand !is fCurrent) {
+            fCurrent.pretendCommit();
+            if (fCurrent.isValid())
+                addToCommandStack(fCurrent);
+        }
+        fCurrent.commit();
+    }
+
+    /**
+     * Reset processChange state.
+     *
+     * @since 3.2
+     */
+    private void resetProcessChangeSate() {
+        fInserting= false;
+        fOverwriting= false;
+        fPreviousDelete.reinitialize();
+    }
+
+    /**
+     * Checks whether the given text starts with a line delimiter and
+     * subsequently contains a white space only.
+     *
+     * @param text the text to check
+     * @return <code>true</code> if the text is a line delimiter followed by whitespace, <code>false</code> otherwise
+     */
+    private bool isWhitespaceText(String text) {
+
+        if (text is null || text.length() is 0)
+            return false;
+
+        String[] delimiters= fTextViewer.getDocument().getLegalLineDelimiters();
+        int index= TextUtilities.startsWith(delimiters, text);
+        if (index > -1) {
+            char c;
+            int length= text.length();
+            for (int i= delimiters[index].length; i < length; i++) {
+                c= text.charAt(i);
+                if (c !is ' ' && c !is '\t')
+                    return false;
+            }
+            return true;
+        }
+
+        return false;
+    }
+
+    private void processChange(int modelStart, int modelEnd, String insertedText, String replacedText, long beforeChangeModificationStamp, long afterChangeModificationStamp) {
+
+        if (insertedText is null)
+            insertedText= ""; //$NON-NLS-1$
+
+        if (replacedText is null)
+            replacedText= ""; //$NON-NLS-1$
+
+        int length= insertedText.length();
+        int diff= modelEnd - modelStart;
+
+        if (fCurrent.fUndoModificationStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP)
+            fCurrent.fUndoModificationStamp= beforeChangeModificationStamp;
+
+        // normalize
+        if (diff < 0) {
+            int tmp= modelEnd;
+            modelEnd= modelStart;
+            modelStart= tmp;
+        }
+
+        if (modelStart is modelEnd) {
+            // text will be inserted
+            if ((length is 1) || isWhitespaceText(insertedText)) {
+                // by typing or whitespace
+                if (!fInserting || (modelStart !is fCurrent.fStart + fTextBuffer.length())) {
+                    fCurrent.fRedoModificationStamp= beforeChangeModificationStamp;
+                    if (fCurrent.attemptCommit())
+                        fCurrent.fUndoModificationStamp= beforeChangeModificationStamp;
+
+                    fInserting= true;
+                }
+                if (fCurrent.fStart < 0)
+                    fCurrent.fStart= fCurrent.fEnd= modelStart;
+                if (length > 0)
+                    fTextBuffer.append(insertedText);
+            } else if (length >= 0) {
+                // by pasting or model manipulation
+                fCurrent.fRedoModificationStamp= beforeChangeModificationStamp;
+                if (fCurrent.attemptCommit())
+                    fCurrent.fUndoModificationStamp= beforeChangeModificationStamp;
+
+                fCurrent.fStart= fCurrent.fEnd= modelStart;
+                fTextBuffer.append(insertedText);
+                fCurrent.fRedoModificationStamp= afterChangeModificationStamp;
+                if (fCurrent.attemptCommit())
+                    fCurrent.fUndoModificationStamp= afterChangeModificationStamp;
+
+            }
+        } else {
+            if (length is 0) {
+                // text will be deleted by backspace or DEL key or empty clipboard
+                length= replacedText.length;
+                String[] delimiters= fTextViewer.getDocument().getLegalLineDelimiters();
+
+                if ((length is 1) || TextUtilities.equals(delimiters, replacedText) > -1) {
+
+                    // whereby selection is empty
+
+                    if (fPreviousDelete.fStart is modelStart && fPreviousDelete.fEnd is modelEnd) {
+                        // repeated DEL
+
+                            // correct wrong settings of fCurrent
+                        if (fCurrent.fStart is modelEnd && fCurrent.fEnd is modelStart) {
+                            fCurrent.fStart= modelStart;
+                            fCurrent.fEnd= modelEnd;
+                        }
+                            // append to buffer && extend command range
+                        fPreservedTextBuffer.append(replacedText);
+                        ++fCurrent.fEnd;
+
+                    } else if (fPreviousDelete.fStart is modelEnd) {
+                        // repeated backspace
+
+                            // insert in buffer and extend command range
+                        fPreservedTextBuffer.select(0, 0);
+                        fPreservedTextBuffer.replace(replacedText);
+                        fCurrent.fStart= modelStart;
+
+                    } else {
+                        // either DEL or backspace for the first time
+
+                        fCurrent.fRedoModificationStamp= beforeChangeModificationStamp;
+                        if (fCurrent.attemptCommit())
+                            fCurrent.fUndoModificationStamp= beforeChangeModificationStamp;
+
+                        // as we can not decide whether it was DEL or backspace we initialize for backspace
+                        fPreservedTextBuffer.append(replacedText);
+                        fCurrent.fStart= modelStart;
+                        fCurrent.fEnd= modelEnd;
+                    }
+
+                    fPreviousDelete.set(modelStart, modelEnd);
+
+                } else if (length > 0) {
+                    // whereby selection is not empty
+                    fCurrent.fRedoModificationStamp= beforeChangeModificationStamp;
+                    if (fCurrent.attemptCommit())
+                        fCurrent.fUndoModificationStamp= beforeChangeModificationStamp;
+
+                    fCurrent.fStart= modelStart;
+                    fCurrent.fEnd= modelEnd;
+                    fPreservedTextBuffer.append(replacedText);
+                }
+            } else {
+                // text will be replaced
+
+                if (length is 1) {
+                    length= replacedText.length;
+                    String[] delimiters= fTextViewer.getDocument().getLegalLineDelimiters();
+
+                    if ((length is 1) || TextUtilities.equals(delimiters, replacedText) > -1) {
+                        // because of overwrite mode or model manipulation
+                        if (!fOverwriting || (modelStart !is fCurrent.fStart +  fTextBuffer.length())) {
+                            fCurrent.fRedoModificationStamp= beforeChangeModificationStamp;
+                            if (fCurrent.attemptCommit())
+                                fCurrent.fUndoModificationStamp= beforeChangeModificationStamp;
+
+                            fOverwriting= true;
+                        }
+
+                        if (fCurrent.fStart < 0)
+                            fCurrent.fStart= modelStart;
+
+                        fCurrent.fEnd= modelEnd;
+                        fTextBuffer.append(insertedText);
+                        fPreservedTextBuffer.append(replacedText);
+                        fCurrent.fRedoModificationStamp= afterChangeModificationStamp;
+                        return;
+                    }
+                }
+                // because of typing or pasting whereby selection is not empty
+                fCurrent.fRedoModificationStamp= beforeChangeModificationStamp;
+                if (fCurrent.attemptCommit())
+                    fCurrent.fUndoModificationStamp= beforeChangeModificationStamp;
+
+                fCurrent.fStart= modelStart;
+                fCurrent.fEnd= modelEnd;
+                fTextBuffer.append(insertedText);
+                fPreservedTextBuffer.append(replacedText);
+            }
+        }
+        // in all cases, the redo modification stamp is updated on the open command
+        fCurrent.fRedoModificationStamp= afterChangeModificationStamp;
+    }
+
+    /**
+     * Shows the given exception in an error dialog.
+     *
+     * @param title the dialog title
+     * @param ex the exception
+     * @since 3.1
+     */
+    private void openErrorDialog(String title, Exception ex) {
+        Shell shell= null;
+        if (isConnected()) {
+            StyledText st= fTextViewer.getTextWidget();
+            if (st !is null && !st.isDisposed())
+                shell= st.getShell();
+        }
+        if (Display.getCurrent() !is null)
+            MessageDialog.openError(shell, title, ex.msg/+getLocalizedMessage()+/);
+        else {
+            Display display;
+            Shell finalShell= shell;
+            if (finalShell !is null)
+                display= finalShell.getDisplay();
+            else
+                display= Display.getDefault();
+            display.syncExec(dgRunnable( {
+                MessageDialog.openError(finalShell, title, ex.msg/+getLocalizedMessage()+/);
+            }));
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#setMaximalUndoLevel(int)
+     */
+    public void setMaximalUndoLevel(int undoLevel) {
+        fUndoLevel= Math.max(0, undoLevel);
+        if (isConnected()) {
+            fHistory.setLimit(fUndoContext, fUndoLevel);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#connect(org.eclipse.jface.text.ITextViewer)
+     */
+    public void connect(ITextViewer textViewer) {
+        if (!isConnected() && textViewer !is null) {
+            fTextViewer= textViewer;
+            if (fUndoContext is null)
+                fUndoContext= new ObjectUndoContext(this);
+
+            fHistory.setLimit(fUndoContext, fUndoLevel);
+
+            initializeCommandStack();
+
+            // open up the current command
+            fCurrent= new TextCommand(fUndoContext);
+
+            fPreviousDelete= new TextCommand(fUndoContext);
+            addListeners();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#disconnect()
+     */
+    public void disconnect() {
+        if (isConnected()) {
+
+            removeListeners();
+
+            fCurrent= null;
+            fTextViewer= null;
+            disposeCommandStack();
+            fTextBuffer.clear();
+            fPreservedTextBuffer.clear();
+            fUndoContext= null;
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#reset()
+     */
+    public void reset() {
+        if (isConnected()) {
+            initializeCommandStack();
+            fCurrent= new TextCommand(fUndoContext);
+            fFoldingIntoCompoundChange= false;
+            fInserting= false;
+            fOverwriting= false;
+            fTextBuffer.truncate(0);
+            fPreservedTextBuffer.truncate(0);
+            fPreservedUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+            fPreservedRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#redoable()
+     */
+    public bool redoable() {
+        return fHistory.canRedo(fUndoContext);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#undoable()
+     */
+    public bool undoable() {
+        return fHistory.canUndo(fUndoContext);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#redo()
+     */
+    public void redo() {
+        if (isConnected() && redoable()) {
+            try {
+                fHistory.redo(fUndoContext, null, null);
+            } catch (ExecutionException ex) {
+                openErrorDialog(JFaceTextMessages.getString("DefaultUndoManager.error.redoFailed.title"), ex); //$NON-NLS-1$
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#undo()
+     */
+    public void undo() {
+        if (isConnected() && undoable()) {
+            try {
+                fHistory.undo(fUndoContext, null, null);
+            } catch (ExecutionException ex) {
+                openErrorDialog(JFaceTextMessages.getString("DefaultUndoManager.error.undoFailed.title"), ex); //$NON-NLS-1$
+            }
+        }
+    }
+
+    /**
+     * Selects and reveals the specified range.
+     *
+     * @param offset the offset of the range
+     * @param length the length of the range
+     * @since 3.0
+     */
+    protected void selectAndReveal(int offset, int length) {
+        if ( cast(ITextViewerExtension5)fTextViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) fTextViewer;
+            extension.exposeModelRange(new Region(offset, length));
+        } else if (!fTextViewer.overlapsWithVisibleRegion(offset, length))
+            fTextViewer.resetVisibleRegion();
+
+        fTextViewer.setSelectedRange(offset, length);
+        fTextViewer.revealRange(offset, length);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManagerExtension#getUndoContext()
+     * @since 3.1
+     */
+    public IUndoContext getUndoContext() {
+        return fUndoContext;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/DocumentClone.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.DocumentClone;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * An {@link org.eclipse.jface.text.IDocument} that is a read-only clone of another document.
+ *
+ * @since 3.0
+ */
+class DocumentClone : AbstractDocument {
+
+    private static class StringTextStore : ITextStore {
+
+        private String fContent;
+
+        /**
+         * Creates a new string text store with the given content.
+         *
+         * @param content the content
+         */
+        public this(String content) {
+            //Assert.isNotNull(content);
+            fContent= content;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextStore#get(int)
+         */
+        public char get(int offset) {
+            return fContent.charAt(offset);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextStore#get(int, int)
+         */
+        public String get(int offset, int length) {
+            return fContent.substring(offset, offset + length);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextStore#getLength()
+         */
+        public int getLength() {
+            return fContent.length();
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextStore#replace(int, int, java.lang.String)
+         */
+        public void replace(int offset, int length, String text) {
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextStore#set(java.lang.String)
+         */
+        public void set(String text) {
+        }
+
+    }
+
+    /**
+     * Creates a new document clone with the given content.
+     *
+     * @param content the content
+     * @param lineDelimiters the line delimiters
+     */
+    public this(String content, String[] lineDelimiters) {
+        super();
+        setTextStore(new StringTextStore(content));
+        ConfigurableLineTracker tracker= new ConfigurableLineTracker(lineDelimiters);
+        setLineTracker(tracker);
+        getTracker().set(content);
+        completeInitialization();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/DocumentCommand.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,483 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.DocumentCommand;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+
+import java.lang.all;
+import java.util.ListIterator;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import tango.core.Exception;
+
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * Represents a text modification as a document replace command. The text
+ * modification is given as a {@link org.eclipse.swt.events.VerifyEvent} and
+ * translated into a document replace command relative to a given offset. A
+ * document command can also be used to initialize a given
+ * <code>VerifyEvent</code>.
+ * <p>
+ * A document command can also represent a list of related changes.</p>
+ */
+public class DocumentCommand {
+
+    /**
+     * A command which is added to document commands.
+     * @since 2.1
+     */
+    private static class Command : Comparable {
+        /** The offset of the range to be replaced */
+        private const int fOffset;
+        /** The length of the range to be replaced. */
+        private const int fLength;
+        /** The replacement text */
+        private const String fText;
+        /** The listener who owns this command */
+        private const IDocumentListener fOwner;
+
+        /**
+         * Creates a new command with the given specification.
+         *
+         * @param offset the offset of the replace command
+         * @param length the length of the replace command
+         * @param text the text to replace with, may be <code>null</code>
+         * @param owner the document command owner, may be <code>null</code>
+         * @since 3.0
+         */
+        public this(int offset, int length, String text, IDocumentListener owner) {
+            if (offset < 0 || length < 0)
+                throw new IllegalArgumentException(null);
+            fOffset= offset;
+            fLength= length;
+            fText= text;
+            fOwner= owner;
+        }
+
+        /**
+         * Returns the length delta for this command.
+         *
+         * @return the length delta for this command
+         */
+        public int getDeltaLength() {
+            return (fText is null ? 0 : fText.length) - fLength;
+        }
+
+        /**
+         * Executes the document command on the specified document.
+         *
+         * @param document the document on which to execute the command.
+         * @throws BadLocationException in case this commands cannot be executed
+         */
+        public void execute(IDocument document)  {
+
+            if (fLength is 0 && fText is null)
+                return;
+
+            if (fOwner !is null)
+                document.removeDocumentListener(fOwner);
+
+            document.replace(fOffset, fLength, fText);
+
+            if (fOwner !is null)
+                document.addDocumentListener(fOwner);
+        }
+
+        /*
+         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+         */
+        public int compareTo(Object object) {
+            if (isEqual(object))
+                return 0;
+
+            final Command command= cast(Command) object;
+
+            // diff middle points if not intersecting
+            if (fOffset + fLength <= command.fOffset || command.fOffset + command.fLength <= fOffset) {
+                int value= (2 * fOffset + fLength) - (2 * command.fOffset + command.fLength);
+                if (value !is 0)
+                    return value;
+            }
+            // the answer
+            return 42;
+        }
+
+        private bool isEqual(Object object) {
+            if (object is this)
+                return true;
+            if (!( cast(Command)object ))
+                return false;
+            final Command command= cast(Command) object;
+            return command.fOffset is fOffset && command.fLength is fLength;
+        }
+    }
+
+    /**
+     * An iterator, which iterates in reverse over a list.
+     */
+    private static class ReverseListIterator : Iterator {
+
+        /** The list iterator. */
+        private const ListIterator fListIterator;
+
+        /**
+         * Creates a reverse list iterator.
+         * @param listIterator the iterator that this reverse iterator is based upon
+         */
+        public this(ListIterator listIterator) {
+            if (listIterator is null)
+                throw new IllegalArgumentException(null);
+            fListIterator= listIterator;
+        }
+
+        /*
+         * @see java.util.Iterator#hasNext()
+         */
+        public bool hasNext() {
+            return fListIterator.hasPrevious();
+        }
+
+        /*
+         * @see java.util.Iterator#next()
+         */
+        public Object next() {
+            return fListIterator.previous();
+        }
+
+        /*
+         * @see java.util.Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    /**
+     * A command iterator.
+     */
+    private static class CommandIterator : Iterator {
+
+        /** The command iterator. */
+        private const Iterator fIterator;
+
+        /** The original command. */
+        private Command fCommand;
+
+        /** A flag indicating the direction of iteration. */
+        private bool fForward;
+
+        /**
+         * Creates a command iterator.
+         *
+         * @param commands an ascending ordered list of commands
+         * @param command the original command
+         * @param forward the direction
+         */
+        public this(List commands, Command command, bool forward) {
+            if (commands is null || command is null)
+                throw new IllegalArgumentException(null);
+            fIterator= forward ? commands.iterator() : new ReverseListIterator(commands.listIterator(commands.size()));
+            fCommand= command;
+            fForward= forward;
+        }
+
+        /*
+         * @see java.util.Iterator#hasNext()
+         */
+        public bool hasNext() {
+            return fCommand !is null || fIterator.hasNext();
+        }
+
+        /*
+         * @see java.util.Iterator#next()
+         */
+        public Object next() {
+
+            if (!hasNext())
+                throw new NoSuchElementException(null);
+
+            if (fCommand is null)
+                return fIterator.next();
+
+            if (!fIterator.hasNext()) {
+                final Command tempCommand= fCommand;
+                fCommand= null;
+                return tempCommand;
+            }
+
+            final Command command= cast(Command) fIterator.next();
+            final int compareValue= command.compareTo(fCommand);
+
+            if ((compareValue < 0) ^ !fForward) {
+                return command;
+
+            } else if ((compareValue > 0) ^ !fForward) {
+                final Command tempCommand= fCommand;
+                fCommand= command;
+                return tempCommand;
+
+            } else {
+                throw new IllegalArgumentException(null);
+            }
+        }
+
+        /*
+         * @see java.util.Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    /** Must the command be updated */
+    public bool doit= false;
+    /** The offset of the command. */
+    public int offset;
+    /** The length of the command */
+    public int length;
+    /** The text to be inserted */
+    public String text;
+    /**
+     * The owner of the document command which will not be notified.
+     * @since 2.1
+     */
+    public IDocumentListener owner;
+    /**
+     * The caret offset with respect to the document before the document command is executed.
+     * @since 2.1
+     */
+    public int caretOffset;
+    /**
+     * Additional document commands.
+     * @since 2.1
+     */
+    private const List fCommands;
+    /**
+     * Indicates whether the caret should be shifted by this command.
+     * @since 3.0
+     */
+    public bool shiftsCaret;
+
+
+    /**
+     * Creates a new document command.
+     */
+    /+protected+/ this() {
+        fCommands= new ArrayList();
+    }
+
+    /**
+     * Translates a verify event into a document replace command using the given offset.
+     *
+     * @param event the event to be translated
+     * @param modelRange the event range as model range
+     */
+    void setEvent(VerifyEvent event, IRegion modelRange) {
+
+        doit= true;
+        text= event.text;
+
+        offset= modelRange.getOffset();
+        length= modelRange.getLength();
+
+        owner= null;
+        caretOffset= -1;
+        shiftsCaret= true;
+        fCommands.clear();
+    }
+
+    /**
+     * Fills the given verify event with the replace text and the <code>doit</code>
+     * flag of this document command. Returns whether the document command
+     * covers the same range as the verify event considering the given offset.
+     *
+     * @param event the event to be changed
+     * @param modelRange to be considered for range comparison
+     * @return <code>true</code> if this command and the event cover the same range
+     */
+    bool fillEvent(VerifyEvent event, IRegion modelRange) {
+        event.text= text;
+        event.doit= (offset is modelRange.getOffset() && length is modelRange.getLength() && doit && caretOffset is -1);
+        return event.doit;
+    }
+
+    /**
+     * Adds an additional replace command. The added replace command must not overlap
+     * with existing ones. If the document command owner is not <code>null</code>, it will not
+     * get document change notifications for the particular command.
+     *
+     * @param commandOffset the offset of the region to replace
+     * @param commandLength the length of the region to replace
+     * @param commandText the text to replace with, may be <code>null</code>
+     * @param commandOwner the command owner, may be <code>null</code>
+     * @throws BadLocationException if the added command intersects with an existing one
+     * @since 2.1
+     */
+    public void addCommand(int commandOffset, int commandLength, String commandText, IDocumentListener commandOwner)  {
+        final Command command= new Command(commandOffset, commandLength, commandText, commandOwner);
+
+        if (intersects(command))
+            throw new BadLocationException();
+
+        final int index= Collections.binarySearch(fCommands, command);
+
+        // a command with exactly the same ranges exists already
+        if (index >= 0)
+            throw new BadLocationException();
+
+        // binary search result is defined as (-(insertionIndex) - 1)
+        final int insertionIndex= -(index + 1);
+
+        // overlaps to the right?
+        if (insertionIndex !is fCommands.size() && intersects(cast(Command) fCommands.get(insertionIndex), command))
+            throw new BadLocationException();
+
+        // overlaps to the left?
+        if (insertionIndex !is 0 && intersects(cast(Command) fCommands.get(insertionIndex - 1), command))
+            throw new BadLocationException();
+
+        fCommands.add(insertionIndex, command);
+    }
+
+    /**
+     * Returns an iterator over the commands in ascending position order.
+     * The iterator includes the original document command.
+     * Commands cannot be removed.
+     *
+     * @return returns the command iterator
+     */
+    public Iterator getCommandIterator() {
+        Command command= new Command(offset, length, text, owner);
+        return new CommandIterator(fCommands, command, true);
+    }
+
+    /**
+     * Returns the number of commands including the original document command.
+     *
+     * @return returns the number of commands
+     * @since 2.1
+     */
+    public int getCommandCount() {
+        return 1 + fCommands.size();
+    }
+
+    /**
+     * Returns whether the two given commands intersect.
+     *
+     * @param command0 the first command
+     * @param command1 the second command
+     * @return <code>true</code> if the commands intersect
+     * @since 2.1
+     */
+    private bool intersects(Command command0, Command command1) {
+        // diff middle points if not intersecting
+        if (command0.fOffset + command0.fLength <= command1.fOffset || command1.fOffset + command1.fLength <= command0.fOffset)
+            return (2 * command0.fOffset + command0.fLength) - (2 * command1.fOffset + command1.fLength) is 0;
+        return true;
+    }
+
+    /**
+     * Returns whether the given command intersects with this command.
+     *
+     * @param command the command
+     * @return <code>true</code> if the command intersects with this command
+     * @since 2.1
+     */
+    private bool intersects(Command command) {
+        // diff middle points if not intersecting
+        if (offset + length <= command.fOffset || command.fOffset + command.fLength <= offset)
+            return (2 * offset + length) - (2 * command.fOffset + command.fLength) is 0;
+        return true;
+    }
+
+    /**
+     * Executes the document commands on a document.
+     *
+     * @param document the document on which to execute the commands
+     * @throws BadLocationException in case access to the given document fails
+     * @since 2.1
+     */
+    void execute(IDocument document)  {
+
+        if (length is 0 && text is null && fCommands.size() is 0)
+            return;
+
+        DefaultPositionUpdater updater= new DefaultPositionUpdater(getCategory());
+        Position caretPosition= null;
+        try {
+            if (updateCaret()) {
+                document.addPositionCategory(getCategory());
+                document.addPositionUpdater(updater);
+                caretPosition= new Position(caretOffset);
+                document.addPosition(getCategory(), caretPosition);
+            }
+
+            final Command originalCommand= new Command(offset, length, text, owner);
+            for (final Iterator iterator= new CommandIterator(fCommands, originalCommand, false); iterator.hasNext(); )
+                (cast(Command) iterator.next()).execute(document);
+
+        } catch (BadLocationException e) {
+            // ignore
+        } catch (BadPositionCategoryException e) {
+            // ignore
+        } finally {
+            delegate(){
+            if (updateCaret()) {
+                document.removePositionUpdater(updater);
+                try {
+                    document.removePositionCategory(getCategory());
+                } catch (BadPositionCategoryException e) {
+                    Assert.isTrue(false);
+                }
+                caretOffset= caretPosition.getOffset();
+            }
+            }();
+        }
+    }
+
+    /**
+     * Returns <code>true</code> if the caret offset should be updated, <code>false</code> otherwise.
+     *
+     * @return <code>true</code> if the caret offset should be updated, <code>false</code> otherwise
+     * @since 3.0
+     */
+    private bool updateCaret() {
+        return shiftsCaret && caretOffset !is -1;
+    }
+
+    /**
+     * Returns the position category for the caret offset position.
+     *
+     * @return the position category for the caret offset position
+     * @since 3.0
+     */
+    private String getCategory() {
+        return toString();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/FindReplaceDocumentAdapterContentProposalProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,524 @@
+/*******************************************************************************
+ * Copyright (c) 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
+ *     Cagatay Calli <ccalli@gmail.com> - [find/replace] retain caps when replacing - https://bugs.eclipse.org/bugs/show_bug.cgi?id=28949
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+
+
+/**
+ * Content assist proposal provider for the {@link FindReplaceDocumentAdapter}.
+ * <p>
+ * Clients can subclass to provide additional proposals in case they are supported
+ * by their own find/replace mechanism.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> Clients must not make any assumptions about the returned
+ * proposals. This can change from release to release to adapt to
+ * changes made in {@link FindReplaceDocumentAdapter}.
+ * </p>
+ *
+ * @since 3.4
+ */
+public class FindReplaceDocumentAdapterContentProposalProvider : IContentProposalProvider {
+
+
+    /**
+     * Proposal computer.
+     */
+    private static class ProposalComputer {
+
+        private static class Proposal : IContentProposal {
+
+            private String fContent;
+            private String fLabel;
+            private String fDescription;
+            private int fCursorPosition;
+
+            this(String content, String label, String description, int cursorPosition) {
+                fContent= content;
+                fLabel= label;
+                fDescription= description;
+                fCursorPosition= cursorPosition;
+            }
+
+            public String getContent() {
+                return fContent;
+            }
+
+            public String getLabel() {
+                return fLabel;
+            }
+
+            public String getDescription() {
+                return fDescription;
+            }
+
+            public int getCursorPosition() {
+                return fCursorPosition;
+            }
+        }
+
+
+        /**
+         * The whole regular expression.
+         */
+        private const String fExpression;
+        /**
+         * The document offset.
+         */
+        private const int fDocumentOffset;
+        /**
+         * The high-priority proposals.
+         */
+        private const ArrayList fPriorityProposals;
+        /**
+         * The low-priority proposals.
+         */
+        private const ArrayList fProposals;
+        /**
+         * <code>true</code> iff <code>fExpression</code> ends with an open escape.
+         */
+        private const bool fIsEscape;
+
+        /**
+         * Creates a new Proposal Computer.
+         * @param contents the contents of the subject control
+         * @param position the cursor position
+         */
+        public this(String contents, int position) {
+            fExpression= contents;
+            fDocumentOffset= position;
+            fPriorityProposals= new ArrayList();
+            fProposals= new ArrayList();
+
+            bool isEscape= false;
+            esc: for (int i= position - 1; i >= 0; i--) {
+                if (fExpression.charAt(i) is '\\')
+                    isEscape= !isEscape;
+                else
+                    break esc;
+            }
+            fIsEscape= isEscape;
+        }
+
+        /**
+         * Computes applicable proposals for the find field.
+         * @return the proposals
+         */
+        public IContentProposal[] computeFindProposals() {
+            //characters
+            addBsProposal("\\\\", RegExMessages.getString("displayString_bs_bs"), RegExMessages.getString("additionalInfo_bs_bs")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBracketProposal("\\0", 2, RegExMessages.getString("displayString_bs_0"), RegExMessages.getString("additionalInfo_bs_0")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBracketProposal("\\x", 2, RegExMessages.getString("displayString_bs_x"), RegExMessages.getString("additionalInfo_bs_x")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBracketProposal("\\u", 2, RegExMessages.getString("displayString_bs_u"), RegExMessages.getString("additionalInfo_bs_u")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\t", RegExMessages.getString("displayString_bs_t"), RegExMessages.getString("additionalInfo_bs_t")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\R", RegExMessages.getString("displayString_bs_R"), RegExMessages.getString("additionalInfo_bs_R")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\n", RegExMessages.getString("displayString_bs_n"), RegExMessages.getString("additionalInfo_bs_n")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\r", RegExMessages.getString("displayString_bs_r"), RegExMessages.getString("additionalInfo_bs_r")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\f", RegExMessages.getString("displayString_bs_f"), RegExMessages.getString("additionalInfo_bs_f")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\a", RegExMessages.getString("displayString_bs_a"), RegExMessages.getString("additionalInfo_bs_a")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\e", RegExMessages.getString("displayString_bs_e"), RegExMessages.getString("additionalInfo_bs_e")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBracketProposal("\\c", 2, RegExMessages.getString("displayString_bs_c"), RegExMessages.getString("additionalInfo_bs_c")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+            if (! fIsEscape)
+                addBracketProposal(".", 1, RegExMessages.getString("displayString_dot"), RegExMessages.getString("additionalInfo_dot")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\d", RegExMessages.getString("displayString_bs_d"), RegExMessages.getString("additionalInfo_bs_d")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\D", RegExMessages.getString("displayString_bs_D"), RegExMessages.getString("additionalInfo_bs_D")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\s", RegExMessages.getString("displayString_bs_s"), RegExMessages.getString("additionalInfo_bs_s")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\S", RegExMessages.getString("displayString_bs_S"), RegExMessages.getString("additionalInfo_bs_S")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\w", RegExMessages.getString("displayString_bs_w"), RegExMessages.getString("additionalInfo_bs_w")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\W", RegExMessages.getString("displayString_bs_W"), RegExMessages.getString("additionalInfo_bs_W")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+            // back reference
+            addBsProposal("\\", RegExMessages.getString("displayString_bs_i"), RegExMessages.getString("additionalInfo_bs_i")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+            //quoting
+            addBsProposal("\\", RegExMessages.getString("displayString_bs"), RegExMessages.getString("additionalInfo_bs")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\Q", RegExMessages.getString("displayString_bs_Q"), RegExMessages.getString("additionalInfo_bs_Q")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\E", RegExMessages.getString("displayString_bs_E"), RegExMessages.getString("additionalInfo_bs_E")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+            //character sets
+            if (! fIsEscape) {
+                addBracketProposal("[]", 1, RegExMessages.getString("displayString_set"), RegExMessages.getString("additionalInfo_set")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("[^]", 2, RegExMessages.getString("displayString_setExcl"), RegExMessages.getString("additionalInfo_setExcl")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("[-]", 1, RegExMessages.getString("displayString_setRange"), RegExMessages.getString("additionalInfo_setRange")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addProposal("&&", RegExMessages.getString("displayString_setInter"), RegExMessages.getString("additionalInfo_setInter")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            }
+            if (! fIsEscape && fDocumentOffset > 0 && fExpression.charAt(fDocumentOffset - 1) is '\\') {
+                addProposal("\\p{}", 3, RegExMessages.getString("displayString_posix"), RegExMessages.getString("additionalInfo_posix")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addProposal("\\P{}", 3, RegExMessages.getString("displayString_posixNot"), RegExMessages.getString("additionalInfo_posixNot")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            } else {
+                addBracketProposal("\\p{}", 3, RegExMessages.getString("displayString_posix"), RegExMessages.getString("additionalInfo_posix")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("\\P{}", 3, RegExMessages.getString("displayString_posixNot"), RegExMessages.getString("additionalInfo_posixNot")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            }
+
+            //boundary matchers
+            if (fDocumentOffset is 0) {
+                addPriorityProposal("^", RegExMessages.getString("displayString_start"), RegExMessages.getString("additionalInfo_start")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            } else if (fDocumentOffset is 1 && fExpression.charAt(0) is '^') {
+                addBracketProposal("^", 1, RegExMessages.getString("displayString_start"), RegExMessages.getString("additionalInfo_start")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            }
+            if (fDocumentOffset is fExpression.length()) {
+                addProposal("$", RegExMessages.getString("displayString_end"), RegExMessages.getString("additionalInfo_end")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            }
+            addBsProposal("\\b", RegExMessages.getString("displayString_bs_b"), RegExMessages.getString("additionalInfo_bs_b")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\B", RegExMessages.getString("displayString_bs_B"), RegExMessages.getString("additionalInfo_bs_B")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\A", RegExMessages.getString("displayString_bs_A"), RegExMessages.getString("additionalInfo_bs_A")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\G", RegExMessages.getString("displayString_bs_G"), RegExMessages.getString("additionalInfo_bs_G")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\Z", RegExMessages.getString("displayString_bs_Z"), RegExMessages.getString("additionalInfo_bs_Z")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            addBsProposal("\\z", RegExMessages.getString("displayString_bs_z"), RegExMessages.getString("additionalInfo_bs_z")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+            if (! fIsEscape) {
+                //capturing groups
+                addBracketProposal("()", 1, RegExMessages.getString("displayString_group"), RegExMessages.getString("additionalInfo_group")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+                //flags
+                addBracketProposal("(?)", 2, RegExMessages.getString("displayString_flag"), RegExMessages.getString("additionalInfo_flag")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("(?:)", 3, RegExMessages.getString("displayString_flagExpr"), RegExMessages.getString("additionalInfo_flagExpr")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+                //non-capturing group
+                addBracketProposal("(?:)", 3, RegExMessages.getString("displayString_nonCap"), RegExMessages.getString("additionalInfo_nonCap")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("(?>)", 3, RegExMessages.getString("displayString_atomicCap"), RegExMessages.getString("additionalInfo_atomicCap")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+                //look around
+                addBracketProposal("(?=)", 3, RegExMessages.getString("displayString_posLookahead"), RegExMessages.getString("additionalInfo_posLookahead")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("(?!)", 3, RegExMessages.getString("displayString_negLookahead"), RegExMessages.getString("additionalInfo_negLookahead")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("(?<=)", 4, RegExMessages.getString("displayString_posLookbehind"), RegExMessages.getString("additionalInfo_posLookbehind")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("(?<!)", 4, RegExMessages.getString("displayString_negLookbehind"), RegExMessages.getString("additionalInfo_negLookbehind")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+                //greedy quantifiers
+                addBracketProposal("?", 1, RegExMessages.getString("displayString_quest"), RegExMessages.getString("additionalInfo_quest")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("*", 1, RegExMessages.getString("displayString_star"), RegExMessages.getString("additionalInfo_star")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("+", 1, RegExMessages.getString("displayString_plus"), RegExMessages.getString("additionalInfo_plus")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("{}", 1, RegExMessages.getString("displayString_exact"), RegExMessages.getString("additionalInfo_exact")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("{,}", 1, RegExMessages.getString("displayString_least"), RegExMessages.getString("additionalInfo_least")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("{,}", 1, RegExMessages.getString("displayString_count"), RegExMessages.getString("additionalInfo_count")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+                //lazy quantifiers
+                addBracketProposal("??", 1, RegExMessages.getString("displayString_questLazy"), RegExMessages.getString("additionalInfo_questLazy")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("*?", 1, RegExMessages.getString("displayString_starLazy"), RegExMessages.getString("additionalInfo_starLazy")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("+?", 1, RegExMessages.getString("displayString_plusLazy"), RegExMessages.getString("additionalInfo_plusLazy")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("{}?", 1, RegExMessages.getString("displayString_exactLazy"), RegExMessages.getString("additionalInfo_exactLazy")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("{,}?", 1, RegExMessages.getString("displayString_leastLazy"), RegExMessages.getString("additionalInfo_leastLazy")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("{,}?", 1, RegExMessages.getString("displayString_countLazy"), RegExMessages.getString("additionalInfo_countLazy")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+                //possessive quantifiers
+                addBracketProposal("?+", 1, RegExMessages.getString("displayString_questPoss"), RegExMessages.getString("additionalInfo_questPoss")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("*+", 1, RegExMessages.getString("displayString_starPoss"), RegExMessages.getString("additionalInfo_starPoss")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("++", 1, RegExMessages.getString("displayString_plusPoss"), RegExMessages.getString("additionalInfo_plusPoss")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("{}+", 1, RegExMessages.getString("displayString_exactPoss"), RegExMessages.getString("additionalInfo_exactPoss")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("{,}+", 1, RegExMessages.getString("displayString_leastPoss"), RegExMessages.getString("additionalInfo_leastPoss")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("{,}+", 1, RegExMessages.getString("displayString_countPoss"), RegExMessages.getString("additionalInfo_countPoss")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+                //alternative
+                addBracketProposal("|", 1, RegExMessages.getString("displayString_alt"), RegExMessages.getString("additionalInfo_alt")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            }
+
+            fPriorityProposals.addAll(fProposals);
+            return arraycast!(IContentProposal)( fPriorityProposals.toArray());
+        }
+
+        /**
+         * Computes applicable proposals for the replace field.
+         * @return the proposals
+         */
+        public IContentProposal[] computeReplaceProposals() {
+            if (fDocumentOffset > 0 && '$' is fExpression.charAt(fDocumentOffset - 1)) {
+                addProposal("", RegExMessages.getString("displayString_dollar"), RegExMessages.getString("additionalInfo_dollar")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            } else {
+                if (! fIsEscape)
+                    addProposal("$", RegExMessages.getString("displayString_dollar"), RegExMessages.getString("additionalInfo_dollar")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBsProposal("\\", RegExMessages.getString("displayString_replace_cap"), RegExMessages.getString("additionalInfo_replace_cap")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBsProposal("\\", RegExMessages.getString("displayString_replace_bs"), RegExMessages.getString("additionalInfo_replace_bs")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBsProposal("\\R", RegExMessages.getString("displayString_replace_bs_R"), RegExMessages.getString("additionalInfo_replace_bs_R")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("\\x", 2, RegExMessages.getString("displayString_bs_x"), RegExMessages.getString("additionalInfo_bs_x")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("\\u", 2, RegExMessages.getString("displayString_bs_u"), RegExMessages.getString("additionalInfo_bs_u")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBsProposal("\\t", RegExMessages.getString("displayString_bs_t"), RegExMessages.getString("additionalInfo_bs_t")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBsProposal("\\n", RegExMessages.getString("displayString_replace_bs_n"), RegExMessages.getString("additionalInfo_replace_bs_n")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBsProposal("\\r", RegExMessages.getString("displayString_replace_bs_r"), RegExMessages.getString("additionalInfo_replace_bs_r")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBsProposal("\\f", RegExMessages.getString("displayString_bs_f"), RegExMessages.getString("additionalInfo_bs_f")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBsProposal("\\a", RegExMessages.getString("displayString_bs_a"), RegExMessages.getString("additionalInfo_bs_a")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBsProposal("\\e", RegExMessages.getString("displayString_bs_e"), RegExMessages.getString("additionalInfo_bs_e")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBracketProposal("\\c", 2, RegExMessages.getString("displayString_bs_c"), RegExMessages.getString("additionalInfo_bs_c")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                addBsProposal("\\C", RegExMessages.getString("displayString_replace_bs_C"), RegExMessages.getString("additionalInfo_replace_bs_C")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            }
+            fPriorityProposals.addAll(fProposals);
+            return arraycast!(IContentProposal)( fPriorityProposals.toArray());
+        }
+
+        /**
+         * Adds a proposal.
+         *
+         * @param proposal the string to be inserted
+         * @param displayString the proposal's label
+         * @param additionalInfo the additional information
+         */
+        private void addProposal(String proposal, String displayString, String additionalInfo) {
+            fProposals.add(new Proposal(proposal, displayString, additionalInfo, proposal.length()));
+        }
+
+        /**
+         * Adds a proposal.
+         *
+         * @param proposal the string to be inserted
+         * @param cursorPosition the cursor position after insertion,
+         *      relative to the start of the proposal
+         * @param displayString the proposal's label
+         * @param additionalInfo the additional information
+         */
+        private void addProposal(String proposal, int cursorPosition, String displayString, String additionalInfo) {
+            fProposals.add(new Proposal(proposal, displayString, additionalInfo, cursorPosition));
+        }
+
+        /**
+         * Adds a proposal to the priority proposals list.
+         *
+         * @param proposal the string to be inserted
+         * @param displayString the proposal's label
+         * @param additionalInfo the additional information
+         */
+        private void addPriorityProposal(String proposal, String displayString, String additionalInfo) {
+            fPriorityProposals.add(new Proposal(proposal, displayString, additionalInfo, proposal.length()));
+        }
+
+        /**
+         * Adds a proposal. Ensures that existing pre- and postfixes are not duplicated.
+         *
+         * @param proposal the string to be inserted
+         * @param cursorPosition the cursor position after insertion,
+         *      relative to the start of the proposal
+         * @param displayString the proposal's label
+         * @param additionalInfo the additional information
+         */
+        private void addBracketProposal(String proposal, int cursorPosition, String displayString, String additionalInfo) {
+            String prolog= fExpression.substring(0, fDocumentOffset);
+            if (! fIsEscape && prolog.endsWith("\\") && proposal.startsWith("\\")) { //$NON-NLS-1$//$NON-NLS-2$
+                fProposals.add(new Proposal(proposal, displayString, additionalInfo, cursorPosition));
+                return;
+            }
+            for (int i= 1; i <= cursorPosition; i++) {
+                String prefix= proposal.substring(0, i);
+                if (prolog.endsWith(prefix)) {
+                    String postfix= proposal.substring(cursorPosition);
+                    String epilog= fExpression.substring(fDocumentOffset);
+                    if (epilog.startsWith(postfix)) {
+                        fPriorityProposals.add(new Proposal(proposal.substring(i, cursorPosition), displayString, additionalInfo, cursorPosition-i));
+                    } else {
+                        fPriorityProposals.add(new Proposal(proposal.substring(i), displayString, additionalInfo, cursorPosition-i));
+                    }
+                    return;
+                }
+            }
+            fProposals.add(new Proposal(proposal, displayString, additionalInfo, cursorPosition));
+        }
+
+        /**
+         * Adds a proposal that starts with a backslash.
+         * Ensures that the backslash is not repeated if already typed.
+         *
+         * @param proposal the string to be inserted
+         * @param displayString the proposal's label
+         * @param additionalInfo the additional information
+         */
+        private void addBsProposal(String proposal, String displayString, String additionalInfo) {
+            String prolog= fExpression.substring(0, fDocumentOffset);
+            int position= proposal.length();
+            // If the string already contains the backslash, do not include in the proposal
+            if (prolog.endsWith("\\")) { //$NON-NLS-1$
+                position--;
+                proposal= proposal.substring(1);
+            }
+
+            if (fIsEscape) {
+                fPriorityProposals.add(new Proposal(proposal, displayString, additionalInfo, position));
+            } else {
+                addProposal(proposal, position, displayString, additionalInfo);
+            }
+        }
+    }
+
+    /**
+     * <code>true</code> iff the processor is for the find field.
+     * <code>false</code> iff the processor is for the replace field.
+     */
+    private const bool fIsFind;
+
+
+    /**
+     * Creates a new completion proposal provider.
+     *
+     * @param isFind <code>true</code> if the provider is used for the 'find' field
+     *                  <code>false</code> if the provider is used for the 'replace' field
+     */
+    public this(bool isFind) {
+        fIsFind= isFind;
+    }
+
+    /*
+     * @see org.eclipse.jface.fieldassist.IContentProposalProvider#getProposals(java.lang.String, int)
+     */
+    public IContentProposal [] getProposals(String contents, int position) {
+        if (fIsFind)
+            return (new ProposalComputer(contents, position)).computeFindProposals();
+        return (new ProposalComputer(contents, position)).computeReplaceProposals();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IAutoEditStrategy.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.IAutoEditStrategy;
+
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+
+import java.lang.all;
+
+
+/**
+ * An auto edit strategy can adapt changes that will be applied to
+ * a text viewer's document. The strategy is informed by the text viewer
+ * about each upcoming change in form of a document command. By manipulating
+ * this document command, the strategy can influence in which way the text
+ * viewer's document is changed. Clients may implement this interface.
+ *
+ * @since 2.1
+ */
+public interface IAutoEditStrategy {
+
+    /**
+     * Allows the strategy to manipulate the document command.
+     *
+     * @param document the document that will be changed
+     * @param command the document command describing the change
+     */
+    void customizeDocumentCommand(IDocument document, DocumentCommand command);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IAutoIndentStrategy.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IAutoIndentStrategy;
+
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+
+import java.lang.all;
+
+/**
+ * Exists for backward compatibility.
+ *
+ * @deprecated since 3.0, use <code>IAutoEditStrategy</code> directly
+ */
+public interface IAutoIndentStrategy : IAutoEditStrategy {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IDelayedInputChangeProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.IDelayedInputChangeProvider;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * A delayed input change provider notifies the registered
+ * {@link IInputChangedListener} about input changes that occur after the normal
+ * operation of the provider.
+ * <p>
+ * Clients can implement that interface and its extension interfaces.</p>
+ * 
+ * @since 3.4
+ */
+public interface IDelayedInputChangeProvider {
+
+    /**
+     * Sets or clears the delayed input change listener.
+     * 
+     * @param inputChangeListener the new delayed input change listener, or
+     *        <code>null</code> if none
+     * @since 3.4
+     */
+    void setDelayedInputChangeListener(IInputChangedListener inputChangeListener);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IDocumentAdapter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IDocumentAdapter;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.swt.custom.StyledTextContent;
+
+
+/**
+ * Adapts an {@link org.eclipse.jface.text.IDocument}to the
+ * {@link org.eclipse.swt.custom.StyledTextContent} interface. The document
+ * adapter is used by {@link org.eclipse.jface.text.TextViewer} to translate
+ * document changes into styled text content changes and vice versa.
+ * <p>
+ * Clients may implement this interface and override
+ * <code>TextViewer.createDocumentAdapter</code> if they want to intercept the
+ * communication between the viewer's text widget and the viewer's document.
+ * <p>
+ * In order to provide backward compatibility for clients of
+ * <code>IDocumentAdapter</code>, extension interfaces are used as a means of
+ * evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.IDocumentAdapterExtension} since version
+ *     2.0 introducing a way of batching a sequence of document changes into a
+ *     single styled text content notification</li>
+ * </ul>
+ *
+ * @see org.eclipse.jface.text.IDocumentAdapterExtension
+ * @see org.eclipse.jface.text.IDocument
+ */
+public interface IDocumentAdapter : StyledTextContent {
+
+    /**
+     * Sets the adapters document.
+     *
+     * @param document the document to be adapted
+     */
+    void setDocument(IDocument document);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IDocumentAdapterExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IDocumentAdapterExtension;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IDocumentAdapter}.
+ * <p>
+ * Introduces the concepts of batching a series of document changes into a
+ * single styled text content change notification. Batching start when a client
+ * calls <code>stopForwardingDocumentChanges</code>. After that call this
+ * document adapter does not send out styled text content change notifications
+ * until <code>resumeForwardingDocumentChanges</code> is called. On
+ * <code>resumeForwardingDocumentChanges</code>, it sends out one styled text
+ * content change notification that covers all changes that have been applied to
+ * the document since calling <code>stopForwardingDocumentChanges</code>.
+ *
+ * @since 2.0
+ */
+public interface IDocumentAdapterExtension {
+
+    /**
+     * Stops forwarding document changes to the styled text.
+     */
+    void stopForwardingDocumentChanges();
+
+    /**
+     * Resumes forwarding document changes to the styled text.
+     * Also forces the styled text to catch up with all the changes
+     * that have been applied since <code>stopForwardingDocumentChanges</code>
+     * has been called.
+     */
+    void resumeForwardingDocumentChanges();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IEditingSupport.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,214 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IEditingSupport;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Implemented by tools supporting the editing process.
+ * <p>
+ * Clients may ask an <code>IEditingSupport</code> whether it is currently
+ * displaying a shell that has focus, and whether it is the origin of a document
+ * event. Depending on the answers to these queries, clients may decide to react
+ * differently to incoming notifications about events. For example, a special
+ * editing mode, that usually deactivates when the main shell looses focus, may
+ * decide to not deactivate if the focus event was triggered by an
+ * <code>IEditingSupport</code>.
+ * </p>
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see IEditingSupportRegistry
+ * @since 3.1
+ */
+public interface IEditingSupport {
+    /**
+     * Returns <code>true</code> if the receiver is the originator of a
+     * <code>DocumentEvent</code> and if that <code>event</code> is related
+     * to <code>subjectRegion</code>.
+     * <p>
+     * The relationship between <code>event</code> and
+     * <code>subjectRegion</code> is not always obvious. Often, the main
+     * editing area being monitored by the caller will be at
+     * <code>subjectRegion</code>, when the receiver modifies the underlying
+     * document at a different location without wanting to interrupt the normal
+     * typing flow of the user.
+     * </p>
+     * <p>
+     * An example would be an editor that automatically increments the section
+     * number of the next section when the user typed in a new section title. In
+     * this example, the subject region is the current typing location, while
+     * the increment results in a document change further down in the text.
+     * </p>
+     *
+     * @param event the <code>DocumentEvent</code> in question
+     * @param subjectRegion the region that the caller is interested in
+     * @return <code>true</code> if <code>event</code> was triggered by the
+     *         receiver and relates to <code>subjectRegion</code>
+     */
+    bool isOriginator(DocumentEvent event, IRegion subjectRegion);
+
+    /**
+     * Returns <code>true</code> if the receiver is showing a shell which has
+     * focus, <code>false</code> if it does not have focus or the helper has
+     * no shell.
+     *
+     * @return <code>true</code> if the support's shell has focus,
+     *         <code>false</code> otherwise
+     */
+    bool ownsFocusShell();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IEditingSupportRegistry.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IEditingSupportRegistry;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * A registry for <code>IEditingSupport</code>s.
+ * <p>
+ * This interface is not meant to be implemented outside the JFace text
+ * framework.</p>
+ * 
+ * @see IEditingSupport
+ * @since 3.1
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IEditingSupportRegistry {
+
+    /**
+     * Register a support with the registry. If the support is already
+     * registered, nothing happens.
+     *
+     * @param support an editor support
+     */
+    public void register(IEditingSupport support);
+
+    /**
+     * Deregister a support with the registry. If the support is not registered,
+     * or <code>support</code> is <code>null</code>, nothing happens.
+     *
+     * @param support the helper to deregister, or <code>null</code>
+     */
+    public void unregister(IEditingSupport support);
+
+    /**
+     * Returns the current editor helpers.
+     *
+     * @return an non- <code>null</code> array of currently registered editor
+     *         helpers
+     */
+    public IEditingSupport[] getRegisteredSupports();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IEventConsumer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.IEventConsumer;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.events.VerifyEvent;
+
+/**
+ * Implementers can register with a text viewer and receive
+ * <code>VerifyEvent</code>s before the text viewer they are registered with.
+ * If the event consumer marks events as processed by turning their
+ * <code>doit</code> field to <code>false</code> the text viewer
+ * subsequently ignores them.</p>
+ * <p>
+ * Clients may implement this interface.</p>
+ * <p>
+ * {@link org.eclipse.jface.text.ITextViewerExtension2}allows clients to manage
+ * the {@link org.eclipse.swt.events.VerifyListener}s of a text viewer. This
+ * makes <code>IEventConsumer</code> obsolete.</p>
+ *
+ * @see org.eclipse.jface.text.ITextViewer
+ * @see org.eclipse.jface.text.ITextViewerExtension2
+ * @see org.eclipse.swt.events.VerifyEvent
+ */
+public interface IEventConsumer {
+
+    /**
+     * Processes the given event and marks it as done if it should
+     * be ignored by subsequent receivers.
+     *
+     * @param event the verify event which will be investigated
+     */
+    public void processEvent(VerifyEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTarget.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,248 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IFindReplaceTarget;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.swt.graphics.Point;
+
+
+/**
+ * Defines the target for finding and replacing strings.
+ * <p>
+ * The two main methods are <code>findAndSelect</code> and
+ * <code>replaceSelection</code>. The target does not provide any way to
+ * modify the content other than replacing the selection.
+ * <p>
+ *
+ * In order to provide backward compatibility for clients of
+ * <code>IFindReplaceTarget</code>, extension interfaces are used as a means
+ * of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.IFindReplaceTargetExtension} since version
+ *     2.0 introducing the notion of find/replace session and of a find/replace
+ *     scope. In additions, in allows clients to replace all occurrences of a given
+ *     find query.</li>
+ * <li>{@link org.eclipse.jface.text.IFindReplaceTargetExtension3} since
+ *     version 3.0 allowing clients to specify search queries as regular
+ *     expressions.</li>
+ * </ul>
+ * <p>
+ * Clients of a <code>IFindReplaceTarget</code> that also implements the
+ * <code>IFindReplaceTargetExtension</code> have to indicate the start of a find/replace
+ * session before using the target and to indicate the end of the session when the
+ * target is no longer used.
+ *
+ * @see org.eclipse.jface.text.IFindReplaceTargetExtension
+ * @see org.eclipse.jface.text.IFindReplaceTargetExtension3
+ */
+public interface IFindReplaceTarget {
+
+    /**
+     * Returns whether a find operation can be performed.
+     *
+     * @return whether a find operation can be performed
+     */
+    bool canPerformFind();
+
+    /**
+     * Searches for a string starting at the given widget offset and using the specified search
+     * directives. If a string has been found it is selected and its start offset is
+     * returned.
+     * <p>
+     * Replaced by {@link IFindReplaceTargetExtension3#findAndSelect(int, String, bool, bool, bool, bool)}.
+     *
+     * @param widgetOffset the widget offset at which searching starts
+     * @param findString the string which should be found
+     * @param searchForward <code>true</code> searches forward, <code>false</code> backwards
+     * @param caseSensitive <code>true</code> performs a case sensitive search, <code>false</code> an insensitive search
+     * @param wholeWord if <code>true</code> only occurrences are reported in which the findString stands as a word by itself
+     * @return the position of the specified string, or -1 if the string has not been found
+     */
+    int findAndSelect(int widgetOffset, String findString, bool searchForward, bool caseSensitive, bool wholeWord);
+
+    /**
+     * Returns the currently selected range of characters as a offset and length in widget coordinates.
+     *
+     * @return the currently selected character range in widget coordinates
+     */
+    Point getSelection();
+
+    /**
+     * Returns the currently selected characters as a string.
+     *
+     * @return the currently selected characters
+     */
+    String getSelectionText();
+
+    /**
+     * Returns whether this target can be modified.
+     *
+     * @return <code>true</code> if target can be modified
+     */
+    bool isEditable();
+
+    /**
+     * Replaces the currently selected range of characters with the given text.
+     * This target must be editable. Otherwise nothing happens.
+     * <p>
+     * Replaced by {@link IFindReplaceTargetExtension3#replaceSelection(String, bool)}.
+     *
+     * @param text the substitution text
+     */
+    void replaceSelection(String text);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTargetExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,241 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IFindReplaceTargetExtension;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IFindReplaceTarget}.
+ * <p>
+ * Introduces the concepts of find/replace sessions, searching in a limiting
+ * scope and a replace-all mode.
+ * <p>
+ * If a scope is set, <code>findAndSelect</code> is limited to the scope.
+ * Occurrences outside of the scope are not considered.
+ *
+ * @since 2.0
+ */
+public interface IFindReplaceTargetExtension {
+
+    /**
+     * Indicates that a session with the target begins.
+     * All calls except <code>beginSession()</code> and <code>endSession()</code> to
+     * <code>IFindReplaceTarget</code> and
+     * <code>IFindReplaceTargetExtension</code> must be embedded within calls to
+     * <code>beginSession()</code> and <code>endSession()</code>.
+     *
+     * @see #endSession()
+     */
+    void beginSession();
+
+    /**
+     * Indicates that a session with the target ends.
+     *
+     * @see #beginSession()
+     */
+    void endSession();
+
+    /**
+     * Returns the find scope of the target, <code>null</code> for global scope.
+     *
+     * @return returns the find scope of the target, may be <code>null</code>
+     */
+    IRegion getScope();
+
+    /**
+     * Sets the find scope of the target to operate on. <code>null</code>
+     * indicates that the global scope should be used.
+     *
+     * @param scope the find scope of the target, may be <code>null</code>
+     */
+    void setScope(IRegion scope_);
+
+    /**
+     * Returns the currently selected range of lines as a offset and length.
+     *
+     * @return the currently selected line range
+     */
+    Point getLineSelection();
+
+    /**
+     * Sets a selection.
+     *
+     * @param offset the offset of the selection
+     * @param length the length of the selection
+     */
+    void setSelection(int offset, int length);
+
+    /**
+     * Sets the scope highlight color
+     *
+     * @param color the color of the scope highlight
+     */
+    void setScopeHighlightColor(Color color);
+
+
+    /**
+     * Sets the target's replace-all mode.
+     *
+     * @param replaceAll <code>true</code> if this target should switch into replace-all mode,
+     *  <code>false</code> if it should leave the replace-all state
+     */
+    void setReplaceAllMode(bool replaceAll);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTargetExtension3.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.IFindReplaceTargetExtension3;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IFindReplaceTarget}.
+ * <p>
+ * Extends the find replace target's <code>findAndSelect</code> and
+ * <code>replaceSelection</code> methods to allow and be aware of regular
+ * expression find/replace.
+ *
+ * @since 3.0
+ */
+public interface IFindReplaceTargetExtension3 {
+
+    /**
+     * Searches for a string starting at the given offset and using the specified search
+     * directives. If a string has been found it is selected and its start offset is
+     * returned. If regExSearch is <code>true</code> the findString is
+     * interpreted as a regular expression.
+     *
+     * @param offset the offset at which searching starts
+     * @param findString the specification of what should be found
+     * @param searchForward <code>true</code> searches forward, <code>false</code> backwards
+     * @param caseSensitive <code>true</code> performs a case sensitive search, <code>false</code> an insensitive search
+     * @param wholeWord if <code>true</code> only occurrences are reported in which the findString stands as a word by itself.
+     *              Must not be used in combination with <code>regExSearch</code>.
+     * @param regExSearch if <code>true</code> findString represents a regular expression
+     *              Must not be used in combination with <code>wholeWord</code>.
+     * @return the position of the specified string, or -1 if the string has not been found
+     * @throws java.util.regex.PatternSyntaxException if regExSearch is <code>true</code> and findString is an invalid regular expression
+     */
+    int findAndSelect(int offset, String findString, bool searchForward, bool caseSensitive, bool wholeWord, bool regExSearch);
+
+    /**
+     * Replaces the currently selected range of characters with the given text.
+     * If regExReplace is <code>true</code> the text is interpreted as a
+     * regular expression that is used to process the selected text in order to
+     * produce the actual replacement of the selected text.
+     * <p>
+     * This target must be editable. Otherwise nothing happens.
+     *
+     * @param text the specification of the substitution text
+     * @param regExReplace if <code>true</code> text represents a regular
+     *            expression
+     * @throws IllegalStateException in case of regular expressions, this call
+     *             is not preceded by a call to <code>findAndSelect</code>
+     * @throws java.util.regex.PatternSyntaxException if regExReplace is
+     *             <code>true</code> and text is an invalid regular expression
+     */
+    void replaceSelection(String text, bool regExReplace);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControl.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,346 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IInformationControl;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+
+
+/**
+ * Interface of a control presenting information. The information is given in
+ * the form of an input object. It can be either the content itself or a
+ * description of the content. The specification of what is required from an
+ * input object is left to the implementers of this interface.
+ * <p>
+ * <em>If this information control is used by a {@link AbstractHoverInformationControlManager}
+ * then that manager will own this control and override any properties that
+ * may have been set before by any other client.</em></p>
+ * <p>
+ * The information control must not grab focus when made visible using
+ * <code>setVisible(true)</code>.
+ *
+ * In order to provide backward compatibility for clients of
+ * <code>IInformationControl</code>, extension interfaces are used as a means
+ * of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.IInformationControlExtension} since
+ *     version 2.0 introducing the predicate of whether the control has anything to
+ *     show or would be empty</li>
+ * <li>{@link org.eclipse.jface.text.IInformationControlExtension2} since
+ *     version 2.1 replacing the original concept of textual input by general input
+ *     objects.</li>
+ * <li>{@link org.eclipse.jface.text.IInformationControlExtension3} since
+ *     version 3.0 providing access to the control's bounds and introducing
+ *     the concept of persistent size and location.</li>
+ * <li>{@link org.eclipse.jface.text.IInformationControlExtension4} since
+ *     version 3.3, adding API which allows to set this information control's status field text.</li>
+ * <li>{@link org.eclipse.jface.text.IInformationControlExtension5} since
+ *     version 3.4, adding API to get the visibility of the control, to
+ *     test whether another control is a child of the information control,
+ *     to compute size constraints based on the information control's main font
+ *     and to return a control creator for an enriched version of this information control.</li>
+ * </ul>
+ * <p>
+ * Clients can implement this interface and its extension interfaces,
+ * subclass {@link AbstractInformationControl}, or use the (text-based)
+ * default implementation {@link DefaultInformationControl}.
+ *
+ * @see org.eclipse.jface.text.IInformationControlExtension
+ * @see org.eclipse.jface.text.IInformationControlExtension2
+ * @see org.eclipse.jface.text.IInformationControlExtension3
+ * @see org.eclipse.jface.text.IInformationControlExtension4
+ * @see org.eclipse.jface.text.IInformationControlExtension5
+ * @see AbstractInformationControl
+ * @see DefaultInformationControl
+ * @since 2.0
+ */
+public interface IInformationControl {
+
+    /**
+     * Sets the information to be presented by this information control.
+     * <p>
+     * Replaced by {@link IInformationControlExtension2#setInput(Object)}.
+     *
+     * @param information the information to be presented
+     */
+    void setInformation(String information);
+
+    /**
+     * Sets the information control's size constraints. A constraint value of
+     * {@link SWT#DEFAULT} indicates no constraint. This method must be called before
+     * {@link #computeSizeHint()} is called.
+     * <p>
+     * Note: An information control which implements {@link IInformationControlExtension3}
+     * may ignore this method or use it as hint for its very first appearance.
+     * </p>
+     * @param maxWidth the maximal width of the control  to present the information, or {@link SWT#DEFAULT} for not constraint
+     * @param maxHeight the maximal height of the control to present the information, or {@link SWT#DEFAULT} for not constraint
+     */
+    void setSizeConstraints(int maxWidth, int maxHeight);
+
+    /**
+     * Computes and returns a proposal for the size of this information control depending
+     * on the information to present. The method tries to honor known size constraints but might
+     * return a size that exceeds them.
+     *
+     * @return the computed size hint
+     */
+    Point computeSizeHint();
+
+    /**
+     * Controls the visibility of this information control.
+     * <p>
+     * <strong>Note:</strong> The information control must not grab focus when
+     * made visible.
+     * </p>
+     * 
+     * @param visible <code>true</code> if the control should be visible
+     */
+    void setVisible(bool visible);
+
+    /**
+     * Sets the size of this information control.
+     *
+     * @param width the width of the control
+     * @param height the height of the control
+     */
+    void setSize(int width, int height);
+
+    /**
+     * Sets the location of this information control.
+     *
+     * @param location the location
+     */
+    void setLocation(Point location);
+
+    /**
+     * Disposes this information control.
+     */
+    void dispose();
+
+    /**
+     * Adds the given listener to the list of dispose listeners.
+     * If the listener is already registered it is not registered again.
+     *
+     * @param listener the listener to be added
+     */
+    void addDisposeListener(DisposeListener listener);
+
+    /**
+     * Removes the given listeners from the list of dispose listeners.
+     * If the listener is not registered this call has no effect.
+     *
+     * @param listener the listener to be removed
+     */
+    void removeDisposeListener(DisposeListener listener);
+
+    /**
+     * Sets the foreground color of this information control.
+     *
+     * @param foreground the foreground color of this information control
+     */
+    void setForegroundColor(Color foreground);
+
+    /**
+     * Sets the background color of this information control.
+     *
+     * @param background the background color of this information control
+     */
+    void setBackgroundColor(Color background);
+
+    /**
+     * Returns whether this information control (or one of its children) has the focus.
+     * The suggested implementation is like this (<code>fShell</code> is this information control's shell):
+     * <pre>return fShell.getDisplay().getActiveShell() is fShell</pre>
+     *
+     * @return <code>true</code> when the information control has the focus, otherwise <code>false</code>
+     */
+    bool isFocusControl();
+
+    /**
+     * Sets the keyboard focus to this information control.
+     */
+    void setFocus();
+
+    /**
+     * Adds the given listener to the list of focus listeners.
+     * If the listener is already registered it is not registered again.
+     * <p>
+     * The suggested implementation is to install listeners for {@link SWT#Activate} and {@link SWT#Deactivate}
+     * on the shell and forward events to the focus listeners. Clients are
+     * encouraged to subclass {@link AbstractInformationControl}, which does this
+     * for free.
+     * </p>
+     * 
+     * @param listener the listener to be added
+     */
+    void addFocusListener(FocusListener listener);
+
+    /**
+     * Removes the given listeners from the list of focus listeners.
+     * If the listener is not registered this call has no affect.
+     *
+     * @param listener the listener to be removed
+     */
+    void removeFocusListener(FocusListener listener);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlCreator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.IInformationControlCreator;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+import org.eclipse.swt.widgets.Shell;
+
+
+/**
+ * Interface of a factory for information controls (
+ * {@link org.eclipse.jface.text.IInformationControl}).
+ *
+ * In order to provide backward compatibility for clients of
+ * <code>IInformationControlCreator</code>, extension interfaces are used as
+ * a means of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.IInformationControlCreatorExtension} since
+ *     version 3.0 introducing checks of whether existing information control can
+ *     be reused and whether information control creators can replace each other.
+ * </li>
+ * </ul>
+ *
+ * @see org.eclipse.jface.text.IInformationControlCreatorExtension
+ * @since 2.0
+ */
+public interface IInformationControlCreator {
+
+    /**
+     * Creates a new information control with the given shell as the control's parent.
+     *
+     * @param parent the parent shell
+     * @return the created information control
+     */
+    IInformationControl createInformationControl(Shell parent);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlCreatorExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IInformationControlCreatorExtension;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IInformationControlCreator}<p>
+ * Introduces tests whether information controls can be reused and whether information
+ * control creators can replace each other.
+ *
+ * @see org.eclipse.jface.text.IInformationControlCreator
+ * @see org.eclipse.jface.text.IInformationControl
+ * @since 3.0
+ */
+public interface IInformationControlCreatorExtension {
+
+    /**
+     * Tests if an existing information control can be reused.
+     *
+     * @param control the information control to test
+     * @return <code>true</code> if the control can be reused
+     */
+    bool canReuse(IInformationControl control);
+
+    /**
+     * Tests whether this information control creator can replace the given
+     * information control creator. This is the case if the two creators create
+     * the same kind of information controls.
+     *
+     * @param creator the creator to be checked
+     * @return <code>true</code> if the given creator can be replaced,
+     *         <code>false</code> otherwise
+     */
+    bool canReplace(IInformationControlCreator creator);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IInformationControlExtension;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+
+/**
+ * Extension interface {@link org.eclipse.jface.text.IInformationControl}.
+ * <p>
+ * As it is the responsibility of the implementer of
+ * {@link org.eclipse.jface.text.IInformationControl} and
+ * {@link org.eclipse.jface.text.IInformationControlExtension2} to specify the
+ * concrete nature of the information control's input, only the implementer can
+ * know whether it has something to show or not.
+ *
+ * @since 2.0
+ */
+public interface IInformationControlExtension {
+
+    /**
+     * Returns whether this information control has contents to be displayed.
+     *
+     * @return <code>true</code> if there is contents to be displayed.
+     */
+    bool hasContents();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IInformationControlExtension2;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IInformationControl}.
+ * <p>
+ * Replaces the concept of textual information to be displayed with the more
+ * general concept of an input of the information control. Text to be displayed
+ * set with <code>setInformation(String)</code> is ignored.
+ *
+ * @see org.eclipse.jface.text.IInformationControl
+ * @since 2.1
+ */
+public interface IInformationControlExtension2 {
+
+    /**
+     * Sets the input to be presented in this information control. The concrete
+     * contract the input object is expected to adhere is defined by the
+     * implementer of this interface.
+     *
+     * @param input the object to be used as input for this control
+     */
+    void setInput(Object input);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlExtension3.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,228 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IInformationControlExtension3;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.swt.graphics.Rectangle;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IInformationControl}.
+ * Adds API which allows to get this information control's bounds and introduces
+ * the concept of persistent size and location by introducing predicates for
+ * whether the information control supports restoring of size and location.
+ * <p>
+ * Note: An information control which implements this interface can ignore calls
+ * to
+ * {@link org.eclipse.jface.text.IInformationControl#setSizeConstraints(int, int)}
+ * or use it as hint for its very first appearance.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.IInformationControl
+ * @since 3.0
+ */
+public interface IInformationControlExtension3 {
+
+    /**
+     * Returns a rectangle describing the receiver's size and location
+     * relative to its parent (or its display if its parent is null).
+     * <p>
+     * Note: If the receiver is already disposed then this methods must
+     * return the last valid location and size.
+     * </p>
+     *
+     * @return the receiver's bounding rectangle
+     */
+    Rectangle getBounds();
+
+    /**
+     * Computes the trim for this control. The trim is the space around the
+     * information control's actual content area. It includes all borders of the
+     * control and other static content placed around the content area (e.g. a
+     * toolbar).
+     * 
+     * @return The receiver's trim. <code>x</code> and <code>y</code> denote
+     *         the upper left corner of the trimming relative to this control's
+     *         location i.e. this will most likely be negative values.
+     *         <code>width</code> and <code>height</code> represent the
+     *         border sizes (the sum of the horizontal and vertical trimmings,
+     *         respectively).
+     */
+    Rectangle computeTrim();
+
+    /**
+     * Tells whether this control allows to restore the previously
+     * used size.
+     * <p>
+     * Note: This is not a static property - it can change during the
+     * lifetime of this control.</p>
+     *
+     * @return <code>true</code> if restoring size is supported
+     */
+    bool restoresSize();
+
+    /**
+     * Tells whether this control allows to restore the previously
+     * used location.
+     * <p>
+     * Note: This is not a static property - it can change during the
+     * lifetime of this control.</p>
+     *
+     * @return <code>true</code> if restoring location is supported
+     */
+    bool restoresLocation();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlExtension4.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IInformationControlExtension4;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IInformationControl}.
+ * Adds API which allows to set this information control's status field text.
+ *
+ * @see org.eclipse.jface.text.IInformationControl
+ * @since 3.3
+ */
+public interface IInformationControlExtension4 {
+
+    /**
+     * Sets the text of the status field.
+     * <p>
+     * The implementor can specify whether the new text affects an
+     * already visible information control.
+     * </p>
+     * 
+     * @param statusFieldText the text to be used in the optional status field
+     *                         or <code>null</code> if the status field should be hidden
+     * @since 3.2
+     */
+    public void setStatusText(String statusFieldText);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlExtension5.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.IInformationControlExtension5;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.jface.resource.JFaceResources;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IInformationControl}.
+ * Adds API
+ * <ul>
+ * <li>to test the visibility of the control,</li>
+ * <li>to test whether another control is a child of the information control,</li>
+ * <li>to compute size constraints based on the information control's main font and</li>
+ * <li>to return a control creator for an enriched version of this information control.</li>
+ * </ul>
+ * 
+ * <p>
+ * <b>Important:</b> Enriching this information control only works properly if
+ * {@link IInformationControl#isFocusControl()} is implemented like this (<code>fShell</code>
+ * is the control's shell):
+ * 
+ * <pre>
+ * return fShell.getDisplay().getActiveShell() is fShell
+ * </pre>
+ * Likewise,
+ * {@link IInformationControl#addFocusListener(org.eclipse.swt.events.FocusListener)}
+ * should install listeners for {@link SWT#Activate} and {@link SWT#Deactivate}
+ * on the shell and forward events to the focus listeners. Clients are
+ * encouraged to subclass {@link AbstractInformationControl}, which does this
+ * for free.
+ * </p>
+ * 
+ * @see org.eclipse.jface.text.IInformationControl
+ * @since 3.4
+ */
+public interface IInformationControlExtension5 {
+
+    /**
+     * Tests whether the given control is this information control
+     * or a child of this information control.
+     * 
+     * @param control the control to test
+     * @return <code>true</code> iff the given control is this information control
+     * or a child of this information control
+     */
+    public bool containsControl(Control control);
+    
+    /**
+     * @return <code>true</code> iff the information control is currently visible
+     */
+    public abstract bool isVisible();
+    
+    /**
+     * Computes the width- and height constraints of the information control in
+     * pixels, based on the given width and height in characters. Implementors
+     * should use the main font of the information control to do the
+     * characters-to-pixels conversion. This is typically the
+     * {@link JFaceResources#getDialogFont() dialog font}.
+     * 
+     * @param widthInChars the width constraint in number of characters
+     * @param heightInChars the height constraint in number of characters
+     * @return a point with width and height in pixels, or <code>null</code>
+     *         to use the subject control's font to calculate the size
+     */
+    public Point computeSizeConstraints(int widthInChars, int heightInChars);
+    
+    /**
+     * Returns the rich information control creator for this information control.
+     * <p>
+     * The returned information control creator is used to create an enriched version of this
+     * information control, e.g. when the mouse is moved into this control and it needs to be
+     * {@link ITextViewerExtension8#setHoverEnrichMode(org.eclipse.jface.text.ITextViewerExtension8.EnrichMode) enriched}
+     * or when it needs to be made sticky for other reasons.
+     * </p>
+     * <p>
+     * The returned information control creator must create information controls
+     * that implement {@link IInformationControlExtension3} and {@link IInformationControlExtension2},
+     * and whose {@link IInformationControlExtension2#setInput(Object)} accepts all inputs that are
+     * also supported by this information control.
+     * </p>
+     * <p>
+     * Note that automatic enriching of this information control works only if it also implements
+     * {@link IInformationControlExtension3}.
+     * </p>
+     * <p>
+     * This method may be called frequently, so implementors should ensure it returns quickly,
+     * e.g. by caching the returned creator.
+     * </p>
+     *
+     * @return the information presenter control creator or <code>null</code> to disable enriching
+     */
+    IInformationControlCreator getInformationPresenterControlCreator();
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IInputChangedListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,175 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.IInputChangedListener;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * A listener which is notified when the target's input changes.
+ * <p>
+ * Clients can implement that interface and its extension interfaces.</p>
+ *
+ * @since 3.4
+ */
+public interface IInputChangedListener {
+
+    /**
+     * Called when a the input has changed.
+     * 
+     * @param newInput the new input, or <code>null</code> iff the listener should not show any new input
+     */
+    void inputChanged(Object newInput);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IMarkRegionTarget.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IMarkRegionTarget;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A mark region target to support marked regions as found in emacs.
+ *
+ * @since 2.0
+ */
+public interface IMarkRegionTarget {
+
+    /**
+     * Sets or clears a mark at the current cursor position.
+     *
+     * @param set sets the mark if <code>true</code>, clears otherwise.
+     */
+    void setMarkAtCursor(bool set);
+
+    /**
+     * Swaps the mark and cursor position if the mark is in the visible region.
+     */
+    void swapMarkAndCursor();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IMarkSelection.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,199 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IMarkSelection;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.jface.viewers.ISelection;
+
+
+/**
+ * A mark selection can be sent out by text viewers. By checking the
+ * type of the selection selection listeners can determine whether a selection
+ * event is about a mark or a normal text selection.
+ * <p>
+ * This interface is not intended to be implemented by clients other than
+ * {@link org.eclipse.jface.text.ITextViewer} implementers.
+ * </p>
+ *
+ * @since 2.0
+ */
+public interface IMarkSelection : ISelection {
+
+    /**
+     * Returns the marked document.
+     *
+     * @return the marked document
+     */
+    IDocument getDocument();
+
+    /**
+     * Returns the mark position. The offset may be <code>-1</code> if there's no marked region.
+     *
+     * @return the mark position or <code>-1</code> if there is no marked region
+     */
+    int getOffset();
+
+    /**
+     * Returns the length of the mark selection. The length may be negative, if the caret
+     * is before the mark position. The length has no meaning if <code>getOffset()</code>
+     * returns <code>-1</code>.
+     *
+     * @return the length of the mark selection. Result is undefined for <code>getOffset is -1</code>
+     */
+    int getLength();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IPaintPositionManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IPaintPositionManager;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+
+/**
+ * Manages and updates positions used by {@link IPainter}s.
+ *
+ * @see org.eclipse.jface.text.IPainter
+ * @since 2.1
+ */
+public interface IPaintPositionManager {
+
+    /**
+     * Starts managing the given position until <code>unmanagePosition</code> is called.
+     *
+     * @param position the position to manage
+     * @see #unmanagePosition(Position)
+     */
+    void managePosition(Position position);
+
+    /**
+     * Stops managing the given position. If the position is not managed
+     * by this managed, this call has no effect.
+     *
+     * @param position the position that should no longer be managed
+     */
+    void unmanagePosition(Position position);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IPainter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,258 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IPainter;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * A painter is responsible for creating, managing, updating, and removing
+ * visual decorations on an <code>ITextViewer</code>'s text widget. Examples
+ * are the highlighting of the caret line, the print margin, or the highlighting
+ * of matching peer characters such as pairs of brackets.</p>
+ * <p>
+ * Clients may implement this interface.</p>
+ * <p>
+ * Painters should be registered with a
+ * {@link org.eclipse.jface.text.PaintManager}. The paint manager tracks
+ * several classes of events issued by an <code>ITextViewer</code> and reacts
+ * by appropriately invoking the registered painters.
+ * <p>
+ * Painters are either active or inactive. Usually, painters are initially
+ * inactive and are activated by the first call to their <code>paint</code>
+ * method. Painters can be deactivated by calling <code>deactivate</code>.
+ * Inactive painter can be reactivated by calling <code>paint</code>.
+ * <p>
+ * Painters usually have to manage state information. E.g., a painter painting a
+ * caret line highlight must redraw the previous and the actual caret line in
+ * the advent of a change of the caret position. This state information must be
+ * adapted to changes of the viewer's content. In order to support this common
+ * scenario, the <code>PaintManager</code> gives a painter access to a
+ * {@link org.eclipse.jface.text.IPaintPositionManager}. The painter can use
+ * this updater to manage its state information.
+ * <p>
+ *
+ * @see org.eclipse.jface.text.PaintManager
+ * @see org.eclipse.jface.text.IPaintPositionManager
+ * @since 2.1
+ */
+public interface IPainter {
+
+    /**
+     * Constant describing the reason of a repaint request: selection changed.
+     */
+    static const int SELECTION= 0;
+    /**
+     * Constant describing the reason of a repaint request: text changed.
+     */
+    static const int TEXT_CHANGE= 1;
+    /**
+     * Constant describing the reason of a repaint request: key pressed.
+     */
+    static const int KEY_STROKE= 2;
+    /**
+     * Constant describing the reason of a repaint request: mouse button pressed.
+     */
+    static const int MOUSE_BUTTON= 4;
+    /**
+     * Constant describing the reason of a repaint request: paint manager internal change.
+     */
+    static const int INTERNAL= 8;
+    /**
+     * Constant describing the reason of a repaint request: paint manager or painter configuration changed.
+     */
+    static const int CONFIGURATION= 16;
+
+
+    /**
+     * Disposes this painter. Prior to disposing, a painter should be deactivated. A disposed
+     * painter can not be reactivated.
+     *
+     * @see #deactivate(bool)
+     */
+    void dispose();
+
+    /**
+     * Requests this painter to repaint because of the given reason. Based on
+     * the given reason the painter can decide whether it will repaint or not.
+     * If it repaints and is inactive, it will activate itself.
+     *
+     * @param reason the repaint reason, value is one of the constants defined
+     *            in this interface
+     */
+    void paint(int reason);
+
+    /**
+     * Deactivates this painter. If the painter is inactive, this call does not
+     * have any effect. <code>redraw</code> indicates whether the painter
+     * should remove any decoration it previously applied. A deactivated painter
+     * can be reactivated by calling <code>paint</code>.
+     *
+     * @param redraw <code>true</code> if any previously applied decoration
+     *            should be removed
+     * @see #paint(int)
+     */
+    void deactivate(bool redraw);
+
+    /**
+     * Sets the paint position manager that can be used by this painter or removes any previously
+     * set paint position manager.
+     *
+     * @param manager the paint position manager or <code>null</code>
+     */
+    void setPositionManager(IPaintPositionManager manager);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IRewriteTarget.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IRewriteTarget;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+ /**
+  * A target publishing the required functions to modify a document that is displayed
+  * in a text viewer. It provides access to the document and control
+  * over the redraw behavior and the grouping of document changes into undo commands.
+  *
+  * @see org.eclipse.jface.text.ITextViewer
+  * @see org.eclipse.jface.text.IDocument
+  * @see org.eclipse.jface.text.IUndoManager
+  * @since 2.0
+  */
+public interface IRewriteTarget {
+
+    /**
+     * Returns the document of this target.
+     *
+     * @return the document of this target
+     */
+    IDocument getDocument();
+
+    /**
+     * Disables/enables redrawing while modifying the target's document.
+     *
+     * @param redraw <code>true</code> if the document's visible presentation
+     *            should be updated, <code>false</code> otherwise
+     */
+    void setRedraw(bool redraw);
+
+    /**
+     * If an undo manager is connected to the document's visible presentation,
+     * this method tells the undo manager to fold all subsequent changes into
+     * one single undo command until <code>endCompoundChange</code> is called.
+     */
+    void beginCompoundChange();
+
+    /**
+     * If an undo manager is connected to the document's visible presentation,
+     * this method tells the undo manager to stop the folding of changes into a
+     * single undo command. After this call, all subsequent changes are
+     * considered to be individually undo-able.
+     */
+    void endCompoundChange();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ISelectionValidator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ISelectionValidator;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+import org.eclipse.jface.viewers.ISelection;
+
+
+/**
+ * A selection validator allows clients to test whether the selection they
+ * received during selection changed notification is valid.
+ * <p>
+ * For example, selection and document changes that occur between the original
+ * selection and the point in time the validator is called cause the selection
+ * to be invalid.</p>
+ * <p>
+ * Clients may implement and use this interface.
+ * </p>
+ *
+ * @since 3.0
+ */
+public interface ISelectionValidator {
+
+    /**
+     * Tests whether the given post selection is still valid.
+     *
+     * @param selection
+     * @return <code>true</code> if the selection is still valid
+     */
+    bool isValid(ISelection selection);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextDoubleClickStrategy.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ITextDoubleClickStrategy;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * A text double click strategy defines the reaction of a text viewer to mouse
+ * double click events. The strategy must be installed on an
+ * {@link org.eclipse.jface.text.ITextViewer}.
+ * <p>
+ * Clients may implement this interface or use the standard implementation
+ * <code>DefaultTextDoubleClickStrategy</code>.</p>
+ *
+ * @see org.eclipse.jface.text.ITextViewer
+ * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
+ */
+public interface ITextDoubleClickStrategy {
+
+    /**
+     * The mouse has been double clicked on the given text viewer.
+     *
+     * @param viewer the viewer into which has been double clicked
+     */
+    void doubleClicked(ITextViewer viewer);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextHover.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,214 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ITextHover;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Computes the information to be shown in a hover popup which appears on top of
+ * the text viewer's text widget when a hover event occurs. If the text hover
+ * does not provide information no hover popup is shown. Any implementer of this
+ * interface must be capable of operating in a non-UI thread.
+ * <p>
+ *
+ * In order to provide backward compatibility for clients of
+ * <code>ITextHover</code>, extension interfaces are used as a means of
+ * evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.ITextHoverExtension} since version 3.0
+ *     allowing a text hover to provide a creator for the hover control. This allows
+ *     for sophisticated hovers in a way that information computed by the hover can
+ *     be displayed in the best possible form.</li>
+ * <li>{@link org.eclipse.jface.text.ITextHoverExtension2} since version 3.4
+ *     allowing a text hover to return hover-specific information objects.</li>
+ * </ul></p>
+ * <p>
+ * Clients may implement this interface.</p>
+ *
+ * @see org.eclipse.jface.text.ITextHoverExtension
+ * @see org.eclipse.jface.text.ITextHoverExtension2
+ * @see org.eclipse.jface.text.ITextViewer
+ */
+public interface ITextHover {
+
+    /**
+     * Returns the information which should be presented when a hover popup is shown
+     * for the specified hover region. The hover region has the same semantics
+     * as the region returned by <code>getHoverRegion</code>. If the returned
+     * information is <code>null</code> or empty no hover popup will be shown.
+     *
+     * @param textViewer the viewer on which the hover popup should be shown
+     * @param hoverRegion the text range in the viewer which is used to determine
+     *      the hover display information
+     * @return the hover popup display information, or <code>null</code> if none available
+     * @deprecated As of 3.4, replaced by {@link ITextHoverExtension2#getHoverInfo2(ITextViewer, IRegion)}
+     */
+    String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion);
+
+    /**
+     * Returns the text region which should serve as the source of information
+     * to compute the hover popup display information. The popup has been requested
+     * for the given offset.<p>
+     * For example, if hover information can be provided on a per method basis in a
+     * source viewer, the offset should be used to find the enclosing method and the
+     * source range of the method should be returned.
+     *
+     * @param textViewer the viewer on which the hover popup should be shown
+     * @param offset the offset for which the hover request has been issued
+     * @return the hover region used to compute the hover display information
+     */
+    IRegion getHoverRegion(ITextViewer textViewer, int offset);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextHoverExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ITextHoverExtension;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.ITextHover}.
+ * <p>
+ * It provides a way for hovers to specify the information control creator they
+ * want to have used in order to create the hover control.</p>
+ *
+ * @see org.eclipse.jface.text.IInformationControlCreator
+ * @see org.eclipse.jface.text.ITextHover
+ * @since 3.0
+ */
+public interface ITextHoverExtension {
+
+    /**
+     * Returns the hover control creator of this text hover or <code>null</code>
+     *
+     * @return the hover control creator or <code>null</code>
+     */
+    IInformationControlCreator getHoverControlCreator();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextHoverExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.ITextHoverExtension2;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.ITextHover}.
+ * <p>
+ * Provides a way for hovers to return hover-specific information objects.
+ * </p>
+ * 
+ * @see org.eclipse.jface.text.ITextHover
+ * @since 3.4
+ */
+public interface ITextHoverExtension2 {
+
+    /**
+     * Returns the information which should be presented when a hover popup is shown
+     * for the specified hover region. The hover region has the same semantics
+     * as the region returned by {@link ITextHover#getHoverRegion(ITextViewer, int)}.
+     * If the returned information is <code>null</code>, no hover popup will be shown.
+     * <p>
+     * <strong>Note:</strong> Implementers have to ensure that {@link ITextHoverExtension#getHoverControlCreator()}
+     * returns {@link IInformationControl}s that implement
+     * {@link IInformationControlExtension2} and whose
+     * {@link IInformationControlExtension2#setInput(Object)} can handle the
+     * information objects returned by this method.</p>
+     * <p>
+     * Callers should ignore the text returned by {@link ITextHover#getHoverInfo(ITextViewer, IRegion)}.</p>
+     *
+     * @param textViewer the viewer on which the hover popup should be shown
+     * @param hoverRegion the text range in the viewer which is used to determine
+     *      the hover display information
+     * @return the hover popup display information, or <code>null</code> if none available
+     */
+    Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion);
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextInputListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ITextInputListener;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+
+/**
+ * Text input listeners registered with an
+ * {@link org.eclipse.jface.text.ITextViewer} are informed if the document
+ * serving as the text viewer's model is replaced.
+ * <p>
+ * Clients may implement this interface.</p>
+ *
+ * @see org.eclipse.jface.text.ITextViewer
+ * @see org.eclipse.jface.text.IDocument
+ */
+public interface ITextInputListener {
+
+    /**
+     * Called before the input document is replaced.
+     *
+     * @param oldInput the text viewer's previous input document
+     * @param newInput the text viewer's new input document
+     */
+    void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput);
+
+    /**
+     * Called after the input document has been replaced.
+     *
+     * @param oldInput the text viewer's previous input document
+     * @param newInput the text viewer's new input document
+     */
+    void inputDocumentChanged(IDocument oldInput, IDocument newInput);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ITextListener;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+
+/**
+ * Text listeners registered with a text viewer are informed about all
+ * modifications of an {@link org.eclipse.jface.text.ITextViewer} by means of
+ * text events. A text event describes a change as a replace operation.
+ * <p>
+ * The changes described in the event are the changes applied to the text
+ * viewer's widget (i.e., its visual representation) and not those applied to the
+ * text viewer's document. The text event can be asked to return the
+ * corresponding document event. If the text event does not contain a document
+ * event, the modification of the text viewer is a presentation change. For
+ * example, changing the visible region of a text viewer, is a presentation
+ * change. A completely empty text event represents a change of the viewer's
+ * redraw state.</p>
+ * <p>
+ * If a text listener receives a text event, it is guaranteed that both the
+ * document and the viewer's visual representation are synchronized.</p>
+ * <p>
+ * Clients may implement this interface.</p>
+ *
+ * @see org.eclipse.jface.text.ITextViewer
+ * @see org.eclipse.jface.text.TextEvent
+ * @see org.eclipse.jface.text.DocumentEvent
+ */
+public interface ITextListener {
+
+    /**
+     * The visual representation of a text viewer this listener is registered with
+     * has been changed.
+     *
+     * @param event the description of the change
+     */
+    void textChanged(TextEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextOperationTarget.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.ITextOperationTarget;
+
+
+import java.lang.all;
+
+
+/**
+ * Defines the target for text operations. <code>canDoOperation</code> informs
+ * the clients about the ability of the target to perform the specified
+ * operation at the current point in time. <code>doOperation</code> executes
+ * the specified operation.
+ * <p>
+ * In order to provide backward compatibility for clients of
+ * <code>ITextOperationTarget</code>, extension interfaces are used as a
+ * means of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.ITextOperationTargetExtension} since
+ *     version 2.0 introducing text operation enabling/disabling.</li>
+ * </ul>
+ *
+ * @see org.eclipse.jface.text.ITextOperationTargetExtension
+ */
+public interface ITextOperationTarget {
+
+
+    /**
+     * Text operation code for undoing the last edit command.
+     */
+    public static const int UNDO= 1;
+
+    /**
+     * Text operation code for redoing the last undone edit command.
+     */
+    public static const int REDO= 2;
+
+    /**
+     * Text operation code for moving the selected text to the clipboard.
+     */
+    public static const int CUT= 3;
+
+    /**
+     * Text operation code for copying the selected text to the clipboard.
+     */
+    public static const int COPY= 4;
+
+    /**
+     * Text operation code for inserting the clipboard content at the
+     * current position.
+     */
+    public static const int PASTE= 5;
+
+    /**
+     * Text operation code for deleting the selected text or if selection
+     * is empty the character  at the right of the current position.
+     */
+    public static const int DELETE= 6;
+
+    /**
+     * Text operation code for selecting the complete text.
+     */
+    public static const int SELECT_ALL= 7;
+
+    /**
+     * Text operation code for shifting the selected text block to the right.
+     */
+    public static const int SHIFT_RIGHT= 8;
+
+    /**
+     * Text operation code for shifting the selected text block to the left.
+     */
+    public static const int SHIFT_LEFT= 9;
+
+    /**
+     * Text operation code for printing the complete text.
+     */
+    public static const int PRINT= 10;
+
+    /**
+     * Text operation code for prefixing the selected text block.
+     */
+    public static const int PREFIX= 11;
+
+    /**
+     * Text operation code for removing the prefix from the selected text block.
+     */
+    public static const int STRIP_PREFIX= 12;
+
+
+    /**
+     * Returns whether the operation specified by the given operation code
+     * can be performed.
+     *
+     * @param operation the operation code
+     * @return <code>true</code> if the specified operation can be performed
+     */
+    bool canDoOperation(int operation);
+
+    /**
+     * Performs the operation specified by the operation code on the target.
+     * <code>doOperation</code> must only be called if <code>canDoOperation</code>
+     * returns <code>true</code>.
+     *
+     * @param operation the operation code
+     */
+    void doOperation(int operation);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextOperationTargetExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ITextOperationTargetExtension;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.ITextOperationTarget}.
+ * <p>
+ * Allows a client to control the enable state of operations provided by this
+ * target.
+ *
+ * @see org.eclipse.jface.text.ITextOperationTarget
+ * @since 2.0
+ */
+public interface ITextOperationTargetExtension {
+
+    /**
+     * Enables/disabled the given text operation.
+     *
+     * @param operation the operation to enable/disable
+     * @param enable <code>true</code> to enable the operation otherwise <code>false</code>
+     */
+    void enableOperation(int operation, bool enable);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextPresentationListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ITextPresentationListener;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Text presentation listeners registered with an
+ * {@link org.eclipse.jface.text.ITextViewer} are informed when a
+ * {@link org.eclipse.jface.text.TextPresentation} is about to be applied to the
+ * text viewer. The listener can apply changes to the text presentation and thus
+ * participate in the process of text presentation creation.
+ *
+ * @since 3.0
+ */
+public interface ITextPresentationListener {
+
+    /**
+     * This method is called when a text presentation is about to be applied to
+     * the text viewer. The receiver is allowed to change the text presentation
+     * during that call.
+     *
+     * @param textPresentation the current text presentation
+     */
+    public void applyTextPresentation(TextPresentation textPresentation);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextSelection.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ITextSelection;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+import org.eclipse.jface.viewers.ISelection;
+
+
+/**
+ * This interface represents a textual selection. A text selection is a range of
+ * characters. Although a text selection is a snapshot taken at a particular
+ * point in time, it must not copy the line information and the selected text
+ * from the selection provider.
+ * <p>
+ * If, for example, the selection provider is a text viewer (
+ * {@link org.eclipse.jface.text.ITextViewer}), and a text selection is created
+ * for the range [5, 10], the line formation for the 5th character must not be
+ * determined and remembered at the point of creation. It can rather be
+ * determined at the point, when <code>getStartLine</code> is called. If the
+ * source viewer range [0, 15] has been changed in the meantime between the
+ * creation of the text selection object and the invocation of
+ * <code>getStartLine</code>, the returned line number may differ from the
+ * line number of the 5th character at the point of creation of the text
+ * selection object.
+ * <p>
+ * The contract of this interface is that weak in order to allow for efficient
+ * implementations.</p>
+ * <p>
+ * Clients may implement this interface or use the default implementation
+ * provided by {@link org.eclipse.jface.text.TextSelection}.</p>
+ *
+ * @see org.eclipse.jface.text.TextSelection
+ */
+public interface ITextSelection : ISelection {
+
+    /**
+     * Returns the offset of the selected text.
+     *
+     * @return the offset of the selected text
+     */
+    int getOffset();
+
+    /**
+     * Returns the length of the selected text.
+     *
+     * @return the length of the selected text
+     */
+    int getLength();
+
+    /**
+     * Returns number of the line containing the offset of the selected text.
+     * If the underlying text has been changed between the creation of this
+     * selection object and the call of this method, the value returned might
+     * differ from what it would have been at the point of creation.
+     *
+     * @return the start line of this selection or <code>-1</code> if there is no valid line information
+     */
+    int getStartLine();
+
+    /**
+     * Returns the number of the line containing the last character of the selected text.
+     * If the underlying text has been changed between the creation of this
+     * selection object and the call of this method, the value returned might
+     * differ from what it would have been at the point of creation.
+     *
+     * @return the end line of this selection or <code>-1</code> if there is no valid line information
+     */
+    int getEndLine();
+
+    /**
+     * Returns the selected text.
+     * If the underlying text has been changed between the creation of this
+     * selection object and the call of this method, the value returned might
+     * differ from what it would have been at the point of creation.
+     *
+     * @return the selected text or <code>null</code> if there is no valid text information
+     */
+    String getText();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,564 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ITextViewer;
+
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.jface.viewers.ISelectionProvider;
+
+
+/**
+ * A text viewer connects a text widget with an
+ * {@link org.eclipse.jface.text.IDocument}. The document is used as the
+ * widget's text model.
+ * <p>
+ * It supports the following kinds of listeners:
+ * <ul>
+ * <li>view port listeners to inform about changes of the viewer's view port</li>
+ * <li>text listeners to inform about changes of the document and the
+ * subsequent viewer change</li>
+ * <li>text input listeners to inform about changes of the viewer's input
+ * document.</li>
+ * </ul>
+ * A text viewer supports a set of configuration options and plug-ins defining
+ * its behavior:
+ * <ul>
+ * <li>undo manager</li>
+ * <li>double click behavior</li>
+ * <li>auto indentation</li>
+ * <li>text hover</li>
+ * </ul>
+ * Installed plug-ins are not automatically activated. Plug-ins must be
+ * activated with the <code>activatePlugins</code> call. Most plug-ins can be
+ * defined per content type. Content types are derived from a partitioning of
+ * the text viewer's input document. In case of documents that support multiple
+ * partitionings, the implementer is responsible for determining the
+ * partitioning to use.
+ * <p>
+ * A text viewer also provides the concept of event consumption. Events handled
+ * by the viewer can be filtered and processed by a dynamic event consumer. With
+ * {@link org.eclipse.jface.text.ITextViewerExtension}, this mechanism has been
+ * replaced with the support for
+ * {@link org.eclipse.swt.custom.VerifyKeyListener}.
+ * <p>
+ * A text viewer provides several text editing functions, some of them are
+ * configurable, through a text operation target interface. It also supports a
+ * presentation mode in which it only shows a specified section of its document.
+ * By calling <code>setVisibleRegion</code> clients define which section is
+ * visible. Clients can get access to this section by calling
+ * <code>getVisibleRegion</code>. The viewer's presentation mode does not
+ * affect any client of the viewer other than text listeners. With
+ * {@link org.eclipse.jface.text.ITextViewerExtension5} the visible region
+ * support has been reworked. With that extension interface, text viewers are
+ * allowed to show fractions of their input document. I.e. a widget selection of
+ * two visually neighboring characters is no longer guaranteed to be two
+ * neighboring characters in the viewer's input document. Thus, viewers
+ * implementing {@link org.eclipse.jface.text.ITextViewerExtension5} are
+ * potentially forced to change the fractions of the input document that are
+ * shown when clients ask for the visible region.
+ * <p>
+ *
+ * In order to provide backward compatibility for clients of
+ * <code>ITextViewer</code>, extension interfaces are used as a means of
+ * evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.ITextViewerExtension} since version 2.0
+ * replacing the event consumer mechanism and introducing the concept of rewrite
+ * targets and means to manage the viewer's redraw behavior</li>
+ * <li>{@link org.eclipse.jface.text.ITextViewerExtension2}since version 2.1
+ * adding a way to invalidate a viewer's presentation and setters for hovers.
+ * </li>
+ * <li>{@link org.eclipse.jface.text.ITextViewerExtension3} since version 2.1
+ * which itself was replaced by
+ * {@link org.eclipse.jface.text.ITextViewerExtension5} in version 3.0</li>
+ * <li>{@link org.eclipse.jface.text.ITextViewerExtension4} since version 3.0
+ * introducing focus handling for widget token keepers and the concept of text
+ * presentation listeners.</li>
+ * <li>{@link org.eclipse.jface.text.ITextViewerExtension5} since version 3.0
+ * extending the visible region concept with explicit handling and conversion
+ * of widget and model coordinates.</li>
+ * <li>{@link org.eclipse.jface.text.ITextViewerExtension6} since version 3.1
+ * extending the text viewer with the ability to detect hyperlinks and access the undo manager.</li>
+ * <li>{@link org.eclipse.jface.text.ITextViewerExtension7} since version 3.3
+ * extending the text viewer with the ability to install tabs to spaces conversion.</li>
+ * <li>{@link org.eclipse.jface.text.ITextViewerExtension8} since version 3.4
+ * extending the text viewer with the ability to print and rich hover support.</li>
+ * </ul></p>
+ * <p>
+ * Clients may implement this interface and its extension interfaces or use the
+ * standard implementation {@link org.eclipse.jface.text.TextViewer}.</p>
+ *
+ * @see org.eclipse.jface.text.ITextViewerExtension
+ * @see org.eclipse.jface.text.ITextViewerExtension2
+ * @see org.eclipse.jface.text.ITextViewerExtension3
+ * @see org.eclipse.jface.text.ITextViewerExtension4
+ * @see org.eclipse.jface.text.ITextViewerExtension5
+ * @see org.eclipse.jface.text.ITextViewerExtension6
+ * @see org.eclipse.jface.text.ITextViewerExtension7
+ * @see org.eclipse.jface.text.ITextViewerExtension8
+ * @see org.eclipse.jface.text.IDocument
+ * @see org.eclipse.jface.text.ITextInputListener
+ * @see org.eclipse.jface.text.IViewportListener
+ * @see org.eclipse.jface.text.ITextListener
+ * @see org.eclipse.jface.text.IEventConsumer
+ */
+public interface ITextViewer {
+
+
+    /* ---------- widget --------- */
+
+    /**
+     * Returns this viewer's SWT control, <code>null</code> if the control is disposed.
+     * <p>
+     * <em>Calling API directly on the widget can interfere with features provided
+     * by a text viewer. Clients who call API directly on the widget are responsible
+     * to resolve such conflicts on their side.</em>
+     * </p>
+     *
+     * @return the SWT control or <code>null</code>
+     */
+    StyledText getTextWidget();
+
+
+    /* --------- plug-ins --------- */
+
+    /**
+     * Sets this viewer's undo manager.
+     *
+     * @param undoManager the new undo manager. <code>null</code> is a valid argument.
+     */
+    void setUndoManager(IUndoManager undoManager);
+
+    /**
+     * Sets this viewer's text double click strategy for the given content type.
+     *
+     * @param strategy the new double click strategy. <code>null</code> is a valid argument.
+     * @param contentType the type for which the strategy is registered
+     */
+    void setTextDoubleClickStrategy(ITextDoubleClickStrategy strategy, String contentType);
+
+    /**
+     * Sets this viewer's auto indent strategy for the given content type. If
+     * the given strategy is <code>null</code> any installed strategy for the
+     * content type is removed. This method has been replaced by
+     * {@link ITextViewerExtension2#prependAutoEditStrategy(IAutoEditStrategy, String)} and
+     * {@link ITextViewerExtension2#removeAutoEditStrategy(IAutoEditStrategy, String)}.
+     * It is now equivalent to
+     * <pre>
+     *      ITextViewerExtension2 extension= cast(ITextViewerExtension2) viewer;
+     *      extension.removeAutoEditStrategy(oldStrategy, contentType);
+     *      extension.prependAutoEditStrategy(strategy, contentType);
+     * </pre>
+     *
+     * @param strategy the new auto indent strategy. <code>null</code> is a
+     *            valid argument.
+     * @param contentType the type for which the strategy is registered
+     * @deprecated since 3.1, use
+     *             {@link ITextViewerExtension2#prependAutoEditStrategy(IAutoEditStrategy, String)} and
+     *             {@link ITextViewerExtension2#removeAutoEditStrategy(IAutoEditStrategy, String)} instead
+     */
+    void setAutoIndentStrategy(IAutoIndentStrategy strategy, String contentType);
+
+    /**
+     * Sets this viewer's text hover for the given content type.
+     * <p>
+     * This method has been replaced by {@link ITextViewerExtension2#setTextHover(ITextHover, String, int)}.
+     * It is now equivalent to
+     * <pre>
+     *    ITextViewerExtension2 extension= cast(ITextViewerExtension2) document;
+     *    extension.setTextHover(textViewerHover, contentType, ITextViewerExtension2#DEFAULT_HOVER_STATE_MASK);
+     * </pre>
+     *
+     *
+     * @param textViewerHover the new hover. <code>null</code> is a valid
+     *            argument.
+     * @param contentType the type for which the hover is registered
+     */
+    void setTextHover(ITextHover textViewerHover, String contentType);
+
+    /**
+     * Activates the installed plug-ins. If the plug-ins are already activated
+     * this call has no effect.
+     */
+    void activatePlugins();
+
+    /**
+     * Resets the installed plug-ins. If plug-ins change their state or
+     * behavior over the course of time, this method causes them to be set
+     * back to their initial state and behavior. E.g., if an {@link IUndoManager}
+     * has been installed on this text viewer, the manager's list of remembered
+     * text editing operations is removed.
+     */
+    void resetPlugins();
+
+
+
+    /* ---------- listeners ------------- */
+
+    /**
+     * Adds the given view port listener to this viewer. The listener
+     * is informed about all changes to the visible area of this viewer.
+     * If the listener is already registered with this viewer, this call
+     * has no effect.
+     *
+     * @param listener the listener to be added
+     */
+    void addViewportListener(IViewportListener listener);
+
+    /**
+     * Removes the given listener from this viewer's set of view port listeners.
+     * If the listener is not registered with this viewer, this call has
+     * no effect.
+     *
+     * @param listener the listener to be removed
+     */
+    void removeViewportListener(IViewportListener listener);
+
+    /**
+     * Adds a text listener to this viewer. If the listener is already registered
+     * with this viewer, this call has no effect.
+     *
+     * @param listener the listener to be added
+     */
+    void addTextListener(ITextListener listener);
+
+    /**
+     * Removes the given listener from this viewer's set of text listeners.
+     * If the listener is not registered with this viewer, this call has
+     * no effect.
+     *
+     * @param listener the listener to be removed
+     */
+    void removeTextListener(ITextListener listener);
+
+    /**
+     * Adds a text input listener to this viewer. If the listener is already registered
+     * with this viewer, this call has no effect.
+     *
+     * @param listener the listener to be added
+     */
+    void addTextInputListener(ITextInputListener listener);
+
+    /**
+     * Removes the given listener from this viewer's set of text input listeners.
+     * If the listener is not registered with this viewer, this call has
+     * no effect.
+     *
+     * @param listener the listener to be removed
+     */
+    void removeTextInputListener(ITextInputListener listener);
+
+
+
+    /* -------------- model manipulation ------------- */
+
+    /**
+     * Sets the given document as the text viewer's model and updates the
+     * presentation accordingly. An appropriate <code>TextEvent</code> is
+     * issued. This text event does not carry a related document event.
+     *
+     * @param document the viewer's new input document <code>null</code> if none
+     */
+    void setDocument(IDocument document);
+
+    /**
+     * Returns the text viewer's input document.
+     *
+     * @return the viewer's input document or <code>null</code> if none
+     */
+    IDocument getDocument();
+
+
+    /* -------------- event handling ----------------- */
+
+    /**
+     * Registers an event consumer with this viewer. This method has been
+     * replaces with the {@link org.eclipse.swt.custom.VerifyKeyListener}
+     * management methods in {@link ITextViewerExtension}.
+     *
+     * @param consumer the viewer's event consumer. <code>null</code> is a
+     *            valid argument.
+     */
+    void setEventConsumer(IEventConsumer consumer);
+
+    /**
+     * Sets the editable state.
+     *
+     * @param editable the editable state
+     */
+    void setEditable(bool editable);
+
+    /**
+     * Returns whether the shown text can be manipulated.
+     *
+     * @return the viewer's editable state
+     */
+    bool isEditable();
+
+
+    /* ----------- visible region support ------------- */
+
+    /**
+     * Sets the given document as this viewer's model and
+     * exposes the specified region. An appropriate
+     * <code>TextEvent</code> is issued. The text event does not carry a
+     * related document event. This method is a convenience method for
+     * <code>setDocument(document);setVisibleRegion(offset, length)</code>.
+     *
+     * @param document the new input document or <code>null</code> if none
+     * @param modelRangeOffset the offset of the model range
+     * @param modelRangeLength the length of the model range
+     */
+    void setDocument(IDocument document, int modelRangeOffset, int modelRangeLength);
+
+    /**
+     * Defines and sets the region of this viewer's document which will be
+     * visible in the presentation. Every character inside the specified region
+     * is supposed to be visible in the viewer's widget after that call.
+     *
+     * @param offset the offset of the visible region
+     * @param length the length of the visible region
+     */
+    void setVisibleRegion(int offset, int length);
+
+    /**
+     * Resets the region of this viewer's document which is visible in the presentation.
+     * Afterwards, the whole input document is visible.
+     */
+    void resetVisibleRegion();
+
+    /**
+     * Returns the current visible region of this viewer's document. The result
+     * may differ from the argument passed to <code>setVisibleRegion</code> if
+     * the document has been modified since then. The visible region is supposed
+     * to be a consecutive region in viewer's input document and every character
+     * inside that region is supposed to visible in the viewer's widget.
+     * <p>
+     * Viewers implementing {@link ITextViewerExtension5} may be forced to
+     * change the fractions of the input document that are shown, in order to
+     * fulfill this contract.
+     *
+     * @return this viewer's current visible region
+     */
+    IRegion getVisibleRegion();
+
+    /**
+     * Returns whether a given range overlaps with the visible region of this
+     * viewer's document.
+     * <p>
+     * Viewers implementing {@link ITextViewerExtension5}may be forced to
+     * change the fractions of the input document that are shown in order to
+     * fulfill this request. This is because the overlap is supposed to be
+     * without gaps.
+     *
+     * @param offset the offset
+     * @param length the length
+     * @return <code>true</code> if the specified range overlaps with the
+     *         visible region
+     */
+    bool overlapsWithVisibleRegion(int offset, int length);
+
+
+
+    /* ------------- presentation manipulation ----------- */
+
+    /**
+     * Applies the color information encoded in the given text presentation.
+     * <code>controlRedraw</code> tells this viewer whether it should take care of
+     * redraw management or not. If, e.g., this call is one in a sequence of multiple
+     * presentation calls, it is more appropriate to explicitly control redrawing at the
+     * beginning and the end of the sequence.
+     *
+     * @param presentation the presentation to be applied to this viewer
+     * @param controlRedraw indicates whether this viewer should manage redraws
+     */
+    void changeTextPresentation(TextPresentation presentation, bool controlRedraw);
+
+    /**
+     * Marks the currently applied text presentation as invalid. It is the
+     * viewer's responsibility to take any action it can to repair the text
+     * presentation.
+     * <p>
+     * See {@link ITextViewerExtension2#invalidateTextPresentation(int, int)}
+     * for a way to invalidate specific regions rather than the presentation as
+     * a whole.
+     *
+     * @since 2.0
+     */
+    void invalidateTextPresentation();
+
+    /**
+     * Applies the given color as text foreground color to this viewer's
+     * selection.
+     *
+     * @param color the color to be applied
+     */
+    void setTextColor(Color color);
+
+    /**
+     * Applies the given color as text foreground color to the specified section
+     * of this viewer. <code>controlRedraw</code> tells this viewer whether it
+     * should take care of redraw management or not.
+     *
+     * @param color the color to be applied
+     * @param offset the offset of the range to be changed
+     * @param length the length of the range to be changed
+     * @param controlRedraw indicates whether this viewer should manage redraws
+     */
+    void setTextColor(Color color, int offset, int length, bool controlRedraw);
+
+
+    /* --------- target handling and configuration ------------ */
+
+    /**
+     * Returns the text operation target of this viewer.
+     *
+     * @return the text operation target of this viewer
+     */
+    ITextOperationTarget getTextOperationTarget();
+
+    /**
+     * Returns the find/replace operation target of this viewer.
+     *
+     * @return the find/replace operation target of this viewer
+     */
+    IFindReplaceTarget getFindReplaceTarget();
+
+    /**
+     * Sets the strings that are used as prefixes when lines of the given content type
+     * are prefixed using the prefix text operation. The prefixes are considered equivalent.
+     * Inserting a prefix always inserts the defaultPrefixes[0].
+     * Removing a prefix removes all of the specified prefixes.
+     *
+     * @param defaultPrefixes the prefixes to be used
+     * @param contentType the content type for which the prefixes are specified
+     * @since 2.0
+     */
+    void setDefaultPrefixes(String[] defaultPrefixes, String contentType);
+
+    /**
+     * Sets the strings that are used as prefixes when lines of the given content type
+     * are shifted using the shift text operation. The prefixes are considered equivalent.
+     * Thus "\t" and "    " can both be used as prefix characters.
+     * Shift right always inserts the indentPrefixes[0].
+     * Shift left removes all of the specified prefixes.
+     *
+     * @param indentPrefixes the prefixes to be used
+     * @param contentType the content type for which the prefixes are specified
+     */
+    void setIndentPrefixes(String[] indentPrefixes, String contentType);
+
+
+
+    /* --------- selection handling -------------- */
+
+    /**
+     * Sets the selection to the specified range.
+     *
+     * @param offset the offset of the selection range
+     * @param length the length of the selection range. A negative length places
+     *            the caret at the visual start of the selection.
+     */
+    void setSelectedRange(int offset, int length);
+
+    /**
+     * Returns the range of the current selection in coordinates of this viewer's document.
+     *
+     * @return a <code>Point</code> with x as the offset and y as the length of the current selection
+     */
+    Point getSelectedRange();
+
+    /**
+     * Returns a selection provider dedicated to this viewer. Subsequent
+     * calls to this method return always the same selection provider.
+     *
+     * @return this viewer's selection provider
+     */
+    ISelectionProvider getSelectionProvider();
+
+
+    /* ------------- appearance manipulation --------------- */
+
+    /**
+     * Ensures that the given range is visible.
+     *
+     * @param offset the offset of the range to be revealed
+     * @param length the length of the range to be revealed
+     */
+    void revealRange(int offset, int length);
+
+    /**
+     * Scrolls the widget so that the given index is the line
+     * with the smallest line number of all visible lines.
+     *
+     * @param index the line which should become the top most line
+     */
+    void setTopIndex(int index);
+
+    /**
+     * Returns the visible line with the smallest line number.
+     *
+     * @return the number of the top most visible line
+     */
+    int getTopIndex();
+
+    /**
+     * Returns the document offset of the upper left corner of this viewer's view port.
+     *
+     * @return the upper left corner offset
+     */
+    int getTopIndexStartOffset();
+
+    /**
+     * Returns the visible line with the highest line number.
+     *
+     * @return the number of the bottom most line
+     */
+    int getBottomIndex();
+
+    /**
+     * Returns the document offset of the lower right
+     * corner of this viewer's view port. This is the visible character
+     * with the highest character position. If the content of this viewer
+     * is shorter, the position of the last character of the content is returned.
+     *
+     * @return the lower right corner offset
+     */
+    int getBottomIndexEndOffset();
+
+    /**
+     * Returns the vertical offset of the first visible line.
+     *
+     * @return the vertical offset of the first visible line
+     */
+    int getTopInset();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ITextViewerExtension;
+
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.swt.custom.VerifyKeyListener;
+import org.eclipse.swt.widgets.Control;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.ITextViewer}.
+ * <p>
+ * This extension interface replaces the event consumer mechanism (
+ * {@link org.eclipse.jface.text.ITextViewer#setEventConsumer(IEventConsumer)})
+ * with a set of methods that allow to manage a sequence of
+ * {@link org.eclipse.swt.custom.VerifyKeyListener}objects. It also adds
+ * <ul>
+ * <li>access to the control of this viewer</li>
+ * <li>marked region support as in emacs</li>
+ * <li>control of the viewer's redraw behavior by introducing
+ *     <code>setRedraw(bool)</code>
+ * <li>access to the viewer's rewrite target.
+ * </ul>
+ *
+ * A rewrite target ({@link org.eclipse.jface.text.IRewriteTarget}) represents
+ * an facade offering the necessary methods to manipulate a document that is the
+ * input document of a text viewer.
+ *
+ * @since 2.0
+ */
+public interface ITextViewerExtension {
+
+    /**
+     * Inserts the verify key listener at the beginning of the viewer's list of
+     * verify key listeners. If the listener is already registered with the
+     * viewer this call moves the listener to the beginning of the list.
+     *
+     * @param listener the listener to be inserted
+     */
+    void prependVerifyKeyListener(VerifyKeyListener listener);
+
+    /**
+     * Appends a verify key listener to the viewer's list of verify key
+     * listeners. If the listener is already registered with the viewer this
+     * call moves the listener to the end of the list.
+     *
+     * @param listener the listener to be added
+     */
+    void appendVerifyKeyListener(VerifyKeyListener listener);
+
+    /**
+     * Removes the verify key listener from the viewer's list of verify key listeners.
+     * If the listener is not registered with this viewer, this call has no effect.
+     *
+     * @param listener the listener to be removed
+     */
+    void removeVerifyKeyListener(VerifyKeyListener listener);
+
+    /**
+     * Returns the control of this viewer.
+     *
+     * @return the control of this viewer
+     */
+    Control getControl();
+
+    /**
+     * Sets a mark at the given offset or clears the mark if the specified
+     * offset is <code>-1</code>. If a mark is set and the selection is
+     * empty, cut and copy actions performed on this text viewer work on the
+     * region described by the positions of the mark and the cursor.
+     *
+     * @param offset the offset of the mark
+     */
+    void setMark(int offset);
+
+    /**
+     * Returns the position of the mark, <code>-1</code> if the mark is not set.
+     *
+     * @return the position of the mark or <code>-1</code> if no mark is set
+     */
+    int getMark();
+
+    /**
+     * Enables/disables the redrawing of this text viewer. This temporarily
+     * disconnects the viewer from its underlying
+     * {@link org.eclipse.swt.custom.StyledText}widget. While being
+     * disconnected only the viewer's selection may be changed using
+     * <code>setSelectedRange</code>. Any direct manipulation of the widget
+     * as well as calls to methods that change the viewer's presentation state
+     * (such as enabling the segmented view) are not allowed. When redrawing is
+     * disabled the viewer does not send out any selection or view port change
+     * notification. When redrawing is enabled again, a selection change
+     * notification is sent out for the selected range and this range is
+     * revealed causing a view port changed notification.
+     *
+     * @param redraw <code>true</code> to enable redrawing, <code>false</code>
+     *            otherwise
+     */
+    void setRedraw(bool redraw);
+
+    /**
+     * Returns the viewer's rewrite target.
+     *
+     * @return the viewer's rewrite target
+     */
+    IRewriteTarget getRewriteTarget();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,277 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.ITextViewerExtension2;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.swt.graphics.Point;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.ITextViewer}.
+ * <p>
+ * It provides
+ * <ul>
+ * <li>text presentation invalidation enhancements</li>
+ * <li>text hover management enhancements</li>
+ * <li>a replacement for auto indent strategies</li>
+ * <li>support for custom painters</li>
+ * </ul>
+ *
+ * It extends the means for text presentation invalidation by allowing a
+ * specific region of the presentation to get invalidated. It replaces
+ * {@link org.eclipse.jface.text.ITextViewer#setTextHover(ITextHover, String)}
+ * with a new method that allows to specify state masks for a better control of
+ * the hover behavior.
+ * <p>
+ * An {@link org.eclipse.jface.text.IAutoEditStrategy} is a generalization of
+ * the original {@link org.eclipse.jface.text.IAutoIndentStrategy}. Auto edit
+ * strategies can be arranged in a list that is executed like a pipeline when
+ * the viewer content is changed.
+ * <p>
+ * A {@link org.eclipse.jface.text.IPainter}is creating and managing visual
+ * decorations on the viewer's text widget. Viewer's can have an open number of
+ * painters. Painters are informed about changes to the viewer content and state
+ * and can take the necessary action in responds to the notification.
+ *
+ * @since 2.1
+ */
+public interface ITextViewerExtension2 {
+
+     /**
+      * The state mask of the default hover (value <code>0xff</code>).
+      */
+     static const int DEFAULT_HOVER_STATE_MASK= 0xff;
+
+    /**
+     * Invalidates the viewer's text presentation for the given range.
+     *
+     * @param offset the offset of the first character to be redrawn
+     * @param length the length of the range to be redrawn
+     */
+    void invalidateTextPresentation(int offset, int length);
+
+    /**
+     * Sets this viewer's text hover for the given content type and the given state mask. If the given text hover
+     * is <code>null</code>, any hover installed for the given content type and state mask is removed.
+     *
+     * @param textViewerHover the new hover or <code>null</code>
+     * @param contentType the type for which the hover is to be registered or unregistered
+     * @param stateMask the SWT event state mask; <code>DEFAULT_HOVER_STATE_MASK</code> indicates that
+     *          the hover is installed as the default hover.
+     */
+    void setTextHover(ITextHover textViewerHover, String contentType, int stateMask);
+
+    /**
+     * Removes all text hovers for the given content type independent from their state mask.
+     * <p>
+     * Note: To remove a hover for a given content type and state mask
+     * use {@link #setTextHover(ITextHover, String, int)} with <code>null</code>
+     * as parameter for the text hover.
+     * </p>
+     * @param contentType the type for which all text hovers are to be unregistered
+     */
+    void removeTextHovers(String contentType);
+
+    /**
+     * Returns the currently displayed text hover if any, <code>null</code> otherwise.
+     *
+     * @return the currently displayed text hover or <code>null</code>
+     */
+    ITextHover getCurrentTextHover();
+
+    /**
+     * Returns the location at which the most recent mouse hover event
+     * has occurred.
+     *
+     * @return the location of the most recent mouse hover event
+     */
+    Point getHoverEventLocation();
+
+    /**
+     * Prepends the given auto edit strategy to the existing list of strategies
+     * for the specified content type. The strategies are called in the order in
+     * which they appear in the list of strategies.
+     *
+     * @param strategy the auto edit strategy
+     * @param contentType the content type
+     */
+    void prependAutoEditStrategy(IAutoEditStrategy strategy, String contentType);
+
+    /**
+     * Removes the first occurrence of the given auto edit strategy in the list of strategies
+     * registered under the specified content type.
+     *
+     * @param strategy the auto edit strategy
+     * @param contentType the content type
+     */
+    void removeAutoEditStrategy(IAutoEditStrategy strategy, String contentType);
+
+    /**
+     * Adds the given painter to this viewer.
+     *
+     * @param painter the painter to be added
+     */
+    void addPainter(IPainter painter);
+
+    /**
+     * Removes the given painter from this viewer. If the painter has not been
+     * added to this viewer, this call is without effect.
+     *
+     * @param painter the painter to be removed
+     */
+    void removePainter(IPainter painter);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension3.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.ITextViewerExtension3;
+
+import org.eclipse.jface.text.IRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.ITextViewer}. <p>
+ * This interface has been replaced by {@link org.eclipse.jface.text.ITextViewerExtension5}.
+ * Clients are not supposed to use this interface any longer.
+ *
+ * @since 2.1
+ * @deprecated replaced by {@link org.eclipse.jface.text.ITextViewerExtension5}
+ */
+public interface ITextViewerExtension3 {
+
+
+    /**
+     * Returns the minimal region of the viewer's document that completely comprises everything that is
+     * visible in the viewer's widget or <code>null</code> if there is no such region.
+     *
+     * @return the minimal region of the viewer's document comprising the contents of the viewer's widget or <code>null</code>
+     */
+    IRegion getModelCoverage();
+
+
+    /**
+     * Returns the widget line that corresponds to the given line of the viewer's document or <code>-1</code> if there is no such line.
+     *
+     * @param modelLine the line of the viewer's document
+     * @return the corresponding widget line or <code>-1</code>
+     */
+    int modelLine2WidgetLine(int modelLine);
+
+    /**
+     * Returns the widget offset that corresponds to the given offset in the viewer's document
+     * or <code>-1</code> if there is no such offset
+     *
+     * @param modelOffset the offset in the viewer's document
+     * @return the corresponding widget offset or <code>-1</code>
+     */
+    int modelOffset2WidgetOffset(int modelOffset);
+
+    /**
+     * Returns the minimal region of the viewer's widget that completely comprises the given region of the
+     * viewer's document or <code>null</code> if there is no such region.
+     *
+     * @param modelRange the region of the viewer's document
+     * @return the minimal region of the widget comprising <code>modelRange</code> or <code>null</code>
+     */
+    IRegion modelRange2WidgetRange(IRegion modelRange);
+
+
+    /**
+     * Returns the offset of the viewer's document that corresponds to the given widget offset
+     * or <code>-1</code> if there is no such offset
+     *
+     * @param widgetOffset the widget offset
+     * @return the corresponding offset in the viewer's document or <code>-1</code>
+     */
+    int widgetOffset2ModelOffset(int widgetOffset);
+
+    /**
+     * Returns the minimal region of the viewer's document that completely comprises the given widget region
+     * or <code>null</code> if there is no such region.
+     *
+     * @param widgetRange the widget region
+     * @return the minimal region of the viewer's document comprising <code>widgetRange</code> or <code>null</code>
+     */
+    IRegion widgetRange2ModelRange(IRegion widgetRange);
+
+    /**
+     * Returns the line of the viewer's document that corresponds to the given widget line or <code>-1</code> if there is no such line.
+     *
+     * @param widgetLine the widget line
+     * @return the corresponding line of the viewer's document or <code>-1</code>
+     */
+    int widgetlLine2ModelLine(int widgetLine);
+
+    /**
+     * Returns the widget line of the given widget offset.
+     *
+     * @param widgetOffset the widget offset
+     * @return the widget line of the widget offset
+     */
+    int widgetLineOfWidgetOffset(int widgetOffset);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension4.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.ITextViewerExtension4;
+
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+
+import java.lang.all;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.ITextViewer}.
+ * Introduces the concept of text presentation listeners and improves focus
+ * handling among widget token keepers.
+ * <p>
+ * A {@link org.eclipse.jface.text.ITextPresentationListener}is a listener that
+ * is informed by the viewer that a text presentation is about to be applied.
+ * During this callback the listener is allowed to modify the presentation. Text
+ * presentation listeners are thus a mean to participate in the process of text
+ * presentation creation.
+ *
+ * @since 3.0
+ */
+public interface ITextViewerExtension4 {
+
+    /**
+     * Instructs the receiver to request the {@link IWidgetTokenKeeper}
+     * currently holding the widget token to take the keyboard focus.
+     *
+     * @return <code>true</code> if there was any
+     *         <code>IWidgetTokenKeeper</code> that was asked to take the
+     *         focus, <code>false</code> otherwise
+     */
+    bool moveFocusToWidgetToken();
+
+    /**
+     * Adds the given text presentation listener to this text viewer.
+     * This call has no effect if the listener is already registered
+     * with this text viewer.
+     *
+     * @param listener the text presentation listener
+     */
+    void addTextPresentationListener(ITextPresentationListener listener);
+
+    /**
+     * Removes the given text presentation listener from this text viewer.
+     * This call has no effect if the listener is not registered with this
+     * text viewer.
+     *
+     * @param listener the text presentation listener
+     */
+    void removeTextPresentationListener(ITextPresentationListener listener);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension5.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.ITextViewerExtension5;
+
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.ITextViewer}. Defines
+ * a conceptual replacement of the original visible region concept. This interface
+ * replaces {@link org.eclipse.jface.text.ITextViewerExtension3}.
+ * <p>
+ * Introduces the explicit concept of model and widget coordinates. For example,
+ * a selection returned by the text viewer's control is a widget selection. A
+ * widget selection always maps to a certain range of the viewer's document.
+ * This range is considered the model selection.
+ * <p>
+ * All model ranges that have a corresponding widget ranges are considered
+ * "exposed model ranges". The viewer can be requested to expose a given model
+ * range. Thus, a visible region is a particular degeneration of exposed model
+ * ranges.
+ * <p>
+ * This interface allows implementers to follow a sophisticated presentation
+ * model in which the visible presentation is a complex projection of the
+ * viewer's input document.
+ *
+ * @since 3.0
+ */
+public interface ITextViewerExtension5 : ITextViewerExtension3 {
+
+    /**
+     * Returns the minimal region of the viewer's input document that completely
+     * comprises everything that is visible in the viewer's widget or
+     * <code>null</code> if there is no such region.
+     *
+     * @return the minimal region of the viewer's document comprising the
+     *         contents of the viewer's widget or <code>null</code>
+     */
+    IRegion getModelCoverage();
+
+    /**
+     * Returns the widget line that corresponds to the given line of the
+     * viewer's input document or <code>-1</code> if there is no such line.
+     *
+     * @param modelLine the line of the viewer's document
+     * @return the corresponding widget line or <code>-1</code>
+     */
+    int modelLine2WidgetLine(int modelLine);
+
+    /**
+     * Returns the widget offset that corresponds to the given offset in the
+     * viewer's input document or <code>-1</code> if there is no such offset
+     *
+     * @param modelOffset the offset in the viewer's document
+     * @return the corresponding widget offset or <code>-1</code>
+     */
+    int modelOffset2WidgetOffset(int modelOffset);
+
+    /**
+     * Returns the minimal region of the viewer's widget that completely
+     * comprises the given region of the viewer's input document or
+     * <code>null</code> if there is no such region.
+     *
+     * @param modelRange the region of the viewer's document
+     * @return the minimal region of the widget comprising
+     *         <code>modelRange</code> or <code>null</code>
+     */
+    IRegion modelRange2WidgetRange(IRegion modelRange);
+
+    /**
+     * Returns the offset of the viewer's input document that corresponds to the
+     * given widget offset or <code>-1</code> if there is no such offset
+     *
+     * @param widgetOffset the widget offset
+     * @return the corresponding offset in the viewer's document or
+     *         <code>-1</code>
+     */
+    int widgetOffset2ModelOffset(int widgetOffset);
+
+    /**
+     * Returns the minimal region of the viewer's input document that completely
+     * comprises the given widget region or <code>null</code> if there is no
+     * such region.
+     *
+     * @param widgetRange the widget region
+     * @return the minimal region of the viewer's document comprising
+     *         <code>widgetlRange</code> or <code>null</code>
+     */
+    IRegion widgetRange2ModelRange(IRegion widgetRange);
+
+    /**
+     * Returns the line of the viewer's input document that corresponds to the
+     * given widget line or <code>-1</code> if there is no such line.
+     *
+     * @param widgetLine the widget line
+     * @return the corresponding line of the viewer's document or
+     *         <code>-1</code>
+     */
+    int widgetLine2ModelLine(int widgetLine);
+
+    /**
+     * Returns the widget line of the given widget offset.
+     *
+     * @param widgetOffset the widget offset
+     * @return the widget line of the widget offset
+     */
+    int widgetLineOfWidgetOffset(int widgetOffset);
+
+
+    /**
+     * Returns the maximal subranges of the given model range thus that there is
+     * no offset inside a subrange for which there is no image offset.
+     *
+     * @param modelRange the model range
+     * @return the list of subranges
+     */
+    IRegion[] getCoveredModelRanges(IRegion modelRange);
+
+    /**
+     * Exposes the given model range. Returns whether this call caused a change
+     * of the set of exposed model ranges.
+     *
+     * @param modelRange the model range to be exposed
+     * @return <code>true</code> if the set of exposed model ranges changed,
+     *         <code>false</code> otherwise
+     */
+    bool exposeModelRange(IRegion modelRange);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension6.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ITextViewerExtension6;
+
+import org.eclipse.jface.text.IUndoManager; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.ITextViewer}.
+ * Introduces the concept of text hyperlinks and adds access to the undo manager.
+ *
+ * @see org.eclipse.jface.text.hyperlink.IHyperlink
+ * @see org.eclipse.jface.text.hyperlink.IHyperlinkDetector
+ * @since 3.1
+ */
+public interface ITextViewerExtension6 {
+
+    /**
+     * Sets this viewer's hyperlink detectors for the given state mask.
+     *
+     * @param hyperlinkDetectors    the new array of hyperlink detectors, <code>null</code>
+     *                                  or an empty array to disable hyperlinking
+     * @param eventStateMask        the SWT event state mask to activate hyperlink mode
+     */
+    void setHyperlinkDetectors(IHyperlinkDetector[] hyperlinkDetectors, int eventStateMask);
+
+    /**
+     * Returns this viewer's undo manager.
+     *
+     * @return the undo manager or <code>null</code> if it has not been plugged-in
+     * @since 3.1
+     */
+    IUndoManager getUndoManager();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension7.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ITextViewerExtension7;
+
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.ITextViewer}.
+ * Adds the ability to install tabs to spaces conversion.
+ *
+ * @since 3.3
+ */
+public interface ITextViewerExtension7 {
+
+    /**
+     * Sets an auto edit strategy can converts tabs to spaces.
+     *
+     * @param converter the converter or <code>null</code> if none should be used
+     */
+    void setTabsToSpacesConverter(IAutoEditStrategy converter);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension8.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 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 org.eclipse.jface.text.ITextViewerExtension8;
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.swt.custom.StyledTextPrintOptions;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.ITextViewer}. Adds the
+ * ability to print and set how hovers should be enriched when the mouse is moved into them.
+ *
+ * @since 3.4
+ */
+public interface ITextViewerExtension8 {
+
+    /**
+     * Print the text viewer contents using the given options.
+     *
+     * @param options the print options
+     */
+    void print(StyledTextPrintOptions options);
+
+    /**
+     * Sets the hover enrich mode.
+     * A non-<code>null</code> <code>mode</code> defines when hovers
+     * should be enriched once the mouse is moved into them.
+     * If <code>mode</code> is <code>null</code>, hovers are automatically closed
+     * when the mouse is moved out of the {@link ITextHover#getHoverRegion(ITextViewer, int) hover region}.
+     * <p>
+     * Note that a hover can only be enriched if its {@link IInformationControlExtension5#getInformationPresenterControlCreator()}
+     * is not <code>null</code>.
+     * </p>
+     *
+     * @param mode the enrich mode, or <code>null</code>
+     */
+    void setHoverEnrichMode(EnrichMode mode);
+
+
+
+}
+
+    /**
+     * Type-safe enum of the available enrich modes.
+     */
+    public final class EnrichMode {
+
+        /**
+         * Enrich the hover shortly after the mouse has been moved into it and
+         * stopped moving.
+         *
+         * @see ITextViewerExtension8#setHoverEnrichMode(org.eclipse.jface.text.ITextViewerExtension8.EnrichMode)
+         */
+        public static const EnrichMode AFTER_DELAY;
+
+        /**
+         * Enrich the hover immediately when the mouse is moved into it.
+         *
+         * @see ITextViewerExtension8#setHoverEnrichMode(org.eclipse.jface.text.ITextViewerExtension8.EnrichMode)
+         */
+        public static const EnrichMode IMMEDIATELY;
+
+        /**
+         * Enrich the hover on explicit mouse click.
+         *
+         * @see ITextViewerExtension8#setHoverEnrichMode(org.eclipse.jface.text.ITextViewerExtension8.EnrichMode)
+         */
+        public static const EnrichMode ON_CLICK;
+
+
+        static this(){
+            AFTER_DELAY= new EnrichMode("after delay"); //$NON-NLS-1$
+            IMMEDIATELY= new EnrichMode("immediately"); //$NON-NLS-1$
+            ON_CLICK= new EnrichMode("on click"); //$NON-NLS-1$;
+        }
+
+        private String fName;
+
+        private this(String name) {
+            fName= name;
+        }
+
+        /*
+         * @see java.lang.Object#toString()
+         */
+        public override String toString() {
+            return fName;
+        }
+    }
+alias EnrichMode ITextViewerExtension8_EnrichMode;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IUndoManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.IUndoManager;
+
+import org.eclipse.jface.text.ITextViewer; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * An undo manager is connected to at most one
+ * {@link org.eclipse.jface.text.ITextViewer}.
+ * <p>
+ * It monitors the text viewer and keeps a history of the changes applied to the
+ * viewer. The undo manager groups those changes into user interactions which on
+ * an undo request are rolled back in one atomic change.</p>
+ * <p>
+ * In order to provide backward compatibility for clients of
+ * <code>IUndoManager</code>, extension interfaces are used as a means of
+ * evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.IUndoManagerExtension} since version 3.1
+ * introducing access to the undo context.</li>
+ * </ul></p>
+ * <p>
+ * Clients may implement this interface or use the standard implementation
+ * <code>TextViewerUndoManager</code>.
+ * </p>
+ *
+ * @see TextViewerUndoManager
+ * @see IUndoManagerExtension
+ */
+public interface IUndoManager {
+
+    /**
+     * Connects this undo manager to the given text viewer.
+     *
+     * @param viewer the viewer the undo manager is connected to
+     */
+    void connect(ITextViewer viewer);
+
+    /**
+     * Disconnects this undo manager from its text viewer.
+     * If this undo manager hasn't been connected before this
+     * operation has no effect.
+     */
+    void disconnect();
+
+    /**
+     * Signals the undo manager that all subsequent changes until
+     * <code>endCompoundChange</code> is called are to be undone in one piece.
+     */
+    void beginCompoundChange();
+
+    /**
+     * Signals the undo manager that the sequence of changes which started with
+     * <code>beginCompoundChange</code> has been finished. All subsequent changes
+     * are considered to be individually undo-able.
+     */
+    void endCompoundChange();
+
+    /**
+     * Resets the history of the undo manager. After that call,
+     * there aren't any undo-able or redo-able text changes.
+     */
+    void reset();
+
+    /**
+     * The given parameter determines the maximal length of the history
+     * remembered by the undo manager.
+     *
+     * @param undoLevel the length of this undo manager's history
+     */
+    void setMaximalUndoLevel(int undoLevel);
+
+    /**
+     * Returns whether at least one text change can be rolled back.
+     *
+     * @return <code>true</code> if at least one text change can be rolled back
+     */
+    bool undoable();
+
+    /**
+     * Returns whether at least one text change can be repeated. A text change
+     * can be repeated only if it was executed and rolled back.
+     *
+     * @return <code>true</code> if at least on text change can be repeated
+     */
+    bool redoable();
+
+    /**
+     * Rolls back the most recently executed text change.
+     */
+    void undo();
+
+    /**
+     * Repeats the most recently rolled back text change.
+     */
+    void redo();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IUndoManagerExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IUndoManagerExtension;
+
+import java.lang.all;
+
+import org.eclipse.core.commands.operations.IUndoContext;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IUndoManager}.
+ * Introduces access to the undo context.
+ *
+ * @see org.eclipse.jface.text.IUndoManager
+ * @since 3.1
+ */
+public interface IUndoManagerExtension {
+
+    /**
+     * Returns this undo manager's undo context.
+     *
+     * @return the undo context or <code>null</code> if the undo manager is not connected
+     * @see org.eclipse.core.commands.operations.IUndoContext
+     */
+    IUndoContext getUndoContext();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IViewportListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IViewportListener;
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Registered with a text viewer, view port listeners are informed about changes
+ * of a text viewer's view port. The view port is that portion of the viewer's
+ * document which is visible in the viewer.
+ * <p>
+ * Clients may implement this interface.</p>
+ *
+ * @see org.eclipse.jface.text.ITextViewer
+ */
+public interface IViewportListener {
+
+    /**
+     * Informs about view port changes. The given vertical position is the new
+     * vertical scrolling offset measured in pixels.
+     *
+     * @param verticalOffset the vertical offset measured in pixels
+     */
+    void viewportChanged(int verticalOffset);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenKeeper.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.IWidgetTokenKeeper;
+
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+
+import java.lang.all;
+
+/**
+ * A widget token keeper may require a widget token from an
+ * {@link org.eclipse.jface.text.IWidgetTokenOwner} and release the token to the
+ * owner after usage. A widget token owner may request the token from the token
+ * keeper. The keeper may deny the return of the token.
+ * <p>
+ * The widget token owner and keeper interplay is used by a text viewer in
+ * order to manage the appearance and disappearance of addition, on-top popup
+ * windows such as text hovers, content assist, etc.
+ *
+ * In order to provide backward compatibility for clients of
+ * <code>IWidgetTokeKeeper</code>, extension interfaces are used as a means
+ * of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.IWidgetTokenKeeperExtension} since version
+ *     3.0 introducing priorities when requesting a widget token and thus replacing
+ *     the non-prioritized scheme. It also allows a client to force a widget token
+ *     keeper to accept focus.</li>
+ * </ul>
+ *
+ * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension
+ * @since 2.0
+ */
+public interface IWidgetTokenKeeper {
+
+    /**
+     * The given widget token owner requests the widget token from this token
+     * keeper. Returns <code>true</code> if the token is released by this
+     * token keeper. Note, the keeper must not call
+     * <code>releaseWidgetToken(IWidgetTokenKeeper)</code> explicitly.
+     * <p>
+     * Replaced by
+     * {@link IWidgetTokenKeeperExtension#requestWidgetToken(IWidgetTokenOwner, int)}.
+     *
+     * @param owner the token owner
+     * @return <code>true</code> if token has been released <code>false</code>
+     *         otherwise
+     */
+    bool requestWidgetToken(IWidgetTokenOwner owner);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenKeeperExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IWidgetTokenKeeperExtension;
+
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IWidgetTokenKeeper}.
+ * <p>
+ * Replaces the original <code>requestWidgetToken</code> functionality with a
+ * new priority based approach. Adds the concept of focus handling.
+ *
+ * @since 3.0
+ */
+public interface IWidgetTokenKeeperExtension {
+
+    /**
+     * The given widget token owner requests the widget token  from
+     * this token keeper. Returns  <code>true</code> if the token is released
+     * by this token keeper. Note, the keeper must not call
+     * <code>releaseWidgetToken(IWidgetTokenKeeper)</code> explicitly.
+     *
+     * <p>The general contract is that the receiver should release the token
+     * if <code>priority</code> exceeds the receiver's priority.</p>
+     *
+     * @param owner the token owner
+     * @param priority the priority of the request
+     * @return <code>true</code> if token has been released <code>false</code> otherwise
+     */
+    bool requestWidgetToken(IWidgetTokenOwner owner, int priority);
+
+    /**
+     * Requests the receiver to give focus to its popup shell, hover, or similar. There is
+     * no assumption made whether the receiver actually succeeded in taking the focus. The return
+     * value gives a hint whether the receiver tried to take focus.
+     *
+     * @param owner the token owner
+     * @return <code>true</code> if the receiver tried to take focus, <code>false</code> if it did not.
+     */
+    bool setFocus(IWidgetTokenOwner owner);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenOwner.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.IWidgetTokenOwner;
+
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+
+import java.lang.all;
+
+
+/**
+ * In order to display information in a temporary window, a widget token must be
+ * acquired. The intent behind this concept is that only one temporary window
+ * should be presented at any moment in time and also to avoid overlapping
+ * temporary windows. This concept is used by the
+ * {@link org.eclipse.jface.text.ITextViewer}.
+ * <p>
+ * In order to provide backward compatibility for clients of
+ * <code>IWidgetTokenOwner</code>, extension interfaces are used as a means
+ * of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.IWidgetTokenOwnerExtension} since version
+ * 3.0 introducing priorities when requesting a widget token and thus replacing
+ * the non-prioritized scheme.</li>
+ * </ul>
+ *
+ * @see org.eclipse.jface.text.IWidgetTokenOwnerExtension
+ * @since 2.0
+ */
+public interface IWidgetTokenOwner {
+
+    /**
+     * Requests the widget token from this token owner. Returns
+     * <code>true</code> if the token has been acquired or is already owned by
+     * the requester. This method is non-blocking.
+     * <p>
+     * Replaced by
+     * {@link IWidgetTokenOwnerExtension#requestWidgetToken(IWidgetTokenKeeper, int)}.
+     *
+     * @param requester the token requester
+     * @return <code>true</code> if requester acquires the token,
+     *         <code>false</code> otherwise
+     */
+    bool requestWidgetToken(IWidgetTokenKeeper requester);
+
+    /**
+     * The given token keeper releases the token to this
+     * token owner. If the token has previously not been held
+     * by the given token keeper, nothing happens. This
+     * method is non-blocking.
+     *
+     * @param tokenKeeper the token keeper
+     */
+    void releaseWidgetToken(IWidgetTokenKeeper tokenKeeper);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenOwnerExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IWidgetTokenOwnerExtension;
+
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+
+import java.lang.all;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IWidgetTokenOwner}.
+ * <p>
+ * Replaces the original <code>requestWidgetToken</code> functionality with a
+ * new priority based approach.
+ *
+ * @since 3.0
+ */
+public interface IWidgetTokenOwnerExtension {
+
+    /**
+     * Requests the widget token from this token owner. Returns
+     * <code>true</code> if the token has been acquired or is
+     * already owned by the requester. This method is non-blocking.
+     *
+     * <p><code>priority</code> is forwarded to any existing token keeper
+     * to give it an estimate on whether the request has higher priority than
+     * the current keeper's. There is, however, no guarantee that another keeper
+     * will release the token even if it has a high priority.</p>
+     *
+     * @param requester the token requester
+     * @param priority the priority of the request
+     * @return <code>true</code> if requester acquires the token,
+     *  <code>false</code> otherwise
+     */
+    bool requestWidgetToken(IWidgetTokenKeeper requester, int priority);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/JFaceTextMessages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.JFaceTextMessages;
+
+import java.lang.all;
+
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+
+class JFaceTextMessages {
+
+//     private static const String RESOURCE_BUNDLE= "org.eclipse.jface.text.JFaceTextMessages";//$NON-NLS-1$
+
+    private static ResourceBundle fgResourceBundle_;//= ResourceBundle.getBundle(RESOURCE_BUNDLE);
+
+    private static ResourceBundle fgResourceBundle(){
+        if( fgResourceBundle_ is null ){
+            synchronized(JFaceTextMessages.classinfo ){
+                if( fgResourceBundle_ is null ){
+                    fgResourceBundle_ = ResourceBundle.getBundle(
+                        getImportData!("org.eclipse.jface.text.JFaceTextMessages.properties"));
+                }
+            }
+        }
+        return fgResourceBundle_;
+    }
+
+    private this() {
+    }
+
+    public static String getString(String key) {
+        try {
+            return fgResourceBundle.getString(key);
+        } catch (MissingResourceException e) {
+            return "!" ~ key ~ "!";//$NON-NLS-2$ //$NON-NLS-1$
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/JFaceTextUtil.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,504 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.text.JFaceTextUtil;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.jface.text.source.ILineRange;
+import org.eclipse.jface.text.source.LineRange;
+
+/**
+ * A collection of JFace Text functions.
+ * <p>
+ * This class is neither intended to be instantiated nor subclassed.
+ * </p>
+ *
+ * @since 3.3
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public final class JFaceTextUtil {
+
+    private this() {
+        // Do not instantiate
+    }
+
+    /**
+     * Computes the line height for the given line range.
+     *
+     * @param textWidget the <code>StyledText</code> widget
+     * @param startLine the start line
+     * @param endLine the end line (exclusive)
+     * @param lineCount the line count used by the old API
+     * @return the height of all lines starting with <code>startLine</code> and ending above <code>endLime</code>
+     */
+    public static int computeLineHeight(StyledText textWidget, int startLine, int endLine, int lineCount) {
+        return getLinePixel(textWidget, endLine) - getLinePixel(textWidget, startLine);
+    }
+
+    /**
+     * Returns the last fully visible line of the widget. The exact semantics of "last fully visible
+     * line" are:
+     * <ul>
+     * <li>the last line of which the last pixel is visible, if any
+     * <li>otherwise, the only line that is partially visible
+     * </ul>
+     *
+     * @param widget the widget
+     * @return the last fully visible line
+     */
+    public static int getBottomIndex(StyledText widget) {
+        int lastPixel= computeLastVisiblePixel(widget);
+
+        // bottom is in [0 .. lineCount - 1]
+        int bottom= widget.getLineIndex(lastPixel);
+
+        // bottom is the first line - no more checking
+        if (bottom is 0)
+            return bottom;
+
+        int pixel= widget.getLinePixel(bottom);
+        // bottom starts on or before the client area start - bottom is the only visible line
+        if (pixel <= 0)
+            return bottom;
+
+        int offset= widget.getOffsetAtLine(bottom);
+        int height= widget.getLineHeight(offset);
+
+        // bottom is not showing entirely - use the previous line
+        if (pixel + height - 1 > lastPixel)
+            return bottom - 1;
+
+        // bottom is fully visible and its last line is exactly the last pixel
+        return bottom;
+    }
+
+    /**
+     * Returns the index of the first (possibly only partially) visible line of the widget
+     *
+     * @param widget the widget
+     * @return the index of the first line of which a pixel is visible
+     */
+    public static int getPartialTopIndex(StyledText widget) {
+        // see StyledText#getPartialTopIndex()
+        int top= widget.getTopIndex();
+        int pixels= widget.getLinePixel(top);
+
+        // FIXME remove when https://bugs.eclipse.org/bugs/show_bug.cgi?id=123770 is fixed
+        if (pixels is -widget.getLineHeight(widget.getOffsetAtLine(top))) {
+            top++;
+            pixels= 0;
+        }
+
+        if (pixels > 0)
+            top--;
+
+        return top;
+    }
+
+    /**
+     * Returns the index of the last (possibly only partially) visible line of the widget
+     *
+     * @param widget the text widget
+     * @return the index of the last line of which a pixel is visible
+     */
+    public static int getPartialBottomIndex(StyledText widget) {
+        // @see StyledText#getPartialBottomIndex()
+        int lastPixel= computeLastVisiblePixel(widget);
+        int bottom= widget.getLineIndex(lastPixel);
+        return bottom;
+    }
+
+    /**
+     * Returns the last visible pixel in the widget's client area.
+     *
+     * @param widget the widget
+     * @return the last visible pixel in the widget's client area
+     */
+    private static int computeLastVisiblePixel(StyledText widget) {
+        int caHeight= widget.getClientArea().height;
+        int lastPixel= caHeight - 1;
+        // XXX what if there is a margin? can't take trim as this includes the scrollbars which are not part of the client area
+//      if ((textWidget.getStyle() & SWT.BORDER) !is 0)
+//          lastPixel -= 4;
+        return lastPixel;
+    }
+
+    /**
+     * Returns the line index of the first visible model line in the viewer. The line may be only
+     * partially visible.
+     *
+     * @param viewer the text viewer
+     * @return the first line of which a pixel is visible, or -1 for no line
+     */
+    public static int getPartialTopIndex(ITextViewer viewer) {
+        StyledText widget= viewer.getTextWidget();
+        int widgetTop= getPartialTopIndex(widget);
+        return widgetLine2ModelLine(viewer, widgetTop);
+    }
+
+    /**
+     * Returns the last, possibly partially, visible line in the view port.
+     *
+     * @param viewer the text viewer
+     * @return the last, possibly partially, visible line in the view port
+     */
+    public static int getPartialBottomIndex(ITextViewer viewer) {
+        StyledText textWidget= viewer.getTextWidget();
+        int widgetBottom= getPartialBottomIndex(textWidget);
+        return widgetLine2ModelLine(viewer, widgetBottom);
+    }
+
+    /**
+     * Returns the range of lines that is visible in the viewer, including any partially visible
+     * lines.
+     *
+     * @param viewer the viewer
+     * @return the range of lines that is visible in the viewer, <code>null</code> if no lines are
+     *         visible
+     */
+    public static ILineRange getVisibleModelLines(ITextViewer viewer) {
+        int top= getPartialTopIndex(viewer);
+        int bottom= getPartialBottomIndex(viewer);
+        if (top is -1 || bottom is -1)
+            return null;
+        return new LineRange(top, bottom - top + 1);
+    }
+
+    /**
+     * Converts a widget line into a model (i.e. {@link IDocument}) line using the
+     * {@link ITextViewerExtension5} if available, otherwise by adapting the widget line to the
+     * viewer's {@link ITextViewer#getVisibleRegion() visible region}.
+     *
+     * @param viewer the viewer
+     * @param widgetLine the widget line to convert.
+     * @return the model line corresponding to <code>widgetLine</code> or -1 to signal that there
+     *         is no corresponding model line
+     */
+    public static int widgetLine2ModelLine(ITextViewer viewer, int widgetLine) {
+        int modelLine;
+        if ( cast(ITextViewerExtension5)viewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) viewer;
+            modelLine= extension.widgetLine2ModelLine(widgetLine);
+        } else {
+            try {
+                IRegion r= viewer.getVisibleRegion();
+                IDocument d= viewer.getDocument();
+                modelLine= widgetLine + d.getLineOfOffset(r.getOffset());
+            } catch (BadLocationException x) {
+                modelLine= widgetLine;
+            }
+        }
+        return modelLine;
+    }
+
+    /**
+     * Converts a model (i.e. {@link IDocument}) line into a widget line using the
+     * {@link ITextViewerExtension5} if available, otherwise by adapting the model line to the
+     * viewer's {@link ITextViewer#getVisibleRegion() visible region}.
+     *
+     * @param viewer the viewer
+     * @param modelLine the model line to convert.
+     * @return the widget line corresponding to <code>modelLine</code> or -1 to signal that there
+     *         is no corresponding widget line
+     */
+    public static int modelLineToWidgetLine(ITextViewer viewer, int modelLine) {
+        int widgetLine;
+        if ( cast(ITextViewerExtension5)viewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) viewer;
+            widgetLine= extension.modelLine2WidgetLine(modelLine);
+        } else {
+            IRegion region= viewer.getVisibleRegion();
+            IDocument document= viewer.getDocument();
+            try {
+                int visibleStartLine= document.getLineOfOffset(region.getOffset());
+                int visibleEndLine= document.getLineOfOffset(region.getOffset() + region.getLength());
+                if (modelLine < visibleStartLine || modelLine > visibleEndLine)
+                    widgetLine= -1;
+                else
+                widgetLine= modelLine - visibleStartLine;
+            } catch (BadLocationException x) {
+                // ignore and return -1
+                widgetLine= -1;
+            }
+        }
+        return widgetLine;
+    }
+
+
+    /**
+     * Returns the number of hidden pixels of the first partially visible line. If there is no
+     * partially visible line, zero is returned.
+     *
+     * @param textWidget the widget
+     * @return the number of hidden pixels of the first partial line, always &gt;= 0
+     */
+    public static int getHiddenTopLinePixels(StyledText textWidget) {
+        int top= getPartialTopIndex(textWidget);
+        return -textWidget.getLinePixel(top);
+    }
+
+    /*
+     * @see StyledText#getLinePixel(int)
+     */
+    public static int getLinePixel(StyledText textWidget, int line) {
+        return textWidget.getLinePixel(line);
+    }
+
+    /*
+     * @see StyledText#getLineIndex(int)
+     */
+    public static int getLineIndex(StyledText textWidget, int y) {
+        int lineIndex= textWidget.getLineIndex(y);
+        return lineIndex;
+    }
+
+    /**
+     * Returns <code>true</code> if the widget displays the entire contents, i.e. it cannot
+     * be vertically scrolled.
+     *
+     * @param widget the widget
+     * @return <code>true</code> if the widget displays the entire contents, i.e. it cannot
+     *         be vertically scrolled, <code>false</code> otherwise
+     */
+    public static bool isShowingEntireContents(StyledText widget) {
+        if (widget.getTopPixel() !is 0) // more efficient shortcut
+            return false;
+
+        int lastVisiblePixel= computeLastVisiblePixel(widget);
+        int lastPossiblePixel= widget.getLinePixel(widget.getLineCount());
+        return lastPossiblePixel <= lastVisiblePixel;
+    }
+
+    /**
+     * Determines the graphical area covered by the given text region in
+     * the given viewer.
+     *
+     * @param region the region whose graphical extend must be computed
+     * @param textViewer the text viewer containing the region
+     * @return the graphical extend of the given region in the given viewer
+     *
+     * @since 3.4
+     */
+    public static Rectangle computeArea(IRegion region, ITextViewer textViewer) {
+        int start= 0;
+        int end= 0;
+        IRegion widgetRegion= modelRange2WidgetRange(region, textViewer);
+        if (widgetRegion !is null) {
+            start= widgetRegion.getOffset();
+            end= start + widgetRegion.getLength();
+        }
+
+        StyledText styledText= textViewer.getTextWidget();
+        Rectangle bounds;
+        if (end > 0 && start < end)
+            bounds= styledText.getTextBounds(start, end - 1);
+        else {
+            Point loc= styledText.getLocationAtOffset(start);
+            bounds= new Rectangle(loc.x, loc.y, getAverageCharWidth(textViewer.getTextWidget()), styledText.getLineHeight(start));
+        }
+
+        return new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height);
+    }
+
+    /**
+     * Translates a given region of the text viewer's document into
+     * the corresponding region of the viewer's widget.
+     *
+     * @param region the document region
+     * @param textViewer the viewer containing the region
+     * @return the corresponding widget region
+     *
+     * @since 3.4
+     */
+    private static IRegion modelRange2WidgetRange(IRegion region, ITextViewer textViewer) {
+        if ( cast(ITextViewerExtension5)textViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) textViewer;
+            return extension.modelRange2WidgetRange(region);
+        }
+
+        IRegion visibleRegion= textViewer.getVisibleRegion();
+        int start= region.getOffset() - visibleRegion.getOffset();
+        int end= start + region.getLength();
+        if (end > visibleRegion.getLength())
+            end= visibleRegion.getLength();
+
+        return new Region(start, end - start);
+    }
+
+    /**
+     * Returns the average character width of the given control's font.
+     *
+     * @param control the control to calculate the average char width for
+     * @return the average character width of the controls font
+     *
+     * @since 3.4
+     */
+    public static int getAverageCharWidth(Control control) {
+        GC gc= new GC(control);
+        gc.setFont(control.getFont());
+        int increment= gc.getFontMetrics().getAverageCharWidth();
+        gc.dispose();
+        return increment;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/MarginPainter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,323 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.MarginPainter;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+
+
+/**
+ * Paints a vertical line (margin line) after a given column respecting the text
+ * viewer's font.
+ * <p>
+ * Clients usually instantiate and configure objects of this class.</p>
+ * <p>
+ * This class is not intended to be subclassed.</p>
+ *
+ * @since 2.1
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class MarginPainter : IPainter, PaintListener {
+
+    /** The widget of the text viewer */
+    private StyledText fTextWidget;
+
+    /** The column after which to paint the line, default value <code>80</code> */
+    private int fMarginWidth= 80;
+    /** The color in which to paint the line */
+    private Color fColor;
+    /** The line style of the line to be painted, default value <code>SWT.LINE_SOLID</code> */
+    private int fLineStyle= SWT.LINE_SOLID;
+    /** The line width of the line to be painted, default value <code>1</code> */
+    private int fLineWidth= 0; // NOTE: 0 means width is 1 but with optimized performance
+    /** The cached x-offset of the <code>fMarginWidth</code> for the current font */
+    private int fCachedWidgetX= -1;
+    /** The active state of this painter */
+    private bool fIsActive= false;
+
+    /**
+     * Creates a new painter for the given text viewer.
+     *
+     * @param textViewer the text viewer
+     */
+    public this(ITextViewer textViewer) {
+        fTextWidget= textViewer.getTextWidget();
+    }
+
+    /**
+     * Sets the column after which to draw the margin line.
+     *
+     * @param width the column
+     */
+    public void setMarginRulerColumn(int width) {
+        fMarginWidth= width;
+        initialize();
+    }
+
+    /**
+     * Sets the line style of the margin line.
+     *
+     * @param lineStyle a <code>SWT</code> style constant describing the line style
+     */
+    public void setMarginRulerStyle(int lineStyle) {
+        fLineStyle= lineStyle;
+    }
+
+    /**
+     * Sets the line width of the margin line.
+     *
+     * @param lineWidth the line width
+     */
+    public void setMarginRulerWidth(int lineWidth) {
+        if (lineWidth is 1)
+            lineWidth= 0; // NOTE: 0 means width is 1 but with optimized performance
+        fLineWidth= lineWidth;
+    }
+
+    /**
+     * Sets the color of the margin line. Must be called before <code>paint</code> is called the first time.
+     *
+     * @param color the color
+     */
+    public void setMarginRulerColor(Color color) {
+        fColor= color;
+    }
+
+    /**
+     * Initializes this painter, by flushing and recomputing all caches and causing
+     * the widget to be redrawn. Must be called explicitly when font of text widget changes.
+     */
+    public void initialize() {
+        computeWidgetX();
+        fTextWidget.redraw();
+    }
+
+    /**
+     * Computes and remembers the x-offset of the margin column for the
+     * current widget font.
+     */
+    private void computeWidgetX() {
+        GC gc= new GC(fTextWidget);
+        int pixels= gc.getFontMetrics().getAverageCharWidth();
+        gc.dispose();
+
+        fCachedWidgetX= pixels * fMarginWidth;
+    }
+
+    /*
+     * @see IPainter#deactivate(bool)
+     */
+    public void deactivate(bool redraw) {
+        if (fIsActive) {
+            fIsActive= false;
+            fCachedWidgetX= -1;
+            fTextWidget.removePaintListener(this);
+            if (redraw)
+                fTextWidget.redraw();
+        }
+    }
+
+    /*
+     * @see IPainter#dispose()
+     */
+    public void dispose() {
+        fTextWidget= null;
+    }
+
+    /*
+     * @see IPainter#paint(int)
+     */
+    public void paint(int reason) {
+        if (!fIsActive) {
+            fIsActive= true;
+            fTextWidget.addPaintListener(this);
+            if (fCachedWidgetX is -1)
+                computeWidgetX();
+            fTextWidget.redraw();
+        } else if (CONFIGURATION is reason || INTERNAL is reason)
+            fTextWidget.redraw();
+    }
+
+    /*
+     * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
+     */
+    public void paintControl(PaintEvent e) {
+        if (fTextWidget !is null) {
+            int x= fCachedWidgetX - fTextWidget.getHorizontalPixel();
+            if (x >= 0) {
+                Rectangle area= fTextWidget.getClientArea();
+                e.gc.setForeground(fColor);
+                e.gc.setLineStyle(fLineStyle);
+                e.gc.setLineWidth(fLineWidth);
+                e.gc.drawLine(x, 0, x, area.height);
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPainter#setPositionManager(org.eclipse.jface.text.IPaintPositionManager)
+     */
+    public void setPositionManager(IPaintPositionManager manager) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/MarkSelection.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.MarkSelection;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Default implementation of {@link org.eclipse.jface.text.IMarkSelection}.
+ *
+ * @since 2.0
+ */
+public class MarkSelection : IMarkSelection {
+
+    /** The marked document. */
+    private const IDocument fDocument;
+    /** The offset of the mark selection. */
+    private const int fOffset;
+    /** The length of the mark selection. */
+    private const int fLength;
+
+    /**
+     * Creates a MarkSelection.
+     *
+     * @param document the marked document
+     * @param offset the offset of the mark
+     * @param length the length of the mark, may be negative if caret before offset
+     */
+    public this(IDocument document, int offset, int length) {
+        fDocument= document;
+        fOffset= offset;
+        fLength= length;
+    }
+
+    /*
+     * @see IMarkSelection#getDocument()
+     */
+    public IDocument getDocument() {
+        return fDocument;
+    }
+
+    /*
+     * @see IMarkSelection#getOffset()
+     */
+    public int getOffset() {
+        return fOffset;
+    }
+
+    /*
+     * @see IMarkSelection#getLength()
+     */
+    public int getLength() {
+        return fLength;
+    }
+
+    /*
+     * @see ISelection#isEmpty()
+     */
+    public bool isEmpty() {
+        return fLength is 0;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/PaintManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,531 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.PaintManager;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+
+
+
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+
+
+/**
+ * Manages the {@link org.eclipse.jface.text.IPainter} object registered with an
+ * {@link org.eclipse.jface.text.ITextViewer}.
+ * <p>
+ * Clients usually instantiate and configure objects of this type.</p>
+ *
+ * @since 2.1
+ */
+public final class PaintManager : KeyListener, MouseListener, ISelectionChangedListener, ITextListener, ITextInputListener {
+
+    /**
+     * Position updater used by the position manager. This position updater differs from the default position
+     * updater in that it extends a position when an insertion happens at the position's offset and right behind
+     * the position.
+     */
+    static class PaintPositionUpdater : DefaultPositionUpdater {
+
+        /**
+         * Creates the position updater for the given category.
+         *
+         * @param category the position category
+         */
+        protected this(String category) {
+            super(category);
+        }
+
+        /**
+         * If an insertion happens at a position's offset, the
+         * position is extended rather than shifted. Also, if something is added
+         * right behind the end of the position, the position is extended rather
+         * than kept stable.
+         */
+        protected void adaptToInsert() {
+
+            int myStart= fPosition.offset;
+            int myEnd=   fPosition.offset + fPosition.length;
+            myEnd= Math.max(myStart, myEnd);
+
+            int yoursStart= fOffset;
+            int yoursEnd=   fOffset + fReplaceLength;// - 1;
+            yoursEnd= Math.max(yoursStart, yoursEnd);
+
+            if (myEnd < yoursStart)
+                return;
+
+            if (myStart <= yoursStart)
+                fPosition.length += fReplaceLength;
+            else
+                fPosition.offset += fReplaceLength;
+        }
+    }
+
+    /**
+     * The paint position manager used by this paint manager. The paint position
+     * manager is installed on a single document and control the creation/disposed
+     * and updating of a position category that will be used for managing positions.
+     */
+    static class PositionManager : IPaintPositionManager {
+
+//      /** The document this position manager works on */
+        private IDocument fDocument;
+        /** The position updater used for the managing position category */
+        private IPositionUpdater fPositionUpdater;
+        /** The managing position category */
+        private String fCategory;
+
+        /**
+         * Creates a new position manager. Initializes the managing
+         * position category using its class name and its hash value.
+         */
+        public this() {
+            fCategory= this.classinfo.name ~ Integer.toString(toHash());
+            fPositionUpdater= new PaintPositionUpdater(fCategory);
+        }
+
+        /**
+         * Installs this position manager in the given document. The position manager stays
+         * active until <code>uninstall</code> or <code>dispose</code>
+         * is called.
+         *
+         * @param document the document to be installed on
+         */
+        public void install(IDocument document) {
+            fDocument= document;
+            fDocument.addPositionCategory(fCategory);
+            fDocument.addPositionUpdater(fPositionUpdater);
+        }
+
+        /**
+         * Disposes this position manager. The position manager is automatically
+         * removed from the document it has previously been installed
+         * on.
+         */
+        public void dispose() {
+            uninstall(fDocument);
+        }
+
+        /**
+         * Uninstalls this position manager form the given document. If the position
+         * manager has no been installed on this document, this method is without effect.
+         *
+         * @param document the document form which to uninstall
+         */
+        public void uninstall(IDocument document) {
+            if (document is fDocument && document !is null) {
+                try {
+                    fDocument.removePositionUpdater(fPositionUpdater);
+                    fDocument.removePositionCategory(fCategory);
+                } catch (BadPositionCategoryException x) {
+                    // should not happen
+                }
+                fDocument= null;
+            }
+        }
+
+        /*
+         * @see IPositionManager#addManagedPosition(Position)
+         */
+        public void managePosition(Position position) {
+            try {
+                fDocument.addPosition(fCategory, position);
+            } catch (BadPositionCategoryException x) {
+                // should not happen
+            } catch (BadLocationException x) {
+                // should not happen
+            }
+        }
+
+        /*
+         * @see IPositionManager#removeManagedPosition(Position)
+         */
+        public void unmanagePosition(Position position) {
+            try {
+                fDocument.removePosition(fCategory, position);
+            } catch (BadPositionCategoryException x) {
+                // should not happen
+            }
+        }
+    }
+
+
+    /** The painters managed by this paint manager. */
+    private List fPainters;
+    /** The position manager used by this paint manager */
+    private PositionManager fManager;
+    /** The associated text viewer */
+    private ITextViewer fTextViewer;
+
+    /**
+     * Creates a new paint manager for the given text viewer.
+     *
+     * @param textViewer the text viewer associated to this newly created paint manager
+     */
+    public this(ITextViewer textViewer) {
+        fPainters= new ArrayList(2);
+        fTextViewer= textViewer;
+    }
+
+
+    /**
+     * Adds the given painter to the list of painters managed by this paint manager.
+     * If the painter is already registered with this paint manager, this method is
+     * without effect.
+     *
+     * @param painter the painter to be added
+     */
+    public void addPainter(IPainter painter) {
+        if (!fPainters.contains(cast(Object)painter)) {
+            fPainters.add(cast(Object)painter);
+            if (fPainters.size() is 1)
+                install();
+            painter.setPositionManager(fManager);
+            painter.paint(IPainter.INTERNAL);
+        }
+    }
+
+    /**
+     * Removes the given painter from the list of painters managed by this
+     * paint manager. If the painter has not previously been added to this
+     * paint manager, this method is without effect.
+     *
+     * @param painter the painter to be removed
+     */
+    public void removePainter(IPainter painter) {
+        if (fPainters.remove(cast(Object)painter)) {
+            painter.deactivate(true);
+            painter.setPositionManager(null);
+        }
+        if (fPainters.size() is 0)
+            dispose();
+    }
+
+    /**
+     * Installs/activates this paint manager. Is called as soon as the
+     * first painter is to be managed by this paint manager.
+     */
+    private void install() {
+
+        fManager= new PositionManager();
+        if (fTextViewer.getDocument() !is null)
+            fManager.install(fTextViewer.getDocument());
+
+        fTextViewer.addTextInputListener(this);
+
+        addListeners();
+    }
+
+    /**
+     * Installs our listener set on the text viewer and the text widget,
+     * respectively.
+     */
+    private void addListeners() {
+        ISelectionProvider provider= fTextViewer.getSelectionProvider();
+        provider.addSelectionChangedListener(this);
+
+        fTextViewer.addTextListener(this);
+
+        StyledText text= fTextViewer.getTextWidget();
+        text.addKeyListener(this);
+        text.addMouseListener(this);
+    }
+
+    /**
+     * Disposes this paint manager. The paint manager uninstalls itself
+     * and clears all registered painters. This method is also called when the
+     * last painter is removed from the list of managed painters.
+     */
+    public void dispose() {
+
+        if (fManager !is null) {
+            fManager.dispose();
+            fManager= null;
+        }
+
+        for (Iterator e = fPainters.iterator(); e.hasNext();)
+            (cast(IPainter) e.next()).dispose();
+        fPainters.clear();
+
+        fTextViewer.removeTextInputListener(this);
+
+        removeListeners();
+    }
+
+    /**
+     * Removes our set of listeners from the text viewer and widget,
+     * respectively.
+     */
+    private void removeListeners() {
+        ISelectionProvider provider= fTextViewer.getSelectionProvider();
+        if (provider !is null)
+            provider.removeSelectionChangedListener(this);
+
+        fTextViewer.removeTextListener(this);
+
+        StyledText text= fTextViewer.getTextWidget();
+        if (text !is null && !text.isDisposed()) {
+            text.removeKeyListener(this);
+            text.removeMouseListener(this);
+        }
+    }
+
+    /**
+     * Triggers all registered painters for the given reason.
+     *
+     * @param reason the reason
+     * @see IPainter
+     */
+    private void paint(int reason) {
+        for (Iterator e = fPainters.iterator(); e.hasNext();)
+            (cast(IPainter) e.next()).paint(reason);
+    }
+
+    /*
+     * @see KeyListener#keyPressed(KeyEvent)
+     */
+    public void keyPressed(KeyEvent e) {
+        paint(IPainter.KEY_STROKE);
+    }
+
+    /*
+     * @see KeyListener#keyReleased(KeyEvent)
+     */
+    public void keyReleased(KeyEvent e) {
+    }
+
+    /*
+     * @see MouseListener#mouseDoubleClick(MouseEvent)
+     */
+    public void mouseDoubleClick(MouseEvent e) {
+    }
+
+    /*
+     * @see MouseListener#mouseDown(MouseEvent)
+     */
+    public void mouseDown(MouseEvent e) {
+        paint(IPainter.MOUSE_BUTTON);
+    }
+
+    /*
+     * @see MouseListener#mouseUp(MouseEvent)
+     */
+    public void mouseUp(MouseEvent e) {
+    }
+
+    /*
+     * @see ISelectionChangedListener#selectionChanged(SelectionChangedEvent)
+     */
+    public void selectionChanged(SelectionChangedEvent event) {
+        paint(IPainter.SELECTION);
+    }
+
+    /*
+     * @see ITextListener#textChanged(TextEvent)
+     */
+    public void textChanged(TextEvent event) {
+
+        if (!event.getViewerRedrawState())
+            return;
+
+        Control control= fTextViewer.getTextWidget();
+        if (control !is null) {
+            control.getDisplay().asyncExec(new class()  Runnable {
+                public void run() {
+                    if (fTextViewer !is null)
+                        paint(IPainter.TEXT_CHANGE);
+                }
+            });
+        }
+    }
+
+    /*
+     * @see ITextInputListener#inputDocumentAboutToBeChanged(IDocument, IDocument)
+     */
+    public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+        if (oldInput !is null) {
+            for (Iterator e = fPainters.iterator(); e.hasNext();)
+                (cast(IPainter) e.next()).deactivate(false);
+            fManager.uninstall(oldInput);
+            removeListeners();
+        }
+    }
+
+    /*
+     * @see ITextInputListener#inputDocumentChanged(IDocument, IDocument)
+     */
+    public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
+        if (newInput !is null) {
+            fManager.install(newInput);
+            paint(IPainter.TEXT_CHANGE);
+            addListeners();
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/PropagatingFontFieldEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,291 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.PropagatingFontFieldEditor;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.jface.preference.FontFieldEditor;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceConverter;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+
+/**
+ * This font field editor implements chaining between a source preference
+ * store and a target preference store. Any time the source preference
+ * store changes, the change is propagated to the target store. Propagation
+ * means that the actual value stored in the source store is set as default
+ * value in the target store. If the target store does not contain a value
+ * other than the default value, the new default value is immediately
+ * effective.
+ *
+ * @see FontFieldEditor
+ * @since 2.0
+ * @deprecated since 3.0 not longer in use, no longer supported
+ */
+public class PropagatingFontFieldEditor : FontFieldEditor {
+
+    /** The editor's parent widget */
+    private Composite fParent;
+    /** The representation of the default font choice */
+    private String fDefaultFontLabel;
+
+    /**
+     * Creates a new font field editor with the given parameters.
+     *
+     * @param name the editor's name
+     * @param labelText the text shown as editor description
+     * @param parent the editor's parent widget
+     * @param defaultFontLabel the label shown in the editor value field when the default value should be taken
+     */
+    public this(String name, String labelText, Composite parent, String defaultFontLabel) {
+        super(name, labelText, parent);
+        fParent= parent;
+        fDefaultFontLabel= defaultFontLabel is null ? "" : defaultFontLabel; //$NON-NLS-1$
+    }
+
+    /*
+     * @see FontFieldEditor#doLoad()
+     */
+    protected void doLoad() {
+        if (getPreferenceStore().isDefault(getPreferenceName()))
+            loadDefault();
+        super.doLoad();
+        checkForDefault();
+    }
+
+    /*
+     * @see FontFieldEditor#doLoadDefault()
+     */
+    protected void doLoadDefault() {
+        super.doLoadDefault();
+        checkForDefault();
+    }
+
+    /**
+     * Checks whether this editor presents the default value "inherited"
+     * from the workbench rather than its own font.
+     */
+    private void checkForDefault() {
+        if (presentsDefaultValue()) {
+            Control c= getValueControl(fParent);
+            if ( cast(Label)c )
+                (cast(Label) c).setText(fDefaultFontLabel);
+        }
+    }
+
+    /**
+     * Propagates the font set in the source store to the
+     * target store using the given keys.
+     *
+     * @param source the store from which to read the text font
+     * @param sourceKey the key under which the font can be found
+     * @param target the store to which to propagate the font
+     * @param targetKey the key under which to store the font
+     */
+    private static void propagateFont(IPreferenceStore source, String sourceKey, IPreferenceStore target, String targetKey) {
+        FontData fd= PreferenceConverter.getFontData(source, sourceKey);
+        if (fd !is null) {
+            bool isDefault= target.isDefault(targetKey); // save old state!
+            PreferenceConverter.setDefault(target, targetKey, fd);
+            if (isDefault) {
+                // restore old state
+                target.setToDefault(targetKey);
+            }
+        }
+    }
+
+    /**
+     * Starts the propagation of the font preference stored in the source preference
+     * store under the source key to the target preference store using the target
+     * preference key.
+     *
+     * @param source the source preference store
+     * @param sourceKey the key to be used in the source preference store
+     * @param target the target preference store
+     * @param targetKey the key to be used in the target preference store
+     */
+    public static void startPropagate(IPreferenceStore source, String sourceKey, IPreferenceStore target, String targetKey) {
+        source.addPropertyChangeListener(new class(source,sourceKey,target,targetKey)  IPropertyChangeListener {
+            IPreferenceStore source_;
+            String sourceKey_;
+            IPreferenceStore target_;
+            String targetKey_;
+            this(IPreferenceStore a, String b, IPreferenceStore c, String d){
+                source_=a;
+                sourceKey_=b;
+                target_=c;
+                targetKey_=d;
+            }
+            public void propertyChange(PropertyChangeEvent event) {
+                if (sourceKey_.equals(event.getProperty()))
+                    propagateFont(source_, sourceKey_, target_, targetKey_);
+            }
+        });
+
+        propagateFont(source, sourceKey, target, targetKey);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/RegExMessages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.RegExMessages;
+
+import java.lang.all;
+
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+
+
+/**
+ * RegEx messages. Helper class to get NLSed messages.
+ *
+ * @since 3.4
+ */
+final class RegExMessages {
+
+    //private static const String RESOURCE_BUNDLE= RegExMessages.classinfo.getName();
+    private static ResourceBundle fgResourceBundle_;//= ResourceBundle.getBundle(RESOURCE_BUNDLE);
+    private static ResourceBundle fgResourceBundle(){
+        if( fgResourceBundle_ is null ){
+            synchronized(RegExMessages.classinfo ){
+                if( fgResourceBundle_ is null ){
+                    fgResourceBundle_ = ResourceBundle.getBundle(
+                        getImportData!("org.eclipse.jface.text.RegExMessages.properties"));
+                }
+            }
+        }
+        return fgResourceBundle_;
+    }
+
+    private this() {
+        // Do not instantiate
+    }
+
+    public static String getString(String key) {
+        try {
+            return fgResourceBundle.getString(key);
+        } catch (MissingResourceException e) {
+            return "!" ~ key ~ "!";//$NON-NLS-2$ //$NON-NLS-1$
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TabsToSpacesConverter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.TabsToSpacesConverter;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Auto edit strategy that converts tabs into spaces.
+ * <p>
+ * Clients usually instantiate and configure this class but
+ * can also extend it in their own subclass.
+ * </p>
+ * 
+ * @since 3.3
+ */
+public class TabsToSpacesConverter : IAutoEditStrategy {
+
+    private int fTabRatio;
+    private ILineTracker fLineTracker;
+
+
+    public void setNumberOfSpacesPerTab(int ratio) {
+        fTabRatio= ratio;
+    }
+
+    public void setLineTracker(ILineTracker lineTracker) {
+        fLineTracker= lineTracker;
+    }
+
+    private int insertTabString(StringBuffer buffer, int offsetInLine) {
+
+        if (fTabRatio is 0)
+            return 0;
+
+        int remainder= offsetInLine % fTabRatio;
+        remainder= fTabRatio - remainder;
+        for (int i= 0; i < remainder; i++)
+            buffer.append(' ');
+        return remainder;
+    }
+
+    public void customizeDocumentCommand(IDocument document, DocumentCommand command) {
+        String text= command.text;
+        if (text is null)
+            return;
+
+        int index= text.indexOf('\t');
+        if (index > -1) {
+
+            StringBuffer buffer= new StringBuffer();
+
+            fLineTracker.set(command.text);
+            int lines= fLineTracker.getNumberOfLines();
+
+            try {
+
+                for (int i= 0; i < lines; i++) {
+
+                    int offset= fLineTracker.getLineOffset(i);
+                    int endOffset= offset + fLineTracker.getLineLength(i);
+                    String line= text.substring(offset, endOffset);
+
+                    int position= 0;
+                    if (i is 0) {
+                        IRegion firstLine= document.getLineInformationOfOffset(command.offset);
+                        position= command.offset - firstLine.getOffset();
+                    }
+
+                    int length= line.length();
+                    for (int j= 0; j < length; j++) {
+                        char c= line.charAt(j);
+                        if (c is '\t') {
+                            position += insertTabString(buffer, position);
+                        } else {
+                            buffer.append(c);
+                            ++ position;
+                        }
+                    }
+
+                }
+
+                command.text= buffer.toString();
+
+            } catch (BadLocationException x) {
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextAttribute.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,329 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.TextAttribute;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+
+
+/**
+ * Description of textual attributes such as color and style. Text attributes
+ * are considered value objects.
+ * <p>
+ * Clients usually instantiate object of the class.</p>
+ */
+public class TextAttribute {
+
+    /**
+     * Text attribute for strikethrough style.
+     * (value <code>1 << 29</code>).
+     * @since 3.1
+     */
+    public static const int STRIKETHROUGH= 1 << 29;
+
+    /**
+     * Text attribute for underline style.
+     * (value <code>1 << 30</code>)
+     * @since 3.1
+     */
+    public static const int UNDERLINE= 1 << 30;
+
+
+    /** Foreground color */
+    private Color foreground;
+
+    /** Background color */
+    private Color background;
+
+    /** The text style */
+    private int style;
+
+    /**
+     * The text font.
+     * @since 3.3
+     */
+    private Font font;
+
+    /**
+     * Cached hash code.
+     * @since 3.3
+     */
+    private int fHashCode;
+
+    /**
+     * Creates a text attribute with the given colors and style.
+     *
+     * @param foreground the foreground color, <code>null</code> if none
+     * @param background the background color, <code>null</code> if none
+     * @param style the style
+     */
+    public this(Color foreground, Color background, int style) {
+        this.foreground= foreground;
+        this.background= background;
+        this.style= style;
+    }
+
+    /**
+     * Creates a text attribute with the given colors and style.
+     *
+     * @param foreground the foreground color, <code>null</code> if none
+     * @param background the background color, <code>null</code> if none
+     * @param style the style
+     * @param font the font, <code>null</code> if none
+     * @since 3.3
+     */
+    public this(Color foreground, Color background, int style, Font font) {
+        this.foreground= foreground;
+        this.background= background;
+        this.style= style;
+        this.font= font;
+    }
+
+    /**
+     * Creates a text attribute for the given foreground color, no background color and
+     * with the SWT normal style.
+     *
+     * @param foreground the foreground color, <code>null</code> if none
+     */
+    public this(Color foreground) {
+        this(foreground, null, SWT.NORMAL);
+    }
+
+    /*
+     * @see Object#equals(Object)
+     */
+    public bool equals(Object object) {
+
+        if (object is this)
+            return true;
+
+        if (!( cast(TextAttribute)object ))
+            return false;
+        TextAttribute a= cast(TextAttribute)object;
+
+        return (a.style is style && equals(a.foreground, foreground) && equals(a.background, background) && equals(a.font, font));
+    }
+
+    /**
+     * Returns whether the two given objects are equal.
+     *
+     * @param o1 the first object, can be <code>null</code>
+     * @param o2 the second object, can be <code>null</code>
+     * @return <code>true</code> if the given objects are equals
+     * @since 2.0
+     */
+    private bool equals(Object o1, Object o2) {
+        if (o1 !is null)
+            return cast(bool) o1.opEquals(o2);
+        return (o2 is null);
+    }
+
+    /*
+     * @see Object#hashCode()
+     */
+     public override hash_t toHash() {
+         if (fHashCode is 0) {
+             int multiplier= 37; // some prime
+             fHashCode= 13; // some random value
+             fHashCode= multiplier * fHashCode + (font is null ? 0 : font.toHash());
+             fHashCode= multiplier * fHashCode + (background is null ? 0 : background.toHash());
+             fHashCode= multiplier * fHashCode + (foreground is null ? 0 : foreground.toHash());
+             fHashCode= multiplier * fHashCode + style;
+         }
+        return fHashCode;
+     }
+
+    /**
+     * Returns the attribute's foreground color.
+     *
+     * @return the attribute's foreground color or <code>null</code> if not set
+     */
+    public Color getForeground() {
+        return foreground;
+    }
+
+    /**
+     * Returns the attribute's background color.
+     *
+     * @return the attribute's background color or <code>null</code> if not set
+     */
+    public Color getBackground() {
+        return background;
+    }
+
+    /**
+     * Returns the attribute's style.
+     *
+     * @return the attribute's style
+     */
+    public int getStyle() {
+        return style;
+    }
+
+    /**
+     * Returns the attribute's font.
+     *
+     * @return the attribute's font or <code>null</code> if not set
+     * @since 3.3
+     */
+    public Font getFont() {
+        return font;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,271 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.TextEvent;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * This event is sent to implementers of
+ * {@link org.eclipse.jface.text.ITextListener}. It represents a change applied
+ * to text viewer. The change is specified as a replace command using offset,
+ * length, inserted text, and replaced text. The text viewer issues a text event
+ * after the viewer has been changed either in response to a change of the
+ * viewer's document or when the viewer's visual content has been changed. In
+ * the first case, the text event also carries the original document event.
+ * Depending on the viewer's presentation mode, the text event coordinates are
+ * different from the document event's coordinates.
+ * <p>
+ * An empty text event usually indicates a change of the viewer's redraw state.</p>
+ * <p>
+ * Clients other than text viewer's don't create instances of this class.</p>
+ *
+ * @see org.eclipse.jface.text.ITextListener
+ * @see org.eclipse.jface.text.ITextViewer
+ * @see org.eclipse.jface.text.DocumentEvent
+ */
+public class TextEvent {
+
+    /** Start offset of the change */
+    private int fOffset;
+    /** The length of the change */
+    private int fLength;
+    /** Inserted text */
+    private String fText;
+    /** Replaced text */
+    private String fReplacedText;
+    /** The original document event, may by null */
+    private DocumentEvent fDocumentEvent;
+    /**
+     * The redraw state of the viewer issuing this event
+     * @since 2.0
+     */
+    private bool fViewerRedrawState;
+
+    /**
+     * Creates a new <code>TextEvent</code> based on the specification.
+     *
+     * @param offset the offset
+     * @param length the length
+     * @param text the inserted text
+     * @param replacedText the replaced text
+     * @param event the associated document event or <code>null</code> if none
+     * @param viewerRedrawState the redraw state of the viewer
+     */
+    /+protected+/ this(int offset, int length, String text, String replacedText, DocumentEvent event, bool viewerRedrawState) {
+        fOffset= offset;
+        fLength= length;
+        fText= text;
+        fReplacedText= replacedText;
+        fDocumentEvent= event;
+        fViewerRedrawState= viewerRedrawState;
+    }
+
+    /**
+     * Returns the offset of the event.
+     *
+     * @return the offset of the event
+     */
+    public int getOffset() {
+        return fOffset;
+    }
+
+    /**
+     * Returns the length of the event.
+     *
+     * @return the length of the event
+     */
+    public int getLength() {
+        return fLength;
+    }
+
+    /**
+     * Returns the text of the event.
+     *
+     * @return the text of the event
+     */
+    public String getText() {
+        return fText;
+    }
+
+    /**
+     * Returns the text replaced by this event.
+     *
+     * @return the text replaced by this event
+     */
+    public String getReplacedText() {
+        return fReplacedText;
+    }
+
+    /**
+     * Returns the corresponding document event that caused the viewer change
+     *
+     * @return the corresponding document event, <code>null</code> if a visual change only
+     */
+    public DocumentEvent getDocumentEvent() {
+        return fDocumentEvent;
+    }
+
+    /**
+     * Returns the viewer's redraw state.
+     *
+     * @return <code>true</code> if the viewer's redraw state is <code>true</code>
+     * @since 2.0
+     */
+    public bool getViewerRedrawState() {
+        return fViewerRedrawState;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextPresentation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,875 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.TextPresentation;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import tango.core.Exception;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * Describes the presentation styles for a section of an indexed text such as a
+ * document or string. A text presentation defines a default style for the whole
+ * section and in addition style differences for individual subsections. Text
+ * presentations can be narrowed down to a particular result window. All methods
+ * are result window aware, i.e. ranges outside the result window are always
+ * ignored.
+ * <p>
+ * All iterators provided by a text presentation assume that they enumerate non
+ * overlapping, consecutive ranges inside the default range. Thus, all these
+ * iterators do not include the default range. The default style range must be
+ * explicitly asked for using <code>getDefaultStyleRange</code>.
+ */
+public class TextPresentation {
+
+    /**
+     * Applies the given presentation to the given text widget. Helper method.
+     *
+     * @param presentation the style information
+     * @param text the widget to which to apply the style information
+     * @since 2.0
+     */
+    public static void applyTextPresentation(TextPresentation presentation, StyledText text) {
+
+        StyleRange[] ranges= new StyleRange[presentation.getDenumerableRanges()];
+
+        int i= 0;
+        Iterator e= presentation.getAllStyleRangeIterator();
+        while (e.hasNext())
+            ranges[i++]= cast(StyleRange) e.next();
+
+        text.setStyleRanges(ranges);
+    }
+
+
+
+
+    /**
+     * Enumerates all the <code>StyleRange</code>s included in the presentation.
+     */
+    class FilterIterator : Iterator {
+
+        /** The index of the next style range to be enumerated */
+        protected int fIndex;
+        /** The upper bound of the indices of style ranges to be enumerated */
+        protected int fLength;
+        /** Indicates whether ranges similar to the default range should be enumerated */
+        protected bool fSkipDefaults;
+        /** The result window */
+        protected IRegion fWindow;
+
+        /**
+         * <code>skipDefaults</code> tells the enumeration to skip all those style ranges
+         * which define the same style as the presentation's default style range.
+         *
+         * @param skipDefaults <code>false</code> if ranges similar to the default range should be enumerated
+         */
+        protected this(bool skipDefaults) {
+
+            fSkipDefaults= skipDefaults;
+
+            fWindow= fResultWindow;
+            fIndex= getFirstIndexInWindow(fWindow);
+            fLength= getFirstIndexAfterWindow(fWindow);
+
+            if (fSkipDefaults)
+                computeIndex();
+        }
+
+        /*
+         * @see Iterator#next()
+         */
+        public Object next() {
+            try {
+                StyleRange r= cast(StyleRange) fRanges.get(fIndex++);
+                return createWindowRelativeRange(fWindow, r);
+            } catch (ArrayIndexOutOfBoundsException x) {
+                throw new NoSuchElementException(null);
+            } finally {
+                if (fSkipDefaults)
+                    computeIndex();
+            }
+        }
+
+        /*
+         * @see Iterator#hasNext()
+         */
+        public bool hasNext() {
+            return fIndex < fLength;
+        }
+
+        /*
+         * @see Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * Returns whether the given object should be skipped.
+         *
+         * @param o the object to be checked
+         * @return <code>true</code> if the object should be skipped by the iterator
+         */
+        protected bool skip(Object o) {
+            StyleRange r= cast(StyleRange) o;
+            return r.similarTo(fDefaultRange);
+        }
+
+        /**
+         * Computes the index of the styled range that is the next to be enumerated.
+         */
+        protected void computeIndex() {
+            while (fIndex < fLength && skip(fRanges.get(fIndex)))
+                ++ fIndex;
+        }
+    }
+
+    /** The style information for the range covered by the whole presentation */
+    private StyleRange fDefaultRange;
+    /** The member ranges of the presentation */
+    private ArrayList fRanges;
+    /** A clipping region against which the presentation can be clipped when asked for results */
+    private IRegion fResultWindow;
+    /**
+     * The optional extent for this presentation.
+     * @since 3.0
+     */
+    private IRegion fExtent;
+
+
+    /**
+     * Creates a new empty text presentation.
+     */
+    public this() {
+        fRanges= new ArrayList(50);
+    }
+
+    /**
+     * Creates a new empty text presentation. <code>sizeHint</code>  tells the
+     * expected size of this presentation.
+     *
+     * @param sizeHint the expected size of this presentation
+     */
+    public this(int sizeHint) {
+        Assert.isTrue(sizeHint > 0);
+        fRanges= new ArrayList(sizeHint);
+    }
+
+    /**
+     * Creates a new empty text presentation with the given extent.
+     * <code>sizeHint</code>  tells the expected size of this presentation.
+     *
+     * @param extent the extent of the created <code>TextPresentation</code>
+     * @param sizeHint the expected size of this presentation
+     * @since 3.0
+     */
+    public this(IRegion extent, int sizeHint) {
+        this(sizeHint);
+        Assert.isNotNull(cast(Object)extent);
+        fExtent= extent;
+    }
+
+    /**
+     * Sets the result window for this presentation. When dealing with
+     * this presentation all ranges which are outside the result window
+     * are ignored. For example, the size of the presentation is 0
+     * when there is no range inside the window even if there are ranges
+     * outside the window. All methods are aware of the result window.
+     *
+     * @param resultWindow the result window
+     */
+    public void setResultWindow(IRegion resultWindow) {
+        fResultWindow= resultWindow;
+    }
+
+    /**
+     * Set the default style range of this presentation.
+     * The default style range defines the overall area covered
+     * by this presentation and its style information.
+     *
+     * @param range the range describing the default region
+     */
+    public void setDefaultStyleRange(StyleRange range) {
+        fDefaultRange= range;
+    }
+
+    /**
+     * Returns this presentation's default style range. The returned <code>StyleRange</code>
+     * is relative to the start of the result window.
+     *
+     * @return this presentation's default style range
+     */
+    public StyleRange getDefaultStyleRange() {
+        StyleRange range= createWindowRelativeRange(fResultWindow, fDefaultRange);
+        if (range is null)
+            return null;
+        return cast(StyleRange)range.clone();
+
+    }
+
+    /**
+     * Add the given range to the presentation. The range must be a
+     * subrange of the presentation's default range.
+     *
+     * @param range the range to be added
+     */
+    public void addStyleRange(StyleRange range) {
+        checkConsistency(range);
+        fRanges.add(range);
+    }
+
+    /**
+     * Replaces the given range in this presentation. The range must be a
+     * subrange of the presentation's default range.
+     *
+     * @param range the range to be added
+     * @since 3.0
+     */
+    public void replaceStyleRange(StyleRange range) {
+        applyStyleRange(range, false);
+    }
+
+    /**
+     * Merges the given range into this presentation. The range must be a
+     * subrange of the presentation's default range.
+     *
+     * @param range the range to be added
+     * @since 3.0
+     */
+    public void mergeStyleRange(StyleRange range) {
+        applyStyleRange(range, true);
+    }
+
+    /**
+     * Applies the given range to this presentation. The range must be a
+     * subrange of the presentation's default range.
+     *
+     * @param range the range to be added
+     * @param merge <code>true</code> if the style should be merged instead of replaced
+     * @since 3.0
+     */
+    private void applyStyleRange(StyleRange range, bool merge) {
+        if (range.length is 0)
+            return;
+
+        checkConsistency(range);
+
+        int start= range.start;
+        int length= range.length;
+        int end= start + length;
+
+        if (fRanges.size() is 0) {
+            StyleRange defaultRange= getDefaultStyleRange();
+            if (defaultRange is null)
+                defaultRange= range;
+
+            defaultRange.start= start;
+            defaultRange.length= length;
+            applyStyle(range, defaultRange, merge);
+            fRanges.add(defaultRange);
+        } else {
+            IRegion rangeRegion= new Region(start, length);
+            int first= getFirstIndexInWindow(rangeRegion);
+
+            if (first is fRanges.size()) {
+                StyleRange defaultRange= getDefaultStyleRange();
+                if (defaultRange is null)
+                    defaultRange= range;
+                defaultRange.start= start;
+                defaultRange.length= length;
+                applyStyle(range, defaultRange, merge);
+                fRanges.add(defaultRange);
+                return;
+            }
+
+            int last= getFirstIndexAfterWindow(rangeRegion);
+            for (int i= first; i < last && length > 0; i++) {
+
+                StyleRange current= cast(StyleRange)fRanges.get(i);
+                int currentStart= current.start;
+                int currentEnd= currentStart + current.length;
+
+                if (end <= currentStart) {
+                    fRanges.add(i, range);
+                    return;
+                }
+
+                if (start >= currentEnd)
+                    continue;
+
+                StyleRange currentCopy= null;
+                if (end < currentEnd)
+                    currentCopy= cast(StyleRange)current.clone();
+
+                if (start < currentStart) {
+                    // Apply style to new default range and add it
+                    StyleRange defaultRange= getDefaultStyleRange();
+                    if (defaultRange is null)
+                        defaultRange= new StyleRange();
+
+                    defaultRange.start= start;
+                    defaultRange.length= currentStart - start;
+                    applyStyle(range, defaultRange, merge);
+                    fRanges.add(i, defaultRange);
+                    i++; last++;
+
+
+                    // Apply style to first part of current range
+                    current.length= Math.min(end, currentEnd) - currentStart;
+                    applyStyle(range, current, merge);
+                }
+
+                if (start >= currentStart) {
+                    // Shorten the current range
+                    current.length= start - currentStart;
+
+                    // Apply the style to the rest of the current range and add it
+                    if (current.length > 0) {
+                        current= cast(StyleRange)current.clone();
+                        i++; last++;
+                        fRanges.add(i, current);
+                    }
+                    applyStyle(range, current, merge);
+                    current.start= start;
+                    current.length= Math.min(end, currentEnd) - start;
+                }
+
+                if (end < currentEnd) {
+                    // Add rest of current range
+                    currentCopy.start= end;
+                    currentCopy.length= currentEnd - end;
+                    i++; last++;
+                    fRanges.add(i,  currentCopy);
+                }
+
+                // Update range
+                range.start=  currentEnd;
+                range.length= Math.max(end - currentEnd, 0);
+                start= range.start;
+                length= range.length;
+            }
+            if (length > 0) {
+                // Apply style to new default range and add it
+                StyleRange defaultRange= getDefaultStyleRange();
+                if (defaultRange is null)
+                    defaultRange= range;
+                defaultRange.start= start;
+                defaultRange.length= end - start;
+                applyStyle(range, defaultRange, merge);
+                fRanges.add(last, defaultRange);
+            }
+        }
+    }
+
+    /**
+     * Replaces the given ranges in this presentation. Each range must be a
+     * subrange of the presentation's default range. The ranges must be ordered
+     * by increasing offset and must not overlap (but may be adjacent).
+     *
+     * @param ranges the ranges to be added
+     * @since 3.0
+     */
+    public void replaceStyleRanges(StyleRange[] ranges) {
+        applyStyleRanges(ranges, false);
+    }
+
+    /**
+     * Merges the given ranges into this presentation. Each range must be a
+     * subrange of the presentation's default range. The ranges must be ordered
+     * by increasing offset and must not overlap (but may be adjacent).
+     *
+     * @param ranges the ranges to be added
+     * @since 3.0
+     */
+    public void mergeStyleRanges(StyleRange[] ranges) {
+        applyStyleRanges(ranges, true);
+    }
+
+    /**
+     * Applies the given ranges to this presentation. Each range must be a
+     * subrange of the presentation's default range. The ranges must be ordered
+     * by increasing offset and must not overlap (but may be adjacent).
+     *
+     * @param ranges the ranges to be added
+     * @param merge <code>true</code> if the style should be merged instead of replaced
+     * @since 3.0
+     */
+    private void applyStyleRanges(StyleRange[] ranges, bool merge) {
+        int j= 0;
+        ArrayList oldRanges= fRanges;
+        ArrayList newRanges= new ArrayList(2*ranges.length + oldRanges.size());
+        for (int i= 0, n= ranges.length; i < n; i++) {
+            StyleRange range= ranges[i];
+            fRanges= oldRanges; // for getFirstIndexAfterWindow(...)
+            for (int m= getFirstIndexAfterWindow(new Region(range.start, range.length)); j < m; j++)
+                newRanges.add(oldRanges.get(j));
+            fRanges= newRanges; // for mergeStyleRange(...)
+            applyStyleRange(range, merge);
+        }
+        for (int m= oldRanges.size(); j < m; j++)
+            newRanges.add(oldRanges.get(j));
+        fRanges= newRanges;
+    }
+
+    /**
+     * Applies the template_'s style to the target.
+     *
+     * @param template_ the style range to be used as template_
+     * @param target the style range to which to apply the template_
+     * @param merge <code>true</code> if the style should be merged instead of replaced
+     * @since 3.0
+     */
+    private void applyStyle(StyleRange template_, StyleRange target, bool merge) {
+        if (merge) {
+            if (template_.font !is null)
+                target.font= template_.font;
+            target.fontStyle|= template_.fontStyle;
+
+            if (template_.metrics !is null)
+                target.metrics= template_.metrics;
+
+            if (template_.foreground !is null)
+                target.foreground= template_.foreground;
+            if (template_.background !is null)
+                target.background= template_.background;
+
+            target.strikeout|= template_.strikeout;
+            if (template_.strikeoutColor !is null)
+                target.strikeoutColor= template_.strikeoutColor;
+
+            target.underline|= template_.underline;
+            if (template_.underlineStyle !is SWT.NONE)
+                target.underlineStyle= template_.underlineStyle;
+            if (template_.underlineColor !is null)
+                target.underlineColor= template_.underlineColor;
+
+            if (template_.borderStyle !is SWT.NONE)
+                target.borderStyle= template_.borderStyle;
+            if (template_.borderColor !is null)
+                target.borderColor= template_.borderColor;
+
+        } else {
+            target.font= template_.font;
+            target.fontStyle= template_.fontStyle;
+            target.metrics= template_.metrics;
+            target.foreground= template_.foreground;
+            target.background= template_.background;
+            target.strikeout= template_.strikeout;
+            target.strikeoutColor= template_.strikeoutColor;
+            target.underline= template_.underline;
+            target.underlineStyle= template_.underlineStyle;
+            target.underlineColor= template_.underlineColor;
+            target.borderStyle= template_.borderStyle;
+            target.borderColor= template_.borderColor;
+        }
+    }
+
+    /**
+     * Checks whether the given range is a subrange of the presentation's
+     * default style range.
+     *
+     * @param range the range to be checked
+     * @exception IllegalArgumentException if range is not a subrange of the presentation's default range
+     */
+    private void checkConsistency(StyleRange range) {
+
+        if (range is null)
+            throw new IllegalArgumentException(null);
+
+        if (fDefaultRange !is null) {
+
+            if (range.start < fDefaultRange.start)
+                range.start= fDefaultRange.start;
+
+            int defaultEnd= fDefaultRange.start + fDefaultRange.length;
+            int end= range.start + range.length;
+            if (end > defaultEnd)
+                range.length -= (end - defaultEnd);
+        }
+    }
+
+    /**
+     * Returns the index of the first range which overlaps with the
+     * specified window.
+     *
+     * @param window the window to be used for searching
+     * @return the index of the first range overlapping with the window
+     */
+    private int getFirstIndexInWindow(IRegion window) {
+        if (window !is null) {
+            int start= window.getOffset();
+            int i= -1, j= fRanges.size();
+            while (j - i > 1) {
+                int k= (i + j) >> 1;
+                StyleRange r= cast(StyleRange) fRanges.get(k);
+                if (r.start + r.length > start)
+                    j= k;
+                else
+                    i= k;
+            }
+            return j;
+        }
+        return 0;
+    }
+
+    /**
+     * Returns the index of the first range which comes after the specified window and does
+     * not overlap with this window.
+     *
+     * @param window the window to be used for searching
+     * @return the index of the first range behind the window and not overlapping with the window
+     */
+    private int getFirstIndexAfterWindow(IRegion window) {
+        if (window !is null) {
+            int end= window.getOffset() + window.getLength();
+            int i= -1, j= fRanges.size();
+            while (j - i > 1) {
+                int k= (i + j) >> 1;
+                StyleRange r= cast(StyleRange) fRanges.get(k);
+                if (r.start < end)
+                    i= k;
+                else
+                    j= k;
+            }
+            return j;
+        }
+        return fRanges.size();
+    }
+
+    /**
+     * Returns a style range which is relative to the specified window and
+     * appropriately clipped if necessary. The original style range is not
+     * modified.
+     *
+     * @param window the reference window
+     * @param range the absolute range
+     * @return the window relative range based on the absolute range
+     */
+    private StyleRange createWindowRelativeRange(IRegion window, StyleRange range) {
+        if (window is null || range is null)
+            return range;
+
+        int start= range.start - window.getOffset();
+        if (start < 0)
+            start= 0;
+
+        int rangeEnd= range.start + range.length;
+        int windowEnd= window.getOffset() + window.getLength();
+        int end= (rangeEnd > windowEnd ? windowEnd : rangeEnd);
+        end -= window.getOffset();
+
+        StyleRange newRange= cast(StyleRange) range.clone();
+        newRange.start= start;
+        newRange.length= end - start;
+        return newRange;
+    }
+
+    /**
+     * Returns the region which is relative to the specified window and
+     * appropriately clipped if necessary.
+     *
+     * @param coverage the absolute coverage
+     * @return the window relative region based on the absolute coverage
+     * @since 3.0
+     */
+    private IRegion createWindowRelativeRegion(IRegion coverage) {
+        if (fResultWindow is null || coverage is null)
+            return coverage;
+
+        int start= coverage.getOffset() - fResultWindow.getOffset();
+        if (start < 0)
+            start= 0;
+
+        int rangeEnd= coverage.getOffset() + coverage.getLength();
+        int windowEnd= fResultWindow.getOffset() + fResultWindow.getLength();
+        int end= (rangeEnd > windowEnd ? windowEnd : rangeEnd);
+        end -= fResultWindow.getOffset();
+
+        return new Region(start, end - start);
+    }
+
+    /**
+     * Returns an iterator which enumerates all style ranged which define a style
+     * different from the presentation's default style range. The default style range
+     * is not enumerated.
+     *
+     * @return a style range iterator
+     */
+    public Iterator getNonDefaultStyleRangeIterator() {
+        return new FilterIterator(fDefaultRange !is null);
+    }
+
+    /**
+     * Returns an iterator which enumerates all style ranges of this presentation
+     * except the default style range. The returned <code>StyleRange</code>s
+     * are relative to the start of the presentation's result window.
+     *
+     * @return a style range iterator
+     */
+    public Iterator getAllStyleRangeIterator() {
+        return new FilterIterator(false);
+    }
+
+    /**
+     * Returns whether this collection contains any style range including
+     * the default style range.
+     *
+     * @return <code>true</code> if there is no style range in this presentation
+     */
+    public bool isEmpty() {
+        return (fDefaultRange is null && getDenumerableRanges() is 0);
+    }
+
+    /**
+     * Returns the number of style ranges in the presentation not counting the default
+     * style range.
+     *
+     * @return the number of style ranges in the presentation excluding the default style range
+     */
+    public int getDenumerableRanges() {
+        int size= getFirstIndexAfterWindow(fResultWindow) - getFirstIndexInWindow(fResultWindow);
+        return (size < 0 ? 0 : size);
+    }
+
+    /**
+     * Returns the style range with the smallest offset ignoring the default style range or null
+     * if the presentation is empty.
+     *
+     * @return the style range with the smallest offset different from the default style range
+     */
+    public StyleRange getFirstStyleRange() {
+        try {
+
+            StyleRange range= cast(StyleRange) fRanges.get(getFirstIndexInWindow(fResultWindow));
+            return createWindowRelativeRange(fResultWindow, range);
+
+        } catch (NoSuchElementException x) {
+        } catch (IndexOutOfBoundsException x) {
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the style range with the highest offset ignoring the default style range.
+     *
+     * @return the style range with the highest offset different from the default style range
+     */
+    public StyleRange getLastStyleRange() {
+        try {
+
+            StyleRange range=  cast(StyleRange) fRanges.get(getFirstIndexAfterWindow(fResultWindow) - 1);
+            return createWindowRelativeRange(fResultWindow, range);
+
+        } catch (NoSuchElementException x) {
+            return null;
+        } catch (IndexOutOfBoundsException x) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the coverage of this presentation as clipped by the presentation's
+     * result window.
+     *
+     * @return the coverage of this presentation
+     */
+    public IRegion getCoverage() {
+
+        if (fDefaultRange !is null) {
+            StyleRange range= getDefaultStyleRange();
+            return new Region(range.start, range.length);
+        }
+
+        StyleRange first= getFirstStyleRange();
+        StyleRange last= getLastStyleRange();
+
+        if (first is null || last is null)
+            return null;
+
+        return new Region(first.start, last.start - first. start + last.length);
+    }
+
+    /**
+     * Returns the extent of this presentation clipped by the
+     * presentation's result window.
+     *
+     * @return the clipped extent
+     * @since 3.0
+     */
+    public IRegion getExtent() {
+        if (fExtent !is null)
+            return createWindowRelativeRegion(fExtent);
+        return getCoverage();
+    }
+
+    /**
+     * Clears this presentation by resetting all applied changes.
+     * @since 2.0
+     */
+    public void clear() {
+        fDefaultRange= null;
+        fResultWindow= null;
+        fRanges.clear();
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextSelection.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,344 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.TextSelection;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Standard implementation of {@link org.eclipse.jface.text.ITextSelection}.
+ * <p>
+ * Makes advantage of the weak contract of correctness of its interface. If
+ * generated from a selection provider, it only remembers its offset and length
+ * and computes the remaining information on request.</p>
+ */
+public class TextSelection : ITextSelection {
+
+    /** Internal empty text selection */
+    private static ITextSelection NULL_;
+    private static ITextSelection NULL(){
+        if( NULL_ is null ){
+            synchronized( TextSelection.classinfo ){
+                if( NULL_ is null ){
+                    NULL_= new TextSelection();
+                }
+            }
+        }
+        return NULL_;
+    }
+
+    /**
+     * Returns a shared instance of an empty text selection.
+     *
+     * @return a shared instance of an empty text selection
+     */
+    public static ITextSelection emptySelection() {
+        return NULL;
+    }
+
+    /** Document which delivers the data of the selection */
+    private IDocument fDocument;
+    /** Offset of the selection */
+    private int fOffset;
+    /** Length of the selection */
+    private int fLength;
+
+
+    /**
+     * Creates an empty text selection.
+     */
+    private this() {
+        this(null, -1, -1);
+    }
+
+    /**
+     * Creates a text selection for the given range. This
+     * selection object describes generically a text range and
+     * is intended to be an argument for the <code>setSelection</code>
+     * method of selection providers.
+     *
+     * @param offset the offset of the range
+     * @param length the length of the range
+     */
+    public this(int offset, int length) {
+        this(null, offset, length);
+    }
+
+    /**
+     * Creates a text selection for the given range of the given document.
+     * This selection object is created by selection providers in responds
+     * <code>getSelection</code>.
+     *
+     * @param document the document whose text range is selected in a viewer
+     * @param offset the offset of the selected range
+     * @param length the length of the selected range
+     */
+    public this(IDocument document, int offset, int length) {
+        fDocument= document;
+        fOffset= offset;
+        fLength= length;
+    }
+
+    /**
+     *
+     * Returns true if the offset and length are smaller than 0.
+     * A selection of length 0, is a valid text selection as it
+     * describes, e.g., the cursor position in a viewer.
+     *
+     * @return <code>true</code> if this selection is empty
+     * @see org.eclipse.jface.viewers.ISelection#isEmpty()
+     */
+    public bool isEmpty() {
+        return fOffset < 0 || fLength < 0;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextSelection#getOffset()
+     */
+    public int getOffset() {
+        return fOffset;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextSelection#getLength()
+     */
+    public int getLength() {
+        return fLength;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextSelection#getStartLine()
+     */
+    public int getStartLine() {
+
+        try {
+            if (fDocument !is null)
+                return fDocument.getLineOfOffset(fOffset);
+        } catch (BadLocationException x) {
+        }
+
+        return -1;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextSelection#getEndLine()
+     */
+    public int getEndLine() {
+        try {
+            if (fDocument !is null) {
+                int endOffset= fOffset + fLength;
+                if (fLength !is 0)
+                    endOffset--;
+                return fDocument.getLineOfOffset(endOffset);
+            }
+        } catch (BadLocationException x) {
+        }
+
+        return -1;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextSelection#getText()
+     */
+    public String getText() {
+        try {
+            if (fDocument !is null)
+                return fDocument.get(fOffset, fLength);
+        } catch (BadLocationException x) {
+        }
+
+        return null;
+    }
+
+    /*
+     * @see java.lang.Object#equals(Object)
+     */
+    public override int opEquals(Object obj) {
+        if (obj is this)
+            return true;
+
+        if (obj is null || this.classinfo !is obj.classinfo)
+            return false;
+
+        TextSelection s= cast(TextSelection) obj;
+        bool sameRange= (s.fOffset is fOffset && s.fLength is fLength);
+        if (sameRange) {
+
+            if (s.fDocument is null && fDocument is null)
+                return true;
+            if (s.fDocument is null || fDocument is null)
+                return false;
+
+            try {
+                String sContent= s.fDocument.get(fOffset, fLength);
+                String content= fDocument.get(fOffset, fLength);
+                return sContent==/+eq+/content;
+            } catch (BadLocationException x) {
+            }
+        }
+
+        return false;
+    }
+
+    /*
+     * @see java.lang.Object#hashCode()
+     */
+    public override hash_t toHash() {
+        int low= fDocument !is null ? (cast(Object)fDocument).toHash() : 0;
+        return (fOffset << 24) | (fLength << 16) | low;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,5545 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.TextViewer;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+import org.eclipse.dwtxhelper.regex;
+import tango.text.convert.Format;
+import java.lang.JThread;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.LineBackgroundEvent;
+import org.eclipse.swt.custom.LineBackgroundListener;
+import org.eclipse.swt.custom.MovementEvent;
+import org.eclipse.swt.custom.MovementListener;
+import org.eclipse.swt.custom.ST;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.custom.StyledTextPrintOptions;
+import org.eclipse.swt.custom.VerifyKeyListener;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.events.VerifyListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.printing.PrintDialog;
+import org.eclipse.swt.printing.Printer;
+import org.eclipse.swt.printing.PrinterData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.internal.text.NonDeletingPositionUpdater;
+import org.eclipse.jface.internal.text.StickyHoverManager;
+import org.eclipse.jface.text.hyperlink.HyperlinkManager;
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension;
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter;
+import org.eclipse.jface.text.hyperlink.HyperlinkManager;
+import org.eclipse.jface.text.projection.ChildDocument;
+import org.eclipse.jface.text.projection.ChildDocumentManager;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.Viewer;
+
+
+/**
+ * SWT based implementation of {@link ITextViewer} and its extension interfaces.
+ * Once the viewer and its SWT control have been created the viewer can only
+ * indirectly be disposed by disposing its SWT control.
+ * <p>
+ * Clients are supposed to instantiate a text viewer and subsequently to
+ * communicate with it exclusively using the
+ * {@link org.eclipse.jface.text.ITextViewer} interface or any of the
+ * implemented extension interfaces.
+ * <p>
+ * A text viewer serves as text operation target. It only partially supports the
+ * external control of the enable state of its text operations. A text viewer is
+ * also a widget token owner. Anything that wants to display an overlay window
+ * on top of a text viewer should implement the
+ * {@link org.eclipse.jface.text.IWidgetTokenKeeper} interface and participate
+ * in the widget token negotiation between the text viewer and all its potential
+ * widget token keepers.
+ * <p>
+ * This class is not intended to be subclassed outside the JFace Text component.</p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class TextViewer : Viewer ,
+                    ITextViewer, ITextViewerExtension, ITextViewerExtension2, ITextViewerExtension4, ITextViewerExtension6, ITextViewerExtension7, ITextViewerExtension8,
+                    IEditingSupportRegistry, ITextOperationTarget, ITextOperationTargetExtension,
+                    IWidgetTokenOwner, IWidgetTokenOwnerExtension, IPostSelectionProvider {
+
+    alias Viewer.fireSelectionChanged fireSelectionChanged;
+
+    /** Internal flag to indicate the debug state. */
+    public static const bool TRACE_ERRORS= false;
+    /** Internal flag to indicate the debug state. */
+    private static const bool TRACE_DOUBLE_CLICK= false;
+
+    /**
+     * Width constraint for text hovers (in characters).
+     * @since 3.4
+     */
+    private static const int TEXT_HOVER_WIDTH_CHARS= 100; //used to be 60 (text font)
+    /**
+     * Height constraint for text hovers (in characters).
+     * @since 3.4
+     */
+    private static const int TEXT_HOVER_HEIGHT_CHARS= 12; //used to be 10 (text font)
+
+    /**
+     * Represents a replace command that brings the text viewer's text widget
+     * back in synchronization with text viewer's document after the document
+     * has been changed.
+     */
+    protected class WidgetCommand {
+
+        /** The document event encapsulated by this command. */
+        public DocumentEvent event;
+        /** The start of the event. */
+        public int start;
+        /** The length of the event. */
+        public int length;
+        /** The inserted and replaced text segments of <code>event</code>. */
+        public String text;
+        /** The replaced text segments of <code>event</code>. */
+        public String preservedText;
+
+        /**
+         * Translates a document event into the presentation coordinates of this text viewer.
+         *
+         * @param e the event to be translated
+         */
+        public void setEvent(DocumentEvent e) {
+
+            event= e;
+
+            start= e.getOffset();
+            length= e.getLength();
+            text= e.getText();
+
+            if (length !is 0) {
+                try {
+
+                    if ( cast(SlaveDocumentEvent)e ) {
+                        SlaveDocumentEvent slave= cast(SlaveDocumentEvent) e;
+                        DocumentEvent master= slave.getMasterEvent();
+                        if (master !is null)
+                            preservedText= master.getDocument().get(master.getOffset(), master.getLength());
+                    } else {
+                        preservedText= e.getDocument().get(e.getOffset(), e.getLength());
+                    }
+
+                } catch (BadLocationException x) {
+                    preservedText= null;
+                    if (TRACE_ERRORS)
+                        System.out_.println(JFaceTextMessages.getString("TextViewer.error.bad_location.WidgetCommand.setEvent")); //$NON-NLS-1$
+                }
+            } else
+                preservedText= null;
+        }
+    }
+
+
+    /**
+     * Connects a text double click strategy to this viewer's text widget.
+     * Calls the double click strategies when the mouse has
+     * been clicked inside the text editor.
+     */
+    class TextDoubleClickStrategyConnector : MouseAdapter , MovementListener {
+
+        /** Internal flag to remember the last double-click selection. */
+        private Point fDoubleClickSelection;
+
+        /*
+         * @see org.eclipse.swt.events.MouseAdapter#mouseUp(org.eclipse.swt.events.MouseEvent)
+         * @since 3.2
+         */
+        public void mouseUp(MouseEvent e) {
+            fDoubleClickSelection= null;
+        }
+
+        /*
+         * @see org.eclipse.swt.custom.MovementListener#getNextOffset(org.eclipse.swt.custom.MovementEvent)
+         * @since 3.3
+         */
+        public void getNextOffset(MovementEvent event) {
+            if (event.movement !is SWT.MOVEMENT_WORD_END)
+                return;
+
+            if (TRACE_DOUBLE_CLICK) {
+                System.out_.println("\n+++"); //$NON-NLS-1$
+                print(event);
+            }
+
+            if (fDoubleClickSelection !is null) {
+                if (fDoubleClickSelection.x <= event.offset && event.offset <= fDoubleClickSelection.y)
+                    event.newOffset= fDoubleClickSelection.y;
+            }
+        }
+
+        /*
+         * @see org.eclipse.swt.custom.MovementListener#getPreviousOffset(org.eclipse.swt.custom.MovementEvent)
+         * @since 3.3
+         */
+        public void getPreviousOffset(MovementEvent event) {
+            if (event.movement !is SWT.MOVEMENT_WORD_START)
+                return;
+
+            if (TRACE_DOUBLE_CLICK) {
+                System.out_.println("\n---"); //$NON-NLS-1$
+                print(event);
+            }
+            if (fDoubleClickSelection is null) {
+                ITextDoubleClickStrategy s= cast(ITextDoubleClickStrategy) selectContentTypePlugin(getSelectedRange().x, fDoubleClickStrategies);
+                if (s !is null) {
+                    StyledText textWidget= getTextWidget();
+                    s.doubleClicked(this.outer);
+                    fDoubleClickSelection= textWidget.getSelection();
+                    event.newOffset= fDoubleClickSelection.x;
+                    if (TRACE_DOUBLE_CLICK)
+                        System.out_.println(Format("- setting selection: x= {}, y= {}", fDoubleClickSelection.x, fDoubleClickSelection.y)); //$NON-NLS-1$ //$NON-NLS-2$
+                }
+            } else {
+                if (fDoubleClickSelection.x <= event.offset && event.offset <= fDoubleClickSelection.y)
+                    event.newOffset= fDoubleClickSelection.x;
+            }
+        }
+    }
+
+    /**
+     * Print trace info about <code>MovementEvent</code>.
+     *
+     * @param e the event to print
+     * @since 3.3
+     */
+    private void print(MovementEvent e) {
+        System.out_.println(Format("line offset: {}", e.lineOffset)); //$NON-NLS-1$
+        System.out_.println(Format("line: {}", e.lineText)); //$NON-NLS-1$
+        System.out_.println(Format("type: {}", e.movement)); //$NON-NLS-1$
+        System.out_.println(Format("offset: {}",  e.offset)); //$NON-NLS-1$
+        System.out_.println(Format("newOffset: {}", e.newOffset)); //$NON-NLS-1$
+    }
+
+    /**
+     * Monitors the area of the viewer's document that is visible in the viewer.
+     * If the area might have changed, it informs the text viewer about this
+     * potential change and its origin. The origin is internally used for optimization
+     * purposes.
+     */
+    class ViewportGuard : MouseAdapter
+        , ControlListener, KeyListener, SelectionListener {
+
+        /*
+         * @see ControlListener#controlResized(ControlEvent)
+         */
+        public void controlResized(ControlEvent e) {
+            updateViewportListeners(RESIZE);
+        }
+
+        /*
+         * @see ControlListener#controlMoved(ControlEvent)
+         */
+        public void controlMoved(ControlEvent e) {
+        }
+
+        /*
+         * @see KeyListener#keyReleased
+         */
+        public void keyReleased(KeyEvent e) {
+            updateViewportListeners(KEY);
+        }
+
+        /*
+         * @see KeyListener#keyPressed
+         */
+        public void keyPressed(KeyEvent e) {
+            updateViewportListeners(KEY);
+        }
+
+        /*
+         * @see MouseListener#mouseUp
+         */
+        public void mouseUp(MouseEvent e) {
+            if (fTextWidget !is null)
+                fTextWidget.removeSelectionListener(this);
+            updateViewportListeners(MOUSE_END);
+        }
+
+        /*
+         * @see MouseListener#mouseDown
+         */
+        public void mouseDown(MouseEvent e) {
+            if (fTextWidget !is null)
+                fTextWidget.addSelectionListener(this);
+        }
+
+        /*
+         * @see SelectionListener#widgetSelected
+         */
+        public void widgetSelected(SelectionEvent e) {
+            if (e.widget is fScroller)
+                updateViewportListeners(SCROLLER);
+            else
+                updateViewportListeners(MOUSE);
+        }
+
+        /*
+         * @see SelectionListener#widgetDefaultSelected
+         */
+        public void widgetDefaultSelected(SelectionEvent e) {}
+    }
+
+    /**
+     * This position updater is used to keep the selection during text shift operations.
+     */
+    static class ShiftPositionUpdater : DefaultPositionUpdater {
+
+        /**
+         * Creates the position updater for the given category.
+         *
+         * @param category the category this updater takes care of
+         */
+        protected this(String category) {
+            super(category);
+        }
+
+        /**
+         * If an insertion happens at the selection's start offset,
+         * the position is extended rather than shifted.
+         */
+        protected void adaptToInsert() {
+
+            int myStart= fPosition.offset;
+            int myEnd=   fPosition.offset + fPosition.length -1;
+            myEnd= Math.max(myStart, myEnd);
+
+            int yoursStart= fOffset;
+            int yoursEnd=   fOffset + fReplaceLength -1;
+            yoursEnd= Math.max(yoursStart, yoursEnd);
+
+            if (myEnd < yoursStart)
+                return;
+
+            if (myStart <= yoursStart) {
+                fPosition.length += fReplaceLength;
+                return;
+            }
+
+            if (myStart > yoursStart)
+                fPosition.offset += fReplaceLength;
+        }
+    }
+
+    /**
+     * Internal document listener on the visible document.
+     */
+    class VisibleDocumentListener : IDocumentListener {
+
+        /*
+         * @see IDocumentListener#documentAboutToBeChanged
+         */
+        public void documentAboutToBeChanged(DocumentEvent e) {
+            if (e.getDocument() is getVisibleDocument())
+                fWidgetCommand.setEvent(e);
+            handleVisibleDocumentAboutToBeChanged(e);
+        }
+
+        /*
+         * @see IDocumentListener#documentChanged
+         */
+        public void documentChanged(DocumentEvent e) {
+            if (fWidgetCommand.event is e)
+                updateTextListeners(fWidgetCommand);
+            fLastSentSelectionChange= null;
+            handleVisibleDocumentChanged(e);
+        }
+    }
+
+    /**
+     * Internal verify listener.
+     */
+    class TextVerifyListener : VerifyListener {
+
+        /**
+         * Indicates whether verify events are forwarded or ignored.
+         * @since 2.0
+         */
+        private bool fForward= true;
+
+        /**
+         * Tells the listener to forward received events.
+         *
+         * @param forward <code>true</code> if forwarding should be enabled.
+         * @since 2.0
+         */
+        public void forward(bool forward) {
+            fForward= forward;
+        }
+
+        /*
+         * @see VerifyListener#verifyText(VerifyEvent)
+         */
+        public void verifyText(VerifyEvent e) {
+            if (fForward)
+                handleVerifyEvent(e);
+        }
+    }
+
+    /**
+     * The viewer's manager responsible for registered verify key listeners.
+     * Uses batches rather than robust iterators because of performance issues.
+     * <p>
+     * The implementation is reentrant, i.e. installed listeners may trigger
+     * further <code>VerifyKeyEvent</code>s that may cause other listeners to be
+     * installed, but not thread safe.
+     * </p>
+     * @since 2.0
+     */
+    class VerifyKeyListenersManager : VerifyKeyListener {
+
+        /**
+         * Represents a batched addListener/removeListener command.
+         */
+        class Batch {
+            /** The index at which to insert the listener. */
+            int index;
+            /** The listener to be inserted. */
+            VerifyKeyListener listener;
+
+            /**
+             * Creates a new batch containing the given listener for the given index.
+             *
+             * @param l the listener to be added
+             * @param i the index at which to insert the listener
+             */
+            public this(VerifyKeyListener l, int i) {
+                listener= l;
+                index= i;
+            }
+        }
+
+        /** List of registered verify key listeners. */
+        private List fListeners;
+        /** List of pending batches. */
+        private List fBatched;
+        /** The reentrance count. */
+        private int fReentranceCount= 0;
+
+        this(){
+            fListeners= new ArrayList();
+            fBatched= new ArrayList();
+        }
+
+        /*
+         * @see VerifyKeyListener#verifyKey(VerifyEvent)
+         */
+        public void verifyKey(VerifyEvent event) {
+            if (fListeners.isEmpty())
+                return;
+
+            try {
+                fReentranceCount++;
+                Iterator iterator= fListeners.iterator();
+                while (iterator.hasNext() && event.doit) {
+                    VerifyKeyListener listener= cast(VerifyKeyListener) iterator.next();
+                    listener.verifyKey(event); // we might trigger reentrant calls on GTK
+                }
+            } finally {
+                fReentranceCount--;
+            }
+            if (fReentranceCount is 0)
+                processBatchedRequests();
+        }
+
+        /**
+         * Processes the pending batched requests.
+         */
+        private void processBatchedRequests() {
+            if (!fBatched.isEmpty()) {
+                Iterator e= fBatched.iterator();
+                while (e.hasNext()) {
+                    Batch batch= cast(Batch) e.next();
+                    insertListener(batch.listener, batch.index);
+                }
+                fBatched.clear();
+            }
+        }
+
+        /**
+         * Returns the number of registered verify key listeners.
+         *
+         * @return the number of registered verify key listeners
+         */
+        public int numberOfListeners() {
+            return fListeners.size();
+        }
+
+        /**
+         * Inserts the given listener at the given index or moves it
+         * to that index.
+         *
+         * @param listener the listener to be inserted
+         * @param index the index of the listener or -1 for remove
+         */
+        public void insertListener(VerifyKeyListener listener, int index) {
+
+            if (index is -1) {
+                removeListener(listener);
+            } else if (listener !is null) {
+
+                if (fReentranceCount > 0) {
+
+                    fBatched.add(new Batch(listener, index));
+
+                } else {
+
+                    int idx= -1;
+
+                    // find index based on identity
+                    int size= fListeners.size();
+                    for (int i= 0; i < size; i++) {
+                        if (cast(Object)listener is fListeners.get(i)) {
+                            idx= i;
+                            break;
+                        }
+                    }
+
+                    // move or add it
+                    if (idx !is index) {
+
+                        if (idx !is -1)
+                            fListeners.remove(idx);
+
+                        if (index > fListeners.size())
+                            fListeners.add(cast(Object)listener);
+                        else
+                            fListeners.add(index, cast(Object)listener);
+                    }
+
+                    if (size is 0)  // checking old size, i.e. current size is size + 1
+                        install();
+                }
+            }
+        }
+
+        /**
+         * Removes the given listener.
+         *
+         * @param listener the listener to be removed
+         */
+        public void removeListener(VerifyKeyListener listener) {
+            if (listener is null)
+                return;
+
+            if (fReentranceCount > 0) {
+
+                fBatched.add(new Batch(listener, -1));
+
+            } else {
+
+                int size= fListeners.size();
+                for (int i= 0; i < size; i++) {
+                    if (cast(Object)listener is fListeners.get(i)) {
+                        fListeners.remove(i);
+                        if (size is 1)  // checking old size, i.e. current size is size - 1
+                            uninstall();
+                        return;
+                    }
+                }
+            }
+        }
+
+        /**
+         * Installs this manager.
+         */
+        private void install() {
+            StyledText textWidget= getTextWidget();
+            if (textWidget !is null && !textWidget.isDisposed())
+                textWidget.addVerifyKeyListener(this);
+        }
+
+        /**
+         * Uninstalls this manager.
+         */
+        private void uninstall() {
+            StyledText textWidget= getTextWidget();
+            if (textWidget !is null && !textWidget.isDisposed())
+                textWidget.removeVerifyKeyListener(this);
+        }
+    }
+
+
+    /**
+     * Reification of a range in which a find replace operation is performed. This range is visually
+     * highlighted in the viewer as long as the replace operation is in progress.
+     *
+     * @since 2.0
+     */
+    class FindReplaceRange : LineBackgroundListener, ITextListener, IPositionUpdater {
+
+        /** Internal name for the position category used to update the range. */
+        private const static String RANGE_CATEGORY= "org.eclipse.jface.text.TextViewer.find.range"; //$NON-NLS-1$
+
+        /** The highlight color of this range. */
+        private Color fHighlightColor;
+        /** The position used to lively update this range's extent. */
+        private Position fPosition;
+
+        /** Creates a new find/replace range with the given extent.
+         *
+         * @param range the extent of this range
+         */
+        public this(IRegion range) {
+            setRange(range);
+        }
+
+        /**
+         * Sets the extent of this range.
+         *
+         * @param range the extent of this range
+         */
+        public void setRange(IRegion range) {
+            fPosition= new Position(range.getOffset(), range.getLength());
+        }
+
+        /**
+         * Returns the extent of this range.
+         *
+         * @return the extent of this range
+         */
+        public IRegion getRange() {
+            return new Region(fPosition.getOffset(), fPosition.getLength());
+        }
+
+        /**
+         * Sets the highlight color of this range. Causes the range to be redrawn.
+         *
+         * @param color the highlight color
+         */
+        public void setHighlightColor(Color color) {
+            fHighlightColor= color;
+            paint();
+        }
+
+        /*
+         * @see LineBackgroundListener#lineGetBackground(LineBackgroundEvent)
+         * @since 2.0
+         */
+        public void lineGetBackground(LineBackgroundEvent event) {
+            /* Don't use cached line information because of patched redrawing events. */
+
+            if (fTextWidget !is null) {
+                int offset= widgetOffset2ModelOffset(event.lineOffset);
+                if (fPosition.includes(offset))
+                    event.lineBackground= fHighlightColor;
+            }
+        }
+
+        /**
+         * Installs this range. The range registers itself as background
+         * line painter and text listener. Also, it creates a category with the
+         * viewer's document to maintain its own extent.
+         */
+        public void install() {
+            this.outer.addTextListener(this);
+            fTextWidget.addLineBackgroundListener(this);
+
+            IDocument document= this.outer.getDocument();
+            try {
+                document.addPositionCategory(RANGE_CATEGORY);
+                document.addPosition(RANGE_CATEGORY, fPosition);
+                document.addPositionUpdater(this);
+            } catch (BadPositionCategoryException e) {
+                // should not happen
+            } catch (BadLocationException e) {
+                // should not happen
+            }
+
+            paint();
+        }
+
+        /**
+         * Uninstalls this range.
+         * @see #install()
+         */
+        public void uninstall() {
+
+            // http://bugs.eclipse.org/bugs/show_bug.cgi?id=19612
+
+            IDocument document= this.outer.getDocument();
+            if (document !is null) {
+                document.removePositionUpdater(this);
+                document.removePosition(fPosition);
+            }
+
+            if (fTextWidget !is null && !fTextWidget.isDisposed())
+                fTextWidget.removeLineBackgroundListener(this);
+
+            this.outer.removeTextListener(this);
+
+            clear();
+        }
+
+        /**
+         * Clears the highlighting of this range.
+         */
+        private void clear() {
+            if (fTextWidget !is null && !fTextWidget.isDisposed())
+                fTextWidget.redraw();
+        }
+
+        /**
+         * Paints the highlighting of this range.
+         */
+        private void paint() {
+
+            IRegion widgetRegion= modelRange2WidgetRange(fPosition);
+            int offset= widgetRegion.getOffset();
+            int length= widgetRegion.getLength();
+
+            int count= fTextWidget.getCharCount();
+            if (offset + length >= count) {
+                length= count - offset; // clip
+
+                Point upperLeft= fTextWidget.getLocationAtOffset(offset);
+                Point lowerRight= fTextWidget.getLocationAtOffset(offset + length);
+                int width= fTextWidget.getClientArea().width;
+                int height= fTextWidget.getLineHeight(offset + length) + lowerRight.y - upperLeft.y;
+                fTextWidget.redraw(upperLeft.x, upperLeft.y, width, height, false);
+            }
+
+            fTextWidget.redrawRange(offset, length, true);
+        }
+
+        /*
+         * @see ITextListener#textChanged(TextEvent)
+         * @since 2.0
+         */
+        public void textChanged(TextEvent event) {
+            if (event.getViewerRedrawState())
+                paint();
+        }
+
+        /*
+         * @see IPositionUpdater#update(DocumentEvent)
+         * @since 2.0
+         */
+        public void update(DocumentEvent event) {
+            int offset= event.getOffset();
+            int length= event.getLength();
+            int delta= event.getText().length - length;
+
+            if (offset < fPosition.getOffset())
+                fPosition.setOffset(fPosition.getOffset() + delta);
+            else if (offset < fPosition.getOffset() + fPosition.getLength())
+                fPosition.setLength(fPosition.getLength() + delta);
+        }
+    }
+
+    /**
+     * This viewer's find/replace target.
+     */
+    class FindReplaceTarget : IFindReplaceTarget, IFindReplaceTargetExtension, IFindReplaceTargetExtension3 {
+
+        /** The range for this target. */
+        private FindReplaceRange fRange;
+        /** The highlight color of the range of this target. */
+        private Color fScopeHighlightColor;
+        /** The document partitioner remembered in case of a "Replace All". */
+        private Map fRememberedPartitioners;
+        /**
+         * The active rewrite session.
+         * @since 3.1
+         */
+        private DocumentRewriteSession fRewriteSession;
+
+        /*
+         * @see IFindReplaceTarget#getSelectionText()
+         */
+        public String getSelectionText() {
+            Point s= this.outer.getSelectedRange();
+            if (s.x > -1 && s.y > -1) {
+                try {
+                    IDocument document= this.outer.getDocument();
+                    return document.get(s.x, s.y);
+                } catch (BadLocationException x) {
+                }
+            }
+            return ""; //$NON-NLS-1$
+        }
+
+        /*
+         * @see IFindReplaceTarget#replaceSelection(String)
+         */
+        public void replaceSelection(String text) {
+            replaceSelection(text, false);
+        }
+
+        /*
+         * @see IFindReplaceTarget#replaceSelection(String)
+         */
+        public void replaceSelection(String text, bool regExReplace) {
+            Point s= this.outer.getSelectedRange();
+            if (s.x > -1 && s.y > -1) {
+                try {
+                    IRegion matchRegion= this.outer.getFindReplaceDocumentAdapter().replace(text, regExReplace);
+                    int length= -1;
+                    if (matchRegion !is null)
+                        length= matchRegion.getLength();
+
+                    if (text !is null && length > 0)
+                        this.outer.setSelectedRange(s.x, length);
+                } catch (BadLocationException x) {
+                }
+            }
+        }
+
+        /*
+         * @see IFindReplaceTarget#isEditable()
+         */
+        public bool isEditable() {
+            return this.outer.isEditable();
+        }
+
+        /*
+         * @see IFindReplaceTarget#getSelection()
+         */
+        public Point getSelection() {
+            Point modelSelection= this.outer.getSelectedRange();
+            Point widgetSelection= modelSelection2WidgetSelection(modelSelection);
+            return widgetSelection !is null ? widgetSelection : new Point(-1, -1);
+        }
+
+        /*
+         * @see IFindReplaceTarget#findAndSelect(int, String, bool, bool, bool)
+         */
+        public int findAndSelect(int widgetOffset, String findString, bool searchForward, bool caseSensitive, bool wholeWord) {
+            try {
+                return findAndSelect(widgetOffset, findString, searchForward, caseSensitive, wholeWord, false);
+            } catch (PatternSyntaxException x) {
+                return -1;
+            }
+        }
+
+        /*
+         * @see IFindReplaceTarget#findAndSelect(int, String, bool, bool, bool)
+         */
+        public int findAndSelect(int widgetOffset, String findString, bool searchForward, bool caseSensitive, bool wholeWord, bool regExSearch) {
+
+            int modelOffset= widgetOffset is -1 ? -1 : widgetOffset2ModelOffset(widgetOffset);
+
+            if (fRange !is null) {
+                IRegion range= fRange.getRange();
+                modelOffset= this.outer.findAndSelectInRange(modelOffset, findString, searchForward, caseSensitive, wholeWord, range.getOffset(), range.getLength(), regExSearch);
+            } else {
+                modelOffset= this.outer.findAndSelect(modelOffset, findString, searchForward, caseSensitive, wholeWord, regExSearch);
+            }
+
+            widgetOffset= modelOffset is -1 ? -1 : modelOffset2WidgetOffset(modelOffset);
+            return widgetOffset;
+        }
+
+        /*
+         * @see IFindReplaceTarget#canPerformFind()
+         */
+        public bool canPerformFind() {
+            return this.outer.canPerformFind();
+        }
+
+        /*
+         * @see IFindReplaceTargetExtension#beginSession()
+         * @since 2.0
+         */
+        public void beginSession() {
+            fRange= null;
+        }
+
+        /*
+         * @see IFindReplaceTargetExtension#endSession()
+         * @since 2.0
+         */
+        public void endSession() {
+            if (fRange !is null) {
+                fRange.uninstall();
+                fRange= null;
+            }
+        }
+
+        /*
+         * @see IFindReplaceTargetExtension#getScope()
+         * @since 2.0
+         */
+        public IRegion getScope() {
+            return fRange is null ? null : fRange.getRange();
+        }
+
+        /*
+         * @see IFindReplaceTargetExtension#getLineSelection()
+         * @since 2.0
+         */
+        public Point getLineSelection() {
+            Point point= this.outer.getSelectedRange();
+
+            try {
+                IDocument document= this.outer.getDocument();
+
+                // beginning of line
+                int line= document.getLineOfOffset(point.x);
+                int offset= document.getLineOffset(line);
+
+                // end of line
+                IRegion lastLineInfo= document.getLineInformationOfOffset(point.x + point.y);
+                int lastLine= document.getLineOfOffset(point.x + point.y);
+                int length;
+                if (lastLineInfo.getOffset() is point.x + point.y && lastLine > 0)
+                    length= document.getLineOffset(lastLine - 1) + document.getLineLength(lastLine - 1) - offset;
+                else
+                    length= lastLineInfo.getOffset() + lastLineInfo.getLength() - offset;
+
+                return new Point(offset, length);
+
+            } catch (BadLocationException e) {
+                // should not happen
+                return new Point(point.x, 0);
+            }
+        }
+
+        /*
+         * @see IFindReplaceTargetExtension#setSelection(int, int)
+         * @since 2.0
+         */
+        public void setSelection(int modelOffset, int modelLength) {
+            this.outer.setSelectedRange(modelOffset, modelLength);
+        }
+
+        /*
+         * @see IFindReplaceTargetExtension#setScope(IRegion)
+         * @since 2.0
+         */
+        public void setScope(IRegion scope_) {
+            if (fRange !is null)
+                fRange.uninstall();
+
+            if (scope_ is null) {
+                fRange= null;
+                return;
+            }
+
+            fRange= new FindReplaceRange(scope_);
+            fRange.setHighlightColor(fScopeHighlightColor);
+            fRange.install();
+        }
+
+        /*
+         * @see IFindReplaceTargetExtension#setScopeHighlightColor(Color)
+         * @since 2.0
+         */
+        public void setScopeHighlightColor(Color color) {
+            if (fRange !is null)
+                fRange.setHighlightColor(color);
+            fScopeHighlightColor= color;
+        }
+
+        /*
+         * @see IFindReplaceTargetExtension#setReplaceAllMode(bool)
+         * @since 2.0
+         */
+        public void setReplaceAllMode(bool replaceAll) {
+
+            // http://bugs.eclipse.org/bugs/show_bug.cgi?id=18232
+
+            IDocument document= this.outer.getDocument();
+
+            if (replaceAll) {
+
+                if ( cast(IDocumentExtension4)document ) {
+                    IDocumentExtension4 extension= cast(IDocumentExtension4) document;
+                    fRewriteSession= extension.startRewriteSession(DocumentRewriteSessionType.SEQUENTIAL);
+                } else {
+                    this.outer.setRedraw(false);
+                    this.outer.startSequentialRewriteMode(false);
+
+                    if (fUndoManager !is null)
+                        fUndoManager.beginCompoundChange();
+
+                    fRememberedPartitioners= TextUtilities.removeDocumentPartitioners(document);
+                }
+
+            } else {
+
+                if ( cast(IDocumentExtension4)document ) {
+                    IDocumentExtension4 extension= cast(IDocumentExtension4) document;
+                    extension.stopRewriteSession(fRewriteSession);
+                } else {
+                    this.outer.setRedraw(true);
+                    this.outer.stopSequentialRewriteMode();
+
+                    if (fUndoManager !is null)
+                        fUndoManager.endCompoundChange();
+
+                    if (fRememberedPartitioners !is null)
+                        TextUtilities.addDocumentPartitioners(document, fRememberedPartitioners);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * The viewer's rewrite target.
+     * @since 2.0
+     */
+    class RewriteTarget : IRewriteTarget {
+
+        /*
+         * @see org.eclipse.jface.text.IRewriteTarget#beginCompoundChange()
+         */
+        public void beginCompoundChange() {
+            if (fUndoManager !is null)
+                fUndoManager.beginCompoundChange();
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IRewriteTarget#endCompoundChange()
+         */
+        public void endCompoundChange() {
+            if (fUndoManager !is null)
+                fUndoManager.endCompoundChange();
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IRewriteTarget#getDocument()
+         */
+        public IDocument getDocument() {
+            return this.outer.getDocument();
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IRewriteTarget#setRedraw(bool)
+         */
+        public void setRedraw(bool redraw) {
+            this.outer.setRedraw(redraw);
+        }
+    }
+
+    /**
+     * Value object used as key in the text hover configuration table. It is
+     * modifiable only inside this compilation unit to allow the reuse of created
+     * objects for efficiency reasons
+     *
+     * @since 2.1
+     */
+    protected class TextHoverKey {
+
+        /** The content type this key belongs to */
+        private String fContentType;
+        /** The state mask */
+        private int fStateMask;
+
+        /**
+         * Creates a new text hover key for the given content type and state mask.
+         *
+         * @param contentType the content type
+         * @param stateMask the state mask
+         */
+        protected this(String contentType, int stateMask) {
+            //Assert.isNotNull(contentType);
+            fContentType= contentType;
+            fStateMask= stateMask;
+        }
+
+        /*
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        public override int opEquals(Object obj) {
+            if (obj is null || obj.classinfo !is this.classinfo )
+                return false;
+            TextHoverKey textHoverKey= cast(TextHoverKey)obj;
+            return textHoverKey.fContentType.equals(fContentType) && textHoverKey.fStateMask is fStateMask;
+        }
+
+        /*
+         * @see java.lang.Object#hashCode()
+         */
+        public override hash_t toHash() {
+            return fStateMask << 16 | .toHash(fContentType);
+        }
+
+        /**
+         * Sets the state mask of this text hover key.
+         *
+         * @param stateMask the state mask
+         */
+        private void setStateMask(int stateMask) {
+            fStateMask= stateMask;
+        }
+    }
+
+    /**
+     * Captures and remembers the viewer state (selection and visual position). {@link TextViewer.ViewerState}
+     * instances are normally used once and then discarded, similar to the following snippet:
+     * <pre>
+     * ViewerState state= new ViewerState(); // remember the state
+     * doStuff(); // operation that may call setRedraw() and perform complex document modifications
+     * state.restore(true); // restore the remembered state
+     * </pre>
+     *
+     * @since 3.3
+     */
+    private final class ViewerState {
+        /** The position tracking the selection. */
+        private Position fSelection;
+        /** <code>true</code> if {@link #fSelection} was originally backwards. */
+        private bool fReverseSelection;
+        /** <code>true</code> if the selection has been updated while in redraw(off) mode. */
+        private bool fSelectionSet;
+        /** The position tracking the visually stable line. */
+        private Position fStableLine;
+        /** The pixel offset of the stable line measured from the client area. */
+        private int fStablePixel;
+
+        /** The position updater for {@link #fSelection} and {@link #fStableLine}. */
+        private IPositionUpdater fUpdater;
+        /** The document that the position updater and the positions are registered with. */
+        private IDocument fUpdaterDocument;
+        /** The position category used by {@link #fUpdater}. */
+        private String fUpdaterCategory;
+
+        /**
+         * Creates a new viewer state instance and connects it to the current document.
+         */
+        public this() {
+            IDocument document= getDocument();
+            if (document !is null)
+                connect(document);
+        }
+
+        /**
+         * Returns the normalized selection, i.e. the the selection length is always non-negative.
+         *
+         * @return the normalized selection
+         */
+        public Point getSelection() {
+            if (fSelection is null)
+                return new Point(-1, -1);
+            return new Point(fSelection.getOffset(), fSelection.getLength());
+        }
+
+        /**
+         * Updates the selection.
+         *
+         * @param offset the new selection offset
+         * @param length the new selection length
+         */
+        public void updateSelection(int offset, int length) {
+            fSelectionSet= true;
+            if (fSelection is null)
+                fSelection= new Position(offset, length);
+            else
+                updatePosition(fSelection, offset, length);
+        }
+
+        /**
+         * Restores the state and disconnects it from the document. The selection is no longer
+         * tracked after this call.
+         *
+         * @param restoreViewport <code>true</code> to restore both selection and viewport,
+         *        <code>false</code> to only restore the selection
+         */
+        public void restore(bool restoreViewport) {
+            if (isConnected())
+                disconnect();
+            if (fSelection !is null) {
+                int offset= fSelection.getOffset();
+                int length= fSelection.getLength();
+                if (fReverseSelection) {
+                    offset-= length;
+                    length= -length;
+                }
+                setSelectedRange(offset, length);
+                if (restoreViewport)
+                    updateViewport();
+            }
+        }
+
+        /**
+         * Updates the viewport, trying to keep the
+         * {@linkplain StyledText#getLinePixel(int) line pixel} of the caret line stable. If the
+         * selection has been updated while in redraw(false) mode, the new selection is revealed.
+         */
+        private void updateViewport() {
+            if (fSelectionSet) {
+                revealRange(fSelection.getOffset(), fSelection.getLength());
+            } else if (fStableLine !is null) {
+                int stableLine;
+                try {
+                    stableLine= fUpdaterDocument.getLineOfOffset(fStableLine.getOffset());
+                } catch (BadLocationException x) {
+                    // ignore and return silently
+                    return;
+                }
+                int stableWidgetLine= getClosestWidgetLineForModelLine(stableLine);
+                if (stableWidgetLine is -1)
+                    return;
+                int linePixel= getTextWidget().getLinePixel(stableWidgetLine);
+                int delta= fStablePixel - linePixel;
+                int topPixel= getTextWidget().getTopPixel();
+                getTextWidget().setTopPixel(topPixel - delta);
+            }
+        }
+
+        /**
+         * Remembers the viewer state.
+         *
+         * @param document the document to remember the state of
+         */
+        private void connect(IDocument document) {
+            Assert.isLegal(document !is null);
+            Assert.isLegal(!isConnected());
+            fUpdaterDocument= document;
+            try {
+                fUpdaterCategory= SELECTION_POSITION_CATEGORY ~ Integer.toString(toHash());
+                fUpdater= new NonDeletingPositionUpdater(fUpdaterCategory);
+                fUpdaterDocument.addPositionCategory(fUpdaterCategory);
+                fUpdaterDocument.addPositionUpdater(fUpdater);
+
+                Point selectionRange= getSelectedRange();
+                fReverseSelection= selectionRange.y < 0;
+                int offset, length;
+                if (fReverseSelection) {
+                    offset= selectionRange.x + selectionRange.y;
+                    length= -selectionRange.y;
+                } else {
+                    offset= selectionRange.x;
+                    length= selectionRange.y;
+                }
+
+                fSelection= new Position(offset, length);
+                fSelectionSet= false;
+                fUpdaterDocument.addPosition(fUpdaterCategory, fSelection);
+
+                int stableLine= getStableLine();
+                int stableWidgetLine= modelLine2WidgetLine(stableLine);
+                fStablePixel= getTextWidget().getLinePixel(stableWidgetLine);
+                IRegion stableLineInfo= fUpdaterDocument.getLineInformation(stableLine);
+                fStableLine= new Position(stableLineInfo.getOffset(), stableLineInfo.getLength());
+                fUpdaterDocument.addPosition(fUpdaterCategory, fStableLine);
+            } catch (BadPositionCategoryException e) {
+                // cannot happen
+                Assert.isTrue(false);
+            } catch (BadLocationException e) {
+                // should not happen except on concurrent modification
+                // ignore and disconnect
+                disconnect();
+            }
+        }
+
+        /**
+         * Updates a position with the given information and clears its deletion state.
+         *
+         * @param position the position to update
+         * @param offset the new selection offset
+         * @param length the new selection length
+         */
+        private void updatePosition(Position position, int offset, int length) {
+            position.setOffset(offset);
+            position.setLength(length);
+            // http://bugs.eclipse.org/bugs/show_bug.cgi?id=32795
+            position.isDeleted_= false;
+        }
+
+        /**
+         * Returns the document line to keep visually stable. If the caret line is (partially)
+         * visible, it is returned, otherwise the topmost (partially) visible line is returned.
+         *
+         * @return the visually stable line of this viewer state
+         */
+        private int getStableLine() {
+            int stableLine; // the model line that we try to keep stable
+            int caretLine= getTextWidget().getLineAtOffset(getTextWidget().getCaretOffset());
+            if (caretLine < JFaceTextUtil.getPartialTopIndex(getTextWidget()) || caretLine > JFaceTextUtil.getPartialBottomIndex(getTextWidget())) {
+                stableLine= JFaceTextUtil.getPartialTopIndex(this.outer);
+            } else {
+                stableLine= widgetLine2ModelLine(caretLine);
+            }
+            return stableLine;
+        }
+
+        /**
+         * Returns <code>true</code> if the viewer state is being tracked, <code>false</code>
+         * otherwise.
+         *
+         * @return the tracking state
+         */
+        private bool isConnected() {
+            return fUpdater !is null;
+        }
+
+        /**
+         * Disconnects from the document.
+         */
+        private void disconnect() {
+            Assert.isTrue(isConnected());
+            try {
+                fUpdaterDocument.removePosition(fUpdaterCategory, fSelection);
+                fUpdaterDocument.removePosition(fUpdaterCategory, fStableLine);
+                fUpdaterDocument.removePositionUpdater(fUpdater);
+                fUpdater= null;
+                fUpdaterDocument.removePositionCategory(fUpdaterCategory);
+                fUpdaterCategory= null;
+            } catch (BadPositionCategoryException x) {
+                // cannot happen
+                Assert.isTrue(false);
+            }
+        }
+    }
+
+    /**
+     * Internal cursor listener i.e. aggregation of mouse and key listener.
+     *
+     * @since 3.0
+     */
+    private class CursorListener : KeyListener, MouseListener {
+
+        /**
+         * Installs this cursor listener.
+         */
+        private void install() {
+            if (fTextWidget !is null && !fTextWidget.isDisposed()) {
+                fTextWidget.addKeyListener(this);
+                fTextWidget.addMouseListener(this);
+            }
+        }
+
+        /**
+         * Uninstalls this cursor listener.
+         */
+        private void uninstall() {
+            if (fTextWidget !is null && !fTextWidget.isDisposed()) {
+                fTextWidget.removeKeyListener(this);
+                fTextWidget.removeMouseListener(this);
+            }
+        }
+
+        /*
+         * @see KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)
+         */
+        public void keyPressed(KeyEvent event) {
+        }
+
+        /*
+         * @see KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)
+         */
+        public void keyReleased(KeyEvent e) {
+            if (fTextWidget.getSelectionCount() is 0) {
+                fLastSentSelectionChange= null;
+                queuePostSelectionChanged(e.character is SWT.DEL);
+            }
+        }
+
+        /*
+         * @see MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseDoubleClick(MouseEvent e) {
+        }
+
+        /*
+         * @see MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseDown(MouseEvent e) {
+        }
+
+        /*
+         * @see MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseUp(MouseEvent event) {
+            if (fTextWidget.getSelectionCount() is 0)
+                queuePostSelectionChanged(false);
+        }
+    }
+
+    /**
+     * Internal listener to document rewrite session state changes.
+     * @since 3.1
+     */
+    private class DocumentRewriteSessionListener : IDocumentRewriteSessionListener {
+
+        /*
+         * @see org.eclipse.jface.text.IDocumentRewriteSessionListener#documentRewriteSessionChanged(org.eclipse.jface.text.DocumentRewriteSessionEvent)
+         */
+        public void documentRewriteSessionChanged(DocumentRewriteSessionEvent event) {
+            IRewriteTarget target= this.outer.getRewriteTarget();
+            // FIXME always use setRedraw to avoid flickering due to scrolling
+            // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=158746
+            bool toggleRedraw= true || event.getSession().getSessionType() !is DocumentRewriteSessionType.UNRESTRICTED_SMALL;
+            final bool viewportStabilize= !toggleRedraw;
+            if (DocumentRewriteSessionEvent.SESSION_START is event.getChangeType()) {
+                if (toggleRedraw)
+                    target.setRedraw(false);
+                target.beginCompoundChange();
+                if (viewportStabilize && fViewerState is null)
+                    fViewerState= new ViewerState();
+            } else if (DocumentRewriteSessionEvent.SESSION_STOP is event.getChangeType()) {
+                if (viewportStabilize && fViewerState !is null) {
+                    fViewerState.restore(true);
+                    fViewerState= null;
+                }
+                target.endCompoundChange();
+                if (toggleRedraw)
+                    target.setRedraw(true);
+            }
+        }
+    }
+
+
+    /**
+     * Identifies the scrollbars as originators of a view port change.
+     */
+    protected static final int SCROLLER=    1;
+    /**
+     * Identifies  mouse moves as originators of a view port change.
+     */
+    protected static final int MOUSE=       2;
+    /**
+     * Identifies mouse button up as originator of a view port change.
+     */
+    protected static final int MOUSE_END=   3;
+    /**
+     * Identifies key strokes as originators of a view port change.
+     */
+    protected static final int KEY=         4;
+    /**
+     * Identifies window resizing as originator of a view port change.
+     */
+    protected static final int RESIZE=      5;
+    /**
+     * Identifies internal reasons as originators of a view port change.
+     */
+    protected static final int INTERNAL=    6;
+
+    /** Internal name of the position category used selection preservation during shift. */
+    protected static final String SHIFTING= "__TextViewer_shifting"; //$NON-NLS-1$
+
+    /**
+     * Base position category name used by the selection updater
+     * @since 3.1
+     */
+    private static const String SELECTION_POSITION_CATEGORY= "_textviewer_selection_category"; //$NON-NLS-1$
+    /** The viewer's text widget */
+    private StyledText fTextWidget;
+    /** The viewer's input document */
+    private IDocument fDocument;
+    /** The viewer's visible document */
+    private IDocument fVisibleDocument;
+    /** The viewer's document adapter */
+    private IDocumentAdapter fDocumentAdapter;
+    /** The slave document manager */
+    private ISlaveDocumentManager fSlaveDocumentManager;
+    /** The text viewer's double click strategies connector */
+    private TextDoubleClickStrategyConnector fDoubleClickStrategyConnector;
+    /** The text viewer's view port guard */
+    private ViewportGuard fViewportGuard;
+    /** Caches the graphical coordinate of the first visible line */
+    private int fTopInset= 0;
+    /** The most recent document modification as widget command */
+    private WidgetCommand fWidgetCommand;
+    /** The SWT control's scrollbars */
+    private ScrollBar fScroller;
+    /** Listener on the visible document */
+    private VisibleDocumentListener fVisibleDocumentListener;
+    /** Verify listener */
+    private TextVerifyListener fVerifyListener;
+    /** The most recent widget modification as document command */
+    private DocumentCommand fDocumentCommand;
+    /** The viewer's find/replace target */
+    private IFindReplaceTarget fFindReplaceTarget;
+    /**
+     * The text viewer's hovering controller
+     * @since 2.0
+     */
+    private TextViewerHoverManager fTextHoverManager;
+    /**
+     * The viewer widget token keeper
+     * @since 2.0
+     */
+    private IWidgetTokenKeeper fWidgetTokenKeeper;
+    /**
+     * The viewer's manager of verify key listeners
+     * @since 2.0
+     */
+    private VerifyKeyListenersManager fVerifyKeyListenersManager;
+    /**
+     * The mark position.
+     * @since 2.0
+     */
+    protected Position fMarkPosition;
+    /**
+     * The mark position category.
+     * @since 2.0
+     */
+    private /+const+/ String MARK_POSITION_CATEGORY;
+    /**
+     * The mark position updater
+     * @since 2.0
+     */
+    private /+const+/ IPositionUpdater fMarkPositionUpdater;
+    /**
+     * The flag indicating the redraw behavior
+     * @since 2.0
+     */
+    private int fRedrawCounter= 0;
+    /**
+     * The viewer's rewrite target
+     * @since 2.0
+     */
+    private IRewriteTarget fRewriteTarget;
+    /**
+     * The viewer's cursor listener.
+     * @since 3.0
+     */
+    private CursorListener fCursorListener;
+    /**
+     * Last selection range sent to selection change listeners.
+     * @since 3.0
+     */
+    private IRegion fLastSentSelectionChange;
+    /**
+     * The registered post selection changed listeners.
+     * @since 3.0
+     */
+    private List fPostSelectionChangedListeners;
+    /**
+     * Queued post selection changed events count.
+     * @since 3.0
+     */
+    private /+const+/ int[1] fNumberOfPostSelectionChangedEvents;
+    /**
+     * Last selection range sent to post selection change listeners.
+     * @since 3.0
+     */
+    private IRegion fLastSentPostSelectionChange;
+    /**
+     * The set of registered editor helpers.
+     * @since 3.1
+     */
+    private Set fEditorHelpers;
+    /**
+     * The internal rewrite session listener.
+     * @since 3.1
+     */
+    private DocumentRewriteSessionListener fDocumentRewriteSessionListener;
+
+    /** Should the auto indent strategies ignore the next edit operation */
+    protected bool  fIgnoreAutoIndent= false;
+    /** The strings a line is prefixed with on SHIFT_RIGHT and removed from each line on SHIFT_LEFT */
+    protected Map fIndentChars;
+    /** The string a line is prefixed with on PREFIX and removed from each line on STRIP_PREFIX */
+    protected Map fDefaultPrefixChars;
+    /** The text viewer's text double click strategies */
+    protected Map fDoubleClickStrategies;
+    /** The text viewer's undo manager */
+    protected IUndoManager fUndoManager;
+    /** The text viewer's auto indent strategies */
+    protected Map fAutoIndentStrategies;
+    /** The text viewer's text hovers */
+    protected Map fTextHovers;
+    /** All registered view port listeners> */
+    protected List fViewportListeners;
+    /** The last visible vertical position of the top line */
+    protected int fLastTopPixel;
+    /** All registered text listeners */
+    protected List fTextListeners;
+    /** All registered text input listeners */
+    protected List fTextInputListeners;
+    /** The text viewer's event consumer */
+    protected IEventConsumer fEventConsumer;
+    /** Indicates whether the viewer's text presentation should be replaced are modified. */
+    protected bool fReplaceTextPresentation= false;
+    /**
+     * The creator of the text hover control
+     * @since 2.0
+     */
+    protected IInformationControlCreator fHoverControlCreator;
+    /**
+     * The mapping between model and visible document.
+     * @since 2.1
+     */
+    protected IDocumentInformationMapping fInformationMapping;
+    /**
+     * The viewer's paint manager.
+     * @since 2.1
+     */
+    protected PaintManager fPaintManager;
+    /**
+     * The viewers partitioning. I.e. the partitioning name the viewer uses to access partitioning information of its input document.
+     * @since 3.0
+     */
+    protected String fPartitioning;
+    /**
+     * All registered text presentation listeners.
+     * since 3.0
+     */
+    protected List fTextPresentationListeners;
+    /**
+     * The find/replace document adapter.
+     * @since 3.0
+     */
+    protected FindReplaceDocumentAdapter fFindReplaceDocumentAdapter;
+
+    /**
+     * The text viewer's hyperlink detectors.
+     * @since 3.1
+     */
+    protected IHyperlinkDetector[] fHyperlinkDetectors;
+    /**
+     * The text viewer's hyperlink presenter.
+     * @since 3.1
+     */
+    protected IHyperlinkPresenter fHyperlinkPresenter;
+    /**
+     * The text viewer's hyperlink manager.
+     * @since 3.1
+     */
+    protected HyperlinkManager fHyperlinkManager;
+    /**
+     * The SWT key modifier mask which in combination
+     * with the left mouse button triggers the hyperlink mode.
+     * @since 3.1
+     */
+    protected int fHyperlinkStateMask;
+    /**
+     * The viewer state when in non-redraw state, <code>null</code> otherwise.
+     * @since 3.3
+     */
+    private ViewerState fViewerState;
+    /**
+     * The editor's tab converter.
+     * @since 3.3
+     */
+    private IAutoEditStrategy fTabsToSpacesConverter;
+
+
+    //---- Construction and disposal ------------------
+
+    private void instanceInit(){
+        fWidgetCommand= new WidgetCommand();
+        fVisibleDocumentListener= new VisibleDocumentListener();
+        fVerifyListener= new TextVerifyListener();
+        fDocumentCommand= new DocumentCommand();
+        fVerifyKeyListenersManager= new VerifyKeyListenersManager();
+        MARK_POSITION_CATEGORY=Format("__mark_category_{}", toHash()); //$NON-NLS-1$
+        fMarkPositionUpdater= new DefaultPositionUpdater(MARK_POSITION_CATEGORY);
+        fDocumentRewriteSessionListener= new DocumentRewriteSessionListener();
+        fEditorHelpers= new HashSet();
+    }
+    /**
+     * Internal use only
+     */
+    protected this() {
+        instanceInit();
+    }
+
+    /**
+     * Create a new text viewer with the given SWT style bits.
+     * The viewer is ready to use but does not have any plug-in installed.
+     *
+     * @param parent the parent of the viewer's control
+     * @param styles the SWT style bits for the viewer's control,
+     *          <em>if <code>SWT.WRAP</code> is set then a custom document adapter needs to be provided, see {@link #createDocumentAdapter()}
+     */
+    public this(Composite parent, int styles) {
+        instanceInit();
+        createControl(parent, styles);
+    }
+
+    /**
+     * Factory method to create the text widget to be used as the viewer's text widget.
+     *
+     * @param parent the parent of the styled text
+     * @param styles the styles for the styled text
+     * @return the text widget to be used
+     */
+    protected StyledText createTextWidget(Composite parent, int styles) {
+        return new StyledText(parent, styles);
+    }
+
+    /**
+     * Factory method to create the document adapter to be used by this viewer.
+     *
+     * @return the document adapter to be used
+     */
+    protected IDocumentAdapter createDocumentAdapter() {
+        return new DefaultDocumentAdapter();
+    }
+
+    /**
+     * Creates the viewer's SWT control. The viewer's text widget either is
+     * the control or is a child of the control.
+     *
+     * @param parent the parent of the viewer's control
+     * @param styles the SWT style bits for the viewer's control
+     */
+    protected void createControl(Composite parent, int styles) {
+
+        fTextWidget= createTextWidget(parent, styles);
+
+        // Support scroll page upon MOD1+MouseWheel
+        fTextWidget.addListener(SWT.MouseWheel, new class()  Listener {
+
+            public void handleEvent(Event event) {
+                if (((event.stateMask & SWT.MOD1) is 0))
+                    return;
+
+                int topIndex= fTextWidget.getTopIndex();
+                int bottomIndex= JFaceTextUtil.getBottomIndex(fTextWidget);
+
+                if (event.count > 0)
+                    fTextWidget.setTopIndex(2 * topIndex - bottomIndex);
+                else
+                    fTextWidget.setTopIndex(bottomIndex);
+
+                updateViewportListeners(INTERNAL);
+            }
+        });
+
+        fTextWidget.addDisposeListener(
+            new class()  DisposeListener {
+                public void widgetDisposed(DisposeEvent e) {
+                    handleDispose();
+                }
+            }
+        );
+
+        fTextWidget.setFont(parent.getFont());
+        fTextWidget.setDoubleClickEnabled(true);
+
+        /*
+         * Disable SWT Shift+TAB traversal in this viewer
+         * 1GIYQ9K: ITPUI:WINNT - StyledText swallows Shift+TAB
+         */
+        fTextWidget.addTraverseListener(new class()  TraverseListener {
+            public void keyTraversed(TraverseEvent e) {
+                if ((SWT.SHIFT is e.stateMask) && ('\t' is e.character))
+                    e.doit= false;
+            }
+        });
+
+        // where does the first line start
+        fTopInset= -fTextWidget.computeTrim(0, 0, 0, 0).y;
+
+        fVerifyListener.forward(true);
+        fTextWidget.addVerifyListener(fVerifyListener);
+
+        fTextWidget.addSelectionListener(new class()  SelectionListener {
+            public void widgetDefaultSelected(SelectionEvent event) {
+                selectionChanged(event.x, event.y - event.x);
+            }
+            public void widgetSelected(SelectionEvent event) {
+                selectionChanged(event.x, event.y - event.x);
+            }
+        });
+
+        fCursorListener= new CursorListener();
+        fCursorListener.install();
+
+        initializeViewportUpdate();
+    }
+
+    /*
+     * @see Viewer#getControl()
+     */
+    public Control getControl() {
+        return fTextWidget;
+    }
+
+    /*
+     * @see ITextViewer#activatePlugins()
+     */
+    public void activatePlugins() {
+
+        if (fDoubleClickStrategies !is null && !fDoubleClickStrategies.isEmpty() && fDoubleClickStrategyConnector is null) {
+            fDoubleClickStrategyConnector= new TextDoubleClickStrategyConnector();
+            fTextWidget.addWordMovementListener(fDoubleClickStrategyConnector);
+            fTextWidget.addMouseListener(fDoubleClickStrategyConnector);
+        }
+
+        ensureHoverControlManagerInstalled();
+        ensureHyperlinkManagerInstalled();
+
+        if (fUndoManager !is null) {
+            fUndoManager.connect(this);
+            fUndoManager.reset();
+        }
+    }
+
+    /**
+     * After this method has been executed the caller knows that any installed text hover has been installed.
+     */
+    private void ensureHoverControlManagerInstalled() {
+        if (fTextHovers !is null && !fTextHovers.isEmpty() && fHoverControlCreator !is null && fTextHoverManager is null) {
+            fTextHoverManager= new TextViewerHoverManager(this, fHoverControlCreator);
+            fTextHoverManager.install(this.getTextWidget());
+            fTextHoverManager.setSizeConstraints(TEXT_HOVER_WIDTH_CHARS, TEXT_HOVER_HEIGHT_CHARS, false, true);
+            fTextHoverManager.setInformationControlReplacer(new StickyHoverManager(this));
+        }
+    }
+
+    /*
+     * @see ITextViewer#resetPlugins()
+     */
+    public void resetPlugins() {
+        if (fUndoManager !is null)
+            fUndoManager.reset();
+    }
+
+    /**
+     * Frees all resources allocated by this viewer. Internally called when the viewer's
+     * control has been disposed.
+     */
+    protected void handleDispose() {
+
+        setDocument(null);
+
+        if (fPaintManager !is null) {
+            fPaintManager.dispose();
+            fPaintManager= null;
+        }
+
+        removeViewPortUpdate();
+        fViewportGuard= null;
+
+        if (fViewportListeners !is null) {
+            fViewportListeners.clear();
+            fViewportListeners= null;
+        }
+
+        if (fTextListeners !is null) {
+            fTextListeners.clear();
+            fTextListeners= null;
+        }
+
+        if (fTextInputListeners !is null)  {
+            fTextInputListeners.clear();
+            fTextInputListeners= null;
+        }
+
+        if (fPostSelectionChangedListeners !is null)  {
+            fPostSelectionChangedListeners.clear();
+            fPostSelectionChangedListeners= null;
+        }
+
+        if (fAutoIndentStrategies !is null) {
+            fAutoIndentStrategies.clear();
+            fAutoIndentStrategies= null;
+        }
+
+        if (fUndoManager !is null) {
+            fUndoManager.disconnect();
+            fUndoManager= null;
+        }
+
+        if (fDoubleClickStrategies !is null) {
+            fDoubleClickStrategies.clear();
+            fDoubleClickStrategies= null;
+        }
+
+        if (fTextHovers !is null) {
+            fTextHovers.clear();
+            fTextHovers= null;
+        }
+
+        fDoubleClickStrategyConnector= null;
+
+        if (fTextHoverManager !is null) {
+            fTextHoverManager.dispose();
+            fTextHoverManager= null;
+        }
+
+        if (fVisibleDocumentListener !is null) {
+            if (fVisibleDocument !is null)
+                fVisibleDocument.removeDocumentListener(fVisibleDocumentListener);
+            fVisibleDocumentListener= null;
+        }
+
+        if (fDocumentAdapter !is null) {
+            fDocumentAdapter.setDocument(null);
+            fDocumentAdapter= null;
+        }
+
+        if (fSlaveDocumentManager !is null) {
+            if (fVisibleDocument !is null)
+                fSlaveDocumentManager.freeSlaveDocument(fVisibleDocument);
+            fSlaveDocumentManager= null;
+        }
+
+        if (fCursorListener !is null) {
+            fCursorListener.uninstall();
+            fCursorListener= null;
+        }
+
+        if (fHyperlinkManager !is null) {
+            fHyperlinkManager.uninstall();
+            fHyperlinkManager= null;
+        }
+
+        fHyperlinkDetectors= null;
+        fVisibleDocument= null;
+        fDocument= null;
+        fScroller= null;
+
+        fTextWidget= null;
+    }
+
+
+    //---- simple getters and setters
+
+    /*
+     * @see org.eclipse.jface.text.ITextViewer#getTextWidget()
+     */
+    public StyledText getTextWidget() {
+        return fTextWidget;
+    }
+
+    /**
+     * The delay in milliseconds before an empty selection
+     * changed event is sent by the cursor listener.
+     * <p>
+     * Note: The return value is used to initialize the cursor
+     * listener. To return a non-constant value has no effect.</p>
+     * <p>
+     * The same value (<code>500</code>) is used in <code>OpenStrategy.TIME</code>.</p>
+     *
+     * @return delay in milliseconds
+     * @see org.eclipse.jface.util.OpenStrategy
+     * @since 3.0
+     */
+    protected int getEmptySelectionChangedEventDelay() {
+        return 500;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @deprecated since 3.1, use
+     *             {@link ITextViewerExtension2#prependAutoEditStrategy(IAutoEditStrategy, String)} and
+     *             {@link ITextViewerExtension2#removeAutoEditStrategy(IAutoEditStrategy, String)} instead
+     */
+    public void setAutoIndentStrategy(IAutoIndentStrategy strategy, String contentType) {
+        setAutoEditStrategies([ strategy ], contentType);
+    }
+
+    /**
+     * Sets the given edit strategy as the only strategy for the given content type.
+     *
+     * @param strategies the auto edit strategies
+     * @param contentType the content type
+     * @since 3.1
+     */
+    protected final void setAutoEditStrategies(IAutoEditStrategy[] strategies, String contentType) {
+        if (fAutoIndentStrategies is null)
+            fAutoIndentStrategies= new HashMap();
+
+        List autoEditStrategies= cast(List) fAutoIndentStrategies.get(contentType);
+
+        if (strategies is null) {
+            if (autoEditStrategies is null)
+                return;
+
+            fAutoIndentStrategies.put(contentType, cast(Object)null);
+
+        } else {
+            if (autoEditStrategies is null) {
+                autoEditStrategies= new ArrayList();
+                fAutoIndentStrategies.put(contentType, cast(Object)autoEditStrategies);
+            }
+
+            autoEditStrategies.clear();
+            autoEditStrategies.addAll(Arrays.asList(arraycast!(Object)(strategies)));
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextViewerExtension2#prependAutoEditStrategy(org.eclipse.jface.text.IAutoEditStrategy, java.lang.String)
+     * @since 2.1
+     */
+    public void prependAutoEditStrategy(IAutoEditStrategy strategy, String contentType) {
+
+        if (strategy is null || contentType is null)
+            throw new IllegalArgumentException("");
+
+        if (fAutoIndentStrategies is null)
+            fAutoIndentStrategies= new HashMap();
+
+        List autoEditStrategies= cast(List) fAutoIndentStrategies.get(contentType);
+        if (autoEditStrategies is null) {
+            autoEditStrategies= new ArrayList();
+            fAutoIndentStrategies.put(contentType,cast(Object) autoEditStrategies);
+        }
+
+        autoEditStrategies.add(0, cast(Object)strategy);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextViewerExtension2#removeAutoEditStrategy(org.eclipse.jface.text.IAutoEditStrategy, java.lang.String)
+     * @since 2.1
+     */
+    public void removeAutoEditStrategy(IAutoEditStrategy strategy, String contentType) {
+        if (fAutoIndentStrategies is null)
+            return;
+
+        List autoEditStrategies= cast(List) fAutoIndentStrategies.get(contentType);
+        if (autoEditStrategies is null)
+            return;
+
+        for (final Iterator iterator= autoEditStrategies.iterator(); iterator.hasNext(); ) {
+            if (iterator.next().opEquals(cast(Object)strategy)) {
+                iterator.remove();
+                break;
+            }
+        }
+
+        if (autoEditStrategies.isEmpty())
+            fAutoIndentStrategies.put(contentType, cast(Object)null);
+    }
+
+    /*
+     * @see ITextViewer#setEventConsumer(IEventConsumer)
+     */
+    public void setEventConsumer(IEventConsumer consumer) {
+        fEventConsumer= consumer;
+    }
+
+    /*
+     * @see ITextViewer#setIndentPrefixes(String[], String)
+     */
+    public void setIndentPrefixes(String[] indentPrefixes, String contentType) {
+
+        int i= -1;
+        bool ok= (indentPrefixes !is null);
+        while (ok &&  ++i < indentPrefixes.length)
+            ok= (indentPrefixes[i] !is null);
+
+        if (ok) {
+
+            if (fIndentChars is null)
+                fIndentChars= new HashMap();
+
+            fIndentChars.put(contentType, new ArrayWrapperObject( stringcast(indentPrefixes)));
+
+        } else if (fIndentChars !is null)
+            fIndentChars.remove(contentType);
+    }
+
+    /*
+     * @see ITextViewer#getTopInset()
+     */
+    public int getTopInset() {
+        return fTopInset;
+    }
+
+    /*
+     * @see ITextViewer#isEditable()
+     */
+    public bool isEditable() {
+        if (fTextWidget is null)
+            return false;
+        return fTextWidget.getEditable();
+    }
+
+    /*
+     * @see ITextViewer#setEditable(bool)
+     */
+    public void setEditable(bool editable) {
+        if (fTextWidget !is null)
+            fTextWidget.setEditable(editable);
+    }
+
+    /*
+     * @see ITextViewer#setDefaultPrefixes
+     * @since 2.0
+     */
+    public void setDefaultPrefixes(String[] defaultPrefixes, String contentType) {
+
+        if (defaultPrefixes !is null && defaultPrefixes.length > 0) {
+            if (fDefaultPrefixChars is null)
+                fDefaultPrefixChars= new HashMap();
+            fDefaultPrefixChars.put(contentType, new ArrayWrapperObject( stringcast(defaultPrefixes)));
+        } else if (fDefaultPrefixChars !is null)
+            fDefaultPrefixChars.remove(contentType);
+    }
+
+    /*
+     * @see ITextViewer#setUndoManager(IUndoManager)
+     */
+    public void setUndoManager(IUndoManager undoManager) {
+        fUndoManager= undoManager;
+    }
+
+    /*
+     * @see ITextViewerExtension6#getUndoManager()
+     * @since 3.1
+     */
+    public IUndoManager getUndoManager() {
+        return fUndoManager;
+    }
+
+    /*
+     * @see ITextViewer#setTextHover(ITextHover, String)
+     */
+    public void setTextHover(ITextHover hover, String contentType) {
+        setTextHover(hover, contentType, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK);
+    }
+
+    /*
+     * @see ITextViewerExtension2#setTextHover(ITextHover, String, int)
+     * @since 2.1
+     */
+    public void setTextHover(ITextHover hover, String contentType, int stateMask) {
+        TextHoverKey key= new TextHoverKey(contentType, stateMask);
+        if (hover !is null) {
+            if (fTextHovers is null) {
+                fTextHovers= new HashMap();
+            }
+            fTextHovers.put(key, cast(Object)hover);
+        } else if (fTextHovers !is null)
+            fTextHovers.remove(key);
+
+        ensureHoverControlManagerInstalled();
+    }
+
+    /*
+     * @see ITextViewerExtension2#removeTextHovers(String)
+     * @since 2.1
+     */
+    public void removeTextHovers(String contentType) {
+        if (fTextHovers is null)
+            return;
+
+        Iterator iter= (new HashSet(fTextHovers.keySet())).iterator();
+        while (iter.hasNext()) {
+            TextHoverKey key= cast(TextHoverKey)iter.next();
+            if (key.fContentType.equals(contentType))
+                fTextHovers.remove(key);
+        }
+    }
+
+    /**
+     * Returns the text hover for a given offset.
+     *
+     * @param offset the offset for which to return the text hover
+     * @return the text hover for the given offset
+     */
+    protected ITextHover getTextHover(int offset) {
+        return getTextHover(offset, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK);
+    }
+    package ITextHover getTextHover_package(int offset) {
+        return getTextHover(offset);
+    }
+
+    /**
+     * Returns the text hover for a given offset and a given state mask.
+     *
+     * @param offset the offset for which to return the text hover
+     * @param stateMask the SWT event state mask
+     * @return the text hover for the given offset and state mask
+     * @since 2.1
+     */
+    package ITextHover getTextHover_package(int offset, int stateMask) {
+        return getTextHover(offset,stateMask);
+    }
+    protected ITextHover getTextHover(int offset, int stateMask) {
+        if (fTextHovers is null)
+            return null;
+
+        IDocument document= getDocument();
+        if (document is null)
+            return null;
+
+        try {
+            TextHoverKey key= new TextHoverKey(TextUtilities.getContentType(document, getDocumentPartitioning(), offset, true), stateMask);
+            Object textHover= fTextHovers.get(key);
+            if (textHover is null) {
+                // Use default text hover
+                key.setStateMask(ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK);
+                textHover= fTextHovers.get(key);
+            }
+            return cast(ITextHover) textHover;
+        } catch (BadLocationException x) {
+            if (TRACE_ERRORS)
+                System.out_.println(JFaceTextMessages.getString("TextViewer.error.bad_location.selectContentTypePlugin")); //$NON-NLS-1$
+        }
+        return null;
+    }
+
+    /**
+     * Returns the text hovering controller of this viewer.
+     *
+     * @return the text hovering controller of this viewer
+     * @since 2.0
+     */
+    protected AbstractInformationControlManager getTextHoveringController() {
+        return fTextHoverManager;
+    }
+
+    /**
+     * Sets the creator for the hover controls.
+     *
+     * @param creator the hover control creator
+     * @since 2.0
+     */
+    public void setHoverControlCreator(IInformationControlCreator creator) {
+        fHoverControlCreator= creator;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 3.4
+     */
+    public void setHoverEnrichMode(ITextViewerExtension8_EnrichMode mode) {
+        if (fTextHoverManager is null)
+            return;
+        fTextHoverManager.setHoverEnrichMode(mode);
+    }
+
+    /*
+     * @see IWidgetTokenOwner#requestWidgetToken(IWidgetTokenKeeper)
+     * @since 2.0
+     */
+     public bool requestWidgetToken(IWidgetTokenKeeper requester) {
+         if (fTextWidget !is null) {
+             if (fWidgetTokenKeeper !is null) {
+                 if (fWidgetTokenKeeper is requester)
+                     return true;
+                 if (fWidgetTokenKeeper.requestWidgetToken(this)) {
+                     fWidgetTokenKeeper= requester;
+                     return true;
+                 }
+            } else {
+                fWidgetTokenKeeper= requester;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenOwnerExtension#requestWidgetToken(org.eclipse.jface.text.IWidgetTokenKeeper, int)
+     * @since 3.0
+     */
+    public bool requestWidgetToken(IWidgetTokenKeeper requester, int priority) {
+        if (fTextWidget !is null) {
+            if (fWidgetTokenKeeper !is null) {
+
+                if (fWidgetTokenKeeper is requester)
+                    return true;
+
+                bool accepted= false;
+                if ( cast(IWidgetTokenKeeperExtension)fWidgetTokenKeeper )  {
+                    IWidgetTokenKeeperExtension extension= cast(IWidgetTokenKeeperExtension) fWidgetTokenKeeper;
+                    accepted= extension.requestWidgetToken(this, priority);
+                } else  {
+                    accepted= fWidgetTokenKeeper.requestWidgetToken(this);
+                }
+
+                if (accepted) {
+                    fWidgetTokenKeeper= requester;
+                    return true;
+                }
+
+           } else {
+               fWidgetTokenKeeper= requester;
+               return true;
+           }
+       }
+       return false;
+   }
+
+    /*
+     * @see IWidgetTokenOwner#releaseWidgetToken(IWidgetTokenKeeper)
+     * @since 2.0
+     */
+    public void releaseWidgetToken(IWidgetTokenKeeper tokenKeeper) {
+        if (fWidgetTokenKeeper is tokenKeeper)
+            fWidgetTokenKeeper= null;
+    }
+
+
+    //---- Selection
+
+    /*
+     * @see ITextViewer#getSelectedRange()
+     */
+    public Point getSelectedRange() {
+
+        if (!redraws() && fViewerState !is null)
+            return fViewerState.getSelection();
+
+        if (fTextWidget !is null) {
+            Point p= fTextWidget.getSelectionRange();
+            p= widgetSelection2ModelSelection(p);
+            if (p !is null)
+                return p;
+        }
+
+        return new Point(-1, -1);
+    }
+
+    /*
+     * @see ITextViewer#setSelectedRange(int, int)
+     */
+    public void setSelectedRange(int selectionOffset, int selectionLength) {
+
+        if (!redraws()) {
+            if (fViewerState !is null)
+                fViewerState.updateSelection(selectionOffset, selectionLength);
+            return;
+        }
+
+        if (fTextWidget is null)
+            return;
+
+        IRegion widgetSelection= modelRange2ClosestWidgetRange(new Region(selectionOffset, selectionLength));
+        if (widgetSelection !is null) {
+
+            int[] selectionRange= [ widgetSelection.getOffset(), widgetSelection.getLength() ];
+            validateSelectionRange(selectionRange);
+            if (selectionRange[0] >= 0) {
+                fTextWidget.setSelectionRange(selectionRange[0], selectionRange[1]);
+                selectionChanged(selectionRange[0], selectionRange[1]);
+            }
+        }
+    }
+
+    /**
+     * Validates and adapts the given selection range if it is not a valid
+     * widget selection. The widget selection is invalid if it starts or ends
+     * inside a multi-character line delimiter. If so, the selection is adapted to
+     * start <b>after</b> the divided line delimiter and to end <b>before</b>
+     * the divided line delimiter.  The parameter passed in is changed in-place
+     * when being adapted. An adaptation to <code>[-1, -1]</code> indicates
+     * that the selection range could not be validated.
+     * Subclasses may reimplement this method.
+     *
+     * @param selectionRange selectionRange[0] is the offset, selectionRange[1]
+     *              the length of the selection to validate.
+     * @since 2.0
+     */
+    protected void validateSelectionRange(int[] selectionRange) {
+
+        IDocument document= getVisibleDocument();
+        if (document is null) {
+            selectionRange[0]= -1;
+            selectionRange[1]= -1;
+            return;
+        }
+
+        int documentLength= document.getLength();
+        int offset= selectionRange[0];
+        int length= selectionRange[1];
+
+        if (length < 0) {
+            length= - length;
+            offset -= length;
+        }
+
+        if (offset <0)
+            offset= 0;
+
+        if (offset > documentLength)
+            offset= documentLength;
+
+        int delta= (offset + length) - documentLength;
+        if (delta > 0)
+            length -= delta;
+
+        try {
+
+            int lineNumber= document.getLineOfOffset(offset);
+            IRegion lineInformation= document.getLineInformation(lineNumber);
+
+            int lineEnd= lineInformation.getOffset() + lineInformation.getLength();
+            delta= offset - lineEnd;
+            if (delta > 0) {
+                // in the middle of a multi-character line delimiter
+                offset= lineEnd;
+                String delimiter= document.getLineDelimiter(lineNumber);
+                if (delimiter !is null)
+                    offset += delimiter.length;
+            }
+
+            int end= offset + length;
+            lineInformation= document.getLineInformationOfOffset(end);
+            lineEnd= lineInformation.getOffset() + lineInformation.getLength();
+            delta= end - lineEnd;
+            if (delta > 0) {
+                // in the middle of a multi-character line delimiter
+                length -= delta;
+            }
+
+        } catch (BadLocationException x) {
+            selectionRange[0]= -1;
+            selectionRange[1]= -1;
+            return;
+        }
+
+        if (selectionRange[1] < 0) {
+            selectionRange[0]= offset + length;
+            selectionRange[1]= -length;
+        } else {
+            selectionRange[0]= offset;
+            selectionRange[1]= length;
+        }
+    }
+
+    /*
+     * @see Viewer#setSelection(ISelection)
+     */
+    public void setSelection(ISelection selection, bool reveal) {
+        if ( cast(ITextSelection)selection ) {
+            ITextSelection s= cast(ITextSelection) selection;
+            setSelectedRange(s.getOffset(), s.getLength());
+            if (reveal)
+                revealRange(s.getOffset(), s.getLength());
+        }
+    }
+
+    /*
+     * @see Viewer#getSelection()
+     */
+    public ISelection getSelection() {
+        Point p= getSelectedRange();
+        if (p.x is -1 || p.y is -1)
+            return TextSelection.emptySelection();
+
+        return new TextSelection(getDocument(), p.x, p.y);
+    }
+
+    /*
+     * @see ITextViewer#getSelectionProvider()
+     */
+    public ISelectionProvider getSelectionProvider() {
+        return this;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPostSelectionProvider#addPostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
+     * @since 3.0
+     */
+    public void addPostSelectionChangedListener(ISelectionChangedListener listener)  {
+
+        Assert.isNotNull(cast(Object)listener);
+
+        if (fPostSelectionChangedListeners is null)
+            fPostSelectionChangedListeners= new ArrayList();
+
+        if (!fPostSelectionChangedListeners.contains(cast(Object)listener))
+            fPostSelectionChangedListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPostSelectionProvider#removePostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
+     * @since 3.0
+     */
+    public void removePostSelectionChangedListener(ISelectionChangedListener listener)  {
+
+        Assert.isNotNull(cast(Object)listener);
+
+        if (fPostSelectionChangedListeners !is null)  {
+            fPostSelectionChangedListeners.remove(cast(Object)listener);
+            if (fPostSelectionChangedListeners.size() is 0)
+                fPostSelectionChangedListeners= null;
+        }
+    }
+
+    /**
+     * Get the text widget's display.
+     *
+     * @return the display or <code>null</code> if the display cannot be retrieved or if the display is disposed
+     * @since 3.0
+     */
+    private Display getDisplay() {
+        if (fTextWidget is null || fTextWidget.isDisposed())
+            return null;
+
+        Display display= fTextWidget.getDisplay();
+        if (display !is null && display.isDisposed())
+            return null;
+
+        return display;
+    }
+
+    /**
+     * Starts a timer to send out a post selection changed event.
+     *
+     * @param fireEqualSelection <code>true</code> iff the event must be fired if the selection does not change
+     * @since 3.0
+     */
+    private void queuePostSelectionChanged(bool fireEqualSelection) {
+        Display display= getDisplay();
+        if (display is null)
+            return;
+
+        fNumberOfPostSelectionChangedEvents[0]++;
+        display.timerExec(getEmptySelectionChangedEventDelay(), dgRunnable( &queuePostSelectionChangedRunner, fNumberOfPostSelectionChangedEvents[0], fireEqualSelection));
+    }
+    private void queuePostSelectionChangedRunner(int id, bool fireEqualSelection_) {
+        if (id is fNumberOfPostSelectionChangedEvents[0]) {
+            // Check again because this is executed after the delay
+            if (getDisplay() !is null)  {
+                Point selection= fTextWidget.getSelectionRange();
+                if (selection !is null) {
+                    IRegion r= widgetRange2ModelRange(new Region(selection.x, selection.y));
+                    if (fireEqualSelection_ || (r !is null && !(cast(Object)r).opEquals(cast(Object)fLastSentPostSelectionChange)) || r is null)  {
+                        fLastSentPostSelectionChange= r;
+                        firePostSelectionChanged(selection.x, selection.y);
+                    }
+                }
+            }
+        }
+    }
+    /**
+     * Sends out a text selection changed event to all registered post selection changed listeners.
+     *
+     * @param offset the offset of the newly selected range in the visible document
+     * @param length the length of the newly selected range in the visible document
+     * @since 3.0
+     */
+    protected void firePostSelectionChanged(int offset, int length) {
+        if (redraws()) {
+            IRegion r= widgetRange2ModelRange(new Region(offset, length));
+            ISelection selection= r !is null ? new TextSelection(getDocument(), r.getOffset(), r.getLength()) : TextSelection.emptySelection();
+            SelectionChangedEvent event= new SelectionChangedEvent(this, selection);
+            firePostSelectionChanged(event);
+        }
+    }
+
+    /**
+     * Sends out a text selection changed event to all registered listeners and
+     * registers the selection changed event to be sent out to all post selection
+     * listeners.
+     *
+     * @param offset the offset of the newly selected range in the visible document
+     * @param length the length of the newly selected range in the visible document
+     */
+    protected void selectionChanged(int offset, int length) {
+        queuePostSelectionChanged(true);
+        fireSelectionChanged(offset, length);
+    }
+
+    /**
+     * Sends out a text selection changed event to all registered listeners.
+     *
+     * @param offset the offset of the newly selected range in the visible document
+     * @param length the length of the newly selected range in the visible document
+     * @since 3.0
+     */
+    protected void fireSelectionChanged(int offset, int length) {
+        if (redraws()) {
+            IRegion r= widgetRange2ModelRange(new Region(offset, length));
+            if ((r !is null && !(cast(Object)r).opEquals(cast(Object)fLastSentSelectionChange)) || r is null)  {
+                fLastSentSelectionChange= r;
+                ISelection selection= r !is null ? new TextSelection(getDocument(), r.getOffset(), r.getLength()) : TextSelection.emptySelection();
+                SelectionChangedEvent event= new SelectionChangedEvent(this, selection);
+                fireSelectionChanged(event);
+            }
+        }
+    }
+
+    /**
+     * Sends the given event to all registered post selection changed listeners.
+     *
+     * @param event the selection event
+     * @since 3.0
+     */
+    private void firePostSelectionChanged(SelectionChangedEvent event) {
+        List listeners= fPostSelectionChangedListeners;
+        if (listeners !is null) {
+            listeners= new ArrayList(listeners);
+            for (int i= 0; i < listeners.size(); i++) {
+                ISelectionChangedListener l= cast(ISelectionChangedListener) listeners.get(i);
+                l.selectionChanged(event);
+            }
+        }
+    }
+
+    /**
+     * Sends out a mark selection changed event to all registered listeners.
+     *
+     * @param offset the offset of the mark selection in the visible document, the offset is <code>-1</code> if the mark was cleared
+     * @param length the length of the mark selection, may be negative if the caret is before the mark.
+     * @since 2.0
+     */
+    protected void markChanged(int offset, int length) {
+        if (redraws()) {
+
+            if (offset !is -1) {
+                IRegion r= widgetRange2ModelRange(new Region(offset, length));
+                offset= r.getOffset();
+                length= r.getLength();
+            }
+
+            ISelection selection= new MarkSelection(getDocument(), offset, length);
+            SelectionChangedEvent event= new SelectionChangedEvent(this, selection);
+            fireSelectionChanged(event);
+        }
+    }
+
+
+    //---- Text listeners
+
+    /*
+     * @see ITextViewer#addTextListener(ITextListener)
+     */
+    public void addTextListener(ITextListener listener) {
+
+        Assert.isNotNull(cast(Object)listener);
+
+        if (fTextListeners is null)
+            fTextListeners= new ArrayList();
+
+        if (!fTextListeners.contains(cast(Object)listener))
+            fTextListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * @see ITextViewer#removeTextListener(ITextListener)
+     */
+    public void removeTextListener(ITextListener listener) {
+
+        Assert.isNotNull(cast(Object)listener);
+
+        if (fTextListeners !is null) {
+            fTextListeners.remove(cast(Object)listener);
+            if (fTextListeners.size() is 0)
+                fTextListeners= null;
+        }
+    }
+
+    /**
+     * Informs all registered text listeners about the change specified by the
+     * widget command. This method does not use a robust iterator.
+     *
+     * @param cmd the widget command translated into a text event sent to all text listeners
+     */
+    protected void updateTextListeners(WidgetCommand cmd) {
+        List textListeners= fTextListeners;
+        if (textListeners !is null) {
+            textListeners= new ArrayList(textListeners);
+            DocumentEvent event= cmd.event;
+            if ( cast(SlaveDocumentEvent)event )
+                event= (cast(SlaveDocumentEvent) event).getMasterEvent();
+
+            TextEvent e= new TextEvent(cmd.start, cmd.length, cmd.text, cmd.preservedText, event, redraws());
+            for (int i= 0; i < textListeners.size(); i++) {
+                ITextListener l= cast(ITextListener) textListeners.get(i);
+                l.textChanged(e);
+            }
+        }
+    }
+
+    //---- Text input listeners
+
+    /*
+     * @see ITextViewer#addTextInputListener(ITextInputListener)
+     */
+    public void addTextInputListener(ITextInputListener listener) {
+
+        Assert.isNotNull(cast(Object)listener);
+
+        if (fTextInputListeners is null)
+            fTextInputListeners= new ArrayList();
+
+        if (!fTextInputListeners.contains(cast(Object)listener))
+            fTextInputListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * @see ITextViewer#removeTextInputListener(ITextInputListener)
+     */
+    public void removeTextInputListener(ITextInputListener listener) {
+
+        Assert.isNotNull(cast(Object)listener);
+
+        if (fTextInputListeners !is null) {
+            fTextInputListeners.remove(cast(Object)listener);
+            if (fTextInputListeners.size() is 0)
+                fTextInputListeners= null;
+        }
+    }
+
+    /**
+     * Informs all registered text input listeners about the forthcoming input change,
+     * This method does not use a robust iterator.
+     *
+     * @param oldInput the old input document
+     * @param newInput the new input document
+     */
+    protected void fireInputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+        List listener= fTextInputListeners;
+        if (listener !is null) {
+            for (int i= 0; i < listener.size(); i++) {
+                ITextInputListener l= cast(ITextInputListener) listener.get(i);
+                l.inputDocumentAboutToBeChanged(oldInput, newInput);
+            }
+        }
+    }
+
+    /**
+     * Informs all registered text input listeners about the successful input change,
+     * This method does not use a robust iterator.
+     *
+     * @param oldInput the old input document
+     * @param newInput the new input document
+     */
+    protected void fireInputDocumentChanged(IDocument oldInput, IDocument newInput) {
+        List listener= fTextInputListeners;
+        if (listener !is null) {
+            for (int i= 0; i < listener.size(); i++) {
+                ITextInputListener l= cast(ITextInputListener) listener.get(i);
+                l.inputDocumentChanged(oldInput, newInput);
+            }
+        }
+    }
+
+    //---- Document
+
+    /*
+     * @see Viewer#getInput()
+     */
+    public Object getInput() {
+        return cast(Object)getDocument();
+    }
+
+    /*
+     * @see ITextViewer#getDocument()
+     */
+    public IDocument getDocument() {
+        return fDocument;
+    }
+
+    /*
+     * @see Viewer#setInput(Object)
+     */
+    public void setInput(Object input) {
+
+        IDocument document= null;
+        if ( cast(IDocument)input )
+            document= cast(IDocument) input;
+
+        setDocument(document);
+    }
+
+    /*
+     * @see ITextViewer#setDocument(IDocument)
+     */
+    public void setDocument(IDocument document) {
+
+        fReplaceTextPresentation= true;
+        fireInputDocumentAboutToBeChanged(fDocument, document);
+
+        IDocument oldDocument= fDocument;
+        fDocument= document;
+
+        setVisibleDocument(fDocument);
+
+        resetPlugins();
+        inputChanged(cast(Object)fDocument, cast(Object)oldDocument);
+
+        fireInputDocumentChanged(oldDocument, fDocument);
+        fLastSentSelectionChange= null;
+        fReplaceTextPresentation= false;
+    }
+
+    /*
+     * @see ITextViewer#setDocument(IDocument, int, int)
+     */
+    public void setDocument(IDocument document, int modelRangeOffset, int modelRangeLength) {
+
+        fReplaceTextPresentation= true;
+        fireInputDocumentAboutToBeChanged(fDocument, document);
+
+        IDocument oldDocument= fDocument;
+        fDocument= document;
+
+        try {
+
+            IDocument slaveDocument= createSlaveDocument(document);
+            updateSlaveDocument(slaveDocument, modelRangeOffset, modelRangeLength);
+            setVisibleDocument(slaveDocument);
+
+        } catch (BadLocationException x) {
+            throw new IllegalArgumentException(JFaceTextMessages.getString("TextViewer.error.invalid_visible_region_1")); //$NON-NLS-1$
+        }
+
+        resetPlugins();
+        inputChanged(cast(Object)fDocument, cast(Object)oldDocument);
+
+        fireInputDocumentChanged(oldDocument, fDocument);
+        fLastSentSelectionChange= null;
+        fReplaceTextPresentation= false;
+    }
+
+    /**
+     * Creates a slave document for the given document if there is a slave document manager
+     * associated with this viewer.
+     *
+     * @param document the master document
+     * @return the newly created slave document
+     * @since 2.1
+     */
+    protected IDocument createSlaveDocument(IDocument document) {
+        ISlaveDocumentManager manager= getSlaveDocumentManager();
+        if (manager !is null) {
+            if (manager.isSlaveDocument(document))
+                return document;
+            return manager.createSlaveDocument(document);
+        }
+        return document;
+    }
+
+    /**
+     * Sets the given slave document to the specified range of its master document.
+     *
+     * @param visibleDocument the slave document
+     * @param visibleRegionOffset the offset of the master document range
+     * @param visibleRegionLength the length of the master document range
+     * @return <code>true</code> if the slave has been adapted successfully
+     * @throws BadLocationException in case the specified range is not valid in the master document
+     * @since 2.1
+     * @deprecated use <code>updateSlaveDocument</code> instead
+     */
+    protected bool updateVisibleDocument(IDocument visibleDocument, int visibleRegionOffset, int visibleRegionLength)  {
+        if ( cast(ChildDocument)visibleDocument ) {
+            ChildDocument childDocument= cast(ChildDocument) visibleDocument;
+
+            IDocument document= childDocument.getParentDocument();
+            int line= document.getLineOfOffset(visibleRegionOffset);
+            int offset= document.getLineOffset(line);
+            int length= (visibleRegionOffset - offset) + visibleRegionLength;
+
+            Position parentRange= childDocument.getParentDocumentRange();
+            if (offset !is parentRange.getOffset() || length !is parentRange.getLength()) {
+                childDocument.setParentDocumentRange(offset, length);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Updates the given slave document to show the specified range of its master document.
+     *
+     * @param slaveDocument the slave document
+     * @param modelRangeOffset the offset of the master document range
+     * @param modelRangeLength the length of the master document range
+     * @return <code>true</code> if the slave has been adapted successfully
+     * @throws BadLocationException in case the specified range is not valid in the master document
+     * @since 3.0
+     */
+    protected bool updateSlaveDocument(IDocument slaveDocument, int modelRangeOffset, int modelRangeLength)  {
+        return updateVisibleDocument(slaveDocument, modelRangeOffset, modelRangeLength);
+    }
+
+
+
+    //---- View ports
+
+    /**
+     * Initializes all listeners and structures required to set up view port listeners.
+     */
+    private void initializeViewportUpdate() {
+
+        if (fViewportGuard !is null)
+            return;
+
+        if (fTextWidget !is null) {
+
+            fViewportGuard= new ViewportGuard();
+            fLastTopPixel= -1;
+
+            fTextWidget.addKeyListener(fViewportGuard);
+            fTextWidget.addMouseListener(fViewportGuard);
+
+            fScroller= fTextWidget.getVerticalBar();
+            if (fScroller !is null)
+                fScroller.addSelectionListener(fViewportGuard);
+        }
+    }
+
+    /**
+     * Removes all listeners and structures required to set up view port listeners.
+     */
+    private void removeViewPortUpdate() {
+
+        if (fTextWidget !is null) {
+
+            fTextWidget.removeKeyListener(fViewportGuard);
+            fTextWidget.removeMouseListener(fViewportGuard);
+
+            if (fScroller !is null && !fScroller.isDisposed()) {
+                fScroller.removeSelectionListener(fViewportGuard);
+                fScroller= null;
+            }
+
+            fViewportGuard= null;
+        }
+    }
+
+    /*
+     * @see ITextViewer#addViewportListener(IViewportListener)
+     */
+    public void addViewportListener(IViewportListener listener) {
+
+        if (fViewportListeners is null) {
+            fViewportListeners= new ArrayList();
+            initializeViewportUpdate();
+        }
+
+        if (!fViewportListeners.contains(cast(Object)listener))
+            fViewportListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * @see ITextViewer#removeViewportListener(IVewportListener)
+     */
+    public void removeViewportListener(IViewportListener listener) {
+        if (fViewportListeners !is null)
+            fViewportListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Checks whether the view port changed and if so informs all registered
+     * listeners about the change.
+     *
+     * @param origin describes under which circumstances this method has been called.
+     *
+     * @see IViewportListener
+     */
+    protected void updateViewportListeners(int origin) {
+
+        if (redraws()) {
+            int topPixel= fTextWidget.getTopPixel();
+            if (topPixel >= 0 && topPixel !is fLastTopPixel) {
+                if (fViewportListeners !is null) {
+                    for (int i= 0; i < fViewportListeners.size(); i++) {
+                        IViewportListener l= cast(IViewportListener) fViewportListeners.get(i);
+                        l.viewportChanged(topPixel);
+                    }
+                }
+                fLastTopPixel= topPixel;
+            }
+        }
+    }
+
+    //---- scrolling and revealing
+
+    /*
+     * @see ITextViewer#getTopIndex()
+     */
+    public int getTopIndex() {
+
+        if (fTextWidget !is null) {
+            int top= fTextWidget.getTopIndex();
+            return widgetLine2ModelLine(top);
+        }
+
+        return -1;
+    }
+
+    /*
+     * @see ITextViewer#setTopIndex(int)
+     */
+    public void setTopIndex(int index) {
+
+        if (fTextWidget !is null) {
+
+            int widgetLine= modelLine2WidgetLine(index);
+            if (widgetLine is -1)
+                widgetLine= getClosestWidgetLineForModelLine(index);
+
+            if (widgetLine > -1) {
+                fTextWidget.setTopIndex(widgetLine);
+                    updateViewportListeners(INTERNAL);
+            }
+        }
+    }
+
+    /**
+     * Returns the number of lines that can fully fit into the viewport. This is computed by
+     * dividing the widget's client area height by the widget's line height. The result is only
+     * accurate if the widget does not use variable line heights - for that reason, clients should
+     * not use this method any longer and use the client area height of the text widget to find out
+     * how much content fits into it.
+     *
+     * @return the view port height in lines
+     * @deprecated as of 3.2
+     */
+    protected int getVisibleLinesInViewport() {
+        if (fTextWidget !is null) {
+            Rectangle clArea= fTextWidget.getClientArea();
+            if (!clArea.isEmpty())
+                return clArea.height / fTextWidget.getLineHeight();
+        }
+        return -1;
+    }
+
+    /*
+     * @see ITextViewer#getBottomIndex()
+     */
+    public int getBottomIndex() {
+
+        if (fTextWidget is null)
+            return -1;
+
+        int widgetBottom= JFaceTextUtil.getBottomIndex(fTextWidget);
+        return widgetLine2ModelLine(widgetBottom);
+    }
+
+    /*
+     * @see ITextViewer#getTopIndexStartOffset()
+     */
+    public int getTopIndexStartOffset() {
+
+        if (fTextWidget !is null) {
+            int top= fTextWidget.getTopIndex();
+            try {
+                top= getVisibleDocument().getLineOffset(top);
+                return widgetOffset2ModelOffset(top);
+            } catch (BadLocationException ex) {
+                if (TRACE_ERRORS)
+                    System.out_.println(JFaceTextMessages.getString("TextViewer.error.bad_location.getTopIndexStartOffset")); //$NON-NLS-1$
+            }
+        }
+
+        return -1;
+    }
+
+    /*
+     * @see ITextViewer#getBottomIndexEndOffset()
+     */
+    public int getBottomIndexEndOffset() {
+        try {
+
+            IRegion line= getDocument().getLineInformation(getBottomIndex());
+            int bottomEndOffset= line.getOffset() + line.getLength() - 1;
+
+            IRegion coverage= getModelCoverage();
+            if (coverage is null)
+                return -1;
+
+            int coverageEndOffset=  coverage.getOffset() + coverage.getLength() - 1;
+            return Math.min(coverageEndOffset, bottomEndOffset);
+
+        } catch (BadLocationException ex) {
+            if (TRACE_ERRORS)
+                System.out_.println(JFaceTextMessages.getString("TextViewer.error.bad_location.getBottomIndexEndOffset")); //$NON-NLS-1$
+            return getDocument().getLength() - 1;
+        }
+    }
+
+    /*
+     * @see ITextViewer#revealRange(int, int)
+     */
+    public void revealRange(int start, int length) {
+
+        if (fTextWidget is null || !redraws())
+            return;
+
+        IRegion modelRange= new Region(start, length);
+        IRegion widgetRange= modelRange2ClosestWidgetRange(modelRange);
+        if (widgetRange !is null) {
+
+            int[] range= [ widgetRange.getOffset(), widgetRange.getLength() ];
+            validateSelectionRange(range);
+            if (range[0] >= 0)
+                internalRevealRange(range[0], range[0] + range[1]);
+
+        } else {
+
+            IRegion coverage= getModelCoverage();
+            int cursor= (coverage is null || start < coverage.getOffset()) ? 0 : getVisibleDocument().getLength();
+            internalRevealRange(cursor, cursor);
+        }
+    }
+
+    /**
+     * Reveals the given range of the visible document.
+     *
+     * @param start the start offset of the range
+     * @param end the end offset of the range
+     */
+    protected void internalRevealRange(int start, int end) {
+
+        try {
+
+            IDocument doc= getVisibleDocument();
+
+            int startLine= doc.getLineOfOffset(start);
+            int endLine= doc.getLineOfOffset(end);
+
+            int top= fTextWidget.getTopIndex();
+            if (top > -1) {
+
+                // scroll vertically
+                int bottom= JFaceTextUtil.getBottomIndex(fTextWidget);
+                int lines= bottom - top;
+
+                // if the widget is not scrollable as it is displaying the entire content
+                // setTopIndex won't have any effect.
+
+                if (startLine >= top && startLine <= bottom && endLine >= top && endLine <= bottom ) {
+
+                    // do not scroll at all as it is already visible
+
+                } else {
+
+                    int delta= Math.max(0, lines - (endLine - startLine));
+                    fTextWidget.setTopIndex(startLine - delta/3);
+                    updateViewportListeners(INTERNAL);
+                }
+
+                // scroll horizontally
+
+                if (endLine < startLine) {
+                    endLine += startLine;
+                    startLine= endLine - startLine;
+                    endLine -= startLine;
+                }
+
+                int startPixel= -1;
+                int endPixel= -1;
+
+                if (endLine > startLine) {
+                    // reveal the beginning of the range in the start line
+                    IRegion extent= getExtent(start, start);
+                    startPixel= extent.getOffset() + fTextWidget.getHorizontalPixel();
+                    endPixel= startPixel;
+
+                } else {
+                    IRegion extent= getExtent(start, end);
+                    startPixel= extent.getOffset() + fTextWidget.getHorizontalPixel();
+                    endPixel= startPixel + extent.getLength();
+                }
+
+                int visibleStart= fTextWidget.getHorizontalPixel();
+                int visibleEnd= visibleStart + fTextWidget.getClientArea().width;
+
+                // scroll only if not yet visible
+                if (startPixel < visibleStart || visibleEnd < endPixel) {
+
+                    // set buffer zone to 10 pixels
+                    int bufferZone= 10;
+
+                    int newOffset= visibleStart;
+
+                    int visibleWidth= visibleEnd - visibleStart;
+                    int selectionPixelWidth= endPixel - startPixel;
+
+                    if (startPixel < visibleStart)
+                        newOffset= startPixel;
+                    else if (selectionPixelWidth  + bufferZone < visibleWidth)
+                        newOffset= endPixel + bufferZone - visibleWidth;
+                    else
+                        newOffset= startPixel;
+
+                    float index= (cast(float)newOffset) / (cast(float)getAverageCharWidth());
+
+                    fTextWidget.setHorizontalIndex(cast(int)Math.round(index));
+                }
+
+            }
+        } catch (BadLocationException e) {
+            throw new IllegalArgumentException(JFaceTextMessages.getString("TextViewer.error.invalid_range")); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Returns the width of the text when being drawn into this viewer's widget.
+     *
+     * @param text the string to measure
+     * @return the width of the presentation of the given string
+     * @deprecated use <code>getWidthInPixels(int, int)</code> instead
+     */
+    final protected int getWidthInPixels(String text) {
+        GC gc= new GC(fTextWidget);
+        gc.setFont(fTextWidget.getFont());
+        Point extent= gc.textExtent(text);
+        gc.dispose();
+        return extent.x;
+    }
+
+    /**
+     * Returns the region covered by the given start and end offset.
+     * The result is relative to the upper left corner of the widget
+     * client area.
+     *
+     * @param start offset relative to the start of this viewer's view port
+     *  0 <= offset <= getCharCount()
+     * @param end offset relative to the start of this viewer's view port
+     *  0 <= offset <= getCharCount()
+     * @return the region covered by start and end offset
+     */
+    final protected IRegion getExtent(int start, int end) {
+        if (end > 0 && start < end) {
+            Rectangle bounds= fTextWidget.getTextBounds(start, end - 1);
+            return new Region(bounds.x, bounds.width);
+        }
+
+        return new Region(fTextWidget.getLocationAtOffset(start).x, 0);
+    }
+
+    /**
+     * Returns the width of the representation of a text range in the
+     * visible region of the viewer's document as drawn in this viewer's
+     * widget.
+     *
+     * @param offset the offset of the text range in the visible region
+     * @param length the length of the text range in the visible region
+     * @return the width of the presentation of the specified text range
+     * @since 2.0
+     */
+    final protected int getWidthInPixels(int offset, int length) {
+        return getExtent(offset, offset + length).getLength();
+    }
+
+    /**
+     * Returns the average character width of this viewer's widget.
+     *
+     * @return the average character width of this viewer's widget
+     */
+    final protected int getAverageCharWidth() {
+        return JFaceTextUtil.getAverageCharWidth(getTextWidget());
+    }
+
+    /*
+     * @see Viewer#refresh()
+     */
+    public void refresh() {
+        setDocument(getDocument());
+    }
+
+    //---- visible range support
+
+    /**
+     * Returns the slave document manager
+     *
+     * @return the slave document manager
+     * @since 2.1
+     */
+    protected ISlaveDocumentManager getSlaveDocumentManager() {
+        if (fSlaveDocumentManager is null)
+            fSlaveDocumentManager= createSlaveDocumentManager();
+        return fSlaveDocumentManager;
+    }
+
+    /**
+     * Creates a new slave document manager. This implementation always
+     * returns a <code>ChildDocumentManager</code>.
+     *
+     * @return ISlaveDocumentManager
+     * @since 2.1
+     */
+    protected ISlaveDocumentManager createSlaveDocumentManager() {
+        return new ChildDocumentManager();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextViewer#invalidateTextPresentation()
+     */
+    public final void invalidateTextPresentation() {
+        if (fVisibleDocument !is null) {
+            fWidgetCommand.event= null;
+            fWidgetCommand.start= 0;
+            fWidgetCommand.length= fVisibleDocument.getLength();
+            fWidgetCommand.text= fVisibleDocument.get();
+            updateTextListeners(fWidgetCommand);
+        }
+    }
+
+    /**
+     * Invalidates the given range of the text presentation.
+     *
+     * @param offset the offset of the range to be invalidated
+     * @param length the length of the range to be invalidated
+     * @since 2.1
+     */
+    public final void invalidateTextPresentation(int offset, int length) {
+        if (fVisibleDocument !is null) {
+
+            IRegion widgetRange= modelRange2WidgetRange(new Region(offset, length));
+            if (widgetRange !is null) {
+
+                fWidgetCommand.event= null;
+                fWidgetCommand.start= widgetRange.getOffset();
+                fWidgetCommand.length= widgetRange.getLength();
+
+                try {
+                    fWidgetCommand.text= fVisibleDocument.get(widgetRange.getOffset(), widgetRange.getLength());
+                    updateTextListeners(fWidgetCommand);
+                } catch (BadLocationException x) {
+                    // can not happen because of previous checking
+                }
+            }
+        }
+    }
+
+    /**
+     * Initializes the text widget with the visual document and
+     * invalidates the overall presentation.
+     */
+    private void initializeWidgetContents() {
+
+        if (fTextWidget !is null && fVisibleDocument !is null) {
+
+            // set widget content
+            if (fDocumentAdapter is null)
+                fDocumentAdapter= createDocumentAdapter();
+
+            fDocumentAdapter.setDocument(fVisibleDocument);
+            fTextWidget.setContent(fDocumentAdapter);
+
+            // invalidate presentation
+            invalidateTextPresentation();
+        }
+    }
+
+    /**
+     * Frees the given document if it is a slave document.
+     *
+     * @param slave the potential slave document
+     * @since 3.0
+     */
+    protected void freeSlaveDocument(IDocument slave) {
+        ISlaveDocumentManager manager= getSlaveDocumentManager();
+        if (manager !is null && manager.isSlaveDocument(slave))
+            manager.freeSlaveDocument(slave);
+    }
+
+    /**
+     * Sets this viewer's visible document. The visible document represents the
+     * visible region of the viewer's input document.
+     *
+     * @param document the visible document
+     */
+    protected void setVisibleDocument(IDocument document) {
+
+        if (fVisibleDocument is document && cast(ChildDocument)fVisibleDocument ) {
+            // optimization for new child documents
+            return;
+        }
+
+        if (fVisibleDocument !is null) {
+            if (fVisibleDocumentListener !is null)
+                fVisibleDocument.removeDocumentListener(fVisibleDocumentListener);
+            if (fVisibleDocument !is document)
+                freeSlaveDocument(fVisibleDocument);
+        }
+
+        fVisibleDocument= document;
+        initializeDocumentInformationMapping(fVisibleDocument);
+
+        initializeWidgetContents();
+
+        fFindReplaceDocumentAdapter= null;
+        if (fVisibleDocument !is null && fVisibleDocumentListener !is null)
+            fVisibleDocument.addDocumentListener(fVisibleDocumentListener);
+    }
+
+    /**
+     * Hook method called when the visible document is about to be changed.
+     * <p>
+     * Subclasses may override.
+     *
+     * @param event the document event
+     * @since 3.0
+     */
+    protected void handleVisibleDocumentAboutToBeChanged(DocumentEvent event) {
+    }
+
+    /**
+     * Hook method called when the visible document has been changed.
+     * <p>
+     * Subclasses may override.
+     *
+     * @param event the document event
+     * @since 3.0
+     */
+    protected void handleVisibleDocumentChanged(DocumentEvent event) {
+    }
+
+    /**
+     * Initializes the document information mapping between the given slave document and
+     * its master document.
+     *
+     * @param visibleDocument the slave document
+     * @since 2.1
+     */
+    protected void initializeDocumentInformationMapping(IDocument visibleDocument) {
+        ISlaveDocumentManager manager= getSlaveDocumentManager();
+        fInformationMapping= manager is null ? null : manager.createMasterSlaveMapping(visibleDocument);
+    }
+
+    /**
+     * Returns the viewer's visible document.
+     *
+     * @return the viewer's visible document
+     */
+    protected IDocument getVisibleDocument() {
+        return fVisibleDocument;
+    }
+
+    /**
+     * Returns the offset of the visible region.
+     *
+     * @return the offset of the visible region
+     */
+    protected int _getVisibleRegionOffset() {
+
+        IDocument document= getVisibleDocument();
+        if ( cast(ChildDocument)document ) {
+            ChildDocument cdoc= cast(ChildDocument) document;
+            return cdoc.getParentDocumentRange().getOffset();
+        }
+
+        return 0;
+    }
+    package int _getVisibleRegionOffset_package() {
+        return _getVisibleRegionOffset();
+    }
+
+    /*
+     * @see ITextViewer#getVisibleRegion()
+     */
+    public IRegion getVisibleRegion() {
+
+        IDocument document= getVisibleDocument();
+        if ( cast(ChildDocument)document ) {
+            Position p= (cast(ChildDocument) document).getParentDocumentRange();
+            return new Region(p.getOffset(), p.getLength());
+        }
+
+        return new Region(0, document is null ? 0 : document.getLength());
+    }
+
+    /*
+     * @see ITextViewer#overlapsWithVisibleRegion(int, int)
+     */
+    public bool overlapsWithVisibleRegion(int start, int length) {
+        IDocument document= getVisibleDocument();
+        if ( cast(ChildDocument)document ) {
+            ChildDocument cdoc= cast(ChildDocument) document;
+            return cdoc.getParentDocumentRange().overlapsWith(start, length);
+        } else if (document !is null) {
+            int size= document.getLength();
+            return (start >= 0 && length >= 0 && start + length <= size);
+        }
+        return false;
+    }
+
+    /*
+     * @see ITextViewer#setVisibleRegion(int, int)
+     */
+    public void setVisibleRegion(int start, int length) {
+
+        IRegion region= getVisibleRegion();
+        if (start is region.getOffset() && length is region.getLength()) {
+            // nothing to change
+            return;
+        }
+
+        setRedraw(false);
+        try {
+
+            IDocument slaveDocument= createSlaveDocument(getVisibleDocument());
+            if (updateSlaveDocument(slaveDocument, start, length))
+                setVisibleDocument(slaveDocument);
+
+        } catch (BadLocationException x) {
+            throw new IllegalArgumentException(JFaceTextMessages.getString("TextViewer.error.invalid_visible_region_2")); //$NON-NLS-1$
+        } finally {
+            setRedraw(true);
+        }
+    }
+
+    /*
+     * @see ITextViewer#resetVisibleRegion()
+     */
+    public void resetVisibleRegion() {
+        ISlaveDocumentManager manager= getSlaveDocumentManager();
+        if (manager !is null) {
+            IDocument slave= getVisibleDocument();
+            IDocument master= manager.getMasterDocument(slave);
+            if (master !is null) {
+                setVisibleDocument(master);
+                manager.freeSlaveDocument(slave);
+            }
+        }
+    }
+
+
+    //--------------------------------------
+
+    /*
+     * @see ITextViewer#setTextDoubleClickStrategy(ITextDoubleClickStrategy, String)
+     */
+    public void setTextDoubleClickStrategy(ITextDoubleClickStrategy strategy, String contentType) {
+
+        if (strategy !is null) {
+            if (fDoubleClickStrategies is null)
+                fDoubleClickStrategies= new HashMap();
+            fDoubleClickStrategies.put(stringcast(contentType), cast(Object)strategy);
+        } else if (fDoubleClickStrategies !is null)
+            fDoubleClickStrategies.remove(contentType);
+    }
+
+    /**
+     * Selects from the given map the one which is registered under
+     * the content type of the partition in which the given offset is located.
+     *
+     * @param plugins the map from which to choose
+     * @param offset the offset for which to find the plug-in
+     * @return the plug-in registered under the offset's content type
+     */
+    protected Object selectContentTypePlugin(int offset, Map plugins) {
+        try {
+            return selectContentTypePlugin(TextUtilities.getContentType(getDocument(), getDocumentPartitioning(), offset, true), plugins);
+        } catch (BadLocationException x) {
+            if (TRACE_ERRORS)
+                System.out_.println(JFaceTextMessages.getString("TextViewer.error.bad_location.selectContentTypePlugin")); //$NON-NLS-1$
+        }
+        return null;
+    }
+
+    /**
+     * Selects from the given <code>plug-ins</code> this one which is
+     * registered for the given content <code>type</code>.
+     *
+     * @param type the type to be used as lookup key
+     * @param plugins the table to be searched
+     * @return the plug-in in the map for the given content type
+     */
+    private Object selectContentTypePlugin(String type, Map plugins) {
+
+        if (plugins is null)
+            return null;
+
+        return plugins.get(type);
+    }
+
+    /**
+     * Hook called on receipt of a <code>VerifyEvent</code>. The event has
+     * been translated into a <code>DocumentCommand</code> which can now be
+     * manipulated by interested parties. By default, the hook forwards the command
+     * to the installed instances of <code>IAutoEditStrategy</code>.
+     *
+     * @param command the document command representing the verify event
+     */
+    protected void customizeDocumentCommand(DocumentCommand command) {
+        if (isIgnoringAutoEditStrategies())
+            return;
+
+        IDocument document= getDocument();
+
+        if (fTabsToSpacesConverter !is null)
+            fTabsToSpacesConverter.customizeDocumentCommand(document, command);
+
+        List strategies= cast(List) selectContentTypePlugin(command.offset, fAutoIndentStrategies);
+        if (strategies is null)
+            return;
+
+        switch (strategies.size()) {
+        // optimization
+        case 0:
+            break;
+
+        case 1:
+            (cast(IAutoEditStrategy) strategies.iterator().next()).customizeDocumentCommand(document, command);
+            break;
+
+        // make iterator robust against adding/removing strategies from within strategies
+        default:
+            strategies= new ArrayList(strategies);
+            for (final Iterator iterator= strategies.iterator(); iterator.hasNext(); )
+                (cast(IAutoEditStrategy) iterator.next()).customizeDocumentCommand(document, command);
+
+            break;
+        }
+    }
+
+    /**
+     * Handles the verify event issued by the viewer's text widget.
+     *
+     * @see VerifyListener#verifyText(VerifyEvent)
+     * @param e the verify event
+     */
+    protected void handleVerifyEvent(VerifyEvent e) {
+
+        if (fEventConsumer !is null) {
+            fEventConsumer.processEvent(e);
+            if (!e.doit)
+                return;
+        }
+
+        IRegion modelRange= event2ModelRange(e);
+        fDocumentCommand.setEvent(e, modelRange);
+        customizeDocumentCommand(fDocumentCommand);
+        if (!fDocumentCommand.fillEvent(e, modelRange)) {
+
+            bool compoundChange= fDocumentCommand.getCommandCount() > 1;
+            try {
+
+                fVerifyListener.forward(false);
+
+                if (compoundChange && fUndoManager !is null)
+                    fUndoManager.beginCompoundChange();
+
+                fDocumentCommand.execute(getDocument());
+
+                if (fTextWidget !is null) {
+                    int documentCaret= fDocumentCommand.caretOffset;
+                    if (documentCaret is -1) {
+                        // old behavior of document command
+                        documentCaret= fDocumentCommand.offset + (fDocumentCommand.text is null ? 0 : fDocumentCommand.text.length());
+                    }
+
+                    int widgetCaret= modelOffset2WidgetOffset(documentCaret);
+                    if (widgetCaret is -1) {
+                        // try to move it to the closest spot
+                        IRegion region= getModelCoverage();
+                        if (region !is null) {
+                            if (documentCaret <= region.getOffset())
+                                widgetCaret= 0;
+                            else if (documentCaret >= region.getOffset() + region.getLength())
+                                widgetCaret= getVisibleRegion().getLength();
+                        }
+                    }
+
+                    if (widgetCaret !is -1) {
+                        // there is a valid widget caret
+                        fTextWidget.setCaretOffset(widgetCaret);
+                    }
+
+                    fTextWidget.showSelection();
+                }
+            } catch (BadLocationException x) {
+
+                if (TRACE_ERRORS)
+                    System.out_.println(JFaceTextMessages.getString("TextViewer.error.bad_location.verifyText")); //$NON-NLS-1$
+
+            } finally {
+
+                if (compoundChange && fUndoManager !is null)
+                    fUndoManager.endCompoundChange();
+
+                fVerifyListener.forward(true);
+
+            }
+        }
+    }
+
+    //---- text manipulation
+
+    /**
+     * Returns whether the marked region of this viewer is empty.
+     *
+     * @return <code>true</code> if the marked region of this viewer is empty, otherwise <code>false</code>
+     * @since 2.0
+     */
+    private bool isMarkedRegionEmpty() {
+        return
+            fTextWidget is null ||
+            fMarkPosition is null ||
+            fMarkPosition.isDeleted() ||
+            modelRange2WidgetRange(fMarkPosition) is null;
+    }
+
+    /*
+     * @see ITextViewer#canDoOperation(int)
+     */
+    public bool canDoOperation(int operation) {
+
+        if (fTextWidget is null || !redraws())
+            return false;
+
+        switch (operation) {
+            case CUT:
+                return isEditable() &&(fTextWidget.getSelectionCount() > 0 || !isMarkedRegionEmpty());
+            case COPY:
+                return fTextWidget.getSelectionCount() > 0 || !isMarkedRegionEmpty();
+            case DELETE:
+            case PASTE:
+                return isEditable();
+            case SELECT_ALL:
+                return true;
+            case SHIFT_LEFT:
+            case SHIFT_RIGHT:
+                return isEditable() && fIndentChars !is null && areMultipleLinesSelected();
+            case PREFIX:
+            case STRIP_PREFIX:
+                return isEditable() && fDefaultPrefixChars !is null;
+            case UNDO:
+                return fUndoManager !is null && fUndoManager.undoable();
+            case REDO:
+                return fUndoManager !is null && fUndoManager.redoable();
+            case PRINT:
+                return isPrintable();
+            default:
+        }
+
+        return false;
+    }
+
+    /*
+     * @see ITextViewer#doOperation(int)
+     */
+    public void doOperation(int operation) {
+
+        if (fTextWidget is null || !redraws())
+            return;
+
+        Point selection= null;
+
+        switch (operation) {
+
+            case UNDO:
+                if (fUndoManager !is null) {
+                    ignoreAutoEditStrategies(true);
+                    fUndoManager.undo();
+                    ignoreAutoEditStrategies(false);
+                }
+                break;
+            case REDO:
+                if (fUndoManager !is null) {
+                    ignoreAutoEditStrategies(true);
+                    fUndoManager.redo();
+                    ignoreAutoEditStrategies(false);
+                }
+                break;
+            case CUT:
+                if (fTextWidget.getSelectionCount() is 0)
+                    copyMarkedRegion(true);
+                else
+                    fTextWidget.cut();
+
+                selection= fTextWidget.getSelectionRange();
+                fireSelectionChanged(selection.x, selection.y);
+
+                break;
+            case COPY:
+                if (fTextWidget.getSelectionCount() is 0)
+                    copyMarkedRegion(false);
+                else
+                    fTextWidget.copy();
+                break;
+            case PASTE:
+//              ignoreAutoEditStrategies(true);
+                fTextWidget.paste();
+                selection= fTextWidget.getSelectionRange();
+                fireSelectionChanged(selection.x, selection.y);
+//              ignoreAutoEditStrategies(false);
+                break;
+            case DELETE:
+                fTextWidget.invokeAction(ST.DELETE_NEXT);
+                selection= fTextWidget.getSelectionRange();
+                fireSelectionChanged(selection.x, selection.y);
+                break;
+            case SELECT_ALL: {
+                if (getDocument() !is null)
+                    setSelectedRange(0, getDocument().getLength());
+                break;
+            }
+            case SHIFT_RIGHT:
+                shift(false, true, false);
+                break;
+            case SHIFT_LEFT:
+                shift(false, false, false);
+                break;
+            case PREFIX:
+                shift(true, true, true);
+                break;
+            case STRIP_PREFIX:
+                shift(true, false, true);
+                break;
+            case PRINT:
+                print();
+                break;
+            default:
+        }
+    }
+
+    /**
+     * Tells this viewer whether the registered auto edit strategies should be ignored.
+     *
+     * @param ignore <code>true</code> if the strategies should be ignored.
+     * @since 2.1
+     */
+    protected void ignoreAutoEditStrategies(bool ignore) {
+        if (fIgnoreAutoIndent is ignore)
+            return;
+
+        fIgnoreAutoIndent= ignore;
+
+        IDocument document= getDocument();
+        if ( cast(IDocumentExtension2)document ) {
+            IDocumentExtension2 extension= cast(IDocumentExtension2) document;
+            if (ignore)
+                extension.ignorePostNotificationReplaces();
+            else
+                extension.acceptPostNotificationReplaces();
+        }
+    }
+    package void ignoreAutoEditStrategies_package(bool ignore) {
+        return ignoreAutoEditStrategies(ignore);
+    }
+
+    /**
+     * Returns whether this viewer ignores the registered auto edit strategies.
+     *
+     * @return <code>true</code> if the strategies are ignored
+     * @since 2.1
+     */
+    protected bool isIgnoringAutoEditStrategies() {
+        return fIgnoreAutoIndent;
+    }
+
+    /*
+     * @see ITextOperationTargetExtension#enableOperation(int, bool)
+     * @since 2.0
+     */
+    public void enableOperation(int operation, bool enable) {
+        /*
+         * NO-OP by default.
+         * Will be changed to regularly disable the known operations.
+         */
+    }
+
+    /**
+     * Copies/cuts the marked region.
+     *
+     * @param delete <code>true</code> if the region should be deleted rather than copied.
+     * @since 2.0
+     */
+    protected void copyMarkedRegion(bool delete_) {
+
+        if (fTextWidget is null)
+            return;
+
+        if (fMarkPosition is null || fMarkPosition.isDeleted() || modelRange2WidgetRange(fMarkPosition) is null)
+            return;
+
+        int widgetMarkOffset= modelOffset2WidgetOffset(fMarkPosition.offset);
+        Point selection= fTextWidget.getSelection();
+        if (selection.x <= widgetMarkOffset)
+            fTextWidget.setSelection(selection.x, widgetMarkOffset);
+        else
+            fTextWidget.setSelection(widgetMarkOffset, selection.x);
+
+        if (delete_) {
+            fTextWidget.cut();
+        } else {
+            fTextWidget.copy();
+            fTextWidget.setSelection(selection.x); // restore old cursor position
+        }
+    }
+
+    /**
+     * Deletes the current selection. If the selection has the length 0
+     * the selection is automatically extended to the right - either by 1
+     * or by the length of line delimiter if at the end of a line.
+     *
+     * @deprecated use <code>StyledText.invokeAction</code> instead
+     */
+    protected void deleteText() {
+        fTextWidget.invokeAction(ST.DELETE_NEXT);
+    }
+
+    /**
+     * A block is selected if the character preceding the start of the
+     * selection is a new line character.
+     *
+     * @return <code>true</code> if a block is selected
+     */
+    protected bool isBlockSelected() {
+
+        Point s= getSelectedRange();
+        if (s.y is 0)
+            return false;
+
+        try {
+
+            IDocument document= getDocument();
+            int line= document.getLineOfOffset(s.x);
+            int start= document.getLineOffset(line);
+            return (s.x is start);
+
+        } catch (BadLocationException x) {
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns <code>true</code> if one line is completely selected or if multiple lines are selected.
+     * Being completely selected means that all characters except the new line characters are
+     * selected.
+     *
+     * @return <code>true</code> if one or multiple lines are selected
+     * @since 2.0
+     */
+    protected bool areMultipleLinesSelected() {
+        Point s= getSelectedRange();
+        if (s.y is 0)
+            return false;
+
+        try {
+
+            IDocument document= getDocument();
+            int startLine= document.getLineOfOffset(s.x);
+            int endLine= document.getLineOfOffset(s.x + s.y);
+            IRegion line= document.getLineInformation(startLine);
+            return startLine !is endLine || (s.x is line.getOffset() && s.y is line.getLength());
+
+        } catch (BadLocationException x) {
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the index of the first line whose start offset is in the given text range.
+     *
+     * @param region the text range in characters where to find the line
+     * @return the first line whose start index is in the given range, -1 if there is no such line
+     */
+    private int getFirstCompleteLineOfRegion(IRegion region) {
+
+        try {
+
+            IDocument d= getDocument();
+
+            int startLine= d.getLineOfOffset(region.getOffset());
+
+            int offset= d.getLineOffset(startLine);
+            if (offset >= region.getOffset())
+                return startLine;
+
+            offset= d.getLineOffset(startLine + 1);
+            return (offset > region.getOffset() + region.getLength() ? -1 : startLine + 1);
+
+        } catch (BadLocationException x) {
+            if (TRACE_ERRORS)
+                System.out_.println(JFaceTextMessages.getString("TextViewer.error.bad_location.getFirstCompleteLineOfRegion")); //$NON-NLS-1$
+        }
+
+        return -1;
+    }
+
+
+    /**
+     * Creates a region describing the text block (something that starts at
+     * the beginning of a line) completely containing the current selection.
+     *
+     * @param selection the selection to use
+     * @return the region describing the text block comprising the given selection
+     * @since 2.0
+     */
+    private IRegion getTextBlockFromSelection(Point selection) {
+
+        try {
+            IDocument document= getDocument();
+            IRegion line= document.getLineInformationOfOffset(selection.x);
+            int length= selection.y is 0 ? line.getLength() : selection.y + (selection.x - line.getOffset());
+            return new Region(line.getOffset(), length);
+
+        } catch (BadLocationException x) {
+        }
+
+        return null;
+    }
+
+    /**
+     * Shifts a text block to the right or left using the specified set of prefix characters.
+     * The prefixes must start at the beginning of the line.
+     *
+     * @param useDefaultPrefixes says whether the configured default or indent prefixes should be used
+     * @param right says whether to shift to the right or the left
+     *
+     * @deprecated use shift(bool, bool, bool) instead
+     */
+    protected void shift(bool useDefaultPrefixes, bool right) {
+        shift(useDefaultPrefixes, right, false);
+    }
+
+    /**
+     * Shifts a text block to the right or left using the specified set of prefix characters.
+     * If white space should be ignored the prefix characters must not be at the beginning of
+     * the line when shifting to the left. There may be whitespace in front of the prefixes.
+     *
+     * @param useDefaultPrefixes says whether the configured default or indent prefixes should be used
+     * @param right says whether to shift to the right or the left
+     * @param ignoreWhitespace says whether whitespace in front of prefixes is allowed
+     * @since 2.0
+     */
+    protected void shift(bool useDefaultPrefixes, bool right, bool ignoreWhitespace) {
+        if (fUndoManager !is null)
+            fUndoManager.beginCompoundChange();
+
+        IDocument d= getDocument();
+        Map partitioners= null;
+        DocumentRewriteSession rewriteSession= null;
+        try {
+            Point selection= getSelectedRange();
+            IRegion block= getTextBlockFromSelection(selection);
+            ITypedRegion[] regions= TextUtilities.computePartitioning(d, getDocumentPartitioning(), block.getOffset(), block.getLength(), false);
+
+            int lineCount= 0;
+            int[] lines= new int[regions.length * 2]; // [start line, end line, start line, end line, ...]
+            for (int i= 0, j= 0; i < regions.length; i++, j+= 2) {
+                // start line of region
+                lines[j]= getFirstCompleteLineOfRegion(regions[i]);
+                // end line of region
+                int length= regions[i].getLength();
+                int offset= regions[i].getOffset() + length;
+                if (length > 0)
+                    offset--;
+                lines[j + 1]= (lines[j] is -1 ? -1 : d.getLineOfOffset(offset));
+                lineCount += lines[j + 1] - lines[j] + 1;
+            }
+
+            if ( cast(IDocumentExtension4)d ) {
+                IDocumentExtension4 extension= cast(IDocumentExtension4) d;
+                rewriteSession= extension.startRewriteSession(DocumentRewriteSessionType.SEQUENTIAL);
+            } else {
+                setRedraw(false);
+                startSequentialRewriteMode(true);
+            }
+            if (lineCount >= 20)
+                partitioners= TextUtilities.removeDocumentPartitioners(d);
+
+            // Perform the shift operation.
+            Map map= (useDefaultPrefixes ? fDefaultPrefixChars : fIndentChars);
+                for (int i= 0, j= 0; i < regions.length; i++, j += 2) {
+                String[] prefixes= stringArrayFromObject(selectContentTypePlugin(regions[i].getType(), map));
+                if (prefixes !is null && prefixes.length > 0 && lines[j] >= 0 && lines[j + 1] >= 0) {
+                    if (right)
+                        shiftRight(lines[j], lines[j + 1], prefixes[0]);
+                    else
+                        shiftLeft(lines[j], lines[j + 1], prefixes, ignoreWhitespace);
+                }
+            }
+
+        } catch (BadLocationException x) {
+            if (TRACE_ERRORS)
+                System.out_.println(JFaceTextMessages.getString("TextViewer.error.bad_location.shift_1")); //$NON-NLS-1$
+
+        } finally {
+
+            if (partitioners !is null)
+                TextUtilities.addDocumentPartitioners(d, partitioners);
+
+            if ( cast(IDocumentExtension4)d ) {
+                IDocumentExtension4 extension= cast(IDocumentExtension4) d;
+                extension.stopRewriteSession(rewriteSession);
+            } else {
+                stopSequentialRewriteMode();
+                setRedraw(true);
+            }
+
+            if (fUndoManager !is null)
+                fUndoManager.endCompoundChange();
+        }
+    }
+
+    /**
+     * Shifts the specified lines to the right inserting the given prefix
+     * at the beginning of each line
+     *
+     * @param prefix the prefix to be inserted
+     * @param startLine the first line to shift
+     * @param endLine the last line to shift
+     * @since 2.0
+     */
+    private void shiftRight(int startLine, int endLine, String prefix) {
+
+        try {
+
+            IDocument d= getDocument();
+            while (startLine <= endLine) {
+                d.replace(d.getLineOffset(startLine++), 0, prefix);
+            }
+
+        } catch (BadLocationException x) {
+            if (TRACE_ERRORS)
+                System.out_.println("TextViewer.shiftRight: BadLocationException"); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Shifts the specified lines to the right or to the left. On shifting to the right
+     * it insert <code>prefixes[0]</code> at the beginning of each line. On shifting to the
+     * left it tests whether each of the specified lines starts with one of the specified
+     * prefixes and if so, removes the prefix.
+     *
+     * @param startLine the first line to shift
+     * @param endLine the last line to shift
+     * @param prefixes the prefixes to be used for shifting
+     * @param ignoreWhitespace <code>true</code> if whitespace should be ignored, <code>false</code> otherwise
+     * @since 2.0
+     */
+    private void shiftLeft(int startLine, int endLine, String[] prefixes, bool ignoreWhitespace) {
+
+        IDocument d= getDocument();
+
+        try {
+
+            IRegion[] occurrences= new IRegion[endLine - startLine + 1];
+
+            // find all the first occurrences of prefix in the given lines
+            for (int i= 0; i < occurrences.length; i++) {
+
+                IRegion line= d.getLineInformation(startLine + i);
+                String text= d.get(line.getOffset(), line.getLength());
+
+                int index= -1;
+                int[] found= TextUtilities.indexOf(prefixes, text, 0);
+                if (found[0] !is -1) {
+                    if (ignoreWhitespace) {
+                        String s= d.get(line.getOffset(), found[0]);
+                        s= s.trim();
+                        if (s.length() is 0)
+                            index= line.getOffset() + found[0];
+                    } else if (found[0] is 0)
+                        index= line.getOffset();
+                }
+
+                if (index > -1) {
+                    // remember where prefix is in line, so that it can be removed
+                    int length= prefixes[found[1]].length();
+                    if (length is 0 && !ignoreWhitespace && line.getLength() > 0) {
+                        // found a non-empty line which cannot be shifted
+                        return;
+                    }
+                    occurrences[i]= new Region(index, length);
+                } else {
+                    // found a line which cannot be shifted
+                    return;
+                }
+            }
+
+            // OK - change the document
+            int decrement= 0;
+            for (int i= 0; i < occurrences.length; i++) {
+                IRegion r= occurrences[i];
+                d.replace(r.getOffset() - decrement, r.getLength(), ""); //$NON-NLS-1$
+                decrement += r.getLength();
+            }
+
+        } catch (BadLocationException x) {
+            if (TRACE_ERRORS)
+                System.out_.println("TextViewer.shiftLeft: BadLocationException"); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Returns whether the shown text can be printed.
+     *
+     * @return the viewer's printable mode
+     */
+    protected bool isPrintable() {
+        /*
+         * 1GK7Q10: ITPUI:WIN98 - internal error after invoking print at editor view
+         * Changed from returning true to testing the length of the printer queue
+         */
+        PrinterData[] printerList= Printer.getPrinterList();
+        return (printerList !is null && printerList.length > 0);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 3.4
+     */
+    public void print(StyledTextPrintOptions options) {
+        final PrintDialog dialog= new PrintDialog(fTextWidget.getShell(), SWT.PRIMARY_MODAL);
+        final PrinterData data= dialog.open();
+
+        if (data !is null) {
+            final Printer printer= new Printer(data);
+            final Runnable styledTextPrinter= fTextWidget.print(printer, options);
+
+            void threadrun() {
+                styledTextPrinter.run();
+                printer.dispose();
+            }
+            JThread printingThread= new JThread( &threadrun );
+            printingThread.setName( "Printing" ); //$NON-NLS-1$
+            printingThread.start();
+        }
+    }
+
+    /**
+     * Brings up a print dialog and calls <code>printContents(Printer)</code>
+     * which performs the actual print.
+     */
+    protected void print() {
+        StyledTextPrintOptions options= new StyledTextPrintOptions();
+        options.printTextFontStyle= true;
+        options.printTextForeground= true;
+        print(options);
+    }
+
+    //------ find support
+
+    /**
+     * Adheres to the contract of {@link IFindReplaceTarget#canPerformFind()}.
+     *
+     * @return <code>true</code> if find can be performed, <code>false</code> otherwise
+     */
+    protected bool canPerformFind() {
+        IDocument d= getVisibleDocument();
+        return (fTextWidget !is null && d !is null && d.getLength() > 0);
+    }
+
+    /**
+     * Adheres to the contract of {@link IFindReplaceTarget#findAndSelect(int, String, bool, bool, bool)}.
+     *
+     * @param startPosition the start position
+     * @param findString the find string specification
+     * @param forwardSearch the search direction
+     * @param caseSensitive <code>true</code> if case sensitive, <code>false</code> otherwise
+     * @param wholeWord <code>true</code> if match must be whole words, <code>false</code> otherwise
+     * @return the model offset of the first match
+     * @deprecated as of 3.0 use {@link #findAndSelect(int, String, bool, bool, bool, bool)}
+     */
+    protected int findAndSelect(int startPosition, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord) {
+        try {
+            return findAndSelect(startPosition, findString, forwardSearch, caseSensitive, wholeWord, false);
+        } catch (IllegalStateException ex) {
+            return -1;
+        } catch (PatternSyntaxException ex) {
+            return -1;
+        }
+    }
+
+    /**
+     * Adheres to the contract of
+     * {@link org.eclipse.jface.text.IFindReplaceTargetExtension3#findAndSelect(int, String, bool, bool, bool, bool)}.
+     *
+     * @param startPosition the start position
+     * @param findString the find string specification
+     * @param forwardSearch the search direction
+     * @param caseSensitive <code>true</code> if case sensitive, <code>false</code> otherwise
+     * @param wholeWord <code>true</code> if matches must be whole words, <code>false</code> otherwise
+     * @param regExSearch <code>true</code> if <code>findString</code> is a regular expression, <code>false</code> otherwise
+     * @return the model offset of the first match
+     *
+     */
+    protected int findAndSelect(int startPosition, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord, bool regExSearch) {
+        if (fTextWidget is null)
+            return -1;
+
+        try {
+
+            int widgetOffset= (startPosition is -1 ? startPosition : modelOffset2WidgetOffset(startPosition));
+            FindReplaceDocumentAdapter adapter= getFindReplaceDocumentAdapter();
+            IRegion matchRegion= adapter.find(widgetOffset, findString, forwardSearch, caseSensitive, wholeWord, regExSearch);
+            if (matchRegion !is null) {
+                int widgetPos= matchRegion.getOffset();
+                int length= matchRegion.getLength();
+
+                // Prevents setting of widget selection with line delimiters at beginning or end
+                char startChar= adapter.charAt(widgetPos);
+                char endChar= adapter.charAt(widgetPos+length-1);
+                bool borderHasLineDelimiter= startChar is '\n' || startChar is '\r' || endChar is '\n' || endChar is '\r';
+                bool redraws_= redraws();
+                if (borderHasLineDelimiter && redraws_)
+                    setRedraw(false);
+
+                if (redraws()) {
+                    fTextWidget.setSelectionRange(widgetPos, length);
+                    internalRevealRange(widgetPos, widgetPos + length);
+                    selectionChanged(widgetPos, length);
+                } else {
+                    setSelectedRange(widgetOffset2ModelOffset(widgetPos), length);
+                    if (redraws_)
+                        setRedraw(true);
+                }
+
+                return widgetOffset2ModelOffset(widgetPos);
+            }
+
+        } catch (BadLocationException x) {
+            if (TRACE_ERRORS)
+                System.out_.println(JFaceTextMessages.getString("TextViewer.error.bad_location.findAndSelect")); //$NON-NLS-1$
+        }
+
+        return -1;
+    }
+
+    /**
+     * Adheres to the contract of {@link org.eclipse.jface.text.IFindReplaceTargetExtension3#findAndSelect(int, String, bool, bool, bool, bool)}.
+     *
+     * @param startPosition the start position
+     * @param findString the find string specification
+     * @param forwardSearch the search direction
+     * @param caseSensitive <code>true</code> if case sensitive, <code>false</code> otherwise
+     * @param wholeWord <code>true</code> if matches must be whole words, <code>false</code> otherwise
+     * @param rangeOffset the search scope offset
+     * @param rangeLength the search scope length
+     * @param regExSearch <code>true</code> if <code>findString</code> is a regular expression, <code>false</code> otherwise
+     * @return the model offset of the first match
+     * @since 3.0
+     */
+    protected int findAndSelectInRange(int startPosition, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord, int rangeOffset, int rangeLength, bool regExSearch) {
+        if (fTextWidget is null)
+            return -1;
+
+        try {
+
+            int modelOffset;
+            if (forwardSearch && (startPosition is -1 || startPosition < rangeOffset)) {
+                modelOffset= rangeOffset;
+            } else if (!forwardSearch && (startPosition is -1 || startPosition > rangeOffset + rangeLength)) {
+                modelOffset= rangeOffset + rangeLength;
+            } else {
+                modelOffset= startPosition;
+            }
+
+            int widgetOffset= modelOffset2WidgetOffset(modelOffset);
+            if (widgetOffset is -1)
+                return -1;
+
+            FindReplaceDocumentAdapter adapter= getFindReplaceDocumentAdapter();
+            IRegion matchRegion= adapter.find(widgetOffset, findString, forwardSearch, caseSensitive, wholeWord, regExSearch);
+            int widgetPos= -1;
+            int length= 0;
+            if (matchRegion !is null) {
+                widgetPos= matchRegion.getOffset();
+                length= matchRegion.getLength();
+            }
+            int modelPos= widgetPos is -1 ? -1 : widgetOffset2ModelOffset(widgetPos);
+
+            if (widgetPos !is -1 && (modelPos < rangeOffset || modelPos + length > rangeOffset + rangeLength))
+                widgetPos= -1;
+
+            if (widgetPos > -1) {
+
+                // Prevents setting of widget selection with line delimiters at beginning or end
+                char startChar= adapter.charAt(widgetPos);
+                char endChar= adapter.charAt(widgetPos+length-1);
+                bool borderHasLineDelimiter= startChar is '\n' || startChar is '\r' || endChar is '\n' || endChar is '\r';
+                bool redraws_= redraws();
+                if (borderHasLineDelimiter && redraws_)
+                    setRedraw(false);
+
+                if (redraws()) {
+                    fTextWidget.setSelectionRange(widgetPos, length);
+                    internalRevealRange(widgetPos, widgetPos + length);
+                    selectionChanged(widgetPos, length);
+                } else {
+                    setSelectedRange(modelPos, length);
+                    if (redraws_)
+                        setRedraw(true);
+                }
+
+                return modelPos;
+            }
+
+
+        } catch (BadLocationException x) {
+            if (TRACE_ERRORS)
+                System.out_.println(JFaceTextMessages.getString("TextViewer.error.bad_location.findAndSelect")); //$NON-NLS-1$
+        }
+
+        return -1;
+    }
+
+    //---------- text presentation support
+
+    /*
+     * @see ITextViewer#setTextColor(Color)
+     */
+    public void setTextColor(Color color) {
+        if (color !is null)
+            setTextColor(color, 0, getDocument().getLength(), true);
+    }
+
+    /*
+     * @see ITextViewer#setTextColor(Color, start, length, bool)
+     */
+    public void setTextColor(Color color, int start, int length, bool controlRedraw) {
+        if (fTextWidget !is null) {
+
+            StyleRange s= new StyleRange();
+            s.foreground= color;
+            s.start= start;
+            s.length= length;
+
+            s= modelStyleRange2WidgetStyleRange(s);
+            if (s !is null) {
+                if (controlRedraw)
+                    fTextWidget.setRedraw(false);
+                try {
+                    fTextWidget.setStyleRange(s);
+                } finally {
+                    if (controlRedraw)
+                        fTextWidget.setRedraw(true);
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds the given presentation to the viewer's style information.
+     *
+     * @param presentation the presentation to be added
+     */
+    private void addPresentation(TextPresentation presentation) {
+
+        StyleRange range= presentation.getDefaultStyleRange();
+        if (range !is null) {
+
+            range= modelStyleRange2WidgetStyleRange(range);
+            if (range !is null)
+                fTextWidget.setStyleRange(range);
+
+            ArrayList ranges= new ArrayList(presentation.getDenumerableRanges());
+            Iterator e= presentation.getNonDefaultStyleRangeIterator();
+            while (e.hasNext()) {
+                range= cast(StyleRange) e.next();
+                range= modelStyleRange2WidgetStyleRange(range);
+                if (range !is null)
+                    ranges.add(range);
+            }
+
+            if (!ranges.isEmpty())
+                fTextWidget.replaceStyleRanges(0, 0, arraycast!(StyleRange)(ranges.toArray()));
+
+        } else {
+            IRegion region= modelRange2WidgetRange(presentation.getCoverage());
+            if (region is null)
+                return;
+
+            List list= new ArrayList(presentation.getDenumerableRanges());
+            Iterator e= presentation.getAllStyleRangeIterator();
+            while (e.hasNext()) {
+                range= cast(StyleRange) e.next();
+                range= modelStyleRange2WidgetStyleRange(range);
+                if (range !is null)
+                    list.add(range);
+            }
+
+            if (!list.isEmpty()) {
+                StyleRange[] ranges= new StyleRange[list.size()];
+                list.toArray(ranges);
+                fTextWidget.replaceStyleRanges(region.getOffset(), region.getLength(), ranges);
+            }
+        }
+    }
+
+    /**
+     * Applies the given presentation to the given text widget. Helper method.
+     *
+     * @param presentation the style information
+     * @since 2.1
+     */
+    private void applyTextPresentation(TextPresentation presentation) {
+
+        List list= new ArrayList(presentation.getDenumerableRanges());
+        Iterator e= presentation.getAllStyleRangeIterator();
+        while (e.hasNext()) {
+            StyleRange range= cast(StyleRange) e.next();
+            range= modelStyleRange2WidgetStyleRange(range);
+            if (range !is null)
+                list.add(range);
+        }
+
+        if (!list.isEmpty()) {
+            StyleRange[] ranges= new StyleRange[list.size()];
+            list.toArray(ranges);
+            fTextWidget.setStyleRanges(ranges);
+        }
+    }
+
+    /**
+     * Returns the visible region if it is not equal to the whole document.
+     * Otherwise returns <code>null</code>.
+     *
+     * @return the viewer's visible region if smaller than input document, otherwise <code>null</code>
+     */
+    protected IRegion _internalGetVisibleRegion() {
+
+        IDocument document= getVisibleDocument();
+        if ( cast(ChildDocument)document ) {
+            Position p= (cast(ChildDocument) document).getParentDocumentRange();
+            return new Region(p.getOffset(), p.getLength());
+        }
+
+        return null;
+    }
+
+    /*
+     * @see ITextViewer#changeTextPresentation(TextPresentation, bool)
+     */
+    public void changeTextPresentation(TextPresentation presentation, bool controlRedraw) {
+
+        if (presentation is null || !redraws())
+            return;
+
+        if (fTextWidget is null)
+            return;
+
+
+        /*
+         * Call registered text presentation listeners
+         * and let them apply their presentation.
+         */
+        if (fTextPresentationListeners !is null) {
+            ArrayList listeners= new ArrayList(fTextPresentationListeners);
+            for (int i= 0, size= listeners.size(); i < size; i++) {
+                ITextPresentationListener listener= cast(ITextPresentationListener)listeners.get(i);
+                listener.applyTextPresentation(presentation);
+            }
+        }
+
+        if (presentation.isEmpty())
+            return;
+
+        if (controlRedraw)
+            fTextWidget.setRedraw(false);
+
+        if (fReplaceTextPresentation)
+            applyTextPresentation(presentation);
+        else
+            addPresentation(presentation);
+
+        if (controlRedraw)
+            fTextWidget.setRedraw(true);
+    }
+
+    /*
+     * @see ITextViewer#getFindReplaceTarget()
+     */
+    public IFindReplaceTarget getFindReplaceTarget() {
+        if (fFindReplaceTarget is null)
+            fFindReplaceTarget= new FindReplaceTarget();
+        return fFindReplaceTarget;
+    }
+
+    /**
+     * Returns the find/replace document adapter.
+     *
+     * @return the find/replace document adapter.
+     * @since 3.0
+     */
+    protected FindReplaceDocumentAdapter getFindReplaceDocumentAdapter() {
+        if (fFindReplaceDocumentAdapter is null)
+            fFindReplaceDocumentAdapter= new FindReplaceDocumentAdapter(getVisibleDocument());
+        return fFindReplaceDocumentAdapter;
+    }
+
+    /*
+     * @see ITextViewer#getTextOperationTarget()
+     */
+    public ITextOperationTarget getTextOperationTarget() {
+        return this;
+    }
+
+    /*
+     * @see ITextViewerExtension#appendVerifyKeyListener(VerifyKeyListener)
+     * @since 2.0
+     */
+    public void appendVerifyKeyListener(VerifyKeyListener listener) {
+        int index= fVerifyKeyListenersManager.numberOfListeners();
+        fVerifyKeyListenersManager.insertListener(listener, index);
+    }
+
+    /*
+     * @see ITextViewerExtension#prependVerifyKeyListener(VerifyKeyListener)
+     * @since 2.0
+     */
+    public void prependVerifyKeyListener(VerifyKeyListener listener) {
+        fVerifyKeyListenersManager.insertListener(listener, 0);
+
+    }
+
+    /*
+     * @see ITextViewerExtension#removeVerifyKeyListener(VerifyKeyListener)
+     * @since 2.0
+     */
+    public void removeVerifyKeyListener(VerifyKeyListener listener) {
+        fVerifyKeyListenersManager.removeListener(listener);
+    }
+
+    /*
+     * @see ITextViewerExtension#getMark()
+     * @since 2.0
+     */
+    public int getMark() {
+        return fMarkPosition is null || fMarkPosition.isDeleted() ? -1 : fMarkPosition.getOffset();
+    }
+
+    /*
+     * @see ITextViewerExtension#setMark(int)
+     * @since 2.0
+     */
+    public void setMark(int offset) {
+
+        // clear
+        if (offset is -1) {
+            if (fMarkPosition !is null && !fMarkPosition.isDeleted()) {
+
+                IDocument document= getDocument();
+                if (document !is null)
+                    document.removePosition(fMarkPosition);
+            }
+
+            fMarkPosition= null;
+
+            markChanged(-1, 0);
+
+        // set
+        } else {
+
+            IDocument document= getDocument();
+            if (document is null) {
+                fMarkPosition= null;
+                return;
+            }
+
+            if (fMarkPosition !is null)
+                document.removePosition(fMarkPosition);
+
+            fMarkPosition= null;
+
+            try {
+
+                Position position= new Position(offset);
+                document.addPosition(MARK_POSITION_CATEGORY, position);
+                fMarkPosition= position;
+
+            } catch (BadLocationException e) {
+                return;
+            } catch (BadPositionCategoryException e) {
+                return;
+            }
+
+            markChanged(modelOffset2WidgetOffset(fMarkPosition.offset), 0);
+        }
+    }
+
+    /*
+     * @see Viewer#inputChanged(Object, Object)
+     * @since 2.0
+     */
+    protected void inputChanged(Object newInput, Object oldInput) {
+
+        IDocument oldDocument= cast(IDocument) oldInput;
+        if (oldDocument !is null) {
+
+            if (fMarkPosition !is null && !fMarkPosition.isDeleted())
+                oldDocument.removePosition(fMarkPosition);
+
+            try {
+                oldDocument.removePositionUpdater(fMarkPositionUpdater);
+                oldDocument.removePositionCategory(MARK_POSITION_CATEGORY);
+
+            } catch (BadPositionCategoryException e) {
+            }
+        }
+
+        fMarkPosition= null;
+
+        if ( cast(IDocumentExtension4)oldDocument ) {
+            IDocumentExtension4 document= cast(IDocumentExtension4) oldDocument;
+            document.removeDocumentRewriteSessionListener(fDocumentRewriteSessionListener);
+        }
+
+        super.inputChanged(newInput, oldInput);
+
+        if ( cast(IDocumentExtension4)newInput ) {
+            IDocumentExtension4 document= cast(IDocumentExtension4) newInput;
+            document.addDocumentRewriteSessionListener(fDocumentRewriteSessionListener);
+        }
+
+        IDocument newDocument= cast(IDocument) newInput;
+        if (newDocument !is null) {
+            newDocument.addPositionCategory(MARK_POSITION_CATEGORY);
+            newDocument.addPositionUpdater(fMarkPositionUpdater);
+        }
+    }
+
+    /**
+     * Informs all text listeners about the change of the viewer's redraw state.
+     * @since 2.0
+     */
+    private void fireRedrawChanged() {
+        fWidgetCommand.start= 0;
+        fWidgetCommand.length= 0;
+        fWidgetCommand.text= null;
+        fWidgetCommand.event= null;
+        updateTextListeners(fWidgetCommand);
+    }
+
+    /**
+     * Enables the redrawing of this text viewer.
+     * @since 2.0
+     */
+    protected void enabledRedrawing() {
+        enabledRedrawing(-1);
+    }
+    /**
+     * Enables the redrawing of this text viewer.
+     *
+     * @param topIndex the top index to be set or <code>-1</code>
+     * @since 3.0
+     */
+    protected void enabledRedrawing(int topIndex) {
+        if ( cast(IDocumentAdapterExtension)fDocumentAdapter ) {
+            IDocumentAdapterExtension extension= cast(IDocumentAdapterExtension) fDocumentAdapter;
+            StyledText textWidget= getTextWidget();
+            if (textWidget !is null && !textWidget.isDisposed()) {
+                extension.resumeForwardingDocumentChanges();
+                if (topIndex > -1) {
+                    try {
+                        setTopIndex(topIndex);
+                    } catch (IllegalArgumentException x) {
+                        // changes don't allow for the previous top pixel
+                    }
+                }
+            }
+        }
+
+        if (fViewerState !is null) {
+            fViewerState.restore(topIndex is -1);
+            fViewerState= null;
+        }
+
+        if (fTextWidget !is null && !fTextWidget.isDisposed())
+            fTextWidget.setRedraw(true);
+
+        fireRedrawChanged();
+    }
+
+    /**
+     * Disables the redrawing of this text viewer. Subclasses may extend.
+     * @since 2.0
+     */
+    protected void disableRedrawing() {
+        if (fViewerState is null)
+            fViewerState= new ViewerState();
+
+        if ( cast(IDocumentAdapterExtension)fDocumentAdapter ) {
+            IDocumentAdapterExtension extension= cast(IDocumentAdapterExtension) fDocumentAdapter;
+            extension.stopForwardingDocumentChanges();
+        }
+
+        if (fTextWidget !is null && !fTextWidget.isDisposed())
+            fTextWidget.setRedraw(false);
+
+        fireRedrawChanged();
+    }
+
+    /*
+     * @see ITextViewerExtension#setRedraw(bool)
+     * @since 2.0
+     */
+    public final void setRedraw(bool redraw) {
+        setRedraw(redraw, -1);
+    }
+
+    /**
+     * Basically same functionality as
+     * <code>ITextViewerExtension.setRedraw(bool)</code>. Adds a way for
+     * subclasses to pass in a desired top index that should be used when
+     * <code>redraw</code> is <code>true</code>. If <code>topIndex</code>
+     * is -1, this method is identical to
+     * <code>ITextViewerExtension.setRedraw(bool)</code>.
+     *
+     * @see ITextViewerExtension#setRedraw(bool)
+     *
+     * @param redraw
+     * @param topIndex
+     * @since 3.0
+     */
+    protected final void setRedraw(bool redraw, int topIndex) {
+        if (!redraw) {
+
+            ++ fRedrawCounter;
+            if (fRedrawCounter is 1)
+                disableRedrawing();
+
+        } else {
+            -- fRedrawCounter;
+            if (fRedrawCounter is 0) {
+                if (topIndex is -1)
+                    enabledRedrawing();
+                else
+                    enabledRedrawing(topIndex);
+            }
+        }
+    }
+
+    /**
+     * Returns whether this viewer redraws itself.
+     *
+     * @return <code>true</code> if this viewer redraws itself
+     * @since 2.0
+     */
+    protected final bool redraws() {
+        return fRedrawCounter <= 0;
+    }
+
+    /**
+     * Starts  the sequential rewrite mode of the viewer's document.
+     *
+     * @param normalized <code>true</code> if the rewrite is performed from the start to the end of the document
+     * @since 2.0
+     * @deprecated since 3.1 use {@link IDocumentExtension4#startRewriteSession(DocumentRewriteSessionType)} instead
+     */
+    protected final void startSequentialRewriteMode(bool normalized) {
+        IDocument document= getDocument();
+        if ( cast(IDocumentExtension)document ) {
+            IDocumentExtension extension= cast(IDocumentExtension) document;
+            extension.startSequentialRewrite(normalized);
+        }
+    }
+
+    /**
+     * Sets the sequential rewrite mode of the viewer's document.
+     *
+     * @since 2.0
+     * @deprecated since 3.1 use {@link IDocumentExtension4#stopRewriteSession(DocumentRewriteSession)} instead
+     */
+    protected final void stopSequentialRewriteMode() {
+        IDocument document= getDocument();
+        if ( cast(IDocumentExtension)document ) {
+            IDocumentExtension extension= cast(IDocumentExtension) document;
+            extension.stopSequentialRewrite();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextViewerExtension#getRewriteTarget()
+     * @since 2.0
+     */
+    public IRewriteTarget getRewriteTarget() {
+        if (fRewriteTarget is null)
+            fRewriteTarget= new RewriteTarget();
+        return fRewriteTarget;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextViewerExtension2#getCurrentTextHover()
+     */
+    public ITextHover getCurrentTextHover() {
+        if (fTextHoverManager is null)
+            return null;
+        return fTextHoverManager.getCurrentTextHover_package();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextViewerExtension2#getHoverEventLocation()
+     */
+    public Point getHoverEventLocation() {
+        if (fTextHoverManager is null)
+            return null;
+        return fTextHoverManager.getHoverEventLocation_package();
+    }
+
+    /**
+     * Returns the paint manager of this viewer.
+     *
+     * @return the paint manager of this viewer
+     * @since 2.1
+     */
+    protected PaintManager getPaintManager() {
+        if (fPaintManager is null)
+            fPaintManager= new PaintManager(this);
+        return fPaintManager;
+    }
+
+    /**
+     * Adds the given  painter to this viewer. If the painter is already registered
+     * this method is without effect.
+     *
+     * @param painter the painter to be added
+     * @since 2.1
+     */
+    public void addPainter(IPainter painter) {
+        getPaintManager().addPainter(painter);
+    }
+
+    /**
+     * Removes the given painter from this viewer. If the painter has previously not been
+     * added to this viewer this method is without effect.
+     *
+     * @param painter the painter to be removed
+     * @since 2.1
+     */
+    public void removePainter(IPainter painter) {
+        getPaintManager().removePainter(painter);
+    }
+
+    // ----------------------------------- conversions -------------------------------------------------------
+
+    /**
+     * Implements the contract of {@link ITextViewerExtension5#modelLine2WidgetLine(int)}.
+     *
+     * @param modelLine the model line
+     * @return the corresponding widget line or <code>-1</code>
+     * @since 2.1
+     */
+    public int modelLine2WidgetLine(int modelLine) {
+        if (fInformationMapping is null)
+            return modelLine;
+
+        try {
+            return fInformationMapping.toImageLine(modelLine);
+        } catch (BadLocationException x) {
+    }
+
+        return -1;
+    }
+
+    /**
+     * Implements the contract of {@link ITextViewerExtension5#modelOffset2WidgetOffset(int)}.
+     *
+     * @param modelOffset the model offset
+     * @return the corresponding widget offset or <code>-1</code>
+     * @since 2.1
+     */
+    public int modelOffset2WidgetOffset(int modelOffset) {
+        if (fInformationMapping is null)
+            return modelOffset;
+
+        try {
+            return fInformationMapping.toImageOffset(modelOffset);
+        } catch (BadLocationException x) {
+        }
+
+        return -1;
+    }
+
+    /**
+     * Implements the contract of {@link ITextViewerExtension5#modelRange2WidgetRange(IRegion)}.
+     *
+     * @param modelRange the model range
+     * @return the corresponding widget range or <code>null</code>
+     * @since 2.1
+     */
+    public IRegion modelRange2WidgetRange(IRegion modelRange) {
+        if (fInformationMapping is null)
+            return modelRange;
+
+        try {
+
+            if (modelRange.getLength() < 0) {
+                Region reversed= new Region(modelRange.getOffset() + modelRange.getLength(), -modelRange.getLength());
+                IRegion result= fInformationMapping.toImageRegion(reversed);
+                if (result !is null)
+                    return new Region(result.getOffset() + result.getLength(), -result.getLength());
+            }
+            return fInformationMapping.toImageRegion(modelRange);
+
+        } catch (BadLocationException x) {
+        }
+
+        return null;
+    }
+
+    /**
+     * Similar to {@link #modelRange2WidgetRange(IRegion)}, but more forgiving:
+     * if <code>modelRange</code> describes a region entirely hidden in the
+     * image, then this method returns the zero-length region at the offset of
+     * the folded region.
+     *
+     * @param modelRange the model range
+     * @return the corresponding widget range, or <code>null</code>
+     * @since 3.1
+     */
+    protected IRegion modelRange2ClosestWidgetRange(IRegion modelRange) {
+        if (!( cast(IDocumentInformationMappingExtension2)fInformationMapping ))
+            return modelRange2WidgetRange(modelRange);
+
+        try {
+            if (modelRange.getLength() < 0) {
+                Region reversed= new Region(modelRange.getOffset() + modelRange.getLength(), -modelRange.getLength());
+                IRegion result= (cast(IDocumentInformationMappingExtension2) fInformationMapping).toClosestImageRegion(reversed);
+                if (result !is null)
+                    return new Region(result.getOffset() + result.getLength(), -result.getLength());
+            }
+            return (cast(IDocumentInformationMappingExtension2) fInformationMapping).toClosestImageRegion(modelRange);
+
+        } catch (BadLocationException x) {
+        }
+
+        return null;
+    }
+
+    /**
+     * Implements the contract of {@link ITextViewerExtension5#widgetLine2ModelLine(int)}.
+     *
+     * @param widgetLine the widget line
+     * @return the corresponding model line
+     * @since 2.1
+     */
+    public int widgetlLine2ModelLine(int widgetLine) {
+        return widgetLine2ModelLine(widgetLine);
+    }
+
+    /**
+     * Implements the contract of {@link ITextViewerExtension5#widgetLine2ModelLine(int)}.
+     *
+     * @param widgetLine the widget line
+     * @return the corresponding model line or <code>-1</code>
+     * @since 3.0
+     */
+    public int widgetLine2ModelLine(int widgetLine) {
+        if (fInformationMapping is null)
+            return widgetLine;
+
+        try {
+            return fInformationMapping.toOriginLine(widgetLine);
+        } catch (BadLocationException x) {
+        }
+
+        return -1;
+    }
+
+    /**
+     * Implements the contract of {@link ITextViewerExtension5#widgetOffset2ModelOffset(int)}.
+     *
+     * @param widgetOffset the widget offset
+     * @return the corresponding model offset or <code>-1</code>
+     * @since 2.1
+     */
+    public int widgetOffset2ModelOffset(int widgetOffset) {
+        if (fInformationMapping is null)
+            return widgetOffset;
+
+        try {
+            return fInformationMapping.toOriginOffset(widgetOffset);
+        } catch (BadLocationException x) {
+            if (widgetOffset is getVisibleDocument().getLength()) {
+                IRegion coverage= fInformationMapping.getCoverage();
+                return coverage.getOffset() + coverage.getLength();
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Implements the contract of {@link ITextViewerExtension5#widgetRange2ModelRange(IRegion)}.
+     *
+     * @param widgetRange the widget range
+     * @return the corresponding model range or <code>null</code>
+     * @since 2.1
+     */
+    public IRegion widgetRange2ModelRange(IRegion widgetRange) {
+        if (fInformationMapping is null)
+            return widgetRange;
+
+        try {
+
+            if (widgetRange.getLength() < 0) {
+                Region reveresed= new Region(widgetRange.getOffset() + widgetRange.getLength(), -widgetRange.getLength());
+                IRegion result= fInformationMapping.toOriginRegion(reveresed);
+                return new Region(result.getOffset() + result.getLength(), -result.getLength());
+            }
+
+            return fInformationMapping.toOriginRegion(widgetRange);
+
+        } catch (BadLocationException x) {
+            int modelOffset= widgetOffset2ModelOffset(widgetRange.getOffset());
+            if (modelOffset > -1) {
+                int modelEndOffset= widgetOffset2ModelOffset(widgetRange.getOffset() + widgetRange.getLength());
+                if (modelEndOffset > -1)
+                    return new Region(modelOffset, modelEndOffset - modelOffset);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Implements the contract of {@link ITextViewerExtension5#getModelCoverage()}.
+     *
+     * @return the model coverage
+     * @since 2.1
+     */
+    public IRegion getModelCoverage() {
+        if (fInformationMapping is null) {
+            IDocument document= getDocument();
+            if (document is null)
+                return null;
+            return new Region(0, document.getLength());
+        }
+
+        return fInformationMapping.getCoverage();
+    }
+
+    /**
+     * Returns the line of the widget whose corresponding line in the viewer's document
+     * is closest to the given line in the viewer's document or <code>-1</code>.
+     *
+     * @param modelLine the line in the viewer's document
+     * @return the line in the widget that corresponds best to the given line in the viewer's document or <code>-1</code>
+     * @since 2.1
+     */
+    protected int getClosestWidgetLineForModelLine(int modelLine) {
+        if (fInformationMapping is null)
+            return modelLine;
+
+        try {
+            return fInformationMapping.toClosestImageLine(modelLine);
+        } catch (BadLocationException x) {
+        }
+
+        return -1;
+    }
+
+    /**
+     * Translates a style range given relative to the viewer's document into style
+     * ranges relative to the viewer's widget or <code>null</code>.
+     *
+     * @param range the style range in the coordinates of the viewer's document
+     * @return the style range in the coordinates of the viewer's widget or <code>null</code>
+     * @since 2.1
+     */
+    protected StyleRange modelStyleRange2WidgetStyleRange(StyleRange range) {
+        IRegion region= modelRange2WidgetRange(new Region(range.start, range.length));
+        if (region !is null) {
+            StyleRange result= cast(StyleRange) range.clone();
+            result.start= region.getOffset();
+            result.length= region.getLength();
+            return result;
+        }
+        return null;
+    }
+
+    /**
+     * Same as {@link #modelRange2WidgetRange(IRegion)} just for a {@link org.eclipse.jface.text.Position}.
+     *
+     * @param modelPosition the position describing a range in the viewer's document
+     * @return a region describing a range in the viewer's widget
+     * @since 2.1
+     */
+    protected IRegion modelRange2WidgetRange(Position modelPosition) {
+        return modelRange2WidgetRange(new Region(modelPosition.getOffset(), modelPosition.getLength()));
+    }
+
+    /**
+     * Translates the widget region of the given verify event into
+     * the corresponding region of the viewer's document.
+     *
+     * @param event the verify event
+     * @return the region of the viewer's document corresponding to the verify event
+     * @since 2.1
+     */
+    protected IRegion event2ModelRange(VerifyEvent event) {
+
+        Region region= null;
+        if (event.start <= event.end)
+            region= new Region(event.start, event.end - event.start);
+        else
+            region= new Region(event.end, event.start - event.end);
+
+        return widgetRange2ModelRange(region);
+    }
+
+    /**
+     * Translates the given widget selection into the corresponding region
+     * of the viewer's document or returns <code>null</code> if this fails.
+     *
+     * @param widgetSelection the widget selection
+     * @return the region of the viewer's document corresponding to the widget selection or <code>null</code>
+     * @since 2.1
+     */
+    protected Point widgetSelection2ModelSelection(Point widgetSelection) {
+        IRegion region= new Region(widgetSelection.x, widgetSelection.y);
+        region= widgetRange2ModelRange(region);
+        return region is null ? null : new Point(region.getOffset(), region.getLength());
+    }
+
+    /**
+     * Translates the given selection range of the viewer's document into
+     * the corresponding widget range or returns <code>null</code> of this fails.
+     *
+     * @param modelSelection the selection range of the viewer's document
+     * @return the widget range corresponding to the selection range or <code>null</code>
+     * @since 2.1
+     */
+    protected Point modelSelection2WidgetSelection(Point modelSelection) {
+        if (fInformationMapping is null)
+            return modelSelection;
+
+        try {
+            IRegion region= new Region(modelSelection.x, modelSelection.y);
+            region= fInformationMapping.toImageRegion(region);
+            if (region !is null)
+                return new Point(region.getOffset(), region.getLength());
+        } catch (BadLocationException x) {
+        }
+
+        return null;
+    }
+
+    /**
+     * Implements the contract of {@link ITextViewerExtension5#widgetLineOfWidgetOffset(int)}.
+     *
+     * @param widgetOffset the widget offset
+     * @return  the corresponding widget line or <code>-1</code>
+     * @since 2.1
+     */
+    public int widgetLineOfWidgetOffset(int widgetOffset) {
+        IDocument document= getVisibleDocument();
+        if (document !is null) {
+            try {
+                return document.getLineOfOffset(widgetOffset);
+            } catch (BadLocationException e) {
+            }
+        }
+        return -1;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextViewerExtension4#moveFocusToWidgetToken()
+     * @since 3.0
+     */
+    public bool moveFocusToWidgetToken() {
+        if ( cast(IWidgetTokenKeeperExtension)fWidgetTokenKeeper ) {
+            IWidgetTokenKeeperExtension extension= cast(IWidgetTokenKeeperExtension) fWidgetTokenKeeper;
+            return extension.setFocus(this);
+        }
+        return false;
+    }
+
+    /**
+     * Sets the document partitioning of this viewer. The partitioning is used by this viewer to
+     * access partitioning information of the viewers input document.
+     *
+     * @param partitioning the partitioning name
+     * @since 3.0
+     */
+    public void setDocumentPartitioning(String partitioning) {
+        fPartitioning= partitioning;
+    }
+
+    /**
+     * Returns the document partitioning for this viewer.
+     *
+     * @return the document partitioning for this viewer
+     * @since 3.0
+     */
+    protected String getDocumentPartitioning() {
+        return fPartitioning;
+    }
+
+    //---- Text presentation listeners ----
+
+    /*
+     * @see ITextViewerExtension4#addTextPresentationListener(ITextPresentationListener)
+     * @since 3.0
+     */
+    public void addTextPresentationListener(ITextPresentationListener listener) {
+
+        Assert.isNotNull(cast(Object)listener);
+
+        if (fTextPresentationListeners is null)
+            fTextPresentationListeners= new ArrayList();
+
+        if (!fTextPresentationListeners.contains(cast(Object)listener))
+            fTextPresentationListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * @see ITextViewerExtension4#removeTextPresentationListener(ITextPresentationListener)
+     * @since 3.0
+     */
+    public void removeTextPresentationListener(ITextPresentationListener listener) {
+
+        Assert.isNotNull(cast(Object)listener);
+
+        if (fTextPresentationListeners !is null) {
+            fTextPresentationListeners.remove(cast(Object)listener);
+            if (fTextPresentationListeners.size() is 0)
+                fTextPresentationListeners= null;
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IEditingSupportRegistry#registerHelper(org.eclipse.jface.text.IEditingSupport)
+     * @since 3.1
+     */
+    public void register(IEditingSupport helper) {
+        Assert.isLegal(helper !is null);
+        fEditorHelpers.add(cast(Object)helper);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IEditingSupportRegistry#deregisterHelper(org.eclipse.jface.text.IEditingSupport)
+     * @since 3.1
+     */
+    public void unregister(IEditingSupport helper) {
+        fEditorHelpers.remove(cast(Object)helper);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IEditingSupportRegistry#getCurrentHelpers()
+     * @since 3.1
+     */
+    public IEditingSupport[] getRegisteredSupports() {
+        return arraycast!(IEditingSupport)( fEditorHelpers.toArray());
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextViewerExtension6#setHyperlinkDetectors(org.eclipse.jface.text.hyperlink.IHyperlinkDetector[], int)
+     * @since 3.1
+     */
+    public void setHyperlinkDetectors(IHyperlinkDetector[] hyperlinkDetectors, int eventStateMask) {
+        if (fHyperlinkDetectors !is null) {
+            for (int i= 0; i < fHyperlinkDetectors.length; i++) {
+                if (cast(IHyperlinkDetectorExtension)fHyperlinkDetectors[i] )
+                    (cast(IHyperlinkDetectorExtension)fHyperlinkDetectors[i]).dispose();
+            }
+        }
+
+        bool enable= hyperlinkDetectors !is null && hyperlinkDetectors.length > 0;
+        fHyperlinkStateMask= eventStateMask;
+        fHyperlinkDetectors= hyperlinkDetectors;
+        if (enable) {
+            if (fHyperlinkManager !is null) {
+                fHyperlinkManager.setHyperlinkDetectors(fHyperlinkDetectors);
+                fHyperlinkManager.setHyperlinkStateMask(fHyperlinkStateMask);
+            }
+            ensureHyperlinkManagerInstalled();
+        } else {
+            if (fHyperlinkManager !is null)
+                fHyperlinkManager.uninstall();
+            fHyperlinkManager= null;
+        }
+    }
+
+    /**
+     * Sets the hyperlink presenter.
+     * <p>
+     * This is only valid as long as the hyperlink manager hasn't
+     * been created yet.
+     * </p>
+     *
+     * @param hyperlinkPresenter the hyperlink presenter
+     * @throws IllegalStateException if the hyperlink manager has already been created
+     * @since 3.1
+     */
+    public void setHyperlinkPresenter(IHyperlinkPresenter hyperlinkPresenter)  {
+        if (fHyperlinkManager !is null)
+            throw new IllegalStateException();
+
+        fHyperlinkPresenter= hyperlinkPresenter;
+        ensureHyperlinkManagerInstalled();
+    }
+
+    /**
+     * Ensures that the hyperlink manager has been
+     * installed if a hyperlink detector is available.
+     *
+     * @since 3.1
+     */
+    private void ensureHyperlinkManagerInstalled() {
+        if (fHyperlinkDetectors !is null && fHyperlinkDetectors.length > 0 && fHyperlinkPresenter !is null && fHyperlinkManager is null) {
+            HyperlinkManager.DETECTION_STRATEGY strategy= fHyperlinkPresenter.canShowMultipleHyperlinks() ? HyperlinkManager.ALL : HyperlinkManager.FIRST;
+            fHyperlinkManager= new HyperlinkManager(strategy);
+            fHyperlinkManager.install(this, fHyperlinkPresenter, fHyperlinkDetectors, fHyperlinkStateMask);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextViewerExtension7#setTabsToSpacesConverter(org.eclipse.jface.text.IAutoEditStrategy)
+     * @since 3.3
+     */
+    public void setTabsToSpacesConverter(IAutoEditStrategy converter) {
+        fTabsToSpacesConverter= converter;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewerHoverManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,541 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.TextViewerHoverManager;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+import java.lang.JThread;
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.runtime.ILog;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+
+
+/**
+ * This manager controls the layout, content, and visibility of an information
+ * control in reaction to mouse hover events issued by the text widget of a
+ * text viewer. It overrides <code>computeInformation</code>, so that the
+ * computation is performed in a dedicated background thread. This implies
+ * that the used <code>ITextHover</code> objects must be capable of
+ * operating in a non-UI thread.
+ *
+ * @since 2.0
+ */
+class TextViewerHoverManager : AbstractHoverInformationControlManager , IWidgetTokenKeeper, IWidgetTokenKeeperExtension {
+
+
+    /**
+     * Priority of the hovers managed by this manager.
+     * Default value: <code>0</code>;
+     * @since 3.0
+     */
+    public const static int WIDGET_PRIORITY= 0;
+
+
+    /** The text viewer */
+    private TextViewer fTextViewer;
+    /** The hover information computation thread */
+    private JThread fThread;
+    /** The stopper of the computation thread */
+    private ITextListener fStopper;
+    /** Internal monitor */
+    private Object fMutex;
+    /** The currently shown text hover. */
+    private /+volatile+/ ITextHover fTextHover;
+    /**
+     * Tells whether the next mouse hover event
+     * should be processed.
+     * @since 3.0
+     */
+    private bool fProcessMouseHoverEvent= true;
+    /**
+     * Internal mouse move listener.
+     * @since 3.0
+     */
+    private MouseMoveListener fMouseMoveListener;
+    /**
+     * Internal view port listener.
+     * @since 3.0
+     */
+    private IViewportListener fViewportListener;
+
+
+    /**
+     * Creates a new text viewer hover manager specific for the given text viewer.
+     * The manager uses the given information control creator.
+     *
+     * @param textViewer the viewer for which the controller is created
+     * @param creator the information control creator
+     */
+    public this(TextViewer textViewer, IInformationControlCreator creator) {
+        fMutex= new Object();
+        super(creator);
+        fTextViewer= textViewer;
+        fStopper= new class() ITextListener {
+            public void textChanged(TextEvent event) {
+                synchronized (fMutex) {
+                    if (fThread !is null) {
+implMissing(__FILE__,__LINE__);
+// SWT FIXME: how to handle Thread.interrupt?
+//                         fThread.interrupt();
+                        fThread= null;
+                    }
+                }
+            }
+        };
+        fViewportListener= new class()  IViewportListener {
+            /*
+             * @see org.eclipse.jface.text.IViewportListener#viewportChanged(int)
+             */
+            public void viewportChanged(int verticalOffset) {
+                fProcessMouseHoverEvent= false;
+            }
+        };
+        fTextViewer.addViewportListener(fViewportListener);
+        fMouseMoveListener= new class()  MouseMoveListener {
+            /*
+             * @see MouseMoveListener#mouseMove(MouseEvent)
+             */
+            public void mouseMove(MouseEvent event) {
+                fProcessMouseHoverEvent= true;
+            }
+        };
+        fTextViewer.getTextWidget().addMouseMoveListener(fMouseMoveListener);
+    }
+
+    /**
+     * Determines all necessary details and delegates the computation into
+     * a background thread.
+     */
+    protected void computeInformation() {
+
+        if (!fProcessMouseHoverEvent) {
+            setInformation(cast(Object)null, null);
+            return;
+        }
+
+        Point location= getHoverEventLocation();
+        int offset= computeOffsetAtLocation(location.x, location.y);
+        if (offset is -1) {
+            setInformation(cast(Object)null, null);
+            return;
+        }
+
+        ITextHover hover= fTextViewer.getTextHover_package(offset, getHoverEventStateMask());
+        if (hover is null) {
+            setInformation(cast(Object)null, null);
+            return;
+        }
+
+        IRegion region= hover.getHoverRegion(fTextViewer, offset);
+        if (region is null) {
+            setInformation(cast(Object)null, null);
+            return;
+        }
+
+        Rectangle area= JFaceTextUtil.computeArea(region, fTextViewer);
+        if (area is null || area.isEmpty()) {
+            setInformation(cast(Object)null, null);
+            return;
+        }
+
+        if (fThread !is null) {
+            setInformation(cast(Object)null, null);
+            return;
+        }
+        fThread= new JThread( dgRunnable( (ITextHover hover_, IRegion region_, Rectangle area_){
+            // http://bugs.eclipse.org/bugs/show_bug.cgi?id=17693
+            bool hasFinished= false;
+            try {
+                if (fThread !is null) {
+                    Object information;
+                    try {
+                        if ( cast(ITextHoverExtension2)hover_ )
+                            information= (cast(ITextHoverExtension2)hover_).getHoverInfo2(fTextViewer, region_);
+                        else
+                            information= stringcast(hover_.getHoverInfo(fTextViewer, region_));
+                    } catch (ArrayIndexOutOfBoundsException x) {
+                        /*
+                            * This code runs in a separate thread which can
+                            * lead to text offsets being out of bounds when
+                            * computing the hover info (see bug 32848).
+                            */
+                        information= null;
+                    }
+
+                    if ( cast(ITextHoverExtension)hover_ )
+                        setCustomInformationControlCreator((cast(ITextHoverExtension) hover_).getHoverControlCreator());
+                    else
+                        setCustomInformationControlCreator(null);
+
+                    setInformation(information, area_);
+                    if (information !is null)
+                        fTextHover= hover_;
+                } else {
+                    setInformation(cast(Object)null, null);
+                }
+                hasFinished= true;
+            } catch (RuntimeException ex) {
+                String PLUGIN_ID= "org.eclipse.jface.text"; //$NON-NLS-1$
+                ILog log= Platform.getLog(Platform.getBundle(PLUGIN_ID));
+                log.log(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK, "Unexpected runtime error while computing a text hover", ex)); //$NON-NLS-1$
+            } finally {
+                synchronized (fMutex) {
+                    if (fTextViewer !is null)
+                        fTextViewer.removeTextListener(fStopper);
+                    fThread= null;
+                    // https://bugs.eclipse.org/bugs/show_bug.cgi?id=44756
+                    if (!hasFinished)
+                        setInformation(cast(Object)null, null);
+                }
+            }
+        }, hover, region, area ) );
+
+        fThread.setName( "Text Viewer Hover Presenter" ); //$NON-NLS-1$
+
+        fThread.setDaemon(true);
+        fThread.setPriority(JThread.MIN_PRIORITY);
+        synchronized (fMutex) {
+            fTextViewer.addTextListener(fStopper);
+            fThread.start();
+        }
+    }
+
+    /**
+     * As computation is done in the background, this method is
+     * also called in the background thread. Delegates the control
+     * flow back into the UI thread, in order to allow displaying the
+     * information in the information control.
+     */
+    protected void presentInformation() {
+        if (fTextViewer is null)
+            return;
+
+        StyledText textWidget= fTextViewer.getTextWidget();
+        if (textWidget !is null && !textWidget.isDisposed()) {
+            Display display= textWidget.getDisplay();
+            if (display is null)
+                return;
+
+            display.asyncExec(new class()  Runnable {
+                public void run() {
+                    doPresentInformation();
+                }
+            });
+        }
+    }
+
+    /*
+     * @see AbstractInformationControlManager#presentInformation()
+     */
+    protected void doPresentInformation() {
+        super.presentInformation();
+    }
+
+    /**
+     * Computes the document offset underlying the given text widget coordinates.
+     * This method uses a linear search as it cannot make any assumption about
+     * how the document is actually presented in the widget. (Covers cases such
+     * as bidirectional text.)
+     *
+     * @param x the horizontal coordinate inside the text widget
+     * @param y the vertical coordinate inside the text widget
+     * @return the document offset corresponding to the given point
+     */
+    private int computeOffsetAtLocation(int x, int y) {
+
+        try {
+
+            StyledText styledText= fTextViewer.getTextWidget();
+            int widgetOffset= styledText.getOffsetAtLocation(new Point(x, y));
+            Point p= styledText.getLocationAtOffset(widgetOffset);
+            if (p.x > x)
+                widgetOffset--;
+
+            if ( cast(ITextViewerExtension5)fTextViewer ) {
+                ITextViewerExtension5 extension= cast(ITextViewerExtension5) fTextViewer;
+                return extension.widgetOffset2ModelOffset(widgetOffset);
+            }
+
+            return widgetOffset + fTextViewer._getVisibleRegionOffset_package();
+
+        } catch (IllegalArgumentException e) {
+            return -1;
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#showInformationControl(org.eclipse.swt.graphics.Rectangle)
+     */
+    protected void showInformationControl(Rectangle subjectArea) {
+        if (fTextViewer !is null && fTextViewer.requestWidgetToken(this, WIDGET_PRIORITY))
+            super.showInformationControl(subjectArea);
+        else
+            if (DEBUG)
+                System.out_.println("TextViewerHoverManager#showInformationControl(..) did not get widget token"); //$NON-NLS-1$
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#hideInformationControl()
+     */
+    protected void hideInformationControl() {
+        try {
+            fTextHover= null;
+            super.hideInformationControl();
+        } finally {
+            if (fTextViewer !is null)
+                fTextViewer.releaseWidgetToken(this);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#replaceInformationControl(bool)
+     * @since 3.4
+     */
+    void replaceInformationControl(bool takeFocus) {
+        if (fTextViewer !is null)
+            fTextViewer.releaseWidgetToken(this);
+        super.replaceInformationControl(takeFocus);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#handleInformationControlDisposed()
+     */
+    protected void handleInformationControlDisposed() {
+        try {
+            super.handleInformationControlDisposed();
+        } finally {
+            if (fTextViewer !is null)
+                fTextViewer.releaseWidgetToken(this);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenKeeper#requestWidgetToken(org.eclipse.jface.text.IWidgetTokenOwner)
+     */
+    public bool requestWidgetToken(IWidgetTokenOwner owner) {
+        fTextHover= null;
+        super.hideInformationControl();
+        return true;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(org.eclipse.jface.text.IWidgetTokenOwner, int)
+     * @since 3.0
+     */
+    public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) {
+        if (priority > WIDGET_PRIORITY) {
+            fTextHover= null;
+            super.hideInformationControl();
+            return true;
+        }
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#setFocus(org.eclipse.jface.text.IWidgetTokenOwner)
+     * @since 3.0
+     */
+    public bool setFocus(IWidgetTokenOwner owner) {
+        if (! hasInformationControlReplacer())
+            return false;
+
+        IInformationControl iControl= getCurrentInformationControl();
+        if (canReplace(iControl)) {
+            if (cancelReplacingDelay())
+                replaceInformationControl(true);
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the currently shown text hover or <code>null</code> if no text
+     * hover is shown.
+     *
+     * @return the currently shown text hover or <code>null</code>
+     */
+    protected ITextHover getCurrentTextHover() {
+        return fTextHover;
+    }
+    package ITextHover getCurrentTextHover_package() {
+        return getCurrentTextHover();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractHoverInformationControlManager#dispose()
+     * @since 3.0
+     */
+    public void dispose() {
+        if (fTextViewer !is null) {
+            fTextViewer.removeViewportListener(fViewportListener);
+            fViewportListener= null;
+
+            StyledText st= fTextViewer.getTextWidget();
+            if (st !is null && !st.isDisposed())
+                st.removeMouseMoveListener(fMouseMoveListener);
+            fMouseMoveListener= null;
+        }
+        super.dispose();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewerUndoManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,590 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.text.TextViewerUndoManager;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.operations.IUndoContext;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.text.undo.DocumentUndoEvent;
+import org.eclipse.text.undo.DocumentUndoManager;
+import org.eclipse.text.undo.DocumentUndoManagerRegistry;
+import org.eclipse.text.undo.IDocumentUndoListener;
+import org.eclipse.text.undo.IDocumentUndoManager;
+
+
+/**
+ * Implementation of {@link org.eclipse.jface.text.IUndoManager} using the shared
+ * document undo manager.
+ * <p>
+ * It registers with the connected text viewer as text input listener, and obtains
+ * its undo manager from the current document.  It also monitors mouse and keyboard
+ * activities in order to partition the stream of text changes into undo-able
+ * edit commands.
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ *
+ * @see ITextViewer
+ * @see ITextInputListener
+ * @see IDocumentUndoManager
+ * @see MouseListener
+ * @see KeyListener
+ * @see DocumentUndoManager
+ *
+ * @since 3.2
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class TextViewerUndoManager : IUndoManager, IUndoManagerExtension {
+
+
+    /**
+     * Internal listener to mouse and key events.
+     */
+    private class KeyAndMouseListener : MouseListener, KeyListener {
+
+        /*
+         * @see MouseListener#mouseDoubleClick
+         */
+        public void mouseDoubleClick(MouseEvent e) {
+        }
+
+        /*
+         * If the right mouse button is pressed, the current editing command is closed
+         * @see MouseListener#mouseDown
+         */
+        public void mouseDown(MouseEvent e) {
+            if (e.button is 1)
+                if (isConnected())
+                    fDocumentUndoManager.commit();
+        }
+
+        /*
+         * @see MouseListener#mouseUp
+         */
+        public void mouseUp(MouseEvent e) {
+        }
+
+        /*
+         * @see KeyListener#keyPressed
+         */
+        public void keyReleased(KeyEvent e) {
+        }
+
+        /*
+         * On cursor keys, the current editing command is closed
+         * @see KeyListener#keyPressed
+         */
+        public void keyPressed(KeyEvent e) {
+            switch (e.keyCode) {
+                case SWT.ARROW_UP:
+                case SWT.ARROW_DOWN:
+                case SWT.ARROW_LEFT:
+                case SWT.ARROW_RIGHT:
+                    if (isConnected()) {
+                        fDocumentUndoManager.commit();
+                    }
+                    break;
+                default:
+            }
+        }
+    }
+
+
+    /**
+     * Internal text input listener.
+     */
+    private class TextInputListener : ITextInputListener {
+
+        /*
+         * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
+         */
+        public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+            disconnectDocumentUndoManager();
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
+         */
+        public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
+            connectDocumentUndoManager(newInput);
+        }
+    }
+
+
+    /**
+     * Internal document undo listener.
+     */
+    private class DocumentUndoListener : IDocumentUndoListener {
+
+        /*
+         * @see org.eclipse.jface.text.IDocumentUndoListener#documentUndoNotification(DocumentUndoEvent)
+         */
+        public void documentUndoNotification(DocumentUndoEvent event ){
+            if (!isConnected()) return;
+
+            int eventType= event.getEventType();
+            if (((eventType & DocumentUndoEvent.ABOUT_TO_UNDO) !is 0) || ((eventType & DocumentUndoEvent.ABOUT_TO_REDO) !is 0))  {
+                if (event.isCompound()) {
+                    ITextViewerExtension extension= null;
+                    if ( cast(ITextViewerExtension)fTextViewer )
+                        extension= cast(ITextViewerExtension) fTextViewer;
+
+                    if (extension !is null)
+                        extension.setRedraw(false);
+                }
+                fTextViewer.getTextWidget().getDisplay().syncExec(new class()  Runnable {
+                    public void run() {
+                        if ( cast(TextViewer)fTextViewer )
+                            (cast(TextViewer)fTextViewer).ignoreAutoEditStrategies_package(true);
+                    }
+                });
+
+            } else if (((eventType & DocumentUndoEvent.UNDONE) !is 0) || ((eventType & DocumentUndoEvent.REDONE) !is 0))  {
+                fTextViewer.getTextWidget().getDisplay().syncExec(new class()  Runnable {
+                    public void run() {
+                        if ( cast(TextViewer)fTextViewer )
+                            (cast(TextViewer)fTextViewer).ignoreAutoEditStrategies_package(false);
+                    }
+                });
+                if (event.isCompound()) {
+                    ITextViewerExtension extension= null;
+                    if ( cast(ITextViewerExtension)fTextViewer )
+                        extension= cast(ITextViewerExtension) fTextViewer;
+
+                    if (extension !is null)
+                        extension.setRedraw(true);
+                }
+
+                // Reveal the change if this manager's viewer has the focus.
+                if (fTextViewer !is null) {
+                    StyledText widget= fTextViewer.getTextWidget();
+                    if (widget !is null && !widget.isDisposed() && (widget.isFocusControl()))// || fTextViewer.getTextWidget() is control))
+                        selectAndReveal(event.getOffset(), event.getText() is null ? 0 : event.getText().length());
+                }
+            }
+        }
+
+    }
+
+    /** The internal key and mouse event listener */
+    private KeyAndMouseListener fKeyAndMouseListener;
+    /** The internal text input listener */
+    private TextInputListener fTextInputListener;
+
+
+    /** The text viewer the undo manager is connected to */
+    private ITextViewer fTextViewer;
+
+    /** The undo level */
+    private int fUndoLevel;
+
+    /** The document undo manager that is active. */
+    private IDocumentUndoManager fDocumentUndoManager;
+
+    /** The document that is active. */
+    private IDocument fDocument;
+
+    /** The document undo listener */
+    private IDocumentUndoListener fDocumentUndoListener;
+
+    /**
+     * Creates a new undo manager who remembers the specified number of edit commands.
+     *
+     * @param undoLevel the length of this manager's history
+     */
+    public this(int undoLevel) {
+        fUndoLevel= undoLevel;
+    }
+
+    /**
+     * Returns whether this undo manager is connected to a text viewer.
+     *
+     * @return <code>true</code> if connected, <code>false</code> otherwise
+     */
+    private bool isConnected() {
+        return fTextViewer !is null && fDocumentUndoManager !is null;
+    }
+
+    /*
+     * @see IUndoManager#beginCompoundChange
+     */
+    public void beginCompoundChange() {
+        if (isConnected()) {
+            fDocumentUndoManager.beginCompoundChange();
+        }
+    }
+
+
+    /*
+     * @see IUndoManager#endCompoundChange
+     */
+    public void endCompoundChange() {
+        if (isConnected()) {
+            fDocumentUndoManager.endCompoundChange();
+        }
+    }
+
+    /**
+     * Registers all necessary listeners with the text viewer.
+     */
+    private void addListeners() {
+        StyledText text= fTextViewer.getTextWidget();
+        if (text !is null) {
+            fKeyAndMouseListener= new KeyAndMouseListener();
+            text.addMouseListener(fKeyAndMouseListener);
+            text.addKeyListener(fKeyAndMouseListener);
+            fTextInputListener= new TextInputListener();
+            fTextViewer.addTextInputListener(fTextInputListener);
+        }
+    }
+
+    /**
+     * Unregister all previously installed listeners from the text viewer.
+     */
+    private void removeListeners() {
+        StyledText text= fTextViewer.getTextWidget();
+        if (text !is null) {
+            if (fKeyAndMouseListener !is null) {
+                text.removeMouseListener(fKeyAndMouseListener);
+                text.removeKeyListener(fKeyAndMouseListener);
+                fKeyAndMouseListener= null;
+            }
+            if (fTextInputListener !is null) {
+                fTextViewer.removeTextInputListener(fTextInputListener);
+                fTextInputListener= null;
+            }
+        }
+    }
+
+    /**
+     * Shows the given exception in an error dialog.
+     *
+     * @param title the dialog title
+     * @param ex the exception
+     */
+    private void openErrorDialog(String title, Exception ex) {
+        Shell shell= null;
+        if (isConnected()) {
+            StyledText st= fTextViewer.getTextWidget();
+            if (st !is null && !st.isDisposed())
+                shell= st.getShell();
+        }
+        if (Display.getCurrent() !is null)
+            MessageDialog.openError(shell, title, ex.msg/+getLocalizedMessage()+/);
+        else {
+            Display display;
+            Shell finalShell= shell;
+            if (finalShell !is null)
+                display= finalShell.getDisplay();
+            else
+                display= Display.getDefault();
+            display.syncExec(dgRunnable((Shell finalShell_, String title_, Exception ex_ ) {
+                MessageDialog.openError(finalShell_, title_, ex_.msg/+getLocalizedMessage()+/);
+            },finalShell, title, ex ));
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#setMaximalUndoLevel(int)
+     */
+    public void setMaximalUndoLevel(int undoLevel) {
+        fUndoLevel= Math.max(0, undoLevel);
+        if (isConnected()) {
+            fDocumentUndoManager.setMaximalUndoLevel(fUndoLevel);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#connect(org.eclipse.jface.text.ITextViewer)
+     */
+    public void connect(ITextViewer textViewer) {
+        if (fTextViewer is null && textViewer !is null) {
+            fTextViewer= textViewer;
+            addListeners();
+        }
+        IDocument doc= fTextViewer.getDocument();
+        connectDocumentUndoManager(doc);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#disconnect()
+     */
+    public void disconnect() {
+        if (fTextViewer !is null) {
+            removeListeners();
+            fTextViewer= null;
+        }
+        disconnectDocumentUndoManager();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#reset()
+     */
+    public void reset() {
+        if (isConnected())
+            fDocumentUndoManager.reset();
+
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#redoable()
+     */
+    public bool redoable() {
+        if (isConnected())
+            return fDocumentUndoManager.redoable();
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#undoable()
+     */
+    public bool undoable() {
+        if (isConnected())
+            return fDocumentUndoManager.undoable();
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#redo()
+     */
+    public void redo() {
+        if (isConnected()) {
+            try {
+                fDocumentUndoManager.redo();
+            } catch (ExecutionException ex) {
+                openErrorDialog(JFaceTextMessages.getString("DefaultUndoManager.error.redoFailed.title"), ex); //$NON-NLS-1$
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManager#undo()
+     */
+    public void undo() {
+        if (isConnected()) {
+            try {
+                fDocumentUndoManager.undo();
+            } catch (ExecutionException ex) {
+                openErrorDialog(JFaceTextMessages.getString("DefaultUndoManager.error.undoFailed.title"), ex); //$NON-NLS-1$
+            }
+        }
+    }
+
+    /**
+     * Selects and reveals the specified range.
+     *
+     * @param offset the offset of the range
+     * @param length the length of the range
+     */
+    private void selectAndReveal(int offset, int length) {
+        if ( cast(ITextViewerExtension5)fTextViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) fTextViewer;
+            extension.exposeModelRange(new Region(offset, length));
+        } else if (!fTextViewer.overlapsWithVisibleRegion(offset, length))
+            fTextViewer.resetVisibleRegion();
+
+        fTextViewer.setSelectedRange(offset, length);
+        fTextViewer.revealRange(offset, length);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IUndoManagerExtension#getUndoContext()
+     */
+    public IUndoContext getUndoContext() {
+        if (isConnected()) {
+            return fDocumentUndoManager.getUndoContext();
+        }
+        return null;
+    }
+
+    private void connectDocumentUndoManager(IDocument document) {
+        disconnectDocumentUndoManager();
+        if (document !is null) {
+            fDocument= document;
+            DocumentUndoManagerRegistry.connect(fDocument);
+            fDocumentUndoManager= DocumentUndoManagerRegistry.getDocumentUndoManager(fDocument);
+            fDocumentUndoManager.connect(this);
+            setMaximalUndoLevel(fUndoLevel);
+            fDocumentUndoListener= new DocumentUndoListener();
+            fDocumentUndoManager.addDocumentUndoListener(fDocumentUndoListener);
+        }
+    }
+
+    private void disconnectDocumentUndoManager() {
+        if (fDocumentUndoManager !is null) {
+            fDocumentUndoManager.disconnect(this);
+            DocumentUndoManagerRegistry.disconnect(fDocument);
+            fDocumentUndoManager.removeDocumentUndoListener(fDocumentUndoListener);
+            fDocumentUndoListener= null;
+            fDocumentUndoManager= null;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/WhitespaceCharacterPainter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,523 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Anton Leherbauer (Wind River Systems) - initial API and implementation - https://bugs.eclipse.org/bugs/show_bug.cgi?id=22712
+ *     Anton Leherbauer (Wind River Systems) - [painting] Long lines take too long to display when "Show Whitespace Characters" is enabled - https://bugs.eclipse.org/bugs/show_bug.cgi?id=196116
+ *     Anton Leherbauer (Wind River Systems) - [painting] Whitespace characters not drawn when scrolling to right slowly - https://bugs.eclipse.org/bugs/show_bug.cgi?id=206633
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.WhitespaceCharacterPainter;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.custom.StyledTextContent;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+
+
+/**
+ * A painter for drawing visible characters for (invisible) whitespace
+ * characters.
+ *
+ * @since 3.3
+ */
+public class WhitespaceCharacterPainter : IPainter, PaintListener {
+
+    private static const char SPACE_SIGN= '\u00b7';
+    private static const char IDEOGRAPHIC_SPACE_SIGN= '\u00b0';
+    private static const char TAB_SIGN= '\u00bb';
+    private static const char CARRIAGE_RETURN_SIGN= '\u00a4';
+    private static const char LINE_FEED_SIGN= '\u00b6';
+
+    /** Indicates whether this painter is active. */
+    private bool fIsActive= false;
+    /** The source viewer this painter is attached to. */
+    private ITextViewer fTextViewer;
+    /** The viewer's widget. */
+    private StyledText fTextWidget;
+    /** Tells whether the advanced graphics sub system is available. */
+    private bool fIsAdvancedGraphicsPresent;
+
+    /**
+     * Creates a new painter for the given text viewer.
+     *
+     * @param textViewer  the text viewer the painter should be attached to
+     */
+    public this(ITextViewer textViewer) {
+//         super();
+        fTextViewer= textViewer;
+        fTextWidget= textViewer.getTextWidget();
+        GC gc= new GC(fTextWidget);
+        gc.setAdvanced(true);
+        fIsAdvancedGraphicsPresent= gc.getAdvanced();
+        gc.dispose();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPainter#dispose()
+     */
+    public void dispose() {
+        fTextViewer= null;
+        fTextWidget= null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPainter#paint(int)
+     */
+    public void paint(int reason) {
+        IDocument document= fTextViewer.getDocument();
+        if (document is null) {
+            deactivate(false);
+            return;
+        }
+        if (!fIsActive) {
+            fIsActive= true;
+            fTextWidget.addPaintListener(this);
+            redrawAll();
+        } else if (reason is CONFIGURATION || reason is INTERNAL) {
+            redrawAll();
+        } else if (reason is TEXT_CHANGE) {
+            // redraw current line only
+            try {
+                IRegion lineRegion =
+                    document.getLineInformationOfOffset(getDocumentOffset(fTextWidget.getCaretOffset()));
+                int widgetOffset= getWidgetOffset(lineRegion.getOffset());
+                int charCount= fTextWidget.getCharCount();
+                int redrawLength= Math.min(lineRegion.getLength(), charCount - widgetOffset);
+                if (widgetOffset >= 0 && redrawLength > 0) {
+                    fTextWidget.redrawRange(widgetOffset, redrawLength, true);
+                }
+            } catch (BadLocationException e) {
+                // ignore
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPainter#deactivate(bool)
+     */
+    public void deactivate(bool redraw) {
+        if (fIsActive) {
+            fIsActive= false;
+            fTextWidget.removePaintListener(this);
+            if (redraw) {
+                redrawAll();
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPainter#setPositionManager(org.eclipse.jface.text.IPaintPositionManager)
+     */
+    public void setPositionManager(IPaintPositionManager manager) {
+        // no need for a position manager
+    }
+
+    /*
+     * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
+     */
+    public void paintControl(PaintEvent event) {
+        if (fTextWidget !is null) {
+            handleDrawRequest(event.gc, event.x, event.y, event.width, event.height);
+        }
+    }
+
+    /**
+     * Draw characters in view range.
+     *
+     * @param gc
+     * @param x
+     * @param y
+     * @param w
+     * @param h
+     */
+    private void handleDrawRequest(GC gc, int x, int y, int w, int h) {
+        int startLine= fTextWidget.getLineIndex(y);
+        int endLine= fTextWidget.getLineIndex(y + h - 1);
+        if (startLine <= endLine && startLine < fTextWidget.getLineCount()) {
+            if (fIsAdvancedGraphicsPresent) {
+                int alpha= gc.getAlpha();
+                gc.setAlpha(100);
+                drawLineRange(gc, startLine, endLine, x, w);
+                gc.setAlpha(alpha);
+            } else
+                drawLineRange(gc, startLine, endLine, x, w);
+        }
+    }
+
+    /**
+     * Draw the given line range.
+     *
+     * @param gc
+     * @param startLine  first line number
+     * @param endLine  last line number (inclusive)
+     * @param x  the X-coordinate of the drawing range
+     * @param w  the width of the drawing range
+     */
+    private void drawLineRange(GC gc, int startLine, int endLine, int x, int w) {
+        final int viewPortWidth= fTextWidget.getClientArea().width;
+        for (int line= startLine; line <= endLine; line++) {
+            int lineOffset= fTextWidget.getOffsetAtLine(line);
+            // line end offset including line delimiter
+            int lineEndOffset;
+            if (line < fTextWidget.getLineCount() - 1) {
+                lineEndOffset= fTextWidget.getOffsetAtLine(line + 1);
+            } else {
+                lineEndOffset= fTextWidget.getCharCount();
+            }
+            // line length excluding line delimiter
+            int lineLength= lineEndOffset - lineOffset;
+            while (lineLength > 0) {
+                char c= fTextWidget.getTextRange(lineOffset + lineLength - 1, 1).charAt(0);
+                if (c !is '\r' && c !is '\n') {
+                    break;
+                }
+                --lineLength;
+            }
+            // compute coordinates of last character on line
+            Point endOfLine= fTextWidget.getLocationAtOffset(lineOffset + lineLength);
+            if (x - endOfLine.x > viewPortWidth) {
+                // line is not visible
+                continue;
+            }
+            // Y-coordinate of line
+            int y= fTextWidget.getLinePixel(line);
+            // compute first visible char offset
+            int startOffset;
+            try {
+                startOffset= fTextWidget.getOffsetAtLocation(new Point(x, y)) - 1;
+                if (startOffset - 2 <= lineOffset) {
+                    startOffset= lineOffset;
+                }
+            } catch (IllegalArgumentException iae) {
+                startOffset= lineOffset;
+            }
+            // compute last visible char offset
+            int endOffset;
+            if (x + w >= endOfLine.x) {
+                // line end is visible
+                endOffset= lineEndOffset;
+            } else {
+                try {
+                    endOffset= fTextWidget.getOffsetAtLocation(new Point(x + w - 1, y)) + 1;
+                    if (endOffset + 2 >= lineEndOffset) {
+                        endOffset= lineEndOffset;
+                    }
+                } catch (IllegalArgumentException iae) {
+                    endOffset= lineEndOffset;
+                }
+            }
+            // draw character range
+            if (endOffset > startOffset) {
+                drawCharRange(gc, startOffset, endOffset);
+            }
+        }
+    }
+
+    /**
+     * Draw characters of content range.
+     *
+     * @param gc the GC
+     * @param startOffset inclusive start index
+     * @param endOffset exclusive end index
+     */
+    private void drawCharRange(GC gc, int startOffset, int endOffset) {
+        StyledTextContent content= fTextWidget.getContent();
+        int length= endOffset - startOffset;
+        String text= content.getTextRange(startOffset, length);
+        StyleRange styleRange= null;
+        Color fg= null;
+        Point selection= fTextWidget.getSelection();
+        StringBuffer visibleChar= new StringBuffer(10);
+        for (int textOffset= 0; textOffset <= length; ++textOffset) {
+            int delta= 0;
+            bool eol= false;
+            if (textOffset < length) {
+                delta= 1;
+                char c= text.charAt(textOffset);
+                switch (c) {
+                case ' ' :
+                    visibleChar.append(SPACE_SIGN);
+                    // 'continue' would improve performance but may produce drawing errors
+                    // for long runs of space if width of space and dot differ
+                    break;
+                case '\u3000' : // ideographic whitespace
+                    visibleChar.append(IDEOGRAPHIC_SPACE_SIGN);
+                    // 'continue' would improve performance but may produce drawing errors
+                    // for long runs of space if width of space and dot differ
+                    break;
+                case '\t' :
+                    visibleChar.append(TAB_SIGN);
+                    break;
+                case '\r' :
+                    visibleChar.append(CARRIAGE_RETURN_SIGN);
+                    if (textOffset >= length - 1 || text.charAt(textOffset + 1) !is '\n') {
+                        eol= true;
+                        break;
+                    }
+                    continue;
+                case '\n' :
+                    visibleChar.append(LINE_FEED_SIGN);
+                    eol= true;
+                    break;
+                default :
+                    delta= 0;
+                    break;
+                }
+            }
+            if (visibleChar.length() > 0) {
+                int widgetOffset= startOffset + textOffset - visibleChar.length() + delta;
+                if (!eol || !isFoldedLine(content.getLineAtOffset(widgetOffset))) {
+                    if (widgetOffset >= selection.x && widgetOffset < selection.y) {
+                        fg= fTextWidget.getSelectionForeground();
+                    } else if (styleRange is null || styleRange.start + styleRange.length <= widgetOffset) {
+                        styleRange= fTextWidget.getStyleRangeAtOffset(widgetOffset);
+                        if (styleRange is null || styleRange.foreground is null) {
+                            fg= fTextWidget.getForeground();
+                        } else {
+                            fg= styleRange.foreground;
+                        }
+                    }
+                    draw(gc, widgetOffset, visibleChar.toString(), fg);
+                }
+                visibleChar.truncate(0);
+            }
+        }
+    }
+
+    /**
+     * Check if the given widget line is a folded line.
+     *
+     * @param widgetLine  the widget line number
+     * @return <code>true</code> if the line is folded
+     */
+    private bool isFoldedLine(int widgetLine) {
+        if ( cast(ITextViewerExtension5)fTextViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5)fTextViewer;
+            int modelLine= extension.widgetLine2ModelLine(widgetLine);
+            int widgetLine2= extension.modelLine2WidgetLine(modelLine + 1);
+            return widgetLine2 is -1;
+        }
+        return false;
+    }
+
+    /**
+     * Redraw all of the text widgets visible content.
+     */
+    private void redrawAll() {
+        fTextWidget.redraw();
+    }
+
+    /**
+     * Draw string at widget offset.
+     *
+     * @param gc
+     * @param offset the widget offset
+     * @param s the string to be drawn
+     * @param fg the foreground color
+     */
+    private void draw(GC gc, int offset, String s, Color fg) {
+        // Compute baseline delta (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=165640)
+        int baseline= fTextWidget.getBaseline(offset);
+        FontMetrics fontMetrics= gc.getFontMetrics();
+        int fontBaseline= fontMetrics.getAscent() + fontMetrics.getLeading();
+        int baslineDelta= baseline - fontBaseline;
+
+        Point pos= fTextWidget.getLocationAtOffset(offset);
+        gc.setForeground(fg);
+        gc.drawString(s, pos.x, pos.y + baslineDelta, true);
+    }
+
+    /**
+     * Convert a document offset to the corresponding widget offset.
+     *
+     * @param documentOffset
+     * @return widget offset
+     */
+    private int getWidgetOffset(int documentOffset) {
+        if ( cast(ITextViewerExtension5)fTextViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5)fTextViewer;
+            return extension.modelOffset2WidgetOffset(documentOffset);
+        }
+        IRegion visible= fTextViewer.getVisibleRegion();
+        int widgetOffset= documentOffset - visible.getOffset();
+        if (widgetOffset > visible.getLength()) {
+            return -1;
+        }
+        return widgetOffset;
+    }
+
+    /**
+     * Convert a widget offset to the corresponding document offset.
+     *
+     * @param widgetOffset
+     * @return document offset
+     */
+    private int getDocumentOffset(int widgetOffset) {
+        if ( cast(ITextViewerExtension5)fTextViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5)fTextViewer;
+            return extension.widgetOffset2ModelOffset(widgetOffset);
+        }
+        IRegion visible= fTextViewer.getVisibleRegion();
+        if (widgetOffset > visible.getLength()) {
+            return -1;
+        }
+        return widgetOffset + visible.getOffset();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AdditionalInfoController.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,690 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.contentassist.AdditionalInfoController;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+import java.lang.JThread;
+import tango.core.sync.Mutex;
+import tango.core.sync.Condition;
+
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.internal.text.InformationControlReplacer;
+import org.eclipse.jface.text.AbstractInformationControlManager;
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
+import org.eclipse.jface.text.DefaultInformationControl;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IInformationControlExtension3;
+
+
+/**
+ * Displays the additional information available for a completion proposal.
+ *
+ * @since 2.0
+ */
+class AdditionalInfoController : AbstractInformationControlManager {
+
+    alias AbstractInformationControlManager.showInformation showInformation;
+
+    /**
+     * A timer thread.
+     *
+     * @since 3.2
+     */
+    private static abstract class Timer : Object.Monitor {
+
+        private Mutex fMutex;
+        private Condition fMutex_cond;
+
+        public override void lock(){
+            fMutex.lock();
+        }
+        public override void unlock(){
+            fMutex.unlock();
+        }
+        public void notifyAll(){
+            fMutex_cond.notifyAll();
+        }
+        public void wait(){
+            fMutex_cond.wait();
+        }
+        public void wait( long delay ){
+            fMutex_cond.wait(delay/1000.0);
+        }
+
+
+        private static const int DELAY_UNTIL_JOB_IS_SCHEDULED= 50;
+
+        /**
+         * A <code>Task</code> is {@link Task#run() run} when {@link #delay()} milliseconds have
+         * elapsed after it was scheduled without a {@link #reset(ICompletionProposal) reset}
+         * to occur.
+         */
+        private static abstract class Task : Runnable {
+            /**
+             * @return the delay in milliseconds before this task should be run
+             */
+            public abstract long delay();
+            /**
+             * Runs this task.
+             */
+            public abstract void run();
+            /**
+             * @return the task to be scheduled after this task has been run
+             */
+            public abstract Task nextTask();
+        }
+
+        /**
+         * IDLE: the initial task, and active whenever the info has been shown. It cannot be run,
+         * but specifies an infinite delay.
+         */
+        private Task IDLE;
+        private void IDLE_init(){
+            IDLE = new class() Task {
+                public void run() {
+                    Assert.isTrue(false);
+                }
+
+                public Task nextTask() {
+                    Assert.isTrue(false);
+                    return null;
+                }
+
+                public long delay() {
+                    return Long.MAX_VALUE;
+                }
+
+                public override String toString() {
+                    return "IDLE"; //$NON-NLS-1$
+                }
+            };
+        }
+        /**
+         * FIRST_WAIT: Schedules a platform {@link Job} to fetch additional info from an {@link ICompletionProposalExtension5}.
+         */
+        private Task FIRST_WAIT;
+        private void FIRST_WAIT_init() {
+            FIRST_WAIT = new class() Task {
+                public void run() {
+                    final ICompletionProposalExtension5 proposal= getCurrentProposalEx();
+                    Job job= new class(JFaceTextMessages.getString("AdditionalInfoController.job_name")) Job { //$NON-NLS-1$
+                        this( String txt ){
+                            super( txt );
+                        }
+                        protected IStatus run(IProgressMonitor monitor) {
+                            Object info;
+                            try {
+                                info= proposal.getAdditionalProposalInfo(monitor);
+                            } catch (RuntimeException x) {
+                                /*
+                                * XXX: This is the safest fix at this point so close to end of 3.2.
+                                *      Will be revisited when fixing https://bugs.eclipse.org/bugs/show_bug.cgi?id=101033
+                                */
+                                return new Status(IStatus.WARNING, "org.eclipse.jface.text", IStatus.OK, "", x); //$NON-NLS-1$ //$NON-NLS-2$
+                            }
+                            setInfo(cast(ICompletionProposal) proposal, info);
+                            return new Status(IStatus.OK, "org.eclipse.jface.text", IStatus.OK, "", null); //$NON-NLS-1$ //$NON-NLS-2$
+                        }
+                    };
+                    job.schedule();
+                }
+
+                public Task nextTask() {
+                    return SECOND_WAIT;
+                }
+
+                public long delay() {
+                    return DELAY_UNTIL_JOB_IS_SCHEDULED;
+                }
+
+                public override String toString() {
+                    return "FIRST_WAIT"; //$NON-NLS-1$
+                }
+            };
+        }
+        /**
+         * SECOND_WAIT: Allows display of additional info obtained from an
+         * {@link ICompletionProposalExtension5}.
+         */
+        private Task SECOND_WAIT;
+        private void SECOND_WAIT_init() {
+            SECOND_WAIT = new class() Task {
+                public void run() {
+                    // show the info
+                    allowShowing();
+                }
+
+                public Task nextTask() {
+                    return IDLE;
+                }
+
+                public long delay() {
+                    return fDelay - DELAY_UNTIL_JOB_IS_SCHEDULED;
+                }
+
+                public override String toString() {
+                    return "SECOND_WAIT"; //$NON-NLS-1$
+                }
+            };
+        }
+        /**
+         * LEGACY_WAIT: Posts a runnable into the display thread to fetch additional info from non-{@link ICompletionProposalExtension5}s.
+         */
+        private Task LEGACY_WAIT;
+        private void LEGACY_WAIT_init() {
+            LEGACY_WAIT = new class()  Task {
+                public void run() {
+                    final ICompletionProposal proposal= getCurrentProposal();
+                    if (!fDisplay.isDisposed()) {
+                        fDisplay.asyncExec(new class()  Runnable {
+                            public void run() {
+                                synchronized (this.outer) {
+                                    if (proposal is getCurrentProposal()) {
+                                        Object info= stringcast(proposal.getAdditionalProposalInfo());
+                                        showInformation(proposal, info);
+                                    }
+                                }
+                            }
+                        });
+                    }
+                }
+
+                public Task nextTask() {
+                    return IDLE;
+                }
+
+                public long delay() {
+                    return fDelay;
+                }
+
+                public override String toString() {
+                    return "LEGACY_WAIT"; //$NON-NLS-1$
+                }
+            };
+        }
+        /**
+         * EXIT: The task that triggers termination of the timer thread.
+         */
+        private Task EXIT;
+        private void EXIT_init() {
+            EXIT = new class()  Task {
+                public long delay() {
+                    return 1;
+                }
+
+                public Task nextTask() {
+                    Assert.isTrue(false);
+                    return EXIT;
+                }
+
+                public void run() {
+                    Assert.isTrue(false);
+                }
+
+                public override String toString() {
+                    return "EXIT"; //$NON-NLS-1$
+                }
+            };
+        }
+
+        /** The timer thread. */
+        private const JThread fThread;
+
+        /** The currently waiting / active task. */
+        private Task fTask;
+        /** The next wake up time. */
+        private long fNextWakeup;
+
+        private ICompletionProposal fCurrentProposal= null;
+        private Object fCurrentInfo= null;
+        private bool fAllowShowing= false;
+
+        private const Display fDisplay;
+        private const int fDelay;
+
+        /**
+         * Creates a new timer.
+         *
+         * @param display the display to use for display thread posting.
+         * @param delay the delay until to show additional info
+         */
+        public this(Display display, int delay) {
+        fMutex = new Mutex();
+        fMutex_cond = new Condition(fMutex);
+
+            // SWT instance init
+            IDLE_init();
+            FIRST_WAIT_init();
+            SECOND_WAIT_init();
+            LEGACY_WAIT_init();
+            EXIT_init();
+
+            fDisplay= display;
+            fDelay= delay;
+            long current= System.currentTimeMillis();
+            schedule(IDLE, current);
+
+            fThread= new JThread( &threadrun );
+            fThread.setName( JFaceTextMessages.getString("InfoPopup.info_delay_timer_name")); //$NON-NLS-1$
+            fThread.start();
+        }
+        void threadrun() {
+            try {
+                loop();
+            } catch (InterruptedException x) {
+            }
+        }
+
+        /**
+         * Terminates the timer thread.
+         */
+        public synchronized final void terminate() {
+            schedule(EXIT, System.currentTimeMillis());
+            notifyAll();
+        }
+
+        /**
+         * Resets the timer thread as the selection has changed to a new proposal.
+         *
+         * @param p the new proposal
+         */
+        public final synchronized void reset(ICompletionProposal p) {
+            if (fCurrentProposal !is p) {
+                fCurrentProposal= p;
+                fCurrentInfo= null;
+                fAllowShowing= false;
+
+                long oldWakeup= fNextWakeup;
+                Task task= taskOnReset(p);
+                schedule(task, System.currentTimeMillis());
+                if (fNextWakeup < oldWakeup)
+                    notifyAll();
+            }
+        }
+
+        private Task taskOnReset(ICompletionProposal p) {
+            if (p is null)
+                return IDLE;
+            if (isExt5(p))
+                return FIRST_WAIT;
+            return LEGACY_WAIT;
+        }
+
+        private synchronized void loop()  {
+            long current= System.currentTimeMillis();
+            Task task= currentTask();
+
+            while (task !is EXIT) {
+                long delay= fNextWakeup - current;
+                if (delay <= 0) {
+                    task.run();
+                    task= task.nextTask();
+                    schedule(task, current);
+                } else {
+                    wait(delay);
+                    current= System.currentTimeMillis();
+                    task= currentTask();
+                }
+            }
+        }
+
+        private Task currentTask() {
+            return fTask;
+        }
+
+        private void schedule(Task task, long current) {
+            fTask= task;
+            long nextWakeup= current + task.delay();
+            if (nextWakeup <= current)
+                fNextWakeup= Long.MAX_VALUE;
+            else
+                fNextWakeup= nextWakeup;
+        }
+
+        private bool isExt5(ICompletionProposal p) {
+            return null !is cast(ICompletionProposalExtension5)p;
+        }
+
+        ICompletionProposal getCurrentProposal() {
+            return fCurrentProposal;
+        }
+
+        ICompletionProposalExtension5 getCurrentProposalEx() {
+            Assert.isTrue( null !is cast(ICompletionProposalExtension5)fCurrentProposal );
+            return cast(ICompletionProposalExtension5) fCurrentProposal;
+        }
+
+        synchronized void setInfo(ICompletionProposal proposal, Object info) {
+            if (proposal is fCurrentProposal) {
+                fCurrentInfo= info;
+                if (fAllowShowing) {
+                    triggerShowing();
+                }
+            }
+        }
+
+        private void triggerShowing() {
+            final Object info= fCurrentInfo;
+            if (!fDisplay.isDisposed()) {
+                fDisplay.asyncExec(new class()  Runnable {
+                    public void run() {
+                        synchronized (this.outer) {
+                            if (info is fCurrentInfo) {
+                                showInformation(fCurrentProposal, info);
+                            }
+                        }
+                    }
+                });
+            }
+        }
+
+        /**
+         * Called in the display thread to show additional info.
+         *
+         * @param proposal the proposal to show information about
+         * @param info the information about <code>proposal</code>
+         */
+        protected abstract void showInformation(ICompletionProposal proposal, Object info);
+
+        void allowShowing() {
+            fAllowShowing= true;
+            triggerShowing();
+        }
+    }
+    /**
+     * Internal table selection listener.
+     */
+    private class TableSelectionListener : SelectionListener {
+
+        /*
+         * @see SelectionListener#widgetSelected(SelectionEvent)
+         */
+        public void widgetSelected(SelectionEvent e) {
+            handleTableSelectionChanged();
+        }
+
+        /*
+         * @see SelectionListener#widgetDefaultSelected(SelectionEvent)
+         */
+        public void widgetDefaultSelected(SelectionEvent e) {
+        }
+    }
+
+    /**
+     * Default control creator for the information control replacer.
+     * @since 3.4
+     */
+    private static class DefaultPresenterControlCreator : AbstractReusableInformationControlCreator {
+        public IInformationControl doCreateInformationControl(Shell shell) {
+            return new DefaultInformationControl(shell, true);
+        }
+    }
+
+    /** The proposal table. */
+    private Table fProposalTable;
+    /** The table selection listener */
+    private SelectionListener fSelectionListener;
+    /** The delay after which additional information is displayed */
+    private const int fDelay;
+    /**
+     * The timer thread.
+     * @since 3.2
+     */
+    private Timer fTimer;
+    /**
+     * The proposal most recently set by {@link #showInformation(ICompletionProposal, Object)},
+     * possibly <code>null</code>.
+     * @since 3.2
+     */
+    private ICompletionProposal fProposal;
+    /**
+     * The information most recently set by {@link #showInformation(ICompletionProposal, Object)},
+     * possibly <code>null</code>.
+     * @since 3.2
+     */
+    private Object fInformation;
+
+    /**
+     * Creates a new additional information controller.
+     *
+     * @param creator the information control creator to be used by this controller
+     * @param delay time in milliseconds after which additional info should be displayed
+     */
+    this(IInformationControlCreator creator, int delay) {
+
+        fSelectionListener= new TableSelectionListener();
+
+        super(creator);
+        fDelay= delay;
+        setAnchor(ANCHOR_RIGHT);
+        setFallbackAnchors([ ANCHOR_RIGHT, ANCHOR_LEFT, ANCHOR_BOTTOM ]);
+
+        /*
+         * Adjust the location by one pixel towards the proposal popup, so that the single pixel
+         * border of the additional info popup overlays with the border of the popup. This avoids
+         * having a double black line.
+         */
+        int spacing= -1;
+        setMargins(spacing, spacing); // see also adjustment in #computeLocation
+
+        InformationControlReplacer replacer= new InformationControlReplacer(new DefaultPresenterControlCreator());
+        getInternalAccessor().setInformationControlReplacer(replacer);
+    }
+
+    /*
+     * @see AbstractInformationControlManager#install(Control)
+     */
+    public void install(Control control) {
+
+        if (fProposalTable is control) {
+            // already installed
+            return;
+        }
+
+        super.install(control.getShell());
+
+        Assert.isTrue( null !is cast(Table)control );
+        fProposalTable= cast(Table) control;
+        fProposalTable.addSelectionListener(fSelectionListener);
+        getInternalAccessor().getInformationControlReplacer().install(fProposalTable);
+
+        fTimer= new class(fProposalTable.getDisplay(), fDelay)  Timer {
+            this( Display d, int del ){
+                super(d,del);
+            }
+            protected void showInformation(ICompletionProposal proposal, Object info) {
+                InformationControlReplacer replacer= getInternalAccessor().getInformationControlReplacer();
+                if (replacer !is null)
+                    replacer.hideInformationControl();
+                this.outer.showInformation(proposal, info);
+            }
+        };
+    }
+
+    /*
+     * @see AbstractInformationControlManager#disposeInformationControl()
+     */
+    public void disposeInformationControl() {
+
+        if (fTimer !is null) {
+            fTimer.terminate();
+            fTimer= null;
+        }
+
+        fProposal= null;
+        fInformation= null;
+
+        if (fProposalTable !is null && !fProposalTable.isDisposed()) {
+            fProposalTable.removeSelectionListener(fSelectionListener);
+            fProposalTable= null;
+        }
+
+        super.disposeInformationControl();
+    }
+
+    /**
+     *Handles a change of the line selected in the associated selector.
+     */
+    public void handleTableSelectionChanged() {
+
+        if (fProposalTable !is null && !fProposalTable.isDisposed() && fProposalTable.isVisible()) {
+            TableItem[] selection= fProposalTable.getSelection();
+            if (selection !is null && selection.length > 0) {
+
+                TableItem item= selection[0];
+
+                Object d= item.getData();
+                if ( cast(ICompletionProposal)d ) {
+                    ICompletionProposal p= cast(ICompletionProposal) d;
+                    fTimer.reset(p);
+                }
+            }
+        }
+    }
+
+    void showInformation(ICompletionProposal proposal, Object info) {
+        if (fProposalTable is null || fProposalTable.isDisposed())
+            return;
+
+        if (fProposal is proposal && ((info is null && fInformation is null) || (info !is null && info.opEquals(fInformation))))
+            return;
+
+        fInformation= info;
+        fProposal= proposal;
+        showInformation();
+    }
+
+    /*
+     * @see AbstractInformationControlManager#computeInformation()
+     */
+    protected void computeInformation() {
+        if ( cast(ICompletionProposalExtension3)fProposal )
+            setCustomInformationControlCreator((cast(ICompletionProposalExtension3) fProposal).getInformationControlCreator());
+        else
+            setCustomInformationControlCreator(null);
+
+        // compute subject area
+        Point size= fProposalTable.getShell().getSize();
+
+        // set information & subject area
+        setInformation(fInformation, new Rectangle(0, 0, size.x, size.y));
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#computeLocation(org.eclipse.swt.graphics.Rectangle, org.eclipse.swt.graphics.Point, org.eclipse.jface.text.AbstractInformationControlManager.Anchor)
+     */
+    protected Point computeLocation(Rectangle subjectArea, Point controlSize, Anchor anchor) {
+        Point location= super.computeLocation(subjectArea, controlSize, anchor);
+
+        /*
+         * The location is computed using subjectControl.toDisplay(), which does not include the
+         * trim of the subject control. As we want the additional info popup aligned with the outer
+         * coordinates of the proposal popup, adjust this here
+         */
+        Rectangle trim= fProposalTable.getShell().computeTrim(0, 0, 0, 0);
+        location.x += trim.x;
+        location.y += trim.y;
+
+        return location;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#computeSizeConstraints(Control, IInformationControl)
+     */
+    protected Point computeSizeConstraints(Control subjectControl, IInformationControl informationControl) {
+        // at least as big as the proposal table
+        Point sizeConstraint= super.computeSizeConstraints(subjectControl, informationControl);
+        Point size= subjectControl.getShell().getSize();
+
+        // AbstractInformationControlManager#internalShowInformationControl(Rectangle, Object) adds trims
+        // to the computed constraints. Need to remove them here, to make the outer bounds of the additional
+        // info shell fit the bounds of the proposal shell:
+        if ( cast(IInformationControlExtension3)fInformationControl ) {
+            Rectangle shellTrim= (cast(IInformationControlExtension3) fInformationControl).computeTrim();
+            size.x -= shellTrim.width;
+            size.y -= shellTrim.height;
+        }
+
+        if (sizeConstraint.x < size.x)
+            sizeConstraint.x= size.x;
+        if (sizeConstraint.y < size.y)
+            sizeConstraint.y= size.y;
+        return sizeConstraint;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#hideInformationControl()
+     */
+    protected void hideInformationControl() {
+        super.hideInformationControl();
+    }
+    package void hideInformationControl_package() {
+        this.hideInformationControl();
+    }
+
+    /**
+     * @return the current information control, or <code>null</code> if none available
+     */
+    public IInformationControl getCurrentInformationControl2() {
+        return getInternalAccessor().getCurrentInformationControl();
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposal.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,171 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.contentassist.CompletionProposal;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * The standard implementation of the <code>ICompletionProposal</code> interface.
+ */
+public final class CompletionProposal : ICompletionProposal {
+
+    /** The string to be displayed in the completion proposal popup. */
+    private String fDisplayString;
+    /** The replacement string. */
+    private String fReplacementString;
+    /** The replacement offset. */
+    private int fReplacementOffset;
+    /** The replacement length. */
+    private int fReplacementLength;
+    /** The cursor position after this proposal has been applied. */
+    private int fCursorPosition;
+    /** The image to be displayed in the completion proposal popup. */
+    private Image fImage;
+    /** The context information of this proposal. */
+    private IContextInformation fContextInformation;
+    /** The additional info of this proposal. */
+    private String fAdditionalProposalInfo;
+
+    /**
+     * Creates a new completion proposal based on the provided information. The replacement string is
+     * considered being the display string too. All remaining fields are set to <code>null</code>.
+     *
+     * @param replacementString the actual string to be inserted into the document
+     * @param replacementOffset the offset of the text to be replaced
+     * @param replacementLength the length of the text to be replaced
+     * @param cursorPosition the position of the cursor following the insert relative to replacementOffset
+     */
+    public this(String replacementString, int replacementOffset, int replacementLength, int cursorPosition) {
+        this(replacementString, replacementOffset, replacementLength, cursorPosition, null, null, null, null);
+    }
+
+    /**
+     * Creates a new completion proposal. All fields are initialized based on the provided information.
+     *
+     * @param replacementString the actual string to be inserted into the document
+     * @param replacementOffset the offset of the text to be replaced
+     * @param replacementLength the length of the text to be replaced
+     * @param cursorPosition the position of the cursor following the insert relative to replacementOffset
+     * @param image the image to display for this proposal
+     * @param displayString the string to be displayed for the proposal
+     * @param contextInformation the context information associated with this proposal
+     * @param additionalProposalInfo the additional information associated with this proposal
+     */
+    public this(String replacementString, int replacementOffset, int replacementLength, int cursorPosition, Image image, String displayString, IContextInformation contextInformation, String additionalProposalInfo) {
+        Assert.isNotNull(replacementString);
+        Assert.isTrue(replacementOffset >= 0);
+        Assert.isTrue(replacementLength >= 0);
+        Assert.isTrue(cursorPosition >= 0);
+
+        fReplacementString= replacementString;
+        fReplacementOffset= replacementOffset;
+        fReplacementLength= replacementLength;
+        fCursorPosition= cursorPosition;
+        fImage= image;
+        fDisplayString= displayString;
+        fContextInformation= contextInformation;
+        fAdditionalProposalInfo= additionalProposalInfo;
+    }
+
+    /*
+     * @see ICompletionProposal#apply(IDocument)
+     */
+    public void apply(IDocument document) {
+        try {
+            document.replace(fReplacementOffset, fReplacementLength, fReplacementString);
+        } catch (BadLocationException x) {
+            // ignore
+        }
+    }
+
+    /*
+     * @see ICompletionProposal#getSelection(IDocument)
+     */
+    public Point getSelection(IDocument document) {
+        return new Point(fReplacementOffset + fCursorPosition, 0);
+    }
+
+    /*
+     * @see ICompletionProposal#getContextInformation()
+     */
+    public IContextInformation getContextInformation() {
+        return fContextInformation;
+    }
+
+    /*
+     * @see ICompletionProposal#getImage()
+     */
+    public Image getImage() {
+        return fImage;
+    }
+
+    /*
+     * @see ICompletionProposal#getDisplayString()
+     */
+    public String getDisplayString() {
+        if (fDisplayString !is null)
+            return fDisplayString;
+        return fReplacementString;
+    }
+
+    /*
+     * @see ICompletionProposal#getAdditionalProposalInfo()
+     */
+    public String getAdditionalProposalInfo() {
+        return fAdditionalProposalInfo;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1887 @@
+/*******************************************************************************
+ * 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
+ *     Sean Montgomery, sean_montgomery@comcast.net - https://bugs.eclipse.org/bugs/show_bug.cgi?id=116454
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.contentassist.CompletionProposalPopup;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.bindings.keys.KeySequence;
+import org.eclipse.jface.bindings.keys.SWTKeySupport;
+import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
+import org.eclipse.jface.internal.text.InformationControlReplacer;
+import org.eclipse.jface.internal.text.TableOwnerDrawSupport;
+import org.eclipse.jface.preference.JFacePreferences;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.text.AbstractInformationControlManager;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IEditingSupport;
+import org.eclipse.jface.text.IEditingSupportRegistry;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.IRewriteTarget;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.AbstractInformationControlManager;
+import org.eclipse.jface.util.Geometry;
+import org.eclipse.jface.viewers.StyledString;
+
+
+/**
+ * This class is used to present proposals to the user. If additional
+ * information exists for a proposal, then selecting that proposal
+ * will result in the information being displayed in a secondary
+ * window.
+ *
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal
+ * @see org.eclipse.jface.text.contentassist.AdditionalInfoController
+ */
+class CompletionProposalPopup : IContentAssistListener {
+    /**
+     * Set to <code>true</code> to use a Table with SWT.VIRTUAL.
+     * XXX: This is a workaround for: https://bugs.eclipse.org/bugs/show_bug.cgi?id=90321
+     *      More details see also: https://bugs.eclipse.org/bugs/show_bug.cgi?id=98585#c36
+     * @since 3.1
+     */
+    private static bool USE_VIRTUAL_;
+    private static bool USE_VIRTUAL_init = false;;
+    private static bool USE_VIRTUAL(){
+        if( !USE_VIRTUAL_init ){
+            USE_VIRTUAL_init = true;
+            USE_VIRTUAL_ = !"motif".equals(SWT.getPlatform()); //$NON-NLS-1$
+        }
+        return USE_VIRTUAL_;
+    }
+
+    /**
+     * Completion proposal selection handler.
+     *
+     * @since 3.4
+     */
+    final class ProposalSelectionHandler : AbstractHandler {
+
+        /**
+         * Selection operation codes.
+         */
+        static const int SELECT_NEXT= 1;
+        static const int SELECT_PREVIOUS= 2;
+
+
+        private int fOperationCode;
+
+        /**
+         * Creates a new selection handler.
+         *
+         * @param operationCode the operation code
+         * @since 3.4
+         */
+        public this(int operationCode) {
+            Assert.isLegal(operationCode is SELECT_NEXT || operationCode is SELECT_PREVIOUS);
+            fOperationCode= operationCode;
+        }
+
+        /*
+         * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+         * @since 3.4
+         */
+        public Object execute(ExecutionEvent event)  {
+            int itemCount= fProposalTable.getItemCount();
+            int selectionIndex= fProposalTable.getSelectionIndex();
+            switch (fOperationCode) {
+            case SELECT_NEXT:
+                selectionIndex+= 1;
+                if (selectionIndex > itemCount - 1)
+                    selectionIndex= 0;
+                break;
+            case SELECT_PREVIOUS:
+                selectionIndex-= 1;
+                if (selectionIndex < 0)
+                    selectionIndex= itemCount - 1;
+                break;
+            default:
+            }
+            selectProposal(selectionIndex, false);
+            return null;
+        }
+
+    }
+
+
+    /**
+     * The empty proposal displayed if there is nothing else to show.
+     *
+     * @since 3.2
+     */
+    private static final class EmptyProposal : ICompletionProposal, ICompletionProposalExtension {
+
+        String fDisplayString;
+        int fOffset;
+        /*
+         * @see ICompletionProposal#apply(IDocument)
+         */
+        public void apply(IDocument document) {
+        }
+
+        /*
+         * @see ICompletionProposal#getSelection(IDocument)
+         */
+        public Point getSelection(IDocument document) {
+            return new Point(fOffset, 0);
+        }
+
+        /*
+         * @see ICompletionProposal#getContextInformation()
+         */
+        public IContextInformation getContextInformation() {
+            return null;
+        }
+
+        /*
+         * @see ICompletionProposal#getImage()
+         */
+        public Image getImage() {
+            return null;
+        }
+
+        /*
+         * @see ICompletionProposal#getDisplayString()
+         */
+        public String getDisplayString() {
+            return fDisplayString;
+        }
+
+        /*
+         * @see ICompletionProposal#getAdditionalProposalInfo()
+         */
+        public String getAdditionalProposalInfo() {
+            return null;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension#apply(org.eclipse.jface.text.IDocument, char, int)
+         */
+        public void apply(IDocument document, char trigger, int offset) {
+        }
+
+        /*
+         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension#isValidFor(org.eclipse.jface.text.IDocument, int)
+         */
+        public bool isValidFor(IDocument document, int offset) {
+            return false;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension#getTriggerCharacters()
+         */
+        public char[] getTriggerCharacters() {
+            return null;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension#getContextInformationPosition()
+         */
+        public int getContextInformationPosition() {
+            return -1;
+        }
+    }
+
+    private final class ProposalSelectionListener : KeyListener {
+        public void keyPressed(KeyEvent e) {
+            if (!Helper.okToUse(fProposalShell))
+                return;
+
+            if (e.character is 0 && e.keyCode is SWT.CTRL) {
+                // http://dev.eclipse.org/bugs/show_bug.cgi?id=34754
+                int index= fProposalTable.getSelectionIndex();
+                if (index >= 0)
+                    selectProposal(index, true);
+            }
+        }
+
+        public void keyReleased(KeyEvent e) {
+            if (!Helper.okToUse(fProposalShell))
+                return;
+
+            if (e.character is 0 && e.keyCode is SWT.CTRL) {
+                // http://dev.eclipse.org/bugs/show_bug.cgi?id=34754
+                int index= fProposalTable.getSelectionIndex();
+                if (index >= 0)
+                    selectProposal(index, false);
+            }
+        }
+    }
+
+    private final class CommandKeyListener : KeyAdapter {
+        private KeySequence fCommandSequence;
+
+        private this(KeySequence keySequence) {
+            fCommandSequence= keySequence;
+        }
+
+        public void keyPressed(KeyEvent e) {
+            if (!Helper.okToUse(fProposalShell))
+                return;
+
+            int accelerator= SWTKeySupport.convertEventToUnmodifiedAccelerator(e);
+            KeySequence sequence= KeySequence.getInstance(SWTKeySupport.convertAcceleratorToKeyStroke(accelerator));
+            if (sequence==/++/fCommandSequence)
+                if (fContentAssistant.isPrefixCompletionEnabled())
+                    incrementalComplete();
+                else
+                    showProposals(false);
+
+        }
+    }
+
+
+    /** The associated text viewer. */
+    private ITextViewer fViewer;
+    /** The associated content assistant. */
+    private ContentAssistant fContentAssistant;
+    /** The used additional info controller. */
+    private AdditionalInfoController fAdditionalInfoController;
+    /** The closing strategy for this completion proposal popup. */
+    private PopupCloser fPopupCloser;
+    /** The popup shell. */
+    private Shell fProposalShell;
+    /** The proposal table. */
+    private Table fProposalTable;
+    /** Indicates whether a completion proposal is being inserted. */
+    private bool fInserting= false;
+    /** The key listener to control navigation. */
+    private ProposalSelectionListener fKeyListener;
+    /** List of document events used for filtering proposals. */
+    private List fDocumentEvents;
+    /** Listener filling the document event queue. */
+    private IDocumentListener fDocumentListener;
+    /** The filter list of proposals. */
+    private ICompletionProposal[] fFilteredProposals;
+    /** The computed list of proposals. */
+    private ICompletionProposal[] fComputedProposals;
+    /** The offset for which the proposals have been computed. */
+    private int fInvocationOffset;
+    /** The offset for which the computed proposals have been filtered. */
+    private int fFilterOffset;
+    /**
+     * The most recently selected proposal.
+     * @since 3.0
+     */
+    private ICompletionProposal fLastProposal;
+    /**
+     * The content assist subject control.
+     * This replaces <code>fViewer</code>
+     *
+     * @since 3.0
+     */
+    private IContentAssistSubjectControl fContentAssistSubjectControl;
+    /**
+     * The content assist subject control adapter.
+     * This replaces <code>fViewer</code>
+     *
+     * @since 3.0
+     */
+    private ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter;
+    /**
+     * Remembers the size for this completion proposal popup.
+     * @since 3.0
+     */
+    private Point fSize;
+    /**
+     * Editor helper that communicates that the completion proposal popup may
+     * have focus while the 'logical focus' is still with the editor.
+     * @since 3.1
+     */
+    private IEditingSupport fFocusHelper;
+    /**
+     * Set to true by {@link #computeFilteredProposals(int, DocumentEvent)} if
+     * the returned proposals are a subset of {@link #fFilteredProposals},
+     * <code>false</code> if not.
+     * @since 3.1
+     */
+    private bool fIsFilteredSubset;
+    /**
+     * The filter runnable.
+     *
+     * @since 3.1.1
+     */
+    private Runnable fFilterRunnable;
+    private void fFilterRunnableInit(){
+        fFilterRunnable = dgRunnable( {
+            if (!fIsFilterPending)
+                return;
+
+            fIsFilterPending= false;
+
+            if (!Helper.okToUse(fContentAssistSubjectControlAdapter.getControl()))
+                return;
+
+            int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
+            ICompletionProposal[] proposals= null;
+            try  {
+                if (offset > -1) {
+                    DocumentEvent event= TextUtilities.mergeProcessedDocumentEvents(fDocumentEvents);
+                    proposals= computeFilteredProposals(offset, event);
+                }
+            } catch (BadLocationException x)  {
+            } finally  {
+                fDocumentEvents.clear();
+            }
+            fFilterOffset= offset;
+
+            if (proposals !is null && proposals.length > 0)
+                setProposals(proposals, fIsFilteredSubset);
+            else
+                hide();
+        });
+    }
+
+    /**
+     * <code>true</code> if <code>fFilterRunnable</code> has been
+     * posted, <code>false</code> if not.
+     *
+     * @since 3.1.1
+     */
+    private bool fIsFilterPending= false;
+    /**
+     * The info message at the bottom of the popup, or <code>null</code> for no popup (if
+     * ContentAssistant does not provide one).
+     *
+     * @since 3.2
+     */
+    private Label fMessageText;
+    /**
+     * The font used for <code>fMessageText</code> or null; dispose when done.
+     *
+     * @since 3.2
+     */
+    private Font fMessageTextFont;
+    /**
+     * The most recent completion offset (used to determine repeated invocation)
+     *
+     * @since 3.2
+     */
+    private int fLastCompletionOffset;
+    /**
+     * The (reusable) empty proposal.
+     *
+     * @since 3.2
+     */
+    private EmptyProposal fEmptyProposal;
+    /**
+     * The text for the empty proposal, or <code>null</code> to use the default text.
+     *
+     * @since 3.2
+     */
+    private String fEmptyMessage= null;
+    /**
+     * Tells whether colored labels support is enabled.
+     * Only valid while the popup is active.
+     *
+     * @since 3.4
+     */
+    private bool fIsColoredLabelsSupportEnabled= false;
+
+
+    /**
+     * Creates a new completion proposal popup for the given elements.
+     *
+     * @param contentAssistant the content assistant feeding this popup
+     * @param viewer the viewer on top of which this popup appears
+     * @param infoController the information control collaborating with this popup
+     * @since 2.0
+     */
+    public this(ContentAssistant contentAssistant, ITextViewer viewer, AdditionalInfoController infoController) {
+        // SWT instance init
+        fDocumentEvents= new ArrayList();
+        fPopupCloser= new PopupCloser();
+        if( fEmptyProposal is null ) fEmptyProposal= new EmptyProposal();
+        fFilterRunnableInit();
+
+        fContentAssistant= contentAssistant;
+        fViewer= viewer;
+        fAdditionalInfoController= infoController;
+        fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fViewer);
+    }
+
+    /**
+     * Creates a new completion proposal popup for the given elements.
+     *
+     * @param contentAssistant the content assistant feeding this popup
+     * @param contentAssistSubjectControl the content assist subject control on top of which this popup appears
+     * @param infoController the information control collaborating with this popup
+     * @since 3.0
+     */
+    public this(ContentAssistant contentAssistant, IContentAssistSubjectControl contentAssistSubjectControl, AdditionalInfoController infoController) {
+        // SWT instance init
+        fDocumentEvents= new ArrayList();
+        fPopupCloser= new PopupCloser();
+        if( fEmptyProposal is null ) fEmptyProposal= new EmptyProposal();
+        fFilterRunnableInit();
+
+        fContentAssistant= contentAssistant;
+        fContentAssistSubjectControl= contentAssistSubjectControl;
+        fAdditionalInfoController= infoController;
+        fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fContentAssistSubjectControl);
+    }
+
+    /**
+     * Computes and presents completion proposals. The flag indicates whether this call has
+     * be made out of an auto activation context.
+     *
+     * @param autoActivated <code>true</code> if auto activation context
+     * @return an error message or <code>null</code> in case of no error
+     */
+    public String showProposals(bool autoActivated) {
+
+        if (fKeyListener is null)
+            fKeyListener= new ProposalSelectionListener();
+
+        final Control control= fContentAssistSubjectControlAdapter.getControl();
+
+        if (!Helper.okToUse(fProposalShell) && control !is null && !control.isDisposed()) {
+            // add the listener before computing the proposals so we don't move the caret
+            // when the user types fast.
+            fContentAssistSubjectControlAdapter.addKeyListener(fKeyListener);
+
+            BusyIndicator.showWhile(control.getDisplay(), dgRunnable((bool autoActivated_) {
+
+                fInvocationOffset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
+                fFilterOffset= fInvocationOffset;
+                fLastCompletionOffset= fFilterOffset;
+                fComputedProposals= computeProposals(fInvocationOffset);
+
+                int count= (fComputedProposals is null ? 0 : fComputedProposals.length);
+                if (count is 0 && hideWhenNoProposals(autoActivated_))
+                    return;
+
+                if (count is 1 && !autoActivated_ && canAutoInsert(fComputedProposals[0])) {
+                    insertProposal(fComputedProposals[0], cast(wchar) 0, 0, fInvocationOffset);
+                    hide();
+                } else {
+                    createProposalSelector();
+                    setProposals(fComputedProposals, false);
+                    displayProposals();
+                }
+            }, autoActivated ));
+        } else {
+            fLastCompletionOffset= fFilterOffset;
+            handleRepeatedInvocation();
+        }
+
+        return getErrorMessage();
+    }
+
+    /**
+     * Hides the popup and returns <code>true</code> if the popup is configured
+     * to never display an empty list. Returns <code>false</code> otherwise.
+     *
+     * @param autoActivated whether the invocation was auto-activated
+     * @return <code>false</code> if an empty list should be displayed, <code>true</code> otherwise
+     * @since 3.2
+     */
+    private bool hideWhenNoProposals(bool autoActivated) {
+        if (autoActivated || !fContentAssistant.isShowEmptyList()) {
+            if (!autoActivated) {
+                Control control= fContentAssistSubjectControlAdapter.getControl();
+                if (control !is null && !control.isDisposed())
+                    control.getDisplay().beep();
+            }
+            hide();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * If content assist is set up to handle cycling, then the proposals are recomputed. Otherwise,
+     * nothing happens.
+     *
+     * @since 3.2
+     */
+    private void handleRepeatedInvocation() {
+        if (fContentAssistant.isRepeatedInvocationMode()) {
+            fComputedProposals= computeProposals(fFilterOffset);
+            setProposals(fComputedProposals, false);
+        }
+    }
+
+    /**
+     * Returns the completion proposal available at the given offset of the
+     * viewer's document. Delegates the work to the content assistant.
+     *
+     * @param offset the offset
+     * @return the completion proposals available at this offset
+     */
+    private ICompletionProposal[] computeProposals(int offset) {
+        if (fContentAssistSubjectControl !is null)
+            return fContentAssistant.computeCompletionProposals(fContentAssistSubjectControl, offset);
+        return fContentAssistant.computeCompletionProposals(fViewer, offset);
+    }
+
+    /**
+     * Returns the error message.
+     *
+     * @return the error message
+     */
+    private String getErrorMessage() {
+        return fContentAssistant.getErrorMessage();
+    }
+
+    /**
+     * Creates the proposal selector.
+     */
+    private void createProposalSelector() {
+        if (Helper.okToUse(fProposalShell))
+            return;
+
+        Control control= fContentAssistSubjectControlAdapter.getControl();
+        fProposalShell= new Shell(control.getShell(), SWT.ON_TOP | SWT.RESIZE );
+        fProposalShell.setFont(JFaceResources.getDefaultFont());
+        if (USE_VIRTUAL) {
+            fProposalTable= new Table(fProposalShell, SWT.H_SCROLL | SWT.V_SCROLL | SWT.VIRTUAL);
+
+            Listener listener= new class()  Listener {
+                public void handleEvent(Event event) {
+                    handleSetData(event);
+                }
+            };
+            fProposalTable.addListener(SWT.SetData, listener);
+        } else {
+            fProposalTable= new Table(fProposalShell, SWT.H_SCROLL | SWT.V_SCROLL);
+        }
+
+        fIsColoredLabelsSupportEnabled= fContentAssistant.isColoredLabelsSupportEnabled();
+        if (fIsColoredLabelsSupportEnabled)
+            TableOwnerDrawSupport.install(fProposalTable);
+
+        fProposalTable.setLocation(0, 0);
+        if (fAdditionalInfoController !is null)
+            fAdditionalInfoController.setSizeConstraints(50, 10, true, true);
+
+        GridLayout layout= new GridLayout();
+        layout.marginWidth= 0;
+        layout.marginHeight= 0;
+        layout.verticalSpacing= 1;
+        fProposalShell.setLayout(layout);
+
+        if (fContentAssistant.isStatusLineVisible()) {
+            createMessageText();
+        }
+
+        GridData data= new GridData(GridData.FILL_BOTH);
+
+        Point size= fContentAssistant.restoreCompletionProposalPopupSize_package();
+        if (size !is null) {
+            fProposalTable.setLayoutData(data);
+            fProposalShell.setSize(size);
+        } else {
+            int height= fProposalTable.getItemHeight() * 10;
+            // use golden ratio as default aspect ratio
+            double aspectRatio= (1 + Math.sqrt(5.0f)) / 2;
+            int width= cast(int) (height * aspectRatio);
+            Rectangle trim= fProposalTable.computeTrim(0, 0, width, height);
+            data.heightHint= trim.height;
+            data.widthHint= trim.width;
+            fProposalTable.setLayoutData(data);
+            fProposalShell.pack();
+        }
+        fContentAssistant.addToLayout(this, fProposalShell, ContentAssistant.LayoutManager.LAYOUT_PROPOSAL_SELECTOR, fContentAssistant.getSelectionOffset());
+
+        fProposalShell.addControlListener(new class()  ControlListener {
+
+            public void controlMoved(ControlEvent e) {}
+
+            public void controlResized(ControlEvent e) {
+                if (fAdditionalInfoController !is null) {
+                    // reset the cached resize constraints
+                    fAdditionalInfoController.setSizeConstraints(50, 10, true, false);
+                }
+
+                fSize= fProposalShell.getSize();
+            }
+        });
+
+        fProposalShell.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_GRAY));
+
+        Color c= getBackgroundColor(control);
+        fProposalTable.setBackground(c);
+
+        c= getForegroundColor(control);
+        fProposalTable.setForeground(c);
+
+        fProposalTable.addSelectionListener(new class()  SelectionListener {
+
+            public void widgetSelected(SelectionEvent e) {}
+
+            public void widgetDefaultSelected(SelectionEvent e) {
+                insertSelectedProposalWithMask(e.stateMask);
+            }
+        });
+
+        fPopupCloser.install(fContentAssistant, fProposalTable, fAdditionalInfoController);
+
+        fProposalShell.addDisposeListener(new class()  DisposeListener {
+            public void widgetDisposed(DisposeEvent e) {
+                unregister(); // but don't dispose the shell, since we're being called from its disposal event!
+            }
+        });
+
+        fProposalTable.setHeaderVisible(false);
+
+        addCommandSupport(fProposalTable);
+    }
+
+    /**
+     * Returns the minimal required height for the proposal, may return 0 if the popup has not been
+     * created yet.
+     *
+     * @return the minimal height
+     * @since 3.3
+     */
+    int getMinimalHeight() {
+        int height= 0;
+        if (Helper.okToUse(fProposalTable)) {
+            int items= fProposalTable.getItemHeight() * 10;
+            Rectangle trim= fProposalTable.computeTrim(0, 0, SWT.DEFAULT, items);
+            height= trim.height;
+        }
+        if (Helper.okToUse(fMessageText))
+            height+= fMessageText.getSize().y + 1;
+        return height;
+    }
+
+    /**
+     * Adds command support to the given control.
+     *
+     * @param control the control to watch for focus
+     * @since 3.2
+     */
+    private void addCommandSupport(Control control) {
+        final KeySequence commandSequence= fContentAssistant.getRepeatedInvocationKeySequence();
+        if (commandSequence !is null && !commandSequence.isEmpty() && fContentAssistant.isRepeatedInvocationMode()) {
+            control.addFocusListener(new class(control,commandSequence)  FocusListener {
+                Control control_;
+                KeySequence commandSequence_;
+                this(Control a, KeySequence b){
+                    control_=a;
+                    commandSequence_=b;
+                }
+                private CommandKeyListener fCommandKeyListener;
+                public void focusGained(FocusEvent e) {
+                    if (Helper.okToUse(control_)) {
+                        if (fCommandKeyListener is null) {
+                            fCommandKeyListener= new CommandKeyListener(commandSequence_);
+                            fProposalTable.addKeyListener(fCommandKeyListener);
+                        }
+                    }
+                }
+                public void focusLost(FocusEvent e) {
+                    if (fCommandKeyListener !is null) {
+                        control_.removeKeyListener(fCommandKeyListener);
+                        fCommandKeyListener= null;
+                    }
+                }
+            });
+        }
+        control.addFocusListener(new class(control)  FocusListener {
+            Control control_;
+            private TraverseListener fTraverseListener;
+            this(Control a){
+                control_=a;
+            }
+            public void focusGained(FocusEvent e) {
+                if (Helper.okToUse(control_)) {
+                    if (fTraverseListener is null) {
+                        fTraverseListener= new class()  TraverseListener {
+                            public void keyTraversed(TraverseEvent event) {
+                                if (event.detail is SWT.TRAVERSE_TAB_NEXT) {
+                                    IInformationControl iControl= fAdditionalInfoController.getCurrentInformationControl2();
+                                    if (fAdditionalInfoController.getInternalAccessor().canReplace(iControl)) {
+                                        fAdditionalInfoController.getInternalAccessor().replaceInformationControl(true);
+                                        event.doit= false;
+                                    }
+                                }
+                            }
+                        };
+                        fProposalTable.addTraverseListener(fTraverseListener);
+                    }
+                }
+            }
+            public void focusLost(FocusEvent e) {
+                if (fTraverseListener !is null) {
+                    control_.removeTraverseListener(fTraverseListener);
+                    fTraverseListener= null;
+                }
+            }
+        });
+    }
+
+    /**
+     * Returns the background color to use.
+     *
+     * @param control the control to get the display from
+     * @return the background color
+     * @since 3.2
+     */
+    private Color getBackgroundColor(Control control) {
+        Color c= fContentAssistant.getProposalSelectorBackground();
+        if (c is null)
+            c= JFaceResources.getColorRegistry().get(JFacePreferences.CONTENT_ASSIST_BACKGROUND_COLOR);
+        return c;
+    }
+
+    /**
+     * Returns the foreground color to use.
+     *
+     * @param control the control to get the display from
+     * @return the foreground color
+     * @since 3.2
+     */
+    private Color getForegroundColor(Control control) {
+        Color c= fContentAssistant.getProposalSelectorForeground();
+        if (c is null)
+            c= JFaceResources.getColorRegistry().get(JFacePreferences.CONTENT_ASSIST_FOREGROUND_COLOR);
+        return c;
+    }
+
+    /**
+     * Creates the caption line under the proposal table.
+     *
+     * @since 3.2
+     */
+    private void createMessageText() {
+        if (fMessageText is null) {
+            fMessageText= new Label(fProposalShell, SWT.RIGHT);
+            GridData textData= new GridData(SWT.FILL, SWT.BOTTOM, true, false);
+            fMessageText.setLayoutData(textData);
+            fMessageText.setText(fContentAssistant.getStatusMessage() ~ " "); //$NON-NLS-1$
+            if (fMessageTextFont is null) {
+                Font font= fMessageText.getFont();
+                Display display= fProposalShell.getDisplay();
+                FontData[] fontDatas= font.getFontData();
+                for (int i= 0; i < fontDatas.length; i++)
+                    fontDatas[i].setHeight(fontDatas[i].getHeight() * 9 / 10);
+                fMessageTextFont= new Font(display, fontDatas);
+            }
+            fMessageText.setFont(fMessageTextFont);
+            fMessageText.setBackground(getBackgroundColor(fProposalShell));
+            fMessageText.setForeground(getForegroundColor(fProposalShell));
+
+            if (fContentAssistant.isRepeatedInvocationMode()) {
+                fMessageText.setCursor(fProposalShell.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
+                fMessageText.addMouseListener(new class()  MouseAdapter {
+                    public void mouseUp(MouseEvent e) {
+                        fLastCompletionOffset= fFilterOffset;
+                        fProposalTable.setFocus();
+                        handleRepeatedInvocation();
+                    }
+
+                    public void mouseDown(MouseEvent e) {
+                    }
+                });
+            }
+        }
+    }
+
+    /*
+     * @since 3.1
+     */
+    private void handleSetData(Event event) {
+        TableItem item= cast(TableItem) event.item;
+        int index= fProposalTable.indexOf(item);
+
+        if (0 <= index && index < fFilteredProposals.length) {
+            ICompletionProposal current= fFilteredProposals[index];
+
+            String displayString;
+            StyleRange[] styleRanges= null;
+            if (fIsColoredLabelsSupportEnabled && cast(ICompletionProposalExtension6)current ) {
+                StyledString styledString= (cast(ICompletionProposalExtension6)current).getStyledDisplayString();
+                displayString= styledString.getString();
+                styleRanges= styledString.getStyleRanges();
+            } else
+                displayString= current.getDisplayString();
+
+            item.setText(displayString);
+            if (fIsColoredLabelsSupportEnabled)
+                TableOwnerDrawSupport.storeStyleRanges(item, 0, styleRanges);
+
+            item.setImage(current.getImage());
+            item.setData(cast(Object)current);
+        } else {
+            // this should not happen, but does on win32
+        }
+    }
+
+    /**
+     * Returns the proposal selected in the proposal selector.
+     *
+     * @return the selected proposal
+     * @since 2.0
+     */
+    private ICompletionProposal getSelectedProposal() {
+        /* Make sure that there is no filter runnable pending.
+         * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=31427
+         */
+        if (fIsFilterPending)
+            fFilterRunnable.run();
+
+        // filter runnable may have hidden the proposals
+        if (!Helper.okToUse(fProposalTable))
+            return null;
+
+        int i= fProposalTable.getSelectionIndex();
+        if (fFilteredProposals is null || i < 0 || i >= fFilteredProposals.length)
+            return null;
+        return fFilteredProposals[i];
+    }
+
+    /**
+     * Takes the selected proposal and applies it.
+     *
+     * @param stateMask the state mask
+     * @since 3.2
+     */
+    private void insertSelectedProposalWithMask(int stateMask) {
+        ICompletionProposal p= getSelectedProposal();
+        hide();
+        if (p !is null)
+            insertProposal(p, cast(wchar) 0, stateMask, fContentAssistSubjectControlAdapter.getSelectedRange().x);
+    }
+
+    /**
+     * Applies the given proposal at the given offset. The given character is the
+     * one that triggered the insertion of this proposal.
+     *
+     * @param p the completion proposal
+     * @param trigger the trigger character
+     * @param stateMask the state mask
+     * @param offset the offset
+     * @since 2.1
+     */
+    private void insertProposal(ICompletionProposal p, char trigger, int stateMask, int offset) {
+
+        fInserting= true;
+        IRewriteTarget target= null;
+        IEditingSupport helper= new class(offset)  IEditingSupport {
+            int offset_;
+            this(int a){
+                offset_=a;
+            }
+            public bool isOriginator(DocumentEvent event, IRegion focus) {
+                return focus.getOffset() <= offset_ && focus.getOffset() + focus.getLength() >= offset_;
+            }
+
+            public bool ownsFocusShell() {
+                return false;
+            }
+
+        };
+
+        try {
+
+            IDocument document= fContentAssistSubjectControlAdapter.getDocument();
+
+            if ( cast(ITextViewerExtension)fViewer ) {
+                ITextViewerExtension extension= cast(ITextViewerExtension) fViewer;
+                target= extension.getRewriteTarget();
+            }
+
+            if (target !is null)
+                target.beginCompoundChange();
+
+            if ( cast(IEditingSupportRegistry)fViewer ) {
+                IEditingSupportRegistry registry= cast(IEditingSupportRegistry) fViewer;
+                registry.register(helper);
+            }
+
+
+            if (cast(ICompletionProposalExtension2)p && fViewer !is null) {
+                ICompletionProposalExtension2 e= cast(ICompletionProposalExtension2) p;
+                e.apply(fViewer, trigger, stateMask, offset);
+            } else if ( cast(ICompletionProposalExtension)p ) {
+                ICompletionProposalExtension e= cast(ICompletionProposalExtension) p;
+                e.apply(document, trigger, offset);
+            } else {
+                p.apply(document);
+            }
+
+            Point selection= p.getSelection(document);
+            if (selection !is null) {
+                fContentAssistSubjectControlAdapter.setSelectedRange(selection.x, selection.y);
+                fContentAssistSubjectControlAdapter.revealRange(selection.x, selection.y);
+            }
+
+            IContextInformation info= p.getContextInformation();
+            if (info !is null) {
+
+                int contextInformationOffset;
+                if ( cast(ICompletionProposalExtension)p ) {
+                    ICompletionProposalExtension e= cast(ICompletionProposalExtension) p;
+                    contextInformationOffset= e.getContextInformationPosition();
+                } else {
+                    if (selection is null)
+                        selection= fContentAssistSubjectControlAdapter.getSelectedRange();
+                    contextInformationOffset= selection.x + selection.y;
+                }
+
+                fContentAssistant.showContextInformation(info, contextInformationOffset);
+            } else
+                fContentAssistant.showContextInformation(null, -1);
+
+
+        } finally {
+            if (target !is null)
+                target.endCompoundChange();
+
+            if ( cast(IEditingSupportRegistry)fViewer ) {
+                IEditingSupportRegistry registry= cast(IEditingSupportRegistry) fViewer;
+                registry.unregister(helper);
+            }
+            fInserting= false;
+        }
+    }
+
+    /**
+     * Returns whether this popup has the focus.
+     *
+     * @return <code>true</code> if the popup has the focus
+     */
+    public bool hasFocus() {
+        if (Helper.okToUse(fProposalShell)) {
+            if ((fProposalShell.isFocusControl() || fProposalTable.isFocusControl()))
+                return true;
+            /*
+             * We have to delegate this query to the additional info controller
+             * as well, since the content assistant is the widget token owner
+             * and its closer does not know that the additional info control can
+             * now also take focus.
+             */
+            if (fAdditionalInfoController !is null) {
+                IInformationControl informationControl= fAdditionalInfoController.getCurrentInformationControl2();
+                if (informationControl !is null && informationControl.isFocusControl())
+                    return true;
+                InformationControlReplacer replacer= fAdditionalInfoController.getInternalAccessor().getInformationControlReplacer();
+                if (replacer !is null) {
+                    informationControl= replacer.getCurrentInformationControl2();
+                    if (informationControl !is null && informationControl.isFocusControl())
+                        return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Hides this popup.
+     */
+    public void hide() {
+
+        unregister();
+
+        if ( cast(IEditingSupportRegistry)fViewer ) {
+            IEditingSupportRegistry registry= cast(IEditingSupportRegistry) fViewer;
+            registry.unregister(fFocusHelper);
+        }
+
+        if (Helper.okToUse(fProposalShell)) {
+
+            fContentAssistant.removeContentAssistListener(this, ContentAssistant.PROPOSAL_SELECTOR);
+
+            fPopupCloser.uninstall();
+            fProposalShell.setVisible(false);
+            fProposalShell.dispose();
+            fProposalShell= null;
+        }
+
+        if (fMessageTextFont !is null) {
+            fMessageTextFont.dispose();
+            fMessageTextFont= null;
+        }
+
+        if (fMessageText !is null) {
+            fMessageText= null;
+        }
+
+        fEmptyMessage= null;
+
+        fLastCompletionOffset= -1;
+
+        fContentAssistant.fireSessionEndEvent();
+    }
+
+    /**
+     * Unregister this completion proposal popup.
+     *
+     * @since 3.0
+     */
+    private void unregister() {
+        if (fDocumentListener !is null) {
+            IDocument document= fContentAssistSubjectControlAdapter.getDocument();
+            if (document !is null)
+                document.removeDocumentListener(fDocumentListener);
+            fDocumentListener= null;
+        }
+        fDocumentEvents.clear();
+
+        if (fKeyListener !is null && fContentAssistSubjectControlAdapter.getControl() !is null && !fContentAssistSubjectControlAdapter.getControl().isDisposed()) {
+            fContentAssistSubjectControlAdapter.removeKeyListener(fKeyListener);
+            fKeyListener= null;
+        }
+
+        if (fLastProposal !is null) {
+            if (cast(ICompletionProposalExtension2)fLastProposal  && fViewer !is null) {
+                ICompletionProposalExtension2 extension= cast(ICompletionProposalExtension2) fLastProposal;
+                extension.unselected(fViewer);
+            }
+            fLastProposal= null;
+        }
+
+        fFilteredProposals= null;
+        fComputedProposals= null;
+
+        fContentAssistant.possibleCompletionsClosed_package();
+    }
+
+    /**
+     *Returns whether this popup is active. It is active if the proposal selector is visible.
+     *
+     * @return <code>true</code> if this popup is active
+     */
+    public bool isActive() {
+        return fProposalShell !is null && !fProposalShell.isDisposed();
+    }
+
+    /**
+     * Initializes the proposal selector with these given proposals.
+     * @param proposals the proposals
+     * @param isFilteredSubset if <code>true</code>, the proposal table is
+     *        not cleared, but the proposals that are not in the passed array
+     *        are removed from the displayed set
+     */
+    private void setProposals(ICompletionProposal[] proposals, bool isFilteredSubset) {
+        ICompletionProposal[] oldProposals= fFilteredProposals;
+        ICompletionProposal oldProposal= getSelectedProposal(); // may trigger filtering and a reentrant call to setProposals()
+        if (oldProposals !is fFilteredProposals) // reentrant call was first - abort
+            return;
+
+        if (Helper.okToUse(fProposalTable)) {
+            if (cast(ICompletionProposalExtension2)oldProposal  && fViewer !is null)
+                (cast(ICompletionProposalExtension2) oldProposal).unselected(fViewer);
+
+            if (proposals is null || proposals.length is 0) {
+                fEmptyProposal.fOffset= fFilterOffset;
+                fEmptyProposal.fDisplayString= fEmptyMessage !is null ? fEmptyMessage : JFaceTextMessages.getString("CompletionProposalPopup.no_proposals"); //$NON-NLS-1$
+                proposals= [ fEmptyProposal ];
+            }
+
+            fFilteredProposals= proposals;
+            final int newLen= proposals.length;
+            if (USE_VIRTUAL) {
+                fProposalTable.setItemCount(newLen);
+                fProposalTable.clearAll();
+            } else {
+                fProposalTable.setRedraw(false);
+                fProposalTable.setItemCount(newLen);
+                TableItem[] items= fProposalTable.getItems();
+                for (int i= 0; i < items.length; i++) {
+                    TableItem item= items[i];
+                    ICompletionProposal proposal= proposals[i];
+                    item.setText(proposal.getDisplayString());
+                    item.setImage(proposal.getImage());
+                    item.setData(cast(Object)proposal);
+                }
+                fProposalTable.setRedraw(true);
+            }
+
+            Point currentLocation= fProposalShell.getLocation();
+            Point newLocation= getLocation();
+            if ((newLocation.x < currentLocation.x && newLocation.y is currentLocation.y) || newLocation.y < currentLocation.y)
+                fProposalShell.setLocation(newLocation);
+
+            selectProposal(0, false);
+        }
+    }
+
+    /**
+     * Returns the graphical location at which this popup should be made visible.
+     *
+     * @return the location of this popup
+     */
+    private Point getLocation() {
+        int caret= fContentAssistSubjectControlAdapter.getCaretOffset();
+        Rectangle location= fContentAssistant.getLayoutManager().computeBoundsBelowAbove_package(fProposalShell, fSize is null ? fProposalShell.getSize() : fSize, caret, this);
+        return Geometry.getLocation(location);
+    }
+
+    /**
+     * Returns the size of this completion proposal popup.
+     *
+     * @return a Point containing the size
+     * @since 3.0
+     */
+    Point getSize() {
+        return fSize;
+    }
+
+    /**
+     * Displays this popup and install the additional info controller, so that additional info
+     * is displayed when a proposal is selected and additional info is available.
+     */
+    private void displayProposals() {
+
+        if (!Helper.okToUse(fProposalShell) ||  !Helper.okToUse(fProposalTable))
+            return;
+
+        if (fContentAssistant.addContentAssistListener(this, ContentAssistant.PROPOSAL_SELECTOR)) {
+
+            ensureDocumentListenerInstalled();
+
+            if (fFocusHelper is null) {
+                fFocusHelper= new class()  IEditingSupport {
+
+                    public bool isOriginator(DocumentEvent event, IRegion focus) {
+                        return false; // this helper just covers the focus change to the proposal shell, no remote editions
+                    }
+
+                    public bool ownsFocusShell() {
+                        return true;
+                    }
+
+                };
+            }
+            if ( cast(IEditingSupportRegistry)fViewer ) {
+                IEditingSupportRegistry registry= cast(IEditingSupportRegistry) fViewer;
+                registry.register(fFocusHelper);
+            }
+
+
+            /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=52646
+             * on GTK, setVisible and such may run the event loop
+             * (see also https://bugs.eclipse.org/bugs/show_bug.cgi?id=47511)
+             * Since the user may have already canceled the popup or selected
+             * an entry (ESC or RETURN), we have to double check whether
+             * the table is still okToUse. See comments below
+             */
+            fProposalShell.setVisible(true); // may run event loop on GTK
+            // transfer focus since no verify key listener can be attached
+            if (!fContentAssistSubjectControlAdapter.supportsVerifyKeyListener() && Helper.okToUse(fProposalShell))
+                fProposalShell.setFocus(); // may run event loop on GTK ??
+
+            if (fAdditionalInfoController !is null && Helper.okToUse(fProposalTable)) {
+                fAdditionalInfoController.install(fProposalTable);
+                fAdditionalInfoController.handleTableSelectionChanged();
+            }
+        } else
+            hide();
+    }
+
+    /**
+     * Installs the document listener if not already done.
+     *
+     * @since 3.2
+     */
+    private void ensureDocumentListenerInstalled() {
+        if (fDocumentListener is null) {
+            fDocumentListener=  new class()   IDocumentListener {
+                public void documentAboutToBeChanged(DocumentEvent event) {
+                    if (!fInserting)
+                        fDocumentEvents.add(event);
+                }
+
+                public void documentChanged(DocumentEvent event) {
+                    if (!fInserting)
+                        filterProposals();
+                }
+            };
+            IDocument document= fContentAssistSubjectControlAdapter.getDocument();
+            if (document !is null)
+                document.addDocumentListener(fDocumentListener);
+        }
+    }
+
+    /*
+     * @see IContentAssistListener#verifyKey(VerifyEvent)
+     */
+    public bool verifyKey(VerifyEvent e) {
+        if (!Helper.okToUse(fProposalShell))
+            return true;
+
+        char key= e.character;
+        if (key is 0) {
+            int newSelection= fProposalTable.getSelectionIndex();
+            int visibleRows= (fProposalTable.getSize().y / fProposalTable.getItemHeight()) - 1;
+            int itemCount= fProposalTable.getItemCount();
+            bool smartToggle= false;
+            switch (e.keyCode) {
+
+                case SWT.ARROW_LEFT :
+                case SWT.ARROW_RIGHT :
+                    filterProposals();
+                    return true;
+
+                case SWT.ARROW_UP :
+                    newSelection -= 1;
+                    if (newSelection < 0)
+                        newSelection= itemCount - 1;
+                    break;
+
+                case SWT.ARROW_DOWN :
+                    newSelection += 1;
+                    if (newSelection > itemCount - 1)
+                        newSelection= 0;
+                    break;
+
+                case SWT.PAGE_DOWN :
+                    newSelection += visibleRows;
+                    if (newSelection >= itemCount)
+                        newSelection= itemCount - 1;
+                    break;
+
+                case SWT.PAGE_UP :
+                    newSelection -= visibleRows;
+                    if (newSelection < 0)
+                        newSelection= 0;
+                    break;
+
+                case SWT.HOME :
+                    newSelection= 0;
+                    break;
+
+                case SWT.END :
+                    newSelection= itemCount - 1;
+                    break;
+
+                default :
+                    if (e.keyCode !is SWT.CAPS_LOCK && e.keyCode !is SWT.MOD1 && e.keyCode !is SWT.MOD2 && e.keyCode !is SWT.MOD3 && e.keyCode !is SWT.MOD4)
+                        hide();
+                    return true;
+            }
+
+            selectProposal(newSelection, smartToggle);
+
+            e.doit= false;
+            return false;
+
+        }
+
+        // key !is 0
+        switch (key) {
+            case 0x1B: // Esc
+                e.doit= false;
+                hide();
+                break;
+
+            case '\n': // Ctrl-Enter on w2k
+            case '\r': // Enter
+                e.doit= false;
+                insertSelectedProposalWithMask(e.stateMask);
+                break;
+
+            case '\t':
+                e.doit= false;
+                fProposalShell.setFocus();
+                return false;
+
+            default:
+                ICompletionProposal p= getSelectedProposal();
+                if ( cast(ICompletionProposalExtension)p ) {
+                    ICompletionProposalExtension t= cast(ICompletionProposalExtension) p;
+                    char[] triggers= t.getTriggerCharacters();
+                    if (contains(triggers, key)) {
+                        e.doit= false;
+                        hide();
+                        insertProposal(p, key, e.stateMask, fContentAssistSubjectControlAdapter.getSelectedRange().x);
+                    }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Selects the entry with the given index in the proposal selector and feeds
+     * the selection to the additional info controller.
+     *
+     * @param index the index in the list
+     * @param smartToggle <code>true</code> if the smart toggle key has been pressed
+     * @since 2.1
+     */
+    private void selectProposal(int index, bool smartToggle) {
+
+        ICompletionProposal oldProposal= getSelectedProposal();
+        if (cast(ICompletionProposalExtension2)oldProposal  && fViewer !is null)
+            (cast(ICompletionProposalExtension2) oldProposal).unselected(fViewer);
+
+        if (fFilteredProposals is null) {
+            fireSelectionEvent(null, smartToggle);
+            return;
+        }
+
+        ICompletionProposal proposal= fFilteredProposals[index];
+        if (cast(ICompletionProposalExtension2)proposal && fViewer !is null)
+            (cast(ICompletionProposalExtension2) proposal).selected(fViewer, smartToggle);
+
+        fireSelectionEvent(proposal, smartToggle);
+
+        fLastProposal= proposal;
+
+        fProposalTable.setSelection(index);
+        fProposalTable.showSelection();
+        if (fAdditionalInfoController !is null)
+            fAdditionalInfoController.handleTableSelectionChanged();
+    }
+
+    /**
+     * Fires a selection event, see {@link ICompletionListener}.
+     *
+     * @param proposal the selected proposal, possibly <code>null</code>
+     * @param smartToggle true if the smart toggle is on
+     * @since 3.2
+     */
+    private void fireSelectionEvent(ICompletionProposal proposal, bool smartToggle) {
+        fContentAssistant.fireSelectionEvent(proposal, smartToggle);
+    }
+
+    /**
+     * Returns whether the given character is contained in the given array of
+     * characters.
+     *
+     * @param characters the list of characters
+     * @param c the character to look for in the list
+     * @return <code>true</code> if character belongs to the list
+     * @since 2.0
+     */
+    private bool contains(char[] characters, char c) {
+
+        if (characters is null)
+            return false;
+
+        for (int i= 0; i < characters.length; i++) {
+            if (c is characters[i])
+                return true;
+        }
+
+        return false;
+    }
+
+    /*
+     * @see IEventConsumer#processEvent(VerifyEvent)
+     */
+    public void processEvent(VerifyEvent e) {
+    }
+
+    /**
+     * Filters the displayed proposal based on the given cursor position and the
+     * offset of the original invocation of the content assistant.
+     */
+    private void filterProposals() {
+        if (!fIsFilterPending) {
+            fIsFilterPending= true;
+            Control control= fContentAssistSubjectControlAdapter.getControl();
+            control.getDisplay().asyncExec(fFilterRunnable);
+        }
+    }
+
+    /**
+     * Computes the subset of already computed proposals that are still valid for
+     * the given offset.
+     *
+     * @param offset the offset
+     * @param event the merged document event
+     * @return the set of filtered proposals
+     * @since 3.0
+     */
+    private ICompletionProposal[] computeFilteredProposals(int offset, DocumentEvent event) {
+
+        if (offset is fInvocationOffset && event is null) {
+            fIsFilteredSubset= false;
+            return fComputedProposals;
+        }
+
+        if (offset < fInvocationOffset) {
+            fIsFilteredSubset= false;
+            fInvocationOffset= offset;
+            fContentAssistant.fireSessionRestartEvent();
+            fComputedProposals= computeProposals(fInvocationOffset);
+            return fComputedProposals;
+        }
+
+        ICompletionProposal[] proposals;
+        if (offset < fFilterOffset) {
+            proposals= fComputedProposals;
+            fIsFilteredSubset= false;
+        } else {
+            proposals= fFilteredProposals;
+            fIsFilteredSubset= true;
+        }
+
+        if (proposals is null) {
+            fIsFilteredSubset= false;
+            return null;
+        }
+
+        IDocument document= fContentAssistSubjectControlAdapter.getDocument();
+        int length= proposals.length;
+        List filtered= new ArrayList(length);
+        for (int i= 0; i < length; i++) {
+
+            if (cast(ICompletionProposalExtension2)proposals[i] ) {
+
+                ICompletionProposalExtension2 p= cast(ICompletionProposalExtension2) proposals[i];
+                if (p.validate(document, offset, event))
+                    filtered.add(cast(Object)p);
+
+            } else if (cast(ICompletionProposalExtension)proposals[i] ) {
+
+                ICompletionProposalExtension p= cast(ICompletionProposalExtension) proposals[i];
+                if (p.isValidFor(document, offset))
+                    filtered.add(cast(Object)p);
+
+            } else {
+                // restore original behavior
+                fIsFilteredSubset= false;
+                fInvocationOffset= offset;
+                fContentAssistant.fireSessionRestartEvent();
+                fComputedProposals= computeProposals(fInvocationOffset);
+                return fComputedProposals;
+            }
+        }
+
+        return arraycast!(ICompletionProposal)( filtered.toArray());
+    }
+
+    /**
+     * Requests the proposal shell to take focus.
+     *
+     * @since 3.0
+     */
+    public void setFocus() {
+        if (Helper.okToUse(fProposalShell)) {
+            fProposalShell.setFocus();
+        }
+    }
+
+    /**
+     * Returns <code>true</code> if <code>proposal</code> should be auto-inserted,
+     * <code>false</code> otherwise.
+     *
+     * @param proposal the single proposal that might be automatically inserted
+     * @return <code>true</code> if <code>proposal</code> can be inserted automatically,
+     *         <code>false</code> otherwise
+     * @since 3.1
+     */
+    private bool canAutoInsert(ICompletionProposal proposal) {
+        if (fContentAssistant.isAutoInserting()) {
+            if ( cast(ICompletionProposalExtension4)proposal ) {
+                ICompletionProposalExtension4 ext= cast(ICompletionProposalExtension4) proposal;
+                return ext.isAutoInsertable();
+            }
+            return true; // default behavior before ICompletionProposalExtension4 was introduced
+        }
+        return false;
+    }
+
+    /**
+     * Completes the common prefix of all proposals directly in the code. If no
+     * common prefix can be found, the proposal popup is shown.
+     *
+     * @return an error message if completion failed.
+     * @since 3.0
+     */
+    public String incrementalComplete() {
+        if (Helper.okToUse(fProposalShell) && fFilteredProposals !is null) {
+            if (fLastCompletionOffset is fFilterOffset) {
+                handleRepeatedInvocation();
+            } else {
+                fLastCompletionOffset= fFilterOffset;
+                completeCommonPrefix();
+            }
+        } else {
+            final Control control= fContentAssistSubjectControlAdapter.getControl();
+
+            if (fKeyListener is null)
+                fKeyListener= new ProposalSelectionListener();
+
+            if (!Helper.okToUse(fProposalShell) && !control.isDisposed())
+                fContentAssistSubjectControlAdapter.addKeyListener(fKeyListener);
+
+            BusyIndicator.showWhile(control.getDisplay(), new class()  Runnable {
+                public void run() {
+
+                    fInvocationOffset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
+                    fFilterOffset= fInvocationOffset;
+                    fLastCompletionOffset= fFilterOffset;
+                    fFilteredProposals= computeProposals(fInvocationOffset);
+
+                    int count= (fFilteredProposals is null ? 0 : fFilteredProposals.length);
+                    if (count is 0 && hideWhenNoProposals(false))
+                        return;
+
+                    if (count is 1 && canAutoInsert(fFilteredProposals[0])) {
+                        insertProposal(fFilteredProposals[0], cast(wchar) 0, 0, fInvocationOffset);
+                        hide();
+                    } else {
+                        ensureDocumentListenerInstalled();
+                        if (count > 0 && completeCommonPrefix())
+                            hide();
+                        else {
+                            fComputedProposals= fFilteredProposals;
+                            createProposalSelector();
+                            setProposals(fComputedProposals, false);
+                            displayProposals();
+                        }
+                    }
+                }
+            });
+        }
+        return getErrorMessage();
+    }
+
+    /**
+     * Acts upon <code>fFilteredProposals</code>: if there is just one valid
+     * proposal, it is inserted, otherwise, the common prefix of all proposals
+     * is inserted into the document. If there is no common prefix, nothing
+     * happens and <code>false</code> is returned.
+     *
+     * @return <code>true</code> if a single proposal was inserted and the
+     *         selector can be closed, <code>false</code> otherwise
+     * @since 3.0
+     */
+    private bool completeCommonPrefix() {
+
+        // 0: insert single proposals
+        if (fFilteredProposals.length is 1) {
+            if (canAutoInsert(fFilteredProposals[0])) {
+                insertProposal(fFilteredProposals[0], cast(wchar) 0, 0, fFilterOffset);
+                hide();
+                return true;
+            }
+            return false;
+        }
+
+        // 1: extract pre- and postfix from all remaining proposals
+        IDocument document= fContentAssistSubjectControlAdapter.getDocument();
+
+        // contains the common postfix in the case that there are any proposals matching our LHS
+        StringBuffer rightCasePostfix;
+        List rightCase= new ArrayList();
+
+        bool isWrongCaseMatch= false;
+
+        // the prefix of all case insensitive matches. This differs from the document
+        // contents and will be replaced.
+        CharSequence wrongCasePrefix= null;
+        int wrongCasePrefixStart= 0;
+        // contains the common postfix of all case-insensitive matches
+        StringBuffer wrongCasePostfix;
+        List wrongCase= new ArrayList();
+
+        for (int i= 0; i < fFilteredProposals.length; i++) {
+            ICompletionProposal proposal= fFilteredProposals[i];
+
+            if (!( cast(ICompletionProposalExtension3)proposal ))
+                return false;
+
+            int start= (cast(ICompletionProposalExtension3)proposal).getPrefixCompletionStart(fContentAssistSubjectControlAdapter.getDocument(), fFilterOffset);
+            CharSequence insertion= (cast(ICompletionProposalExtension3)proposal).getPrefixCompletionText(fContentAssistSubjectControlAdapter.getDocument(), fFilterOffset);
+            if (insertion is null)
+                insertion= new StringCharSequence(proposal.getDisplayString());
+            try {
+                int prefixLength= fFilterOffset - start;
+                int relativeCompletionOffset= Math.min(insertion.length(), prefixLength);
+                String prefix= document.get(start, prefixLength);
+                if (!isWrongCaseMatch && insertion.toString().startsWith(prefix)) {
+                    isWrongCaseMatch= false;
+                    rightCase.add(cast(Object)proposal);
+                    CharSequence newPostfix= insertion.subSequence(relativeCompletionOffset, insertion.length());
+                    if (rightCasePostfix is null)
+                        rightCasePostfix= new StringBuffer(newPostfix.toString());
+                    else
+                        truncatePostfix(rightCasePostfix, newPostfix);
+                } else if (i is 0 || isWrongCaseMatch) {
+                    CharSequence newPrefix= insertion.subSequence(0, relativeCompletionOffset);
+                    if (isPrefixCompatible(wrongCasePrefix, wrongCasePrefixStart, newPrefix, start, document)) {
+                        isWrongCaseMatch= true;
+                        wrongCasePrefix= newPrefix;
+                        wrongCasePrefixStart= start;
+                        CharSequence newPostfix= insertion.subSequence(relativeCompletionOffset, insertion.length());
+                        if (wrongCasePostfix is null)
+                            wrongCasePostfix= new StringBuffer(newPostfix.toString());
+                        else
+                            truncatePostfix(wrongCasePostfix, newPostfix);
+                        wrongCase.add(cast(Object)proposal);
+                    } else {
+                        return false;
+                    }
+                } else
+                    return false;
+            } catch (BadLocationException e2) {
+                // bail out silently
+                return false;
+            }
+
+            if (rightCasePostfix !is null && rightCasePostfix.length() is 0 && rightCase.size() > 1)
+                return false;
+        }
+
+        // 2: replace single proposals
+
+        if (rightCase.size() is 1) {
+            ICompletionProposal proposal= cast(ICompletionProposal) rightCase.get(0);
+            if (canAutoInsert(proposal) && rightCasePostfix.length() > 0) {
+                insertProposal(proposal, cast(wchar) 0, 0, fInvocationOffset);
+                hide();
+                return true;
+            }
+            return false;
+        } else if (isWrongCaseMatch && wrongCase.size() is 1) {
+            ICompletionProposal proposal= cast(ICompletionProposal) wrongCase.get(0);
+            if (canAutoInsert(proposal)) {
+                insertProposal(proposal, cast(wchar) 0, 0, fInvocationOffset);
+                hide();
+            return true;
+            }
+            return false;
+        }
+
+        // 3: replace post- / prefixes
+
+        CharSequence prefix;
+        if (isWrongCaseMatch)
+            prefix= wrongCasePrefix;
+        else
+            prefix= new StringCharSequence("");  //$NON-NLS-1$
+
+        CharSequence postfix;
+        if (isWrongCaseMatch)
+            postfix= new StringCharSequence(wrongCasePostfix.toString);
+        else
+            postfix= new StringCharSequence(rightCasePostfix.toString);
+
+        if (prefix is null || postfix is null)
+            return false;
+
+        try {
+            // 4: check if parts of the postfix are already in the document
+            int to= Math.min(document.getLength(), fFilterOffset + postfix.length());
+            StringBuffer inDocument= new StringBuffer(document.get(fFilterOffset, to - fFilterOffset));
+            truncatePostfix(inDocument, postfix);
+
+            // 5: replace and reveal
+            document.replace(fFilterOffset - prefix.length(), prefix.length() + inDocument.length(), prefix.toString() ~ postfix.toString());
+
+            fContentAssistSubjectControlAdapter.setSelectedRange(fFilterOffset + postfix.length(), 0);
+            fContentAssistSubjectControlAdapter.revealRange(fFilterOffset + postfix.length(), 0);
+            fFilterOffset+= postfix.length();
+            fLastCompletionOffset= fFilterOffset;
+
+            return false;
+        } catch (BadLocationException e) {
+            // ignore and return false
+            return false;
+        }
+    }
+
+    /*
+     * @since 3.1
+     */
+    private bool isPrefixCompatible(CharSequence oneSequence, int oneOffset, CharSequence twoSequence, int twoOffset, IDocument document)  {
+        if (oneSequence is null || twoSequence is null)
+            return true;
+
+        int min= Math.min(oneOffset, twoOffset);
+        int oneEnd= oneOffset + oneSequence.length();
+        int twoEnd= twoOffset + twoSequence.length();
+
+        String one= document.get(oneOffset, min - oneOffset) ~ oneSequence.toString ~ document.get(oneEnd, Math.min(fFilterOffset, fFilterOffset - oneEnd));
+        String two= document.get(twoOffset, min - twoOffset) ~ twoSequence.toString ~ document.get(twoEnd, Math.min(fFilterOffset, fFilterOffset - twoEnd));
+
+        return one.equals(two);
+    }
+
+    /**
+     * Truncates <code>buffer</code> to the common prefix of <code>buffer</code>
+     * and <code>sequence</code>.
+     *
+     * @param buffer the common postfix to truncate
+     * @param sequence the characters to truncate with
+     */
+    private void truncatePostfix(StringBuffer buffer, CharSequence sequence) {
+        // find common prefix
+        int min= Math.min(buffer.length(), sequence.length());
+        for (int c= 0; c < min; c++) {
+            if (sequence.charAt(c) !is buffer.slice()[c]) {
+                buffer.truncate(c);
+                return;
+            }
+        }
+
+        // all equal up to minimum
+        buffer.truncate(min);
+    }
+
+    /**
+     * Sets the message for the repetition affordance text at the bottom of the proposal. Only has
+     * an effect if {@link ContentAssistant#isRepeatedInvocationMode()} returns <code>true</code>.
+     *
+     * @param message the new caption
+     * @since 3.2
+     */
+    void setMessage(String message) {
+        Assert.isNotNull(message);
+        if (isActive() && fMessageText !is null)
+            fMessageText.setText(message ~ " "); //$NON-NLS-1$
+    }
+
+    /**
+     * Sets the text to be displayed if no proposals are available. Only has an effect if
+     * {@link ContentAssistant#isShowEmptyList()} returns <code>true</code>.
+     *
+     * @param message the empty message
+     * @since 3.2
+     */
+    void setEmptyMessage(String message) {
+        Assert.isNotNull(message);
+        fEmptyMessage= message;
+    }
+
+    /**
+     * Enables or disables showing of the caption line. See also {@link #setMessage(String)}.
+     *
+     * @param show
+     * @since 3.2
+     */
+    public void setStatusLineVisible(bool show) {
+        if (!isActive() || show is (fMessageText !is null))
+            return; // nothing to do
+
+        if (show) {
+            createMessageText();
+        } else {
+            fMessageText.dispose();
+            fMessageText= null;
+        }
+        fProposalShell.layout();
+    }
+
+    /**
+     * Informs the popup that it is being placed above the caret line instead of below.
+     *
+     * @param above <code>true</code> if the location of the popup is above the caret line, <code>false</code> if it is below
+     * @since 3.3
+     */
+    void switchedPositionToAbove(bool above) {
+        if (fAdditionalInfoController !is null) {
+            fAdditionalInfoController.setFallbackAnchors([
+                    AbstractInformationControlManager.ANCHOR_RIGHT,
+                    AbstractInformationControlManager.ANCHOR_LEFT,
+                    above ? AbstractInformationControlManager.ANCHOR_TOP : AbstractInformationControlManager.ANCHOR_BOTTOM
+            ]);
+        }
+    }
+
+    /**
+     * Returns a new proposal selection handler.
+     *
+     * @param operationCode the operation code
+     * @return the handler
+     * @since 3.4
+     */
+    IHandler createProposalSelectionHandler(int operationCode) {
+        return new ProposalSelectionHandler(operationCode);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * 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
+ *     Anton Leherbauer (Wind River Systems) - [content assist][api] ContentAssistEvent should contain information about auto activation - https://bugs.eclipse.org/bugs/show_bug.cgi?id=193728
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.contentassist.ContentAssistEvent;
+
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Describes the state that the content assistant is in when completing proposals.
+ * <p>
+ * Clients may use this class.
+ * </p>
+ * 
+ * @since 3.2
+ * @see ICompletionListener
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public final class ContentAssistEvent {
+    /**
+     * Creates a new event.
+     * 
+     * @param ca the assistant
+     * @param proc the processor
+     * @param isAutoActivated whether content assist was triggered by auto activation
+     * @since 3.4
+     */
+    this(IContentAssistant ca, IContentAssistProcessor proc, bool isAutoActivated) {
+        assistant= ca;
+        processor= proc;
+        this.isAutoActivated= isAutoActivated;
+    }
+
+    /**
+     * Creates a new event.
+     * 
+     * @param ca the assistant
+     * @param proc the processor
+     */
+    this(ContentAssistant ca, IContentAssistProcessor proc) {
+        this(ca, proc, false);
+    }
+
+    /**
+     * The content assistant computing proposals.
+     */
+    public const IContentAssistant assistant;
+    /**
+     * The processor for the current partition.
+     */
+    public const IContentAssistProcessor processor;
+    /**
+     * Tells, whether content assist was triggered by auto activation.
+     * <p>
+     * <strong>Note:</strong> This flag is only valid in {@link ICompletionListener#assistSessionStarted(ContentAssistEvent)}.
+     * </p>
+     * 
+     * @since 3.4
+     */
+    public const bool isAutoActivated;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistSubjectControlAdapter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,451 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.custom.VerifyKeyListener;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
+import org.eclipse.jface.contentassist.ISubjectControlContextInformationPresenter;
+import org.eclipse.jface.contentassist.ISubjectControlContextInformationValidator;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IEventConsumer;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension;
+import org.eclipse.jface.text.contentassist.ContextInformationPopup;
+
+
+/**
+ * This content assist adapter delegates the calls either to
+ * a text viewer or to a content assist subject control.
+ *
+ * @since 3.0
+ */
+final class ContentAssistSubjectControlAdapter : IContentAssistSubjectControl {
+
+    /**
+     * The text viewer which is used as content assist subject control.
+     */
+    private ITextViewer fViewer;
+
+    /**
+     * The content assist subject control.
+     */
+    private IContentAssistSubjectControl fContentAssistSubjectControl;
+
+
+    /**
+     * Creates an adapter for the given content assist subject control.
+     *
+     * @param contentAssistSubjectControl the content assist subject control
+     */
+    this(IContentAssistSubjectControl contentAssistSubjectControl) {
+        Assert.isNotNull(cast(Object)contentAssistSubjectControl);
+        fContentAssistSubjectControl= contentAssistSubjectControl;
+    }
+
+    /**
+     * Creates an adapter for the given text viewer.
+     *
+     * @param viewer the text viewer
+     */
+    public this(ITextViewer viewer) {
+        Assert.isNotNull(cast(Object)viewer);
+        fViewer= viewer;
+    }
+
+    /*
+     * @see IContentAssistSubjectControl#getLineHeight()
+     */
+    public int getLineHeight() {
+        if (fContentAssistSubjectControl !is null)
+            return fContentAssistSubjectControl.getLineHeight();
+
+        return fViewer.getTextWidget().getLineHeight(getCaretOffset());
+    }
+
+    /*
+     * @see IContentAssistSubjectControl#getControl()
+     */
+    public Control getControl() {
+        if (fContentAssistSubjectControl !is null)
+            return fContentAssistSubjectControl.getControl();
+        return fViewer.getTextWidget();
+    }
+
+    /*
+     * @see IContentAssistSubjectControl#getLocationAtOffset(int)
+     */
+    public Point getLocationAtOffset(int offset) {
+        if (fContentAssistSubjectControl !is null)
+            return fContentAssistSubjectControl.getLocationAtOffset(offset);
+        return fViewer.getTextWidget().getLocationAtOffset(offset);
+    }
+
+    /*
+     * @see IContentAssistSubjectControl#getWidgetSelectionRange()
+     */
+    public Point getWidgetSelectionRange() {
+        if (fContentAssistSubjectControl !is null)
+            return fContentAssistSubjectControl.getWidgetSelectionRange();
+        return fViewer.getTextWidget().getSelectionRange();
+    }
+
+    /*
+     * @see IContentAssistSubjectControl#getSelectedRange()
+     */
+    public Point getSelectedRange() {
+        if (fContentAssistSubjectControl !is null)
+            return fContentAssistSubjectControl.getSelectedRange();
+        return fViewer.getSelectedRange();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistSubjectControl#getCaretOffset()
+     */
+    public int getCaretOffset() {
+        if (fContentAssistSubjectControl !is null)
+            return fContentAssistSubjectControl.getCaretOffset();
+        return fViewer.getTextWidget().getCaretOffset();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistSubjectControl#getLineDelimiter()
+     */
+    public String getLineDelimiter() {
+        if (fContentAssistSubjectControl !is null)
+            return fContentAssistSubjectControl.getLineDelimiter();
+        return fViewer.getTextWidget().getLineDelimiter();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistSubjectControl#addKeyListener(org.eclipse.swt.events.KeyListener)
+     */
+    public void addKeyListener(KeyListener keyListener) {
+        if (fContentAssistSubjectControl !is null)
+            fContentAssistSubjectControl.addKeyListener(keyListener);
+        else
+            fViewer.getTextWidget().addKeyListener(keyListener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistSubjectControl#removeKeyListener(org.eclipse.swt.events.KeyListener)
+     */
+    public void removeKeyListener(KeyListener keyListener) {
+        if (fContentAssistSubjectControl !is null)
+            fContentAssistSubjectControl.removeKeyListener(keyListener);
+        else
+            fViewer.getTextWidget().removeKeyListener(keyListener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistSubjectControl#getDocument()
+     */
+    public IDocument getDocument() {
+        if (fContentAssistSubjectControl !is null)
+            return fContentAssistSubjectControl.getDocument();
+        return fViewer.getDocument();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistSubjectControl#prependVerifyKeyListener(VerifyKeyListener)
+     */
+    public bool prependVerifyKeyListener(VerifyKeyListener verifyKeyListener) {
+        if (fContentAssistSubjectControl !is null) {
+            return fContentAssistSubjectControl.prependVerifyKeyListener(verifyKeyListener);
+        } else if ( cast(ITextViewerExtension)fViewer ) {
+            ITextViewerExtension e= cast(ITextViewerExtension) fViewer;
+            e.prependVerifyKeyListener(verifyKeyListener);
+            return true;
+        } else {
+
+            StyledText textWidget= fViewer.getTextWidget();
+            if (Helper.okToUse(textWidget)) {
+                textWidget.addVerifyKeyListener(verifyKeyListener);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistSubjectControl#appendVerifyKeyListener(org.eclipse.swt.custom.VerifyKeyListener)
+     */
+    public bool appendVerifyKeyListener(VerifyKeyListener verifyKeyListener) {
+        if (fContentAssistSubjectControl !is null)
+            return fContentAssistSubjectControl.appendVerifyKeyListener(verifyKeyListener);
+        else if ( cast(ITextViewerExtension)fViewer ) {
+            ITextViewerExtension extension= cast(ITextViewerExtension)fViewer;
+            extension.appendVerifyKeyListener(verifyKeyListener);
+            return true;
+        } else {
+            StyledText textWidget= fViewer.getTextWidget();
+            if (Helper.okToUse(textWidget)) {
+                textWidget.addVerifyKeyListener(verifyKeyListener);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistSubjectControl#removeVerifyKeyListener(org.eclipse.swt.custom.VerifyKeyListener)
+     */
+    public void removeVerifyKeyListener(VerifyKeyListener verifyKeyListener) {
+        if (fContentAssistSubjectControl !is null) {
+            fContentAssistSubjectControl.removeVerifyKeyListener(verifyKeyListener);
+        } else if ( cast(ITextViewerExtension)fViewer ) {
+            ITextViewerExtension extension= cast(ITextViewerExtension) fViewer;
+            extension.removeVerifyKeyListener(verifyKeyListener);
+        } else {
+            StyledText textWidget= fViewer.getTextWidget();
+            if (Helper.okToUse(textWidget))
+                textWidget.removeVerifyKeyListener(verifyKeyListener);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistSubjectControl#setEventConsumer(org.eclipse.jface.text.contentassist.ContentAssistant.InternalListener)
+     */
+    public void setEventConsumer(IEventConsumer eventConsumer) {
+        if (fContentAssistSubjectControl !is null)
+            fContentAssistSubjectControl.setEventConsumer(eventConsumer);
+        else
+            fViewer.setEventConsumer(eventConsumer);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistSubjectControl#setSelectedRange(int, int)
+     */
+    public void setSelectedRange(int i, int j) {
+        if (fContentAssistSubjectControl !is null)
+            fContentAssistSubjectControl.setSelectedRange(i, j);
+        else
+            fViewer.setSelectedRange(i, j);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistSubjectControl#revealRange(int, int)
+     */
+    public void revealRange(int i, int j) {
+        if (fContentAssistSubjectControl !is null)
+            fContentAssistSubjectControl.revealRange(i, j);
+        else
+            fViewer.revealRange(i, j);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistSubjectControl#canAddVerifyKeyListener()
+     */
+    public bool supportsVerifyKeyListener() {
+        if (fContentAssistSubjectControl !is null)
+            return fContentAssistSubjectControl.supportsVerifyKeyListener();
+        return true;
+    }
+    /**
+     * Returns the characters which when typed by the user should automatically
+     * initiate proposing completions. The position is used to determine the
+     * appropriate content assist processor to invoke.
+     *
+     * @param contentAssistant the content assistant
+     * @param offset a document offset
+     * @return the auto activation characters
+     * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
+     */
+    public char[] getCompletionProposalAutoActivationCharacters(ContentAssistant contentAssistant, int offset) {
+        if (fContentAssistSubjectControl !is null)
+            return contentAssistant.getCompletionProposalAutoActivationCharacters(fContentAssistSubjectControl, offset);
+        return contentAssistant.getCompletionProposalAutoActivationCharacters(fViewer, offset);
+    }
+
+    /**
+     * Returns the characters which when typed by the user should automatically
+     * initiate the presentation of context information. The position is used
+     * to determine the appropriate content assist processor to invoke.
+     *
+     * @param contentAssistant the content assistant
+     * @param offset a document offset
+     * @return the auto activation characters
+     *
+     * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters()
+     */
+    char[] getContextInformationAutoActivationCharacters(ContentAssistant contentAssistant, int offset) {
+        if (fContentAssistSubjectControl !is null)
+            return contentAssistant.getContextInformationAutoActivationCharacters(fContentAssistSubjectControl, offset);
+        return contentAssistant.getContextInformationAutoActivationCharacters(fViewer, offset);
+    }
+
+    /**
+    * Creates and returns a completion proposal popup for the given content assistant.
+    *
+    * @param contentAssistant the content assistant
+    * @param controller the additional info controller
+    * @return the completion proposal popup
+    */
+    CompletionProposalPopup createCompletionProposalPopup(ContentAssistant contentAssistant, AdditionalInfoController controller) {
+        if (fContentAssistSubjectControl !is null)
+            return new CompletionProposalPopup(contentAssistant, fContentAssistSubjectControl, controller);
+        return new CompletionProposalPopup(contentAssistant, fViewer, controller);
+
+    }
+
+    /**
+     * Creates and returns a context info popup for the given content assistant.
+     *
+     * @param contentAssistant the content assistant
+     * @return the context info popup or <code>null</code>
+     */
+    ContextInformationPopup createContextInfoPopup(ContentAssistant contentAssistant) {
+        if (fContentAssistSubjectControl !is null)
+            return new ContextInformationPopup(contentAssistant, fContentAssistSubjectControl);
+        return new ContextInformationPopup(contentAssistant, fViewer);
+
+    }
+
+    /**
+     * Returns the context information validator that should be used to
+     * determine when the currently displayed context information should
+     * be dismissed. The position is used to determine the appropriate
+     * content assist processor to invoke.
+     *
+     * @param contentAssistant the content assistant
+     * @param offset a document offset
+     * @return an validator
+     */
+    public IContextInformationValidator getContextInformationValidator(ContentAssistant contentAssistant, int offset) {
+        if (fContentAssistSubjectControl !is null)
+            return contentAssistant.getContextInformationValidator(fContentAssistSubjectControl, offset);
+        return contentAssistant.getContextInformationValidator(fViewer, offset);
+    }
+
+    /**
+     * Returns the context information presenter that should be used to
+     * display context information. The position is used to determine the
+     * appropriate content assist processor to invoke.
+     *
+     * @param contentAssistant the content assistant
+     * @param offset a document offset
+     * @return a presenter
+     */
+    public IContextInformationPresenter getContextInformationPresenter(ContentAssistant contentAssistant, int offset) {
+        if (fContentAssistSubjectControl !is null)
+            return contentAssistant.getContextInformationPresenter(fContentAssistSubjectControl, offset);
+        return contentAssistant.getContextInformationPresenter(fViewer, offset);
+    }
+
+    /**
+     * Installs this adapter's information validator on the given context frame.
+     *
+     * @param frame the context frame
+     */
+    public void installValidator(ContextInformationPopup_ContextFrame frame) {
+        if (fContentAssistSubjectControl !is null) {
+            if (cast(ISubjectControlContextInformationValidator)frame.fValidator )
+                (cast(ISubjectControlContextInformationValidator)frame.fValidator).install(frame.fInformation, fContentAssistSubjectControl, frame.fOffset);
+        } else
+            frame.fValidator.install(frame.fInformation, fViewer, frame.fOffset);
+    }
+
+    /**
+     * Installs this adapter's information presenter on the given context frame.
+     *
+     * @param frame the context frame
+     */
+    public void installContextInformationPresenter(ContextInformationPopup_ContextFrame frame) {
+        if (fContentAssistSubjectControl !is null) {
+            if (cast(ISubjectControlContextInformationPresenter)frame.fPresenter )
+                (cast(ISubjectControlContextInformationPresenter)frame.fValidator).install(frame.fInformation, fContentAssistSubjectControl, frame.fBeginOffset);
+        } else
+            frame.fPresenter.install(frame.fInformation, fViewer, frame.fBeginOffset);
+    }
+
+    /**
+     * Returns an array of context information objects computed based
+     * on the specified document position. The position is used to determine
+     * the appropriate content assist processor to invoke.
+     *
+     * @param contentAssistant the content assistant
+     * @param offset a document offset
+     * @return an array of context information objects
+     * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int)
+     */
+    public IContextInformation[] computeContextInformation(ContentAssistant contentAssistant, int offset) {
+        if (fContentAssistSubjectControl !is null)
+            return contentAssistant.computeContextInformation(fContentAssistSubjectControl, offset);
+        return contentAssistant.computeContextInformation(fViewer, offset);
+    }
+
+    /*
+     * @see IContentAssistSubjectControl#addSelectionListener(SelectionListener)
+     */
+    public bool addSelectionListener(SelectionListener selectionListener) {
+        if (fContentAssistSubjectControl !is null)
+            return fContentAssistSubjectControl.addSelectionListener(selectionListener);
+        fViewer.getTextWidget().addSelectionListener(selectionListener);
+        return true;
+    }
+
+    /*
+     * @see IContentAssistSubjectControl#removeSelectionListener(SelectionListener)
+     */
+    public void removeSelectionListener(SelectionListener selectionListener) {
+        if (fContentAssistSubjectControl !is null)
+            fContentAssistSubjectControl.removeSelectionListener(selectionListener);
+        else
+            fViewer.getTextWidget().removeSelectionListener(selectionListener);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,2532 @@
+/*******************************************************************************
+ * 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
+ *     Guy Gurfinkel, guy.g@zend.com - [content assist][api] provide better access to ContentAssistant - https://bugs.eclipse.org/bugs/show_bug.cgi?id=169954
+ *     Anton Leherbauer (Wind River Systems) - [content assist][api] ContentAssistEvent should contain information about auto activation - https://bugs.eclipse.org/bugs/show_bug.cgi?id=193728
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.contentassist.ContentAssistant;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+import java.lang.all;
+import java.util.Stack;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.lang.JThread;
+import tango.core.sync.Mutex;
+import tango.core.sync.Condition;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.custom.VerifyKeyListener;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Monitor;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.bindings.keys.KeySequence;
+import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
+import org.eclipse.jface.contentassist.ISubjectControlContentAssistProcessor;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.preference.JFacePreferences;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension3;
+import org.eclipse.jface.text.IEventConsumer;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.IViewportListener;
+import org.eclipse.jface.text.IWidgetTokenKeeper;
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension;
+import org.eclipse.jface.text.IWidgetTokenOwner;
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.util.Geometry;
+
+
+/**
+ * The standard implementation of the <code>IContentAssistant</code> interface. Usually, clients
+ * instantiate this class and configure it before using it.
+ */
+public class ContentAssistant : IContentAssistant, IContentAssistantExtension, IContentAssistantExtension2, IContentAssistantExtension3, IContentAssistantExtension4, IWidgetTokenKeeper, IWidgetTokenKeeperExtension {
+
+
+
+    /**
+     * Content assist command identifier for 'select next proposal'.
+     *
+     * @since 3.4
+     */
+    public static const String SELECT_NEXT_PROPOSAL_COMMAND_ID= "org.eclipse.ui.edit.text.contentAssist.selectNextProposal"; //$NON-NLS-1$
+    /**
+     * Content assist command identifier for 'select previous proposal'.
+     *
+     * @since 3.4
+     */
+    public static const String SELECT_PREVIOUS_PROPOSAL_COMMAND_ID= "org.eclipse.ui.edit.text.contentAssist.selectPreviousProposal"; //$NON-NLS-1$
+
+
+    /**
+     * A generic closer class used to monitor various interface events in order to determine whether
+     * content-assist should be terminated and all associated windows closed.
+     */
+    class Closer : ControlListener, MouseListener, FocusListener, DisposeListener, IViewportListener {
+
+        /** The shell that a <code>ControlListener</code> is registered with. */
+        private Shell fShell;
+        /**
+         * The control that a <code>MouseListener</code>, a<code>FocusListener</code> and a
+         * <code>DisposeListener</code> are registered with.
+         */
+        private Control fControl;
+
+        /**
+         * Installs this closer on it's viewer's text widget.
+         */
+        protected void install() {
+            Control control= fContentAssistSubjectControlAdapter.getControl();
+            fControl= control;
+            if (Helper.okToUse(control)) {
+
+                Shell shell= control.getShell();
+                fShell= shell;
+                shell.addControlListener(this);
+
+                control.addMouseListener(this);
+                control.addFocusListener(this);
+
+                /*
+                 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of
+                 * Internal Errors
+                 */
+                control.addDisposeListener(this);
+            }
+            if (fViewer !is null)
+                fViewer.addViewportListener(this);
+        }
+
+        /**
+         * Uninstalls this closer from the viewer's text widget.
+         */
+        protected void uninstall() {
+            Control shell= fShell;
+            fShell= null;
+            if (Helper.okToUse(shell))
+                shell.removeControlListener(this);
+
+            Control control= fControl;
+            fControl= null;
+            if (Helper.okToUse(control)) {
+
+                control.removeMouseListener(this);
+                control.removeFocusListener(this);
+
+                /*
+                 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of
+                 * Internal Errors
+                 */
+                control.removeDisposeListener(this);
+            }
+
+            if (fViewer !is null)
+                fViewer.removeViewportListener(this);
+        }
+
+        /*
+         * @see ControlListener#controlResized(ControlEvent)
+         */
+        public void controlResized(ControlEvent e) {
+            hide();
+        }
+
+        /*
+         * @see ControlListener#controlMoved(ControlEvent)
+         */
+        public void controlMoved(ControlEvent e) {
+            hide();
+        }
+
+        /*
+         * @see MouseListener#mouseDown(MouseEvent)
+         */
+        public void mouseDown(MouseEvent e) {
+            hide();
+        }
+
+        /*
+         * @see MouseListener#mouseUp(MouseEvent)
+         */
+        public void mouseUp(MouseEvent e) {
+        }
+
+        /*
+         * @see MouseListener#mouseDoubleClick(MouseEvent)
+         */
+        public void mouseDoubleClick(MouseEvent e) {
+            hide();
+        }
+
+        /*
+         * @see FocusListener#focusGained(FocusEvent)
+         */
+        public void focusGained(FocusEvent e) {
+        }
+
+        /*
+         * @see FocusListener#focusLost(FocusEvent)
+         */
+        public void focusLost(FocusEvent e) {
+            Control control= fControl;
+            if (Helper.okToUse(control)) {
+                Display d= control.getDisplay();
+                if (d !is null) {
+                    d.asyncExec(new class()  Runnable {
+                        public void run() {
+                            if (!fProposalPopup.hasFocus() && (fContextInfoPopup is null || !fContextInfoPopup.hasFocus()))
+                                hide();
+                        }
+                    });
+                }
+            }
+        }
+
+        /*
+         * @seeDisposeListener#widgetDisposed(DisposeEvent)
+         */
+        public void widgetDisposed(DisposeEvent e) {
+            /*
+             * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal
+             * Errors
+             */
+            hide();
+        }
+
+        /*
+         * @see IViewportListener#viewportChanged(int)
+         */
+        public void viewportChanged(int topIndex) {
+            hide();
+        }
+    }
+
+    /**
+     * An implementation of <code>IContentAssistListener</code>, this class is used to monitor
+     * key events in support of automatic activation of the content assistant. If enabled, the
+     * implementation utilizes a thread to watch for input characters matching the activation
+     * characters specified by the content assist processor, and if detected, will wait the
+     * indicated delay interval before activating the content assistant.
+     *
+     * @since 3.4 protected, was added in 2.1 as private class
+     */
+    protected class AutoAssistListener : KeyAdapter , KeyListener, Runnable, VerifyKeyListener {
+
+        // SWT intf impl
+        public void keyReleased(KeyEvent e) {
+            super.keyReleased(e);
+        }
+
+        private JThread fThread;
+        private bool fIsReset= false;
+        private Mutex fMutex;
+        private Condition fMutex_cond;
+        private int fShowStyle;
+
+        private const static int SHOW_PROPOSALS= 1;
+        private const static int SHOW_CONTEXT_INFO= 2;
+
+        protected this() {
+            fMutex = new Mutex();
+            fMutex_cond = new Condition(fMutex);
+        }
+
+        protected void start(int showStyle) {
+            fShowStyle= showStyle;
+            fThread= new JThread(this);
+            fThread.setName( JFaceTextMessages.getString("ContentAssistant.assist_delay_timer_name")); //$NON-NLS-1$
+            fThread.start();
+        }
+
+        public override void run() {
+            try {
+                while (true) {
+                    synchronized (fMutex) {
+                        if (fAutoActivationDelay !is 0)
+                            fMutex_cond.wait(fAutoActivationDelay);
+                        if (fIsReset) {
+                            fIsReset= false;
+                            continue;
+                        }
+                    }
+                    showAssist(fShowStyle);
+                    break;
+                }
+            } catch (InterruptedException e) {
+            }
+            fThread= null;
+        }
+
+        protected void reset(int showStyle) {
+            synchronized (fMutex) {
+                fShowStyle= showStyle;
+                fIsReset= true;
+                fMutex_cond.notifyAll();
+            }
+        }
+
+        protected void stop() {
+            JThread threadToStop= fThread;
+            if (threadToStop !is null && threadToStop.isAlive())
+                threadToStop.interrupt();
+        }
+
+        private bool contains(char[] characters, char character) {
+            if (characters !is null) {
+                for (int i= 0; i < characters.length; i++) {
+                    if (character is characters[i])
+                        return true;
+                }
+            }
+            return false;
+        }
+
+        public void keyPressed(KeyEvent e) {
+            // Only act on typed characters and ignore modifier-only events
+            if (e.character is 0 && (e.keyCode & SWT.KEYCODE_BIT) is 0)
+                return;
+
+            if (e.character !is 0 && (e.stateMask is SWT.ALT))
+                return;
+
+            // Only act on characters that are trigger candidates. This
+            // avoids computing the model selection on every keystroke
+            if (computeAllAutoActivationTriggers().indexOf(e.character) < 0) {
+                stop();
+                return;
+            }
+
+            int showStyle;
+            int pos= fContentAssistSubjectControlAdapter.getSelectedRange().x;
+            char[] activation;
+
+            activation= fContentAssistSubjectControlAdapter.getCompletionProposalAutoActivationCharacters(this.outer, pos);
+
+            if (contains(activation, e.character) && !isProposalPopupActive())
+                showStyle= SHOW_PROPOSALS;
+            else {
+                activation= fContentAssistSubjectControlAdapter.getContextInformationAutoActivationCharacters(this.outer, pos);
+                if (contains(activation, e.character) && !isContextInfoPopupActive())
+                    showStyle= SHOW_CONTEXT_INFO;
+                else {
+                    stop();
+                    return;
+                }
+            }
+
+            if (fThread !is null && fThread.isAlive())
+                reset(showStyle);
+            else
+                start(showStyle);
+        }
+
+        /*
+         * @see org.eclipse.swt.custom.VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
+         */
+        public void verifyKey(VerifyEvent event) {
+            keyPressed(event);
+        }
+
+        protected void showAssist(int showStyle) {
+            final Control control= fContentAssistSubjectControlAdapter.getControl();
+            if (control is null)
+                return;
+
+            final Display d= control.getDisplay();
+            if (d is null)
+                return;
+
+            try {
+                d.syncExec(new class()  Runnable {
+                    public void run() {
+                        if (isProposalPopupActive())
+                            return;
+
+                        if (control.isDisposed() || !control.isFocusControl())
+                            return;
+
+                        if (showStyle is SHOW_PROPOSALS) {
+                            if (!prepareToShowCompletions(true))
+                                return;
+                            fProposalPopup.showProposals(true);
+                            fLastAutoActivation= System.currentTimeMillis();
+                        } else if (showStyle is SHOW_CONTEXT_INFO && fContextInfoPopup !is null) {
+                            promoteKeyListener();
+                            fContextInfoPopup.showContextProposals(true);
+                        }
+                    }
+                });
+            } catch (SWTError e) {
+            }
+        }
+    }
+
+    /**
+     * The layout manager layouts the various windows associated with the content assistant based on
+     * the settings of the content assistant.
+     */
+    class LayoutManager : Listener {
+
+        // Presentation types.
+        /** The presentation type for the proposal selection popup. */
+        public const static int LAYOUT_PROPOSAL_SELECTOR= 0;
+        /** The presentation type for the context selection popup. */
+        public const static int LAYOUT_CONTEXT_SELECTOR= 1;
+        /** The presentation type for the context information hover . */
+        public const static int LAYOUT_CONTEXT_INFO_POPUP= 2;
+
+        int fContextType= LAYOUT_CONTEXT_SELECTOR;
+        Shell[] fShells;
+        Object[] fPopups;
+
+        this(){
+            fShells= new Shell[3];
+            fPopups= new Object[3];
+        }
+
+        protected void add(Object popup, Shell shell, int type, int offset) {
+            Assert.isNotNull(popup);
+            Assert.isTrue(shell !is null && !shell.isDisposed());
+            checkType(type);
+
+            if (fShells[type] !is shell) {
+                if (fShells[type] !is null)
+                    fShells[type].removeListener(SWT.Dispose, this);
+                shell.addListener(SWT.Dispose, this);
+                fShells[type]= shell;
+            }
+
+            fPopups[type]= popup;
+            if (type is LAYOUT_CONTEXT_SELECTOR || type is LAYOUT_CONTEXT_INFO_POPUP)
+                fContextType= type;
+
+            layout(type, offset);
+            adjustListeners(type);
+        }
+
+        protected void checkType(int type) {
+            Assert.isTrue(type is LAYOUT_PROPOSAL_SELECTOR ||
+                type is LAYOUT_CONTEXT_SELECTOR || type is LAYOUT_CONTEXT_INFO_POPUP);
+        }
+
+        public void handleEvent(Event event) {
+            Widget source= event.widget;
+            source.removeListener(SWT.Dispose, this);
+
+            int type= getShellType(source);
+            checkType(type);
+            fShells[type]= null;
+
+            switch (type) {
+                case LAYOUT_PROPOSAL_SELECTOR:
+                    if (fContextType is LAYOUT_CONTEXT_SELECTOR &&
+                            Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
+                        // Restore event notification to the tip popup.
+                        addContentAssistListener(cast(IContentAssistListener) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
+                    }
+                    break;
+
+                case LAYOUT_CONTEXT_SELECTOR:
+                    if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
+                        if (fProposalPopupOrientation is PROPOSAL_STACKED)
+                            layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
+                        // Restore event notification to the proposal popup.
+                        addContentAssistListener(cast(IContentAssistListener) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
+                    }
+                    fContextType= LAYOUT_CONTEXT_INFO_POPUP;
+                    break;
+
+                case LAYOUT_CONTEXT_INFO_POPUP:
+                    if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
+                        if (fContextInfoPopupOrientation is CONTEXT_INFO_BELOW)
+                            layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
+                    }
+                    fContextType= LAYOUT_CONTEXT_SELECTOR;
+                    break;
+                default:
+            }
+        }
+
+        protected int getShellType(Widget shell) {
+            for (int i= 0; i < fShells.length; i++) {
+                if (fShells[i] is shell)
+                    return i;
+            }
+            return -1;
+        }
+
+        /**
+         * Layouts the popup defined by <code>type</code> at the given widget offset.
+         *
+         * @param type the kind of popup to layout
+         * @param offset the widget offset
+         */
+        protected void layout(int type, int offset) {
+            switch (type) {
+                case LAYOUT_PROPOSAL_SELECTOR:
+                    layoutProposalSelector(offset);
+                    break;
+                case LAYOUT_CONTEXT_SELECTOR:
+                    layoutContextSelector(offset);
+                    break;
+                case LAYOUT_CONTEXT_INFO_POPUP:
+                    layoutContextInfoPopup(offset);
+                    break;
+                default:
+            }
+        }
+
+        protected void layoutProposalSelector(int offset) {
+            if (fContextType is LAYOUT_CONTEXT_INFO_POPUP &&
+                    fContextInfoPopupOrientation is CONTEXT_INFO_BELOW &&
+                    Helper.okToUse(fShells[LAYOUT_CONTEXT_INFO_POPUP])) {
+                // Stack proposal selector beneath the tip box.
+                Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+                Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
+                shell.setLocation(getStackedLocation(shell, parent));
+            } else if (fContextType !is LAYOUT_CONTEXT_SELECTOR ||
+                        !Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
+                // There are no other presentations to be concerned with,
+                // so place the proposal selector beneath the cursor line.
+                Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+                CompletionProposalPopup popup= cast(CompletionProposalPopup) fPopups[LAYOUT_PROPOSAL_SELECTOR];
+                shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, popup));
+            } else {
+                CompletionProposalPopup popup= (cast(CompletionProposalPopup) fPopups[LAYOUT_PROPOSAL_SELECTOR]);
+                switch (fProposalPopupOrientation) {
+                    case PROPOSAL_REMOVE: {
+                        // Remove the tip selector and place the
+                        // proposal selector beneath the cursor line.
+                        fShells[LAYOUT_CONTEXT_SELECTOR].dispose();
+                        Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+                        shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, popup));
+                        break;
+                    }
+                    case PROPOSAL_OVERLAY: {
+                        // Overlay the tip selector with the proposal selector.
+                        Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+                        shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, popup));
+                        break;
+                    }
+                    case PROPOSAL_STACKED: {
+                        // Stack the proposal selector beneath the tip selector.
+                        Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+                        Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
+                        shell.setLocation(getStackedLocation(shell, parent));
+                        break;
+                    }
+                    default:
+                }
+            }
+        }
+
+        protected void layoutContextSelector(int offset) {
+            // Always place the context selector beneath the cursor line.
+            Shell shell= fShells[LAYOUT_CONTEXT_SELECTOR];
+            shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, null));
+
+            if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
+                switch (fProposalPopupOrientation) {
+                    case PROPOSAL_REMOVE:
+                        // Remove the proposal selector.
+                        fShells[LAYOUT_PROPOSAL_SELECTOR].dispose();
+                        break;
+
+                    case PROPOSAL_OVERLAY:
+                        // The proposal selector has been overlaid by the tip selector.
+                        break;
+
+                    case PROPOSAL_STACKED: {
+                        // Stack the proposal selector beneath the tip selector.
+                        shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+                        Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
+                        shell.setLocation(getStackedLocation(shell, parent));
+                        break;
+                    }
+                    default:
+                }
+            }
+        }
+
+        protected void layoutContextInfoPopup(int offset) {
+            switch (fContextInfoPopupOrientation) {
+                case CONTEXT_INFO_ABOVE: {
+                    // Place the popup above the cursor line.
+                    Shell shell= fShells[LAYOUT_CONTEXT_INFO_POPUP];
+                    shell.setBounds(computeBoundsAboveBelow(shell, shell.getSize(), offset));
+                    break;
+                }
+                case CONTEXT_INFO_BELOW: {
+                    // Place the popup beneath the cursor line.
+                    Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
+                    parent.setBounds(computeBoundsBelowAbove(parent, parent.getSize(), offset, null));
+                    if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
+                        // Stack the proposal selector beneath the context info popup.
+                        Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+                        shell.setLocation(getStackedLocation(shell, parent));
+                    }
+                    break;
+                }
+                default:
+            }
+        }
+
+        /**
+         * Moves <code>point</code> such that <code>rectangle</code> does not bleed outside of
+         * <code>bounds</code>. All coordinates must have the same reference.
+         *
+         * @param point the point to move if needed
+         * @param shellSize the size of the shell that may be moved
+         * @param bounds the bounds
+         * @since 3.3
+         */
+        protected void constrainLocation(Point point, Point shellSize, Rectangle bounds) {
+            if (point.x + shellSize.x > bounds.x + bounds.width)
+                point.x= bounds.x + bounds.width - shellSize.x;
+
+            if (point.x < bounds.x)
+                point.x= bounds.x;
+
+            if (point.y + shellSize.y > bounds.y + bounds.height)
+                point.y= bounds.y + bounds.height - shellSize.y;
+
+            if (point.y < bounds.y)
+                point.y= bounds.y;
+        }
+
+        protected Rectangle constrainHorizontally(Rectangle rect, Rectangle bounds) {
+            // clip width
+            if (rect.width > bounds.width)
+                rect.width= bounds.width;
+
+            if (rect.x + rect.width > bounds.x + bounds.width)
+                rect.x= bounds.x + bounds.width - rect.width;
+            if (rect.x < bounds.x)
+                rect.x= bounds.x;
+
+            return rect;
+        }
+
+        /**
+         * Returns the display bounds for <code>shell</code> such that it appears right above
+         * <code>offset</code>, or below it if above is not suitable. The returned bounds lie
+         * within the monitor at the caret location and never overlap with the caret line.
+         *
+         * @param shell the shell to compute the placement for
+         * @param preferred the preferred size for <code>shell</code>
+         * @param offset the caret offset in the subject control
+         * @return the point right above <code>offset</code> in display coordinates
+         * @since 3.3
+         */
+        protected Rectangle computeBoundsAboveBelow(Shell shell, Point preferred, int offset) {
+            Control subjectControl= fContentAssistSubjectControlAdapter.getControl();
+            Display display= subjectControl.getDisplay();
+            Rectangle caret= getCaretRectangle(offset);
+            org.eclipse.swt.widgets.Monitor.Monitor monitor= getClosestMonitor(display, caret);
+            Rectangle bounds= monitor.getClientArea();
+            Geometry.moveInside(caret, bounds);
+
+            int spaceAbove= caret.y - bounds.y;
+            int caretLowerY= caret.y + caret.height;
+            int spaceBelow= bounds.y + bounds.height - caretLowerY;
+            Rectangle rect;
+            if (spaceAbove >= preferred.y)
+                rect= new Rectangle(caret.x, caret.y - preferred.y, preferred.x, preferred.y);
+            else if (spaceBelow >= preferred.y)
+                rect= new Rectangle(caret.x, caretLowerY, preferred.x, preferred.y);
+            // we can't fit in the preferred size - squeeze into larger area
+            else if (spaceBelow <= spaceAbove)
+                rect= new Rectangle(caret.x, bounds.y, preferred.x, spaceAbove);
+            else
+                rect= new Rectangle(caret.x, caretLowerY, preferred.x, spaceBelow);
+
+            return constrainHorizontally(rect, bounds);
+        }
+        package Rectangle computeBoundsAboveBelow_package(Shell shell, Point preferred, int offset) {
+            return computeBoundsAboveBelow(shell, preferred, offset);
+        }
+
+        /**
+         * Returns the display bounds for <code>shell</code> such that it appears right below
+         * <code>offset</code>, or above it if below is not suitable. The returned bounds lie
+         * within the monitor at the caret location and never overlap with the caret line.
+         *
+         * @param shell the shell to compute the placement for
+         * @param preferred the preferred size for <code>shell</code>
+         * @param offset the caret offset in the subject control
+         * @param popup a popup to inform if the location was switched to above, <code>null</code> to do nothing
+         * @return the point right below <code>offset</code> in display coordinates
+         * @since 3.3
+         */
+        protected Rectangle computeBoundsBelowAbove(Shell shell, Point preferred, int offset, CompletionProposalPopup popup) {
+            Control subjectControl= fContentAssistSubjectControlAdapter.getControl();
+            Display display= subjectControl.getDisplay();
+            Rectangle caret= getCaretRectangle(offset);
+            org.eclipse.swt.widgets.Monitor.Monitor monitor= getClosestMonitor(display, caret);
+            Rectangle bounds= monitor.getClientArea();
+            Geometry.moveInside(caret, bounds);
+
+            int threshold= popup is null ? Integer.MAX_VALUE : popup.getMinimalHeight();
+            int spaceAbove= caret.y - bounds.y;
+            int spaceBelow= bounds.y + bounds.height - (caret.y + caret.height);
+            Rectangle rect;
+            bool switched= false;
+            if (spaceBelow >= preferred.y)
+                rect= new Rectangle(caret.x, caret.y + caret.height, preferred.x, preferred.y);
+            // squeeze in below if we have at least threshold space
+            else if (spaceBelow >= threshold)
+                rect= new Rectangle(caret.x, caret.y + caret.height, preferred.x, spaceBelow);
+            else if (spaceAbove >= preferred.y) {
+                rect= new Rectangle(caret.x, caret.y - preferred.y, preferred.x, preferred.y);
+                switched= true;
+            } else if (spaceBelow >= spaceAbove) {
+                // we can't fit in the preferred size - squeeze into larger area
+                rect= new Rectangle(caret.x, caret.y + caret.height, preferred.x, spaceBelow);
+            } else {
+                rect= new Rectangle(caret.x, bounds.y, preferred.x, spaceAbove);
+                switched= true;
+            }
+
+            if (popup !is null)
+                popup.switchedPositionToAbove(switched);
+
+            return constrainHorizontally(rect, bounds);
+        }
+        package Rectangle computeBoundsBelowAbove_package(Shell shell, Point preferred, int offset, CompletionProposalPopup popup) {
+            return computeBoundsBelowAbove(shell, preferred, offset, popup );
+        }
+
+        private Rectangle getCaretRectangle(int offset) {
+            Point location= fContentAssistSubjectControlAdapter.getLocationAtOffset(offset);
+            Control subjectControl= fContentAssistSubjectControlAdapter.getControl();
+            Point controlSize= subjectControl.getSize();
+            constrainLocation(location, new Point(0, 0), new Rectangle(0, 0, controlSize.x, controlSize.y));
+            location= subjectControl.toDisplay(location);
+            Rectangle subjectRectangle= new Rectangle(location.x, location.y, 1, fContentAssistSubjectControlAdapter.getLineHeight());
+            return subjectRectangle;
+        }
+
+        protected Point getStackedLocation(Shell shell, Shell parent) {
+            Point p= parent.getLocation();
+            Point size= parent.getSize();
+            p.x += size.x / 4;
+            p.y += size.y;
+
+            p= parent.toDisplay(p);
+
+            Point shellSize= shell.getSize();
+            org.eclipse.swt.widgets.Monitor.Monitor monitor= getClosestMonitor(parent.getDisplay(), new Rectangle(p.x, p.y, 0, 0));
+            Rectangle displayBounds= monitor.getClientArea();
+            constrainLocation(p, shellSize, displayBounds);
+
+            return p;
+        }
+
+        protected void adjustListeners(int type) {
+            switch (type) {
+                case LAYOUT_PROPOSAL_SELECTOR:
+                    if (fContextType is LAYOUT_CONTEXT_SELECTOR &&
+                            Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR]))
+                        // Disable event notification to the tip selector.
+                        removeContentAssistListener(cast(IContentAssistListener) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
+                    break;
+                case LAYOUT_CONTEXT_SELECTOR:
+                    if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR]))
+                        // Disable event notification to the proposal selector.
+                        removeContentAssistListener(cast(IContentAssistListener) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
+                    break;
+                case LAYOUT_CONTEXT_INFO_POPUP:
+                    break;
+                default:
+            }
+        }
+
+        /**
+         * Copied from org.eclipse.jface.window.Window.
+         * Returns the monitor whose client area contains the given point. If no
+         * monitor contains the point, returns the monitor that is closest to the
+         * point. If this is ever made public, it should be moved into a separate
+         * utility class.
+         *
+         * @param toSearch
+         *            point to find (display coordinates)
+         * @param rectangle
+         *            rectangle to find (display coordinates)
+         * @return the monitor closest to the given point
+         * @since 3.3
+         */
+        private org.eclipse.swt.widgets.Monitor.Monitor getClosestMonitor(Display toSearch, Rectangle rectangle) {
+            int closest = Integer.MAX_VALUE;
+
+            Point toFind= Geometry.centerPoint(rectangle);
+            org.eclipse.swt.widgets.Monitor.Monitor[] monitors = toSearch.getMonitors();
+            org.eclipse.swt.widgets.Monitor.Monitor result = monitors[0];
+
+            for (int idx = 0; idx < monitors.length; idx++) {
+                org.eclipse.swt.widgets.Monitor.Monitor current = monitors[idx];
+
+                Rectangle clientArea = current.getClientArea();
+
+                if (clientArea.contains(toFind)) {
+                    return current;
+                }
+
+                int distance = Geometry.distanceSquared(Geometry.centerPoint(clientArea), toFind);
+                if (distance < closest) {
+                    closest = distance;
+                    result = current;
+                }
+            }
+
+            return result;
+        }
+    }
+
+    /**
+     * Internal key listener and event consumer.
+     */
+    class InternalListener : VerifyKeyListener, IEventConsumer {
+
+        /**
+         * Verifies key events by notifying the registered listeners. Each listener is allowed to
+         * indicate that the event has been handled and should not be further processed.
+         *
+         * @param e the verify event
+         * @see VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
+         */
+        public void verifyKey(VerifyEvent e) {
+            IContentAssistListener[] listeners= arraycast!(IContentAssistListener)( fListeners.dup );
+            for (int i= 0; i < listeners.length; i++) {
+                if (listeners[i] !is null) {
+                    if (!listeners[i].verifyKey(e) || !e.doit)
+                        break;
+                }
+            }
+            if (fAutoAssistListener !is null)
+                fAutoAssistListener.keyPressed(e);
+        }
+
+        /*
+         * @see IEventConsumer#processEvent
+         */
+        public void processEvent(VerifyEvent event) {
+
+            installKeyListener();
+
+            IContentAssistListener[] listeners= arraycast!(IContentAssistListener)( fListeners.dup );
+            for (int i= 0; i < listeners.length; i++) {
+                if (listeners[i] !is null) {
+                    listeners[i].processEvent(event);
+                    if (!event.doit)
+                        return;
+                }
+            }
+        }
+    }
+
+    /**
+     * Dialog store constants.
+     *
+     * @since 3.0
+     */
+    public static const String STORE_SIZE_X= "size.x"; //$NON-NLS-1$
+    public static const String STORE_SIZE_Y= "size.y"; //$NON-NLS-1$
+
+    // Content-Assist Listener types
+    const static int CONTEXT_SELECTOR= 0;
+    const static int PROPOSAL_SELECTOR= 1;
+    const static int CONTEXT_INFO_POPUP= 2;
+
+    /**
+     * The popup priority: &gt; linked position proposals and hover pop-ups. Default value:
+     * <code>20</code>;
+     *
+     * @since 3.0
+     */
+    public static const int WIDGET_PRIORITY= 20;
+
+    private static const int DEFAULT_AUTO_ACTIVATION_DELAY= 500;
+
+    private IInformationControlCreator fInformationControlCreator;
+    private int fAutoActivationDelay= DEFAULT_AUTO_ACTIVATION_DELAY;
+    private bool fIsAutoActivated= false;
+    private bool fIsAutoInserting= false;
+    private int fProposalPopupOrientation= PROPOSAL_OVERLAY;
+    private int fContextInfoPopupOrientation= CONTEXT_INFO_ABOVE;
+    private Map fProcessors;
+
+    /**
+     * The partitioning.
+     *
+     * @since 3.0
+     */
+    private String fPartitioning;
+
+    private Color fContextInfoPopupBackground;
+    private Color fContextInfoPopupForeground;
+    private Color fContextSelectorBackground;
+    private Color fContextSelectorForeground;
+    private Color fProposalSelectorBackground;
+    private Color fProposalSelectorForeground;
+
+    private ITextViewer fViewer;
+    private String fLastErrorMessage;
+
+    private Closer fCloser;
+    LayoutManager fLayoutManager;
+    private AutoAssistListener fAutoAssistListener;
+    private InternalListener fInternalListener;
+    private CompletionProposalPopup fProposalPopup;
+    private ContextInformationPopup fContextInfoPopup;
+
+    /**
+     * Flag which tells whether a verify key listener is hooked.
+     *
+     * @since 3.0
+     */
+    private bool fVerifyKeyListenerHooked= false;
+    private IContentAssistListener[] fListeners;
+    /**
+     * The content assist subject control.
+     *
+     * @since 3.0
+     */
+    private IContentAssistSubjectControl fContentAssistSubjectControl;
+    /**
+     * The content assist subject control's shell.
+     *
+     * @since 3.2
+     */
+    private Shell fContentAssistSubjectControlShell;
+    /**
+     * The content assist subject control's shell traverse listener.
+     *
+     * @since 3.2
+     */
+    private TraverseListener fCASCSTraverseListener;
+    /**
+     * The content assist subject control adapter.
+     *
+     * @since 3.0
+     */
+    private ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter;
+    /**
+     * The dialog settings for the control's bounds.
+     *
+     * @since 3.0
+     */
+    private IDialogSettings fDialogSettings;
+    /**
+     * Prefix completion setting.
+     *
+     * @since 3.0
+     */
+    private bool fIsPrefixCompletionEnabled= false;
+    /**
+     * The list of completion listeners.
+     *
+     * @since 3.2
+     */
+    private ListenerList fCompletionListeners;
+    /**
+     * The message to display at the bottom of the proposal popup.
+     *
+     * @since 3.2
+     */
+    private String fMessage= ""; //$NON-NLS-1$
+    /**
+     * The cycling mode property.
+     *
+     * @since 3.2
+     */
+    private bool fIsRepetitionMode= false;
+    /**
+     * The show empty property.
+     *
+     * @since 3.2
+     */
+    private bool fShowEmptyList= false;
+    /**
+     * The message line property.
+     *
+     * @since 3.2
+     */
+    private bool fIsStatusLineVisible;
+    /**
+     * The last system time when auto activation performed.
+     *
+     * @since 3.2
+     */
+    private long fLastAutoActivation= Long.MIN_VALUE;
+    /**
+     * The iteration key sequence to listen for, or <code>null</code>.
+     *
+     * @since 3.2
+     */
+    private KeySequence fRepeatedInvocationKeySequence;
+
+    /**
+     * Maps handler to command identifiers.
+     *
+     * @since 3.4
+     */
+    private Map fHandlers;
+
+    /**
+     * Tells whether colored labels support is enabled.
+     *
+     * @since 3.4
+     */
+    private bool fIsColoredLabelsSupportEnabled= false;
+
+
+    /**
+     * Creates a new content assistant. The content assistant is not automatically activated,
+     * overlays the completion proposals with context information list if necessary, and shows the
+     * context information above the location at which it was activated. If auto activation will be
+     * enabled, without further configuration steps, this content assistant is activated after a 500
+     * milliseconds delay. It uses the default partitioning.
+     */
+    public this() {
+        fListeners= new IContentAssistListener[4];
+        fCompletionListeners= new ListenerList(ListenerList.IDENTITY);
+        fPartitioning= IDocumentExtension3.DEFAULT_PARTITIONING;
+    }
+
+    /**
+     * Sets the document partitioning this content assistant is using.
+     *
+     * @param partitioning the document partitioning for this content assistant
+     * @since 3.0
+     */
+    public void setDocumentPartitioning(String partitioning) {
+        Assert.isNotNull(partitioning);
+        fPartitioning= partitioning;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension#getDocumentPartitioning()
+     * @since 3.0
+     */
+    public String getDocumentPartitioning() {
+        return fPartitioning;
+    }
+
+    /**
+     * Registers a given content assist processor for a particular content type. If there is already
+     * a processor registered for this type, the new processor is registered instead of the old one.
+     *
+     * @param processor the content assist processor to register, or <code>null</code> to remove
+     *        an existing one
+     * @param contentType the content type under which to register
+     */
+    public void setContentAssistProcessor(IContentAssistProcessor processor, String contentType) {
+
+        Assert.isNotNull(contentType);
+
+        if (fProcessors is null)
+            fProcessors= new HashMap();
+
+        if (processor is null)
+            fProcessors.remove(contentType);
+        else
+            fProcessors.put(contentType, cast(Object)processor);
+    }
+
+    /*
+     * @see IContentAssistant#getContentAssistProcessor
+     */
+    public IContentAssistProcessor getContentAssistProcessor(String contentType) {
+        if (fProcessors is null)
+            return null;
+
+        return cast(IContentAssistProcessor) fProcessors.get(contentType);
+    }
+
+    /**
+     * Computes the sorted set of all auto activation trigger characters.
+     *
+     * @return the sorted set of all auto activation trigger characters
+     * @since 3.1
+     */
+    private String computeAllAutoActivationTriggers() {
+        if (fProcessors is null)
+            return ""; //$NON-NLS-1$
+
+        StringBuffer buf= new StringBuffer(5);
+        Iterator iter= fProcessors.entrySet().iterator();
+        while (iter.hasNext()) {
+            Map.Entry entry= cast(Map.Entry) iter.next();
+            IContentAssistProcessor processor= cast(IContentAssistProcessor) entry.getValue();
+            char[] triggers= processor.getCompletionProposalAutoActivationCharacters();
+            if (triggers !is null)
+                buf.append(triggers);
+            triggers= processor.getContextInformationAutoActivationCharacters();
+            if (triggers !is null)
+                buf.append(triggers);
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Enables the content assistant's auto activation mode.
+     *
+     * @param enabled indicates whether auto activation is enabled or not
+     */
+    public void enableAutoActivation(bool enabled) {
+        fIsAutoActivated= enabled;
+        manageAutoActivation(fIsAutoActivated);
+    }
+
+    /**
+     * Enables the content assistant's auto insertion mode. If enabled, the content assistant
+     * inserts a proposal automatically if it is the only proposal. In the case of ambiguities, the
+     * user must make the choice.
+     *
+     * @param enabled indicates whether auto insertion is enabled or not
+     * @since 2.0
+     */
+    public void enableAutoInsert(bool enabled) {
+        fIsAutoInserting= enabled;
+    }
+
+    /**
+     * Returns whether this content assistant is in the auto insertion mode or not.
+     *
+     * @return <code>true</code> if in auto insertion mode
+     * @since 2.0
+     */
+    bool isAutoInserting() {
+        return fIsAutoInserting;
+    }
+
+    /**
+     * Installs and uninstall the listeners needed for auto activation.
+     *
+     * @param start <code>true</code> if listeners must be installed, <code>false</code> if they
+     *        must be removed
+     * @since 2.0
+     */
+    private void manageAutoActivation(bool start) {
+        if (start) {
+
+            if ((fContentAssistSubjectControlAdapter !is null) && fAutoAssistListener is null) {
+                fAutoAssistListener= createAutoAssistListener();
+                // For details see https://bugs.eclipse.org/bugs/show_bug.cgi?id=49212
+                if (fContentAssistSubjectControlAdapter.supportsVerifyKeyListener())
+                    fContentAssistSubjectControlAdapter.appendVerifyKeyListener(fAutoAssistListener);
+                else
+                    fContentAssistSubjectControlAdapter.addKeyListener(fAutoAssistListener);
+            }
+
+        } else if (fAutoAssistListener !is null) {
+            // For details see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=49212
+            if (fContentAssistSubjectControlAdapter.supportsVerifyKeyListener())
+                fContentAssistSubjectControlAdapter.removeVerifyKeyListener(fAutoAssistListener);
+            else
+                fContentAssistSubjectControlAdapter.removeKeyListener(fAutoAssistListener);
+            fAutoAssistListener= null;
+        }
+    }
+
+    /**
+     * This method allows subclasses to provide their own {@link AutoAssistListener}.
+     *
+     * @return a new auto assist listener
+     * @since 3.4
+     */
+    protected AutoAssistListener createAutoAssistListener() {
+        return new AutoAssistListener();
+    }
+
+    /**
+     * Sets the delay after which the content assistant is automatically invoked if the cursor is
+     * behind an auto activation character.
+     *
+     * @param delay the auto activation delay
+     */
+    public void setAutoActivationDelay(int delay) {
+        fAutoActivationDelay= delay;
+    }
+
+    /**
+     * Gets the delay after which the content assistant is automatically invoked if the cursor is
+     * behind an auto activation character.
+     *
+     * @return the auto activation delay
+     * @since 3.4
+     */
+    public int getAutoActivationDelay() {
+        return fAutoActivationDelay;
+    }
+
+    /**
+     * Sets the proposal pop-ups' orientation. The following values may be used:
+     * <ul>
+     *   <li>PROPOSAL_OVERLAY<p>
+     *     proposal popup windows should overlay each other
+     *   </li>
+     *   <li>PROPOSAL_REMOVE<p>
+     *     any currently shown proposal popup should be closed
+     *   </li>
+     *   <li>PROPOSAL_STACKED<p>
+     *     proposal popup windows should be vertical stacked, with no overlap,
+     *     beneath the line containing the current cursor location
+     *   </li>
+     * </ul>
+     *
+     * @param orientation the popup's orientation
+     */
+    public void setProposalPopupOrientation(int orientation) {
+        fProposalPopupOrientation= orientation;
+    }
+
+    /**
+     * Sets the context information popup's orientation.
+     * The following values may be used:
+     * <ul>
+     *   <li>CONTEXT_ABOVE<p>
+     *     context information popup should always appear above the line containing
+     *     the current cursor location
+     *   </li>
+     *   <li>CONTEXT_BELOW<p>
+     *     context information popup should always appear below the line containing
+     *     the current cursor location
+     *   </li>
+     * </ul>
+     *
+     * @param orientation the popup's orientation
+     */
+    public void setContextInformationPopupOrientation(int orientation) {
+        fContextInfoPopupOrientation= orientation;
+    }
+
+    /**
+     * Sets the context information popup's background color.
+     *
+     * @param background the background color
+     */
+    public void setContextInformationPopupBackground(Color background) {
+        fContextInfoPopupBackground= background;
+    }
+
+    /**
+     * Returns the background of the context information popup.
+     *
+     * @return the background of the context information popup
+     * @since 2.0
+     */
+    Color getContextInformationPopupBackground() {
+        return fContextInfoPopupBackground;
+    }
+
+    /**
+     * Sets the context information popup's foreground color.
+     *
+     * @param foreground the foreground color
+     * @since 2.0
+     */
+    public void setContextInformationPopupForeground(Color foreground) {
+        fContextInfoPopupForeground= foreground;
+    }
+
+    /**
+     * Returns the foreground of the context information popup.
+     *
+     *
+     * @return the foreground of the context information popup
+     * @since 2.0
+     */
+    Color getContextInformationPopupForeground() {
+        return fContextInfoPopupForeground;
+    }
+
+    /**
+     * Sets the proposal selector's background color.
+     * <p>
+     * <strong>Note:</strong> As of 3.4, you should only call this
+     * method if you want to override the {@link JFacePreferences#CONTENT_ASSIST_BACKGROUND_COLOR}.
+     * </p>
+     *
+     * @param background the background color
+     * @since 2.0
+     */
+    public void setProposalSelectorBackground(Color background) {
+        fProposalSelectorBackground= background;
+    }
+
+    /**
+     * Returns the custom background color of the proposal selector.
+     *
+     * @return the background of the proposal selector or <code>null</code> if not set
+     * @since 2.0
+     */
+    Color getProposalSelectorBackground() {
+        return fProposalSelectorBackground;
+    }
+
+    /**
+     * Sets the proposal's foreground color.
+     * <p>
+     * <strong>Note:</strong> As of 3.4, you should only call this
+     * method if you want to override the {@link JFacePreferences#CONTENT_ASSIST_FOREGROUND_COLOR}.
+     * </p>
+     *
+     * @param foreground the foreground color
+     * @since 2.0
+     */
+    public void setProposalSelectorForeground(Color foreground) {
+        fProposalSelectorForeground= foreground;
+    }
+
+    /**
+     * Returns the custom foreground color of the proposal selector.
+     *
+     * @return the foreground of the proposal selector or <code>null</code> if not set
+     * @since 2.0
+     */
+    Color getProposalSelectorForeground() {
+        return fProposalSelectorForeground;
+    }
+
+    /**
+     * Sets the context selector's background color.
+     *
+     * @param background the background color
+     * @since 2.0
+     */
+    public void setContextSelectorBackground(Color background) {
+        fContextSelectorBackground= background;
+    }
+
+    /**
+     * Returns the background of the context selector.
+     *
+     * @return the background of the context selector
+     * @since 2.0
+     */
+    Color getContextSelectorBackground() {
+        return fContextSelectorBackground;
+    }
+
+    /**
+     * Sets the context selector's foreground color.
+     *
+     * @param foreground the foreground color
+     * @since 2.0
+     */
+    public void setContextSelectorForeground(Color foreground) {
+        fContextSelectorForeground= foreground;
+    }
+
+    /**
+     * Returns the foreground of the context selector.
+     *
+     * @return the foreground of the context selector
+     * @since 2.0
+     */
+    Color getContextSelectorForeground() {
+        return fContextSelectorForeground;
+    }
+
+    /**
+     * Sets the information control creator for the additional information control.
+     *
+     * @param creator the information control creator for the additional information control
+     * @since 2.0
+     */
+    public void setInformationControlCreator(IInformationControlCreator creator) {
+        fInformationControlCreator= creator;
+    }
+
+    /*
+     * @see IControlContentAssistant#install(IContentAssistSubjectControl)
+     * @since 3.0
+     */
+    protected void install(IContentAssistSubjectControl contentAssistSubjectControl) {
+        fContentAssistSubjectControl= contentAssistSubjectControl;
+        fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fContentAssistSubjectControl);
+        install();
+    }
+
+    /*
+     * @see IContentAssist#install
+     * @since 3.0
+     */
+    public void install(ITextViewer textViewer) {
+        fViewer= textViewer;
+        fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fViewer);
+        install();
+    }
+
+    protected void install() {
+
+        fLayoutManager= new LayoutManager();
+        fInternalListener= new InternalListener();
+
+        AdditionalInfoController controller= null;
+        if (fInformationControlCreator !is null) {
+            int delay= fAutoActivationDelay;
+            if (delay is 0)
+                delay= DEFAULT_AUTO_ACTIVATION_DELAY;
+            delay= cast(int)Math.round(delay * 1.5f);
+            controller= new AdditionalInfoController(fInformationControlCreator, delay);
+        }
+
+        fContextInfoPopup= fContentAssistSubjectControlAdapter.createContextInfoPopup(this);
+        fProposalPopup= fContentAssistSubjectControlAdapter.createCompletionProposalPopup(this, controller);
+
+        registerHandler(SELECT_NEXT_PROPOSAL_COMMAND_ID, fProposalPopup.createProposalSelectionHandler(CompletionProposalPopup.ProposalSelectionHandler.SELECT_NEXT));
+        registerHandler(SELECT_PREVIOUS_PROPOSAL_COMMAND_ID, fProposalPopup.createProposalSelectionHandler(CompletionProposalPopup.ProposalSelectionHandler.SELECT_PREVIOUS));
+
+        if (Helper.okToUse(fContentAssistSubjectControlAdapter.getControl())) {
+            fContentAssistSubjectControlShell= fContentAssistSubjectControlAdapter.getControl().getShell();
+            fCASCSTraverseListener= new class()  TraverseListener {
+                public void keyTraversed(TraverseEvent e) {
+                    if (e.detail is SWT.TRAVERSE_ESCAPE && isProposalPopupActive())
+                        e.doit= false;
+                }
+            };
+            fContentAssistSubjectControlShell.addTraverseListener(fCASCSTraverseListener);
+        }
+
+        manageAutoActivation(fIsAutoActivated);
+    }
+
+    /*
+     * @see IContentAssist#uninstall
+     */
+    public void uninstall() {
+        hide();
+        manageAutoActivation(false);
+
+        if (fHandlers !is null) {
+            fHandlers.clear();
+            fHandlers= null;
+        }
+
+        if (fCloser !is null) {
+            fCloser.uninstall();
+            fCloser= null;
+        }
+
+        if (Helper.okToUse(fContentAssistSubjectControlShell))
+            fContentAssistSubjectControlShell.removeTraverseListener(fCASCSTraverseListener);
+        fCASCSTraverseListener= null;
+        fContentAssistSubjectControlShell= null;
+
+        fViewer= null;
+        fContentAssistSubjectControl= null;
+        fContentAssistSubjectControlAdapter= null;
+    }
+
+    /**
+     * Adds the given shell of the specified type to the layout. Valid types are defined by
+     * <code>LayoutManager</code>.
+     *
+     * @param popup a content assist popup
+     * @param shell the shell of the content-assist popup
+     * @param type the type of popup
+     * @param visibleOffset the offset at which to layout the popup relative to the offset of the
+     *        viewer's visible region
+     * @since 2.0
+     */
+    void addToLayout(Object popup, Shell shell, int type, int visibleOffset) {
+        fLayoutManager.add(popup, shell, type, visibleOffset);
+    }
+
+    /**
+     * Layouts the registered popup of the given type relative to the given offset. The offset is
+     * relative to the offset of the viewer's visible region. Valid types are defined by
+     * <code>LayoutManager</code>.
+     *
+     * @param type the type of popup to layout
+     * @param visibleOffset the offset at which to layout relative to the offset of the viewer's
+     *        visible region
+     * @since 2.0
+     */
+    void layout(int type, int visibleOffset) {
+        fLayoutManager.layout(type, visibleOffset);
+    }
+
+    /**
+     * Returns the layout manager.
+     *
+     * @return the layout manager
+     * @since 3.3
+     */
+    LayoutManager getLayoutManager() {
+        return fLayoutManager;
+    }
+
+    /**
+     * Notifies the controller that a popup has lost focus.
+     *
+     * @param e the focus event
+     */
+    void popupFocusLost(FocusEvent e) {
+        fCloser.focusLost(e);
+    }
+
+    /**
+     * Returns the offset of the selection relative to the offset of the visible region.
+     *
+     * @return the offset of the selection relative to the offset of the visible region
+     * @since 2.0
+     */
+    int getSelectionOffset() {
+        return fContentAssistSubjectControlAdapter.getWidgetSelectionRange().x;
+    }
+
+    /**
+     * Returns whether the widget token could be acquired. The following are valid listener types:
+     * <ul>
+     *   <li>AUTO_ASSIST</li>
+     *   <li>CONTEXT_SELECTOR</li>
+     *   <li>PROPOSAL_SELECTOR</li>
+     *   <li>CONTEXT_INFO_POPUP</li>
+     * </ul>
+     *
+     * @param type the listener type for which to acquire
+     * @return <code>true</code> if the widget token could be acquired
+     * @since 2.0
+     */
+    private bool acquireWidgetToken(int type) {
+        switch (type) {
+            case CONTEXT_SELECTOR:
+            case PROPOSAL_SELECTOR:
+                if ( cast(IWidgetTokenOwnerExtension)fContentAssistSubjectControl ) {
+                    IWidgetTokenOwnerExtension extension= cast(IWidgetTokenOwnerExtension) fContentAssistSubjectControl;
+                    return extension.requestWidgetToken(this, WIDGET_PRIORITY);
+                } else if ( cast(IWidgetTokenOwner)fContentAssistSubjectControl ) {
+                    IWidgetTokenOwner owner= cast(IWidgetTokenOwner) fContentAssistSubjectControl;
+                    return owner.requestWidgetToken(this);
+                } else if ( cast(IWidgetTokenOwnerExtension)fViewer ) {
+                    IWidgetTokenOwnerExtension extension= cast(IWidgetTokenOwnerExtension) fViewer;
+                    return extension.requestWidgetToken(this, WIDGET_PRIORITY);
+                } else if ( cast(IWidgetTokenOwner)fViewer ) {
+                    IWidgetTokenOwner owner= cast(IWidgetTokenOwner) fViewer;
+                    return owner.requestWidgetToken(this);
+                }
+            default:
+        }
+        return true;
+    }
+
+    /**
+     * Registers a content assist listener. The following are valid listener types:
+     * <ul>
+     *   <li>AUTO_ASSIST</li>
+     *   <li>CONTEXT_SELECTOR</li>
+     *   <li>PROPOSAL_SELECTOR</li>
+     *   <li>CONTEXT_INFO_POPUP</li>
+     * </ul>
+     * Returns whether the listener could be added successfully. A listener can not be added if the
+     * widget token could not be acquired.
+     *
+     * @param listener the listener to register
+     * @param type the type of listener
+     * @return <code>true</code> if the listener could be added
+     */
+    bool addContentAssistListener(IContentAssistListener listener, int type) {
+
+        if (acquireWidgetToken(type)) {
+
+            fListeners[type]= listener;
+
+            if (fCloser is null && getNumberOfListeners() is 1) {
+                fCloser= new Closer();
+                fCloser.install();
+                fContentAssistSubjectControlAdapter.setEventConsumer(fInternalListener);
+                installKeyListener();
+            } else
+                promoteKeyListener();
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Re-promotes the key listener to the first position, using prependVerifyKeyListener. This
+     * ensures no other instance is filtering away the keystrokes underneath, if we've been up for a
+     * while (e.g. when the context info is showing.
+     *
+     * @since 3.0
+     */
+    private void promoteKeyListener() {
+        uninstallVerifyKeyListener();
+        installKeyListener();
+    }
+
+    /**
+     * Installs a key listener on the text viewer's widget.
+     */
+    private void installKeyListener() {
+        if (!fVerifyKeyListenerHooked) {
+            if (Helper.okToUse(fContentAssistSubjectControlAdapter.getControl())) {
+                fVerifyKeyListenerHooked= fContentAssistSubjectControlAdapter.prependVerifyKeyListener(fInternalListener);
+            }
+        }
+    }
+
+    /**
+     * Releases the previously acquired widget token if the token is no longer necessary. The
+     * following are valid listener types:
+     * <ul>
+     *   <li>AUTO_ASSIST</li>
+     *   <li>CONTEXT_SELECTOR</li>
+     *   <li>PROPOSAL_SELECTOR</li>
+     *   <li>CONTEXT_INFO_POPUP</li>
+     * </ul>
+     *
+     * @param type the listener type
+     * @since 2.0
+     */
+    private void releaseWidgetToken(int type) {
+        if (fListeners[CONTEXT_SELECTOR] is null && fListeners[PROPOSAL_SELECTOR] is null) {
+            IWidgetTokenOwner owner= null;
+            if ( cast(IWidgetTokenOwner)fContentAssistSubjectControl )
+                owner= cast(IWidgetTokenOwner) fContentAssistSubjectControl;
+            else if ( cast(IWidgetTokenOwner)fViewer )
+                owner= cast(IWidgetTokenOwner) fViewer;
+            if (owner !is null)
+                owner.releaseWidgetToken(this);
+        }
+    }
+
+    /**
+     * Unregisters a content assist listener.
+     *
+     * @param listener the listener to unregister
+     * @param type the type of listener
+     * @see #addContentAssistListener(IContentAssistListener, int)
+     */
+    void removeContentAssistListener(IContentAssistListener listener, int type) {
+        fListeners[type]= null;
+
+        if (getNumberOfListeners() is 0) {
+
+            if (fCloser !is null) {
+                fCloser.uninstall();
+                fCloser= null;
+            }
+
+            uninstallVerifyKeyListener();
+            fContentAssistSubjectControlAdapter.setEventConsumer(null);
+        }
+
+        releaseWidgetToken(type);
+    }
+
+    /**
+     * Uninstall the key listener from the text viewer's widget.
+     *
+     * @since 3.0
+     */
+    private void uninstallVerifyKeyListener() {
+        if (fVerifyKeyListenerHooked) {
+            if (Helper.okToUse(fContentAssistSubjectControlAdapter.getControl()))
+                fContentAssistSubjectControlAdapter.removeVerifyKeyListener(fInternalListener);
+            fVerifyKeyListenerHooked= false;
+        }
+    }
+
+    /**
+     * Returns the number of listeners.
+     *
+     * @return the number of listeners
+     * @since 2.0
+     */
+    private int getNumberOfListeners() {
+        int count= 0;
+        for (int i= 0; i <= CONTEXT_INFO_POPUP; i++) {
+            if (fListeners[i] !is null)
+                ++count;
+        }
+        return count;
+    }
+
+    /*
+     * @see IContentAssist#showPossibleCompletions
+     */
+    public String showPossibleCompletions() {
+        if (!prepareToShowCompletions(false))
+            return null;
+        if (fIsPrefixCompletionEnabled)
+            return fProposalPopup.incrementalComplete();
+        return fProposalPopup.showProposals(false);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension#completePrefix()
+     * @since 3.0
+     */
+    public String completePrefix() {
+        if (!prepareToShowCompletions(false))
+            return null;
+        return fProposalPopup.incrementalComplete();
+    }
+
+    /**
+     * Prepares to show content assist proposals. It returns false if auto activation has kicked in
+     * recently.
+     *
+     * @param isAutoActivated  whether completion was triggered by auto activation
+     * @return <code>true</code> if the caller should continue and show the proposals,
+     *         <code>false</code> otherwise.
+     * @since 3.2
+     */
+    private bool prepareToShowCompletions(bool isAutoActivated) {
+        long current= System.currentTimeMillis();
+        int gracePeriod= Math.max(fAutoActivationDelay, 200);
+        if (current < fLastAutoActivation + gracePeriod)
+            return false;
+
+        promoteKeyListener();
+        fireSessionBeginEvent(isAutoActivated);
+        return true;
+    }
+
+    /**
+     * Callback to signal this content assistant that the presentation of the possible completions
+     * has been stopped.
+     *
+     * @since 2.1
+     */
+    protected void possibleCompletionsClosed() {
+        fLastAutoActivation= Long.MIN_VALUE;
+        storeCompletionProposalPopupSize();
+    }
+    package void possibleCompletionsClosed_package() {
+        possibleCompletionsClosed();
+    }
+
+    /*
+     * @see IContentAssist#showContextInformation
+     */
+    public String showContextInformation() {
+        promoteKeyListener();
+        if (fContextInfoPopup !is null)
+            return fContextInfoPopup.showContextProposals(false);
+        return null;
+    }
+
+    /**
+     * Callback to signal this content assistant that the presentation of the context information
+     * has been stopped.
+     *
+     * @since 2.1
+     */
+    protected void contextInformationClosed() {
+    }
+    package void contextInformationClosed_package() {
+        contextInformationClosed();
+    }
+
+    /**
+     * Requests that the specified context information to be shown.
+     *
+     * @param contextInformation the context information to be shown
+     * @param offset the offset to which the context information refers to
+     * @since 2.0
+     */
+    void showContextInformation(IContextInformation contextInformation, int offset) {
+        if (fContextInfoPopup !is null)
+            fContextInfoPopup.showContextInformation(contextInformation, offset);
+    }
+
+    /**
+     * Returns the current content assist error message.
+     *
+     * @return an error message or <code>null</code> if no error has occurred
+     */
+    String getErrorMessage() {
+        return fLastErrorMessage;
+    }
+
+    /**
+     * Returns the content assist processor for the content type of the specified document position.
+     *
+     * @param viewer the text viewer
+     * @param offset a offset within the document
+     * @return a content-assist processor or <code>null</code> if none exists
+     * @since 3.0
+     */
+    private IContentAssistProcessor getProcessor(ITextViewer viewer, int offset) {
+        try {
+
+            IDocument document= viewer.getDocument();
+            String type= TextUtilities.getContentType(document, getDocumentPartitioning(), offset, true);
+
+            return getContentAssistProcessor(type);
+
+        } catch (BadLocationException x) {
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the content assist processor for the content type of the specified document position.
+     *
+     * @param contentAssistSubjectControl the content assist subject control
+     * @param offset a offset within the document
+     * @return a content-assist processor or <code>null</code> if none exists
+     * @since 3.0
+     */
+    private IContentAssistProcessor getProcessor(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
+        try {
+
+            IDocument document= contentAssistSubjectControl.getDocument();
+            String type;
+            if (document !is null)
+                type= TextUtilities.getContentType(document, getDocumentPartitioning(), offset, true);
+            else
+                type= IDocument.DEFAULT_CONTENT_TYPE;
+
+            return getContentAssistProcessor(type);
+
+        } catch (BadLocationException x) {
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns an array of completion proposals computed based on the specified document position.
+     * The position is used to determine the appropriate content assist processor to invoke.
+     *
+     * @param contentAssistSubjectControl the content assist subject control
+     * @param offset a document offset
+     * @return an array of completion proposals
+     * @see IContentAssistProcessor#computeCompletionProposals(ITextViewer, int)
+     * @since 3.0
+     */
+    ICompletionProposal[] computeCompletionProposals(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
+        fLastErrorMessage= null;
+
+        ICompletionProposal[] result= null;
+
+        IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
+        if ( cast(ISubjectControlContentAssistProcessor)p ) {
+            result= (cast(ISubjectControlContentAssistProcessor) p).computeCompletionProposals(contentAssistSubjectControl, offset);
+            fLastErrorMessage= p.getErrorMessage();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns an array of completion proposals computed based on the specified document position.
+     * The position is used to determine the appropriate content assist processor to invoke.
+     *
+     * @param viewer the viewer for which to compute the proposals
+     * @param offset a document offset
+     * @return an array of completion proposals or <code>null</code> if no proposals are possible
+     * @see IContentAssistProcessor#computeCompletionProposals(ITextViewer, int)
+     */
+    ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
+        fLastErrorMessage= null;
+
+        ICompletionProposal[] result= null;
+
+        IContentAssistProcessor p= getProcessor(viewer, offset);
+        if (p !is null) {
+            result= p.computeCompletionProposals(viewer, offset);
+            fLastErrorMessage= p.getErrorMessage();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns an array of context information objects computed based on the specified document
+     * position. The position is used to determine the appropriate content assist processor to
+     * invoke.
+     *
+     * @param viewer the viewer for which to compute the context information
+     * @param offset a document offset
+     * @return an array of context information objects
+     * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int)
+     */
+    IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
+        fLastErrorMessage= null;
+
+        IContextInformation[] result= null;
+
+        IContentAssistProcessor p= getProcessor(viewer, offset);
+        if (p !is null) {
+            result= p.computeContextInformation(viewer, offset);
+            fLastErrorMessage= p.getErrorMessage();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns an array of context information objects computed based on the specified document
+     * position. The position is used to determine the appropriate content assist processor to
+     * invoke.
+     *
+     * @param contentAssistSubjectControl the content assist subject control
+     * @param offset a document offset
+     * @return an array of context information objects
+     * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int)
+     * @since 3.0
+     */
+    IContextInformation[] computeContextInformation(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
+        fLastErrorMessage= null;
+
+        IContextInformation[] result= null;
+
+        IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
+        if ( cast(ISubjectControlContentAssistProcessor)p ) {
+            result= (cast(ISubjectControlContentAssistProcessor) p).computeContextInformation(contentAssistSubjectControl, offset);
+            fLastErrorMessage= p.getErrorMessage();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns the context information validator that should be used to determine when the currently
+     * displayed context information should be dismissed. The position is used to determine the
+     * appropriate content assist processor to invoke.
+     *
+     * @param viewer the text viewer
+     * @param offset a document offset
+     * @return an validator
+     * @see IContentAssistProcessor#getContextInformationValidator()
+     * @since 3.0
+     */
+    IContextInformationValidator getContextInformationValidator(ITextViewer viewer, int offset) {
+        IContentAssistProcessor p= getProcessor(viewer, offset);
+        return p !is null ? p.getContextInformationValidator() : null;
+    }
+
+    /**
+     * Returns the context information validator that should be used to determine when the currently
+     * displayed context information should be dismissed. The position is used to determine the
+     * appropriate content assist processor to invoke.
+     *
+     * @param contentAssistSubjectControl the content assist subject control
+     * @param offset a document offset
+     * @return an validator
+     * @see IContentAssistProcessor#getContextInformationValidator()
+     * @since 3.0
+     */
+    IContextInformationValidator getContextInformationValidator(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
+        IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
+        return p !is null ? p.getContextInformationValidator() : null;
+    }
+
+    /**
+     * Returns the context information presenter that should be used to display context information.
+     * The position is used to determine the appropriate content assist processor to invoke.
+     *
+     * @param viewer the text viewer
+     * @param offset a document offset
+     * @return a presenter
+     * @since 2.0
+     */
+    IContextInformationPresenter getContextInformationPresenter(ITextViewer viewer, int offset) {
+        IContextInformationValidator validator= getContextInformationValidator(viewer, offset);
+        if ( cast(IContextInformationPresenter)validator )
+            return cast(IContextInformationPresenter) validator;
+        return null;
+    }
+
+    /**
+     * Returns the context information presenter that should be used to display context information.
+     * The position is used to determine the appropriate content assist processor to invoke.
+     *
+     * @param contentAssistSubjectControl the content assist subject control
+     * @param offset a document offset
+     * @return a presenter
+     * @since 3.0
+     */
+    IContextInformationPresenter getContextInformationPresenter(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
+        IContextInformationValidator validator= getContextInformationValidator(contentAssistSubjectControl, offset);
+        if ( cast(IContextInformationPresenter)validator )
+            return cast(IContextInformationPresenter) validator;
+        return null;
+    }
+
+    /**
+     * Returns the characters which when typed by the user should automatically initiate proposing
+     * completions. The position is used to determine the appropriate content assist processor to
+     * invoke.
+     *
+     * @param contentAssistSubjectControl the content assist subject control
+     * @param offset a document offset
+     * @return the auto activation characters
+     * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
+     * @since 3.0
+     */
+    char[] getCompletionProposalAutoActivationCharacters(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
+        IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
+        return p !is null ? p.getCompletionProposalAutoActivationCharacters() : null;
+    }
+
+    /**
+     * Returns the characters which when typed by the user should automatically initiate proposing
+     * completions. The position is used to determine the appropriate content assist processor to
+     * invoke.
+     *
+     * @param viewer the text viewer
+     * @param offset a document offset
+     * @return the auto activation characters
+     * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
+     */
+    char[] getCompletionProposalAutoActivationCharacters(ITextViewer viewer, int offset) {
+        IContentAssistProcessor p= getProcessor(viewer, offset);
+        return p !is null ? p.getCompletionProposalAutoActivationCharacters() : null;
+    }
+
+    /**
+     * Returns the characters which when typed by the user should automatically initiate the
+     * presentation of context information. The position is used to determine the appropriate
+     * content assist processor to invoke.
+     *
+     * @param viewer the text viewer
+     * @param offset a document offset
+     * @return the auto activation characters
+     * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters()
+     * @since 3.0
+     */
+    char[] getContextInformationAutoActivationCharacters(ITextViewer viewer, int offset) {
+        IContentAssistProcessor p= getProcessor(viewer, offset);
+        return p !is null ? p.getContextInformationAutoActivationCharacters() : null;
+    }
+
+    /**
+     * Returns the characters which when typed by the user should automatically initiate the
+     * presentation of context information. The position is used to determine the appropriate
+     * content assist processor to invoke.
+     *
+     * @param contentAssistSubjectControl the content assist subject control
+     * @param offset a document offset
+     * @return the auto activation characters
+     * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters()
+     * @since 3.0
+     */
+    char[] getContextInformationAutoActivationCharacters(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
+        IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
+        return p !is null ? p.getContextInformationAutoActivationCharacters() : null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner)
+     * @since 2.0
+     */
+    public bool requestWidgetToken(IWidgetTokenOwner owner) {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(org.eclipse.jface.text.IWidgetTokenOwner,
+     *      int)
+     * @since 3.0
+     */
+    public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) {
+        if (priority > WIDGET_PRIORITY) {
+            hide();
+            return true;
+        }
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#setFocus(org.eclipse.jface.text.IWidgetTokenOwner)
+     * @since 3.0
+     */
+    public bool setFocus(IWidgetTokenOwner owner) {
+        if (fProposalPopup !is null) {
+            fProposalPopup.setFocus();
+            return fProposalPopup.hasFocus();
+        }
+        return false;
+    }
+
+    /**
+     * Hides any open pop-ups.
+     *
+     * @since 3.0
+     */
+    protected void hide() {
+        if (fProposalPopup !is null)
+            fProposalPopup.hide();
+
+        if (fContextInfoPopup !is null)
+            fContextInfoPopup.hide();
+    }
+    package void hide_package() {
+        hide();
+    }
+
+    // ------ control's size handling dialog settings ------
+
+    /**
+     * Tells this information control manager to open the information control with the values
+     * contained in the given dialog settings and to store the control's last valid size in the
+     * given dialog settings.
+     * <p>
+     * Note: This API is only valid if the information control implements
+     * {@link org.eclipse.jface.text.IInformationControlExtension3}. Not following this restriction
+     * will later result in an {@link UnsupportedOperationException}.
+     * </p>
+     * <p>
+     * The constants used to store the values are:
+     * <ul>
+     * <li>{@link ContentAssistant#STORE_SIZE_X}</li>
+     * <li>{@link ContentAssistant#STORE_SIZE_Y}</li>
+     * </ul>
+     * </p>
+     *
+     * @param dialogSettings
+     * @since 3.0
+     */
+    public void setRestoreCompletionProposalSize(IDialogSettings dialogSettings) {
+        Assert.isTrue(dialogSettings !is null);
+        fDialogSettings= dialogSettings;
+    }
+
+    /**
+     * Stores the content assist pop-up's size.
+     */
+    protected void storeCompletionProposalPopupSize() {
+        if (fDialogSettings is null || fProposalPopup is null)
+            return;
+
+        Point size= fProposalPopup.getSize();
+        if (size is null)
+            return;
+
+        fDialogSettings.put(STORE_SIZE_X, size.x);
+        fDialogSettings.put(STORE_SIZE_Y, size.y);
+    }
+
+    /**
+     * Restores the content assist pop-up's size.
+     *
+     * @return the stored size
+     * @since 3.0
+     */
+    protected Point restoreCompletionProposalPopupSize() {
+        if (fDialogSettings is null)
+            return null;
+
+        Point size= new Point(-1, -1);
+
+        try {
+            size.x= fDialogSettings.getInt(STORE_SIZE_X);
+            size.y= fDialogSettings.getInt(STORE_SIZE_Y);
+        } catch (NumberFormatException ex) {
+            size.x= -1;
+            size.y= -1;
+        }
+
+        // sanity check
+        if (size.x is -1 && size.y is -1)
+            return null;
+
+        Rectangle maxBounds= null;
+        if (fContentAssistSubjectControl !is null && Helper.okToUse(fContentAssistSubjectControl.getControl()))
+            maxBounds= fContentAssistSubjectControl.getControl().getDisplay().getBounds();
+        else {
+            // fallback
+            Display display= Display.getCurrent();
+            if (display is null)
+                display= Display.getDefault();
+            if (display !is null && !display.isDisposed())
+                maxBounds= display.getBounds();
+        }
+
+        if (size.x > -1 && size.y > -1) {
+            if (maxBounds !is null) {
+                size.x= Math.min(size.x, maxBounds.width);
+                size.y= Math.min(size.y, maxBounds.height);
+            }
+
+            // Enforce an absolute minimal size
+            size.x= Math.max(size.x, 30);
+            size.y= Math.max(size.y, 30);
+        }
+
+        return size;
+    }
+    package Point restoreCompletionProposalPopupSize_package() {
+        return restoreCompletionProposalPopupSize();
+    }
+
+    /**
+     * Sets the prefix completion property. If enabled, content assist delegates completion to
+     * prefix completion.
+     *
+     * @param enabled <code>true</code> to enable prefix completion, <code>false</code> to
+     *        disable
+     */
+    public void enablePrefixCompletion(bool enabled) {
+        fIsPrefixCompletionEnabled= enabled;
+    }
+
+    /**
+     * Returns the prefix completion state.
+     *
+     * @return <code>true</code> if prefix completion is enabled, <code>false</code> otherwise
+     * @since 3.2
+     */
+    bool isPrefixCompletionEnabled() {
+        return fIsPrefixCompletionEnabled;
+    }
+
+    /**
+     * Returns whether the content assistant proposal popup has the focus.
+     *
+     * @return <code>true</code> if the proposal popup has the focus
+     * @since 3.0
+     */
+    public bool hasProposalPopupFocus() {
+        return fProposalPopup.hasFocus();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#addCompletionListener(org.eclipse.jface.text.contentassist.ICompletionListener)
+     * @since 3.2
+     */
+    public void addCompletionListener(ICompletionListener listener) {
+        Assert.isLegal(listener !is null);
+        fCompletionListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#removeCompletionListener(org.eclipse.jface.text.contentassist.ICompletionListener)
+     * @since 3.2
+     */
+    public void removeCompletionListener(ICompletionListener listener) {
+        fCompletionListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Fires a session begin event to all registered {@link ICompletionListener}s.
+     *
+     * @param isAutoActivated  <code>true</code> if this session was triggered by auto activation
+     * @since 3.2
+     */
+    void fireSessionBeginEvent(bool isAutoActivated) {
+        if (fContentAssistSubjectControlAdapter !is null && !isProposalPopupActive()) {
+            IContentAssistProcessor processor= getProcessor(fContentAssistSubjectControlAdapter, fContentAssistSubjectControlAdapter.getSelectedRange().x);
+            ContentAssistEvent event= new ContentAssistEvent(this, processor, isAutoActivated);
+            Object[] listeners= fCompletionListeners.getListeners();
+            for (int i= 0; i < listeners.length; i++) {
+                ICompletionListener listener= cast(ICompletionListener)listeners[i];
+                listener.assistSessionStarted(event);
+            }
+        }
+    }
+
+    /**
+     * Fires a session restart event to all registered {@link ICompletionListener}s.
+     *
+     * @since 3.4
+     */
+    void fireSessionRestartEvent() {
+        if (fContentAssistSubjectControlAdapter !is null) {
+            IContentAssistProcessor processor= getProcessor(fContentAssistSubjectControlAdapter, fContentAssistSubjectControlAdapter.getSelectedRange().x);
+            ContentAssistEvent event= new ContentAssistEvent(this, processor);
+            Object[] listeners= fCompletionListeners.getListeners();
+            for (int i= 0; i < listeners.length; i++) {
+                ICompletionListener listener= cast(ICompletionListener)listeners[i];
+                if ( cast(ICompletionListenerExtension)listener )
+                    (cast(ICompletionListenerExtension)listener).assistSessionRestarted(event);
+            }
+        }
+    }
+
+    /**
+     * Fires a session end event to all registered {@link ICompletionListener}s.
+     *
+     * @since 3.2
+     */
+    void fireSessionEndEvent() {
+        if (fContentAssistSubjectControlAdapter !is null) {
+            IContentAssistProcessor processor= getProcessor(fContentAssistSubjectControlAdapter, fContentAssistSubjectControlAdapter.getSelectedRange().x);
+            ContentAssistEvent event= new ContentAssistEvent(this, processor);
+            Object[] listeners= fCompletionListeners.getListeners();
+            for (int i= 0; i < listeners.length; i++) {
+                ICompletionListener listener= cast(ICompletionListener)listeners[i];
+                listener.assistSessionEnded(event);
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setRepeatedInvocationMode(bool)
+     * @since 3.2
+     */
+    public void setRepeatedInvocationMode(bool cycling) {
+        fIsRepetitionMode= cycling;
+    }
+
+    /**
+     * Returns <code>true</code> if repeated invocation mode is enabled, <code>false</code>
+     * otherwise.
+     *
+     * @return <code>true</code> if repeated invocation mode is enabled, <code>false</code>
+     *         otherwise
+     * @since 3.2
+     */
+    bool isRepeatedInvocationMode() {
+        return fIsRepetitionMode;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setShowEmptyList(bool)
+     * @since 3.2
+     */
+    public void setShowEmptyList(bool showEmpty) {
+        fShowEmptyList= showEmpty;
+    }
+
+    /**
+     * Returns <code>true</code> if empty lists should be displayed, <code>false</code>
+     * otherwise.
+     *
+     * @return <code>true</code> if empty lists should be displayed, <code>false</code>
+     *         otherwise
+     * @since 3.2
+     */
+    bool isShowEmptyList() {
+        return fShowEmptyList;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setStatusLineVisible(bool)
+     * @since 3.2
+     */
+    public void setStatusLineVisible(bool show) {
+        fIsStatusLineVisible= show;
+        if (fProposalPopup !is null)
+            fProposalPopup.setStatusLineVisible(show);
+    }
+
+    /**
+     * Returns <code>true</code> if a message line should be displayed, <code>false</code>
+     * otherwise.
+     *
+     * @return <code>true</code> if a message line should be displayed, <code>false</code>
+     *         otherwise
+     * @since 3.2
+     */
+    bool isStatusLineVisible() {
+        return fIsStatusLineVisible;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setStatusMessage(java.lang.String)
+     * @since 3.2
+     */
+    public void setStatusMessage(String message) {
+        Assert.isLegal(message !is null);
+        fMessage= message;
+        if (fProposalPopup !is null)
+            fProposalPopup.setMessage(message);
+    }
+
+    /**
+     * Returns the affordance caption for the cycling affordance at the bottom of the pop-up.
+     *
+     * @return the affordance caption for the cycling affordance at the bottom of the pop-up
+     * @since 3.2
+     */
+    String getStatusMessage() {
+        return fMessage;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setEmptyMessage(java.lang.String)
+     * @since 3.2
+     */
+    public void setEmptyMessage(String message) {
+        Assert.isLegal(message !is null);
+        if (fProposalPopup !is null)
+            fProposalPopup.setEmptyMessage(message);
+    }
+
+    /**
+     * Fires a selection event, see {@link ICompletionListener}.
+     *
+     * @param proposal the selected proposal, possibly <code>null</code>
+     * @param smartToggle true if the smart toggle is on
+     * @since 3.2
+     */
+    void fireSelectionEvent(ICompletionProposal proposal, bool smartToggle) {
+        Object[] listeners= fCompletionListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++) {
+            ICompletionListener listener= cast(ICompletionListener)listeners[i];
+            listener.selectionChanged(proposal, smartToggle);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension3#setInvocationTrigger(org.eclipse.jface.bindings.keys.KeySequence)
+     * @since 3.2
+     */
+    public void setRepeatedInvocationTrigger(KeySequence sequence) {
+        fRepeatedInvocationKeySequence= sequence;
+    }
+
+    /**
+     * Returns the repeated invocation key sequence.
+     *
+     * @return the repeated invocation key sequence or <code>null</code>, if none
+     * @since 3.2
+     */
+    KeySequence getRepeatedInvocationKeySequence() {
+        return fRepeatedInvocationKeySequence;
+    }
+
+    /**
+     * Returns whether proposal popup is active.
+     *
+     * @return <code>true</code> if the proposal popup is active, <code>false</code> otherwise
+     * @since 3.4
+     */
+    protected bool isProposalPopupActive(){
+        return fProposalPopup !is null && fProposalPopup.isActive();
+    }
+
+    /**
+     * Returns whether the context information popup is active.
+     *
+     * @return <code>true</code> if the context information popup is active, <code>false</code> otherwise
+     * @since 3.4
+     */
+    protected bool isContextInfoPopupActive(){
+        return fContextInfoPopup !is null && fContextInfoPopup.isActive();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 3.4
+     */
+    public final IHandler getHandler(String commandId) {
+        if (fHandlers is null)
+            throw new IllegalStateException();
+
+        IHandler handler= cast(IHandler)fHandlers.get(commandId);
+        if (handler !is null)
+            return handler;
+
+        Assert.isLegal(false);
+        return null;
+    }
+
+    /**
+     * Registers the given handler under the given command identifier.
+     *
+     * @param commandId the command identifier
+     * @param handler the handler
+     * @since 3.4
+     */
+    protected final void registerHandler(String commandId, IHandler handler) {
+        if (fHandlers is null)
+            fHandlers= new HashMap(2);
+        fHandlers.put(commandId, cast(Object)handler);
+    }
+
+    /**
+     * Tells whether the support for colored labels is enabled.
+     *
+     * @return <code>true</code> if the support for colored labels is enabled, <code>false</code> otherwise
+     * @since 3.4
+     */
+    bool isColoredLabelsSupportEnabled() {
+        return fIsColoredLabelsSupportEnabled;
+    }
+
+    /**
+     * Enables the support for colored labels in the proposal popup.
+     * <p>Completion proposals can implement {@link ICompletionProposalExtension6}
+     * to provide colored proposal labels.</p>
+     *
+     * @param isEnabled if <code>true</code> the support for colored labels is enabled in the proposal popup
+     * @since 3.4
+     */
+    public void enableColoredLabels(bool isEnabled) {
+        fIsColoredLabelsSupportEnabled= isEnabled;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.contentassist.ContextInformation;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.core.runtime.Assert;
+
+
+
+/**
+ * A default implementation of the <code>IContextInformation</code> interface.
+ */
+public final class ContextInformation : IContextInformation {
+
+    /** The name of the context. */
+    private const String fContextDisplayString;
+    /** The information to be displayed. */
+    private const String fInformationDisplayString;
+    /** The image to be displayed. */
+    private const Image fImage;
+
+    /**
+     * Creates a new context information without an image.
+     *
+     * @param contextDisplayString the string to be used when presenting the context
+     * @param informationDisplayString the string to be displayed when presenting the context information
+     */
+    public this(String contextDisplayString, String informationDisplayString) {
+        this(null, contextDisplayString, informationDisplayString);
+    }
+
+    /**
+     * Creates a new context information with an image.
+     *
+     * @param image the image to display when presenting the context information
+     * @param contextDisplayString the string to be used when presenting the context
+     * @param informationDisplayString the string to be displayed when presenting the context information,
+     *      may not be <code>null</code>
+     */
+    public this(Image image, String contextDisplayString, String informationDisplayString) {
+
+        Assert.isNotNull(informationDisplayString);
+
+        fImage= image;
+        fContextDisplayString= contextDisplayString;
+        fInformationDisplayString= informationDisplayString;
+    }
+
+    /*
+     * @see IContextInformation#equals(Object)
+     */
+    public bool equals(Object object) {
+        if ( cast(IContextInformation)object ) {
+            IContextInformation contextInformation= cast(IContextInformation) object;
+            bool equals= fInformationDisplayString.equalsIgnoreCase(contextInformation.getInformationDisplayString());
+            if (fContextDisplayString !is null)
+                equals= equals && fContextDisplayString.equalsIgnoreCase(contextInformation.getContextDisplayString());
+            return equals;
+        }
+        return false;
+    }
+
+    /*
+     * @see java.lang.Object#hashCode()
+     * @since 3.1
+     */
+    public override hash_t toHash() {
+        int low= fContextDisplayString !is null ? .toHash(fContextDisplayString) : 0;
+        return (.toHash(fInformationDisplayString) << 16) | low;
+    }
+
+    /*
+     * @see IContextInformation#getInformationDisplayString()
+     */
+    public String getInformationDisplayString() {
+        return fInformationDisplayString;
+    }
+
+    /*
+     * @see IContextInformation#getImage()
+     */
+    public Image getImage() {
+        return fImage;
+    }
+
+    /*
+     * @see IContextInformation#getContextDisplayString()
+     */
+    public String getContextDisplayString() {
+        if (fContextDisplayString !is null)
+            return fContextDisplayString;
+        return fInformationDisplayString;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformationPopup.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,888 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.contentassist.ContextInformationPopup;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Stack;
+import java.util.Iterator;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.TextPresentation;
+
+    /**
+     * Represents the state necessary for embedding contexts.
+     *
+     * @since 2.0
+     */
+    static class ContextFrame {
+
+        final int fBeginOffset;
+        final int fOffset;
+        final int fVisibleOffset;
+        final IContextInformation fInformation;
+        final IContextInformationValidator fValidator;
+        final IContextInformationPresenter fPresenter;
+
+        /*
+         * @since 3.1
+         */
+        public this(IContextInformation information, int beginOffset, int offset, int visibleOffset, IContextInformationValidator validator, IContextInformationPresenter presenter) {
+            fInformation = information;
+            fBeginOffset = beginOffset;
+            fOffset = offset;
+            fVisibleOffset = visibleOffset;
+            fValidator = validator;
+            fPresenter = presenter;
+        }
+
+        /*
+         * @see java.lang.Object#equals(java.lang.Object)
+         * @since 3.0
+         */
+        public override int opEquals(Object obj) {
+            if ( cast(ContextFrame)obj ) {
+                ContextFrame frame= cast(ContextFrame) obj;
+                return fInformation==/++/frame.fInformation && fBeginOffset is frame.fBeginOffset;
+            }
+            return super.opEquals(obj);
+        }
+
+        /*
+         * @see java.lang.Object#hashCode()
+         * @since 3.1
+         */
+        public override hash_t toHash() {
+            return ((cast(Object)fInformation).toHash() << 16) | fBeginOffset;
+        }
+    }
+
+    alias ContextFrame ContextInformationPopup_ContextFrame;
+/**
+ * This class is used to present context information to the user.
+ * If multiple contexts are valid at the current cursor location,
+ * a list is presented from which the user may choose one context.
+ * Once the user makes their choice, or if there was only a single
+ * possible context, the context information is shown in a tool tip like popup. <p>
+ * If the tool tip is visible and the user wants to see context information of
+ * a context embedded into the one for which context information is displayed,
+ * context information for the embedded context is shown. As soon as the
+ * cursor leaves the embedded context area, the context information for
+ * the embedding context is shown again.
+ *
+ * @see IContextInformation
+ * @see IContextInformationValidator
+ */
+class ContextInformationPopup : IContentAssistListener {
+
+
+
+    private ITextViewer fViewer;
+    private ContentAssistant fContentAssistant;
+
+    private PopupCloser fPopupCloser;
+    private Shell fContextSelectorShell;
+    private Table fContextSelectorTable;
+    private IContextInformation[] fContextSelectorInput;
+    private String fLineDelimiter= null;
+
+    private Shell fContextInfoPopup;
+    private StyledText fContextInfoText;
+    private TextPresentation fTextPresentation;
+
+    private Stack fContextFrameStack;
+    /**
+     * The content assist subject control.
+     *
+     * @since 3.0
+     */
+    private IContentAssistSubjectControl fContentAssistSubjectControl;
+    /**
+     * The content assist subject control adapter.
+     *
+     * @since 3.0
+     */
+    private ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter;
+
+    /**
+     * Selection listener on the text widget which is active
+     * while a context information pop up is shown.
+     *
+     * @since 3.0
+     */
+    private SelectionListener fTextWidgetSelectionListener;
+
+    /**
+     * The last removed context frame is remembered in order to not re-query the
+     * user about which context should be used.
+     *
+     * @since 3.0
+     */
+    private ContextFrame fLastContext= null;
+
+    /**
+     * Creates a new context information popup.
+     *
+     * @param contentAssistant the content assist for computing the context information
+     * @param viewer the viewer on top of which the context information is shown
+     */
+    public this(ContentAssistant contentAssistant, ITextViewer viewer) {
+        fPopupCloser= new PopupCloser();
+        fContextFrameStack= new Stack();
+
+        fContentAssistant= contentAssistant;
+        fViewer= viewer;
+        fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fViewer);
+    }
+
+    /**
+     * Creates a new context information popup.
+     *
+     * @param contentAssistant the content assist for computing the context information
+     * @param contentAssistSubjectControl the content assist subject control on top of which the context information is shown
+     * @since 3.0
+     */
+    public this(ContentAssistant contentAssistant, IContentAssistSubjectControl contentAssistSubjectControl) {
+        fPopupCloser= new PopupCloser();
+        fContextFrameStack= new Stack();
+
+        fContentAssistant= contentAssistant;
+        fContentAssistSubjectControl= contentAssistSubjectControl;
+        fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fContentAssistSubjectControl);
+    }
+
+    /**
+     * Shows all possible contexts for the given cursor position of the viewer.
+     *
+     * @param autoActivated <code>true</code>  if auto activated
+     * @return  a potential error message or <code>null</code> in case of no error
+     */
+    public String showContextProposals(bool autoActivated) {
+        final Control control= fContentAssistSubjectControlAdapter.getControl();
+        BusyIndicator.showWhile(control.getDisplay(), dgRunnable( {
+            int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
+
+            IContextInformation[] contexts= computeContextInformation(offset);
+            int count = (contexts is null ? 0 : contexts.length);
+            if (count is 1) {
+
+                ContextFrame frame= createContextFrame(contexts[0], offset);
+                if (isDuplicate(frame))
+                    validateContextInformation();
+                else
+                    // Show context information directly
+                    internalShowContextInfo(frame);
+
+            } else if (count > 0) {
+
+                // if any of the proposed context matches any of the contexts on the stack,
+                // assume that one (so, if context info is invoked repeatedly, the current
+                // info is kept)
+                for (int i= 0; i < contexts.length; i++) {
+                    IContextInformation info= contexts[i];
+                    ContextFrame frame= createContextFrame(info, offset);
+
+                    // check top of stack and stored context
+                    if (isDuplicate(frame)) {
+                        validateContextInformation();
+                        return;
+                    }
+
+                    if (isLastFrame(frame)) {
+                        internalShowContextInfo(frame);
+                        return;
+                    }
+
+                    // also check all other contexts
+                    for (Iterator it= fContextFrameStack.iterator(); it.hasNext(); ) {
+                        ContextFrame stackFrame= cast(ContextFrame) it.next();
+                        if (stackFrame==/++/frame) {
+                            validateContextInformation();
+                            return;
+                        }
+                    }
+                }
+
+                // otherwise:
+                // Precise context must be selected
+
+                if (fLineDelimiter is null)
+                    fLineDelimiter= fContentAssistSubjectControlAdapter.getLineDelimiter();
+
+                createContextSelector();
+                setContexts(contexts);
+                displayContextSelector();
+            }
+        }));
+
+        return getErrorMessage();
+    }
+
+    /**
+     * Displays the given context information for the given offset.
+     *
+     * @param info the context information
+     * @param offset the offset
+     * @since 2.0
+     */
+    public void showContextInformation(IContextInformation info, int offset) {
+        Control control= fContentAssistSubjectControlAdapter.getControl();
+        BusyIndicator.showWhile(control.getDisplay(), dgRunnable( (IContextInformation info_, int offset_){
+            if (info_ is null)
+                validateContextInformation();
+            else {
+                ContextFrame frame= createContextFrame(info_, offset_);
+                if (isDuplicate(frame))
+                    validateContextInformation();
+                else
+                    internalShowContextInfo(frame);
+                hideContextSelector();
+            }
+        }, info, offset ));
+    }
+
+    /**
+     * Displays the given context information for the given offset.
+     *
+     * @param frame the context frame to display, or <code>null</code>
+     * @since 3.0
+     */
+    private void internalShowContextInfo(ContextFrame frame) {
+        if (frame !is null) {
+            fContextFrameStack.push(frame);
+            if (fContextFrameStack.size() is 1)
+                fLastContext= null;
+            internalShowContextFrame(frame, fContextFrameStack.size() is 1);
+            validateContextInformation();
+        }
+    }
+
+    /**
+     * Creates a context frame for the given offset.
+     *
+     * @param information the context information
+     * @param offset the offset
+     * @return the created context frame
+     * @since 3.0
+     */
+    private ContextFrame createContextFrame(IContextInformation information, int offset) {
+        IContextInformationValidator validator= fContentAssistSubjectControlAdapter.getContextInformationValidator(fContentAssistant, offset);
+
+        if (validator !is null) {
+            int beginOffset= ( cast(IContextInformationExtension)information ) ? (cast(IContextInformationExtension) information).getContextInformationPosition() : offset;
+            if (beginOffset is -1) beginOffset= offset;
+            int visibleOffset= fContentAssistSubjectControlAdapter.getWidgetSelectionRange().x - (offset - beginOffset);
+            IContextInformationPresenter presenter = fContentAssistSubjectControlAdapter.getContextInformationPresenter(fContentAssistant, offset);
+            return new ContextFrame(information, beginOffset, offset, visibleOffset, validator, presenter);
+        }
+
+        return null;
+    }
+
+    /**
+     * Compares <code>frame</code> with the top of the stack, returns <code>true</code>
+     * if the frames are the same.
+     *
+     * @param frame the frame to check
+     * @return <code>true</code> if <code>frame</code> matches the top of the stack
+     * @since 3.0
+     */
+    private bool isDuplicate(ContextFrame frame) {
+        if (frame is null)
+            return false;
+        if (fContextFrameStack.isEmpty())
+            return false;
+        // stack not empty
+        ContextFrame top= cast(ContextFrame) fContextFrameStack.peek();
+        return cast(bool) frame.opEquals(top);
+    }
+
+    /**
+     * Compares <code>frame</code> with most recently removed context frame, returns <code>true</code>
+     * if the frames are the same.
+     *
+     * @param frame the frame to check
+     * @return <code>true</code> if <code>frame</code> matches the most recently removed
+     * @since 3.0
+     */
+    private bool isLastFrame(ContextFrame frame) {
+        return frame !is null && frame.opEquals(fLastContext);
+    }
+
+    /**
+     * Shows the given context frame.
+     *
+     * @param frame the frame to display
+     * @param initial <code>true</code> if this is the first frame to be displayed
+     * @since 2.0
+     */
+    private void internalShowContextFrame(ContextFrame frame, bool initial) {
+
+        fContentAssistSubjectControlAdapter.installValidator(frame);
+
+        if (frame.fPresenter !is null) {
+            if (fTextPresentation is null)
+                fTextPresentation= new TextPresentation();
+            fContentAssistSubjectControlAdapter.installContextInformationPresenter(frame);
+            frame.fPresenter.updatePresentation(frame.fOffset, fTextPresentation);
+        }
+
+        createContextInfoPopup();
+
+        fContextInfoText.setText(frame.fInformation.getInformationDisplayString());
+        if (fTextPresentation !is null)
+            TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText);
+        resize(frame.fVisibleOffset);
+
+        if (initial) {
+            if (fContentAssistant.addContentAssistListener(this, ContentAssistant.CONTEXT_INFO_POPUP)) {
+                if (fContentAssistSubjectControlAdapter.getControl() !is null) {
+                    fTextWidgetSelectionListener= new class()  SelectionAdapter {
+                        /*
+                         * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+                         */
+                        public void widgetSelected(SelectionEvent e) {
+                            validateContextInformation();
+                        }};
+                    fContentAssistSubjectControlAdapter.addSelectionListener(fTextWidgetSelectionListener);
+                }
+                fContentAssistant.addToLayout(this, fContextInfoPopup, ContentAssistant.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset);
+                fContextInfoPopup.setVisible(true);
+            }
+        } else {
+            fContentAssistant.layout(ContentAssistant.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset);
+        }
+    }
+
+    /**
+     * Computes all possible context information for the given offset.
+     *
+     * @param offset the offset
+     * @return all possible context information for the given offset
+     * @since 2.0
+     */
+    private IContextInformation[] computeContextInformation(int offset) {
+        return fContentAssistSubjectControlAdapter.computeContextInformation(fContentAssistant, offset);
+    }
+
+    /**
+     *Returns the error message generated while computing context information.
+     *
+     * @return the error message
+     */
+    private String getErrorMessage() {
+        return fContentAssistant.getErrorMessage();
+    }
+
+    /**
+     * Creates the context information popup. This is the tool tip like overlay window.
+     */
+    private void createContextInfoPopup() {
+        if (Helper.okToUse(fContextInfoPopup))
+            return;
+
+        Control control= fContentAssistSubjectControlAdapter.getControl();
+        Display display= control.getDisplay();
+
+        fContextInfoPopup= new Shell(control.getShell(), SWT.NO_TRIM | SWT.ON_TOP);
+        fContextInfoPopup.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
+
+        fContextInfoText= new StyledText(fContextInfoPopup, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP);
+
+        Color c= fContentAssistant.getContextInformationPopupBackground();
+        if (c is null)
+            c= display.getSystemColor(SWT.COLOR_INFO_BACKGROUND);
+        fContextInfoText.setBackground(c);
+
+        c= fContentAssistant.getContextInformationPopupForeground();
+        if (c is null)
+            c= display.getSystemColor(SWT.COLOR_INFO_FOREGROUND);
+        fContextInfoText.setForeground(c);
+    }
+
+    /**
+     * Resizes the context information popup.
+     *
+     * @param offset the caret offset in widget coordinates
+     * @since 2.0
+     */
+    private void resize(int offset) {
+        Point size= fContextInfoText.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+        final int TEXT_PAD= 0;
+        final int BORDER_PAD= 2;
+        final int PAD= TEXT_PAD + BORDER_PAD;
+        size.x += PAD;
+        Rectangle bounds= fContentAssistant.getLayoutManager().computeBoundsAboveBelow_package(fContextInfoPopup, size, offset);
+        if (bounds.width < size.x)
+            // we don't fit on the screen - try again and wrap
+            size= fContextInfoText.computeSize(bounds.width - PAD, SWT.DEFAULT, true);
+
+        size.x += TEXT_PAD;
+        fContextInfoText.setSize(size);
+        fContextInfoText.setLocation(1,1);
+        size.x += BORDER_PAD;
+        size.y += BORDER_PAD;
+        fContextInfoPopup.setSize(size);
+    }
+
+    /**
+     * Hides the context information popup.
+     */
+    private void hideContextInfoPopup() {
+
+        if (Helper.okToUse(fContextInfoPopup)) {
+
+            int size= fContextFrameStack.size();
+            if (size > 0) {
+                fLastContext= cast(ContextFrame) fContextFrameStack.pop();
+                -- size;
+            }
+
+            if (size > 0) {
+                ContextFrame current= cast(ContextFrame) fContextFrameStack.peek();
+                internalShowContextFrame(current, false);
+            } else {
+
+                fContentAssistant.removeContentAssistListener(this, ContentAssistant.CONTEXT_INFO_POPUP);
+
+                if (fContentAssistSubjectControlAdapter.getControl() !is null)
+                    fContentAssistSubjectControlAdapter.removeSelectionListener(fTextWidgetSelectionListener);
+                fTextWidgetSelectionListener= null;
+
+                fContextInfoPopup.setVisible(false);
+                fContextInfoPopup.dispose();
+                fContextInfoPopup= null;
+
+                if (fTextPresentation !is null) {
+                    fTextPresentation.clear();
+                    fTextPresentation= null;
+                }
+            }
+        }
+
+        if (fContextInfoPopup is null)
+            fContentAssistant.contextInformationClosed_package();
+    }
+
+    /**
+     * Creates the context selector in case the user has the choice between multiple valid contexts
+     * at a given offset.
+     */
+    private void createContextSelector() {
+        if (Helper.okToUse(fContextSelectorShell))
+            return;
+
+        Control control= fContentAssistSubjectControlAdapter.getControl();
+        fContextSelectorShell= new Shell(control.getShell(), SWT.ON_TOP | SWT.RESIZE);
+        GridLayout layout= new GridLayout();
+        layout.marginWidth= 0;
+        layout.marginHeight= 0;
+        fContextSelectorShell.setLayout(layout);
+        fContextSelectorShell.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_BLACK));
+
+
+        fContextSelectorTable= new Table(fContextSelectorShell, SWT.H_SCROLL | SWT.V_SCROLL);
+        fContextSelectorTable.setLocation(1, 1);
+        GridData gd= new GridData(GridData.FILL_BOTH);
+        gd.heightHint= fContextSelectorTable.getItemHeight() * 10;
+        gd.widthHint= 300;
+        fContextSelectorTable.setLayoutData(gd);
+
+        fContextSelectorShell.pack(true);
+
+        Color c= fContentAssistant.getContextSelectorBackground();
+        if (c is null)
+            c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
+        fContextSelectorTable.setBackground(c);
+
+        c= fContentAssistant.getContextSelectorForeground();
+        if (c is null)
+            c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
+        fContextSelectorTable.setForeground(c);
+
+        fContextSelectorTable.addSelectionListener(new class()  SelectionListener {
+            public void widgetSelected(SelectionEvent e) {
+            }
+
+            public void widgetDefaultSelected(SelectionEvent e) {
+                insertSelectedContext();
+                hideContextSelector();
+            }
+        });
+
+        fPopupCloser.install(fContentAssistant, fContextSelectorTable);
+
+        fContextSelectorTable.setHeaderVisible(false);
+        fContentAssistant.addToLayout(this, fContextSelectorShell, ContentAssistant.LayoutManager.LAYOUT_CONTEXT_SELECTOR, fContentAssistant.getSelectionOffset());
+    }
+
+    /**
+     * Returns the minimal required height for the popup, may return 0 if the popup has not been
+     * created yet.
+     *
+     * @return the minimal height
+     * @since 3.3
+     */
+    int getMinimalHeight() {
+        int height= 0;
+        if (Helper.okToUse(fContextSelectorTable)) {
+            int items= fContextSelectorTable.getItemHeight() * 10;
+            Rectangle trim= fContextSelectorTable.computeTrim(0, 0, SWT.DEFAULT, items);
+            height= trim.height;
+        }
+        return height;
+    }
+
+    /**
+     * Causes the context information of the context selected in the context selector
+     * to be displayed in the context information popup.
+     */
+    private void insertSelectedContext() {
+        int i= fContextSelectorTable.getSelectionIndex();
+
+        if (i < 0 || i >= fContextSelectorInput.length)
+            return;
+
+        int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
+        internalShowContextInfo(createContextFrame(fContextSelectorInput[i], offset));
+    }
+
+    /**
+     * Sets the contexts in the context selector to the given set.
+     *
+     * @param contexts the possible contexts
+     */
+    private void setContexts(IContextInformation[] contexts) {
+        if (Helper.okToUse(fContextSelectorTable)) {
+
+            fContextSelectorInput= contexts;
+
+            fContextSelectorTable.setRedraw(false);
+            fContextSelectorTable.removeAll();
+
+            TableItem item;
+            IContextInformation t;
+            for (int i= 0; i < contexts.length; i++) {
+                t= contexts[i];
+                item= new TableItem(fContextSelectorTable, SWT.NULL);
+                if (t.getImage() !is null)
+                    item.setImage(t.getImage());
+                item.setText(t.getContextDisplayString());
+            }
+
+            fContextSelectorTable.select(0);
+            fContextSelectorTable.setRedraw(true);
+        }
+    }
+
+    /**
+     * Displays the context selector.
+     */
+    private void displayContextSelector() {
+        if (fContentAssistant.addContentAssistListener(this, ContentAssistant.CONTEXT_SELECTOR))
+            fContextSelectorShell.setVisible(true);
+    }
+
+    /**
+     * Hides the context selector.
+     */
+    private void hideContextSelector() {
+        if (Helper.okToUse(fContextSelectorShell)) {
+            fContentAssistant.removeContentAssistListener(this, ContentAssistant.CONTEXT_SELECTOR);
+
+            fPopupCloser.uninstall();
+            fContextSelectorShell.setVisible(false);
+            fContextSelectorShell.dispose();
+            fContextSelectorShell= null;
+        }
+
+        if (!Helper.okToUse(fContextInfoPopup))
+            fContentAssistant.contextInformationClosed_package();
+    }
+
+    /**
+     *Returns whether the context selector has the focus.
+     *
+     * @return <code>true</code> if the context selector has the focus
+     */
+    public bool hasFocus() {
+        if (Helper.okToUse(fContextSelectorShell))
+            return (fContextSelectorShell.isFocusControl() || fContextSelectorTable.isFocusControl());
+
+        return false;
+    }
+
+    /**
+     * Hides context selector and context information popup.
+     */
+    public void hide() {
+        hideContextSelector();
+        hideContextInfoPopup();
+    }
+
+    /**
+     * Returns whether this context information popup is active. I.e., either
+     * a context selector or context information is displayed.
+     *
+     * @return <code>true</code> if the context selector is active
+     */
+    public bool isActive() {
+        return (Helper.okToUse(fContextInfoPopup) || Helper.okToUse(fContextSelectorShell));
+    }
+
+    /*
+     * @see IContentAssistListener#verifyKey(VerifyEvent)
+     */
+    public bool verifyKey(VerifyEvent e) {
+        if (Helper.okToUse(fContextSelectorShell))
+            return contextSelectorKeyPressed(e);
+        if (Helper.okToUse(fContextInfoPopup))
+            return contextInfoPopupKeyPressed(e);
+        return true;
+    }
+
+    /**
+     * Processes a key stroke in the context selector.
+     *
+     * @param e the verify event describing the key stroke
+     * @return <code>true</code> if processing can be stopped
+     */
+    private bool contextSelectorKeyPressed(VerifyEvent e) {
+
+        char key= e.character;
+        if (key is 0) {
+
+            int newSelection= fContextSelectorTable.getSelectionIndex();
+            int visibleRows= (fContextSelectorTable.getSize().y / fContextSelectorTable.getItemHeight()) - 1;
+            int itemCount= fContextSelectorTable.getItemCount();
+            switch (e.keyCode) {
+                case SWT.ARROW_UP :
+                    newSelection -= 1;
+                    if (newSelection < 0)
+                        newSelection= itemCount - 1;
+                    break;
+
+                case SWT.ARROW_DOWN :
+                    newSelection += 1;
+                    if (newSelection > itemCount - 1)
+                        newSelection= 0;
+                    break;
+
+                case SWT.PAGE_DOWN :
+                    newSelection += visibleRows;
+                    if (newSelection >= itemCount)
+                        newSelection= itemCount - 1;
+                    break;
+
+                case SWT.PAGE_UP :
+                    newSelection -= visibleRows;
+                    if (newSelection < 0)
+                        newSelection= 0;
+                    break;
+
+                case SWT.HOME :
+                    newSelection= 0;
+                    break;
+
+                case SWT.END :
+                    newSelection= itemCount - 1;
+                    break;
+
+                default :
+                    if (e.keyCode !is SWT.CAPS_LOCK && e.keyCode !is SWT.MOD1 && e.keyCode !is SWT.MOD2 && e.keyCode !is SWT.MOD3 && e.keyCode !is SWT.MOD4)
+                        hideContextSelector();
+                    return true;
+            }
+
+            fContextSelectorTable.setSelection(newSelection);
+            fContextSelectorTable.showSelection();
+            e.doit= false;
+            return false;
+
+        } else if ('\t' is key) {
+            // switch focus to selector shell
+            e.doit= false;
+            fContextSelectorShell.setFocus();
+            return false;
+        } else if (key is SWT.ESC) {
+            e.doit= false;
+            hideContextSelector();
+        }
+
+        return true;
+    }
+
+    /**
+     * Processes a key stroke while the info popup is up.
+     *
+     * @param e the verify event describing the key stroke
+     * @return <code>true</code> if processing can be stopped
+     */
+    private bool contextInfoPopupKeyPressed(KeyEvent e) {
+
+        char key= e.character;
+        if (key is 0) {
+
+            switch (e.keyCode) {
+                case SWT.ARROW_LEFT:
+                case SWT.ARROW_RIGHT:
+                    validateContextInformation();
+                    break;
+                default:
+                    if (e.keyCode !is SWT.CAPS_LOCK && e.keyCode !is SWT.MOD1 && e.keyCode !is SWT.MOD2 && e.keyCode !is SWT.MOD3 && e.keyCode !is SWT.MOD4)
+                        hideContextInfoPopup();
+                    break;
+            }
+
+        } else if (key is SWT.ESC) {
+            e.doit= false;
+            hideContextInfoPopup();
+        } else {
+            validateContextInformation();
+        }
+        return true;
+    }
+
+    /*
+     * @see IEventConsumer#processEvent(VerifyEvent)
+     */
+    public void processEvent(VerifyEvent event) {
+        if (Helper.okToUse(fContextSelectorShell))
+            contextSelectorProcessEvent(event);
+        if (Helper.okToUse(fContextInfoPopup))
+            contextInfoPopupProcessEvent(event);
+    }
+
+    /**
+     * Processes a key stroke in the context selector.
+     *
+     * @param e the verify event describing the key stroke
+     */
+    private void contextSelectorProcessEvent(VerifyEvent e) {
+
+        if (e.start is e.end && e.text !is null && e.text.equals(fLineDelimiter)) {
+            e.doit= false;
+            insertSelectedContext();
+        }
+
+        hideContextSelector();
+    }
+
+    /**
+     * Processes a key stroke while the info popup is up.
+     *
+     * @param e the verify event describing the key stroke
+     */
+    private void contextInfoPopupProcessEvent(VerifyEvent e) {
+        if (e.start !is e.end && (e.text is null || e.text.length() is 0))
+            validateContextInformation();
+    }
+
+    /**
+     * Validates the context information for the viewer's actual cursor position.
+     */
+    private void validateContextInformation() {
+        /*
+         * Post the code in the event queue in order to ensure that the
+         * action described by this verify key event has already been executed.
+         * Otherwise, we'd validate the context information based on the
+         * pre-key-stroke state.
+         */
+        if (!Helper.okToUse(fContextInfoPopup))
+            return;
+
+        fContextInfoPopup.getDisplay().asyncExec(new class()  Runnable {
+
+            private ContextFrame fFrame;
+
+            this() {
+                fFrame= cast(ContextFrame) fContextFrameStack.peek();
+            }
+
+            public void run() {
+                // only do this if no other frames have been added in between
+                if (!fContextFrameStack.isEmpty() && fFrame is fContextFrameStack.peek()) {
+                    int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
+
+                    // iterate all contexts on the stack
+                    while (Helper.okToUse(fContextInfoPopup) && !fContextFrameStack.isEmpty()) {
+                        ContextFrame top= cast(ContextFrame) fContextFrameStack.peek();
+                        if (top.fValidator is null || !top.fValidator.isContextInformationValid(offset)) {
+                            hideContextInfoPopup(); // loop variant: reduces the number of contexts on the stack
+                        } else if (top.fPresenter !is null && top.fPresenter.updatePresentation(offset, fTextPresentation)) {
+                            int widgetOffset= fContentAssistSubjectControlAdapter.getWidgetSelectionRange().x;
+                            TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText);
+                            resize(widgetOffset);
+                            break;
+                        } else
+                            break;
+                    }
+                }
+            }
+        });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformationValidator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.contentassist.ContextInformationValidator;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * A default implementation of the <code>IContextInfomationValidator</code> interface.
+ * This implementation determines whether the information is valid by asking the content
+ * assist processor for all  context information objects for the current position. If the
+ * currently displayed information is in the result set, the context information is
+ * considered valid.
+ */
+public final class ContextInformationValidator : IContextInformationValidator {
+
+    /** The content assist processor. */
+    private IContentAssistProcessor fProcessor;
+    /** The context information to be validated. */
+    private IContextInformation fContextInformation;
+    /** The associated text viewer. */
+    private ITextViewer fViewer;
+
+    /**
+     * Creates a new context information validator which is ready to be installed on
+     * a particular context information.
+     *
+     * @param processor the processor to be used for validation
+     */
+    public this(IContentAssistProcessor processor) {
+        fProcessor= processor;
+    }
+
+    /*
+     * @see IContextInformationValidator#install(IContextInformation, ITextViewer, int)
+     */
+    public void install(IContextInformation contextInformation, ITextViewer viewer, int offset) {
+        fContextInformation= contextInformation;
+        fViewer= viewer;
+    }
+
+    /*
+     * @see IContentAssistTipCloser#isContextInformationValid(int)
+     */
+    public bool isContextInformationValid(int offset) {
+        IContextInformation[] infos= fProcessor.computeContextInformation(fViewer, offset);
+        if (infos !is null && infos.length > 0) {
+            for (int i= 0; i < infos.length; i++)
+                if (fContextInformation==/+eq+/infos[i])
+                    return true;
+        }
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/Helper.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.contentassist.Helper;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.swt.widgets.Widget;
+
+
+/**
+ * Helper class for testing widget state.
+ */
+class Helper {
+
+    /**
+     * Returns whether the widget is <code>null</code> or disposed.
+     *
+     * @param widget the widget to check
+     * @return <code>true</code> if the widget is neither <code>null</code> nor disposed
+     */
+    public static bool okToUse(Widget widget) {
+        return (widget !is null && !widget.isDisposed());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.jface.text.contentassist.ICompletionListener;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * A completion listener is informed before the content assistant computes completion proposals.
+ * <p>
+ * In order to provide backward compatibility for clients of <code>ICompletionListener</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces exist:
+ * <ul>
+ *   <li>{@link org.eclipse.jface.text.contentassist.ICompletionListenerExtension} since version 3.4 introducing
+ *      the following functions:
+ *      <ul>
+ *          <li>additional notification about restarting the current code assist session</li>
+ *      </ul>
+ *   </li>
+ * </ul>
+ * </p>
+ *
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ * 
+ * @since 3.2
+ */
+public interface ICompletionListener {
+    /**
+     * Called when code assist is invoked when there is no current code assist session.
+     * 
+     * @param event the content assist event
+     */
+    void assistSessionStarted(ContentAssistEvent event);
+
+    /**
+     * Called when a code assist session ends (for example, the proposal popup is closed).
+     * 
+     * @param event the content assist event
+     */
+    void assistSessionEnded(ContentAssistEvent event);
+
+    /**
+     * Called when the selection in the proposal popup is changed or if the insert-mode changed.
+     * 
+     * @param proposal the newly selected proposal, possibly <code>null</code>
+     * @param smartToggle <code>true</code> if the insert-mode toggle is being pressed,
+     *        <code>false</code> otherwise
+     */
+    void selectionChanged(ICompletionProposal proposal, bool smartToggle);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionListenerExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.jface.text.contentassist.ICompletionListenerExtension;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Extends {@link org.eclipse.jface.text.contentassist.ICompletionListener}
+ * with an additional notification about restarting the current code assist session.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ * 
+ * @since 3.4
+ */
+public interface ICompletionListenerExtension {
+    /**
+     * Called when code assist is invoked when there is already a current code assist session.
+     * 
+     * @param event the content assist event
+     */
+    void assistSessionRestarted(ContentAssistEvent event);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposal.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.contentassist.ICompletionProposal;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * The interface of completion proposals generated by content assist processors.
+ * A completion proposal contains information used to present the proposed completion
+ * to the user, to insert the completion should the user select it, and to present
+ * context information for the chosen completion once it has been inserted.
+ * <p>
+ * In order to provide backward compatibility for clients of <code>ICompletionProposal</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.contentassist.ICompletionProposalExtension} since version 2.0 introducing
+ *      the following functions:
+ *          <ul>
+ *              <li>handling of trigger characters other than ENTER</li>
+ *              <li>completion proposal validation for a given offset</li>
+ *              <li>context information can be freely positioned</li>
+ *          </ul>
+ * </li>
+ * <li>{@link org.eclipse.jface.text.contentassist.ICompletionProposalExtension2} since version 2.1 introducing
+ *      the following functions:
+ *          <ul>
+ *              <li>handling of trigger characters with modifiers</li>
+ *              <li>visual indication for selection of a proposal</li>
+ *          </ul>
+ * </li>
+ * <li>{@link org.eclipse.jface.text.contentassist.ICompletionProposalExtension3} since version 3.0 introducing
+ *      the following functions:
+ *          <ul>
+ *              <li>provision of a custom information control creator</li>
+ *              <li>provide a custom completion text and offset for prefix completion</li>
+ *          </ul>
+ * </li>
+ * <li>{@link org.eclipse.jface.text.contentassist.ICompletionProposalExtension4} since version 3.1 introducing
+ *      the following functions:
+ *          <ul>
+ *              <li>specify whether a proposal is automatically insertable</li>
+ *          </ul>
+ * </li>
+ * <li>{@link org.eclipse.jface.text.contentassist.ICompletionProposalExtension5} since version 3.2 introducing
+ *      the following function:
+ *          <ul>
+ *              <li>Allow background computation of the additional info</li>
+ *          </ul>
+ * </li>
+ * <li>{@link org.eclipse.jface.text.contentassist.ICompletionProposalExtension6} since version 3.4 introducing
+ *      the following function:
+ *          <ul>
+ *              <li>Allow styled ranges in the display string.</li>
+ *          </ul>
+ * </li>
+ * </ul>
+ * </p>
+ * <p>
+ * This interface can be implemented by clients. By default, clients use
+ * {@link org.eclipse.jface.text.contentassist.CompletionProposal} as the
+ * standard implementer of this interface.
+ * </p>
+ *
+ * @see IContentAssistProcessor
+ */
+public interface ICompletionProposal {
+
+    /**
+     * Inserts the proposed completion into the given document.
+     *
+     * @param document the document into which to insert the proposed completion
+     */
+    void apply(IDocument document);
+
+    /**
+     * Returns the new selection after the proposal has been applied to
+     * the given document in absolute document coordinates. If it returns
+     * <code>null</code>, no new selection is set.
+     *
+     * A document change can trigger other document changes, which have
+     * to be taken into account when calculating the new selection. Typically,
+     * this would be done by installing a document listener or by using a
+     * document position during {@link #apply(IDocument)}.
+     *
+     * @param document the document into which the proposed completion has been inserted
+     * @return the new selection in absolute document coordinates
+     */
+    Point getSelection(IDocument document);
+
+    /**
+     * Returns optional additional information about the proposal. The additional information will
+     * be presented to assist the user in deciding if the selected proposal is the desired choice.
+     * <p>
+     * If {@link ICompletionProposalExtension5} is implemented, this method should not be called any
+     * longer. This method may be deprecated in a future release.
+     * </p>
+     * 
+     * @return the additional information or <code>null</code>
+     */
+    String getAdditionalProposalInfo();
+
+    /**
+     * Returns the string to be displayed in the list of completion proposals.
+     *
+     * @return the string to be displayed
+     */
+    String getDisplayString();
+
+    /**
+     * Returns the image to be displayed in the list of completion proposals.
+     * The image would typically be shown to the left of the display string.
+     *
+     * @return the image to be shown or <code>null</code> if no image is desired
+     */
+    Image getImage();
+
+    /**
+     * Returns optional context information associated with this proposal.
+     * The context information will automatically be shown if the proposal
+     * has been applied.
+     *
+     * @return the context information for this proposal or <code>null</code>
+     */
+    IContextInformation getContextInformation();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.contentassist.ICompletionProposalExtension;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * Extends {@link org.eclipse.jface.text.contentassist.ICompletionProposal}
+ * with the following functions:
+ * <ul>
+ *  <li>handling of trigger characters other than ENTER</li>
+ *  <li>completion proposal validation for a given offset</li>
+ *  <li>context information can be freely positioned</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public interface ICompletionProposalExtension {
+
+    /**
+     * Applies the proposed completion to the given document. The insertion
+     * has been triggered by entering the given character at the given offset.
+     * This method assumes that {@link #isValidFor(IDocument, int)} returns
+     * <code>true</code> if called for <code>offset</code>.
+     *
+     * @param document the document into which to insert the proposed completion
+     * @param trigger the trigger to apply the completion
+     * @param offset the offset at which the trigger has been activated
+     */
+    void apply(IDocument document, char trigger, int offset);
+
+    /**
+     * Returns whether this completion proposal is valid for the given
+     * position in the given document.
+     *
+     * @param document the document for which the proposal is tested
+     * @param offset the offset for which the proposal is tested
+     * @return <code>true</code> iff valid
+     */
+    bool isValidFor(IDocument document, int offset);
+
+    /**
+     * Returns the characters which trigger the application of this completion proposal.
+     *
+     * @return the completion characters for this completion proposal or <code>null</code>
+     *      if no completion other than the new line character is possible
+     */
+    char[] getTriggerCharacters();
+
+    /**
+     * Returns the position to which the computed context information refers to or
+     * <code>-1</code> if no context information can be provided by this completion proposal.
+     *
+     * @return the position to which the context information refers to or <code>-1</code> for no information
+     */
+    int getContextInformationPosition();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * Extends {@link org.eclipse.jface.text.contentassist.ICompletionProposal}
+ * with the following functions:
+ * <ul>
+ *  <li>handling of trigger characters with modifiers</li>
+ *  <li>visual indication for selection of a proposal</li>
+ * </ul>
+ *
+ * @since 2.1
+ */
+public interface ICompletionProposalExtension2 {
+
+    /**
+     * Applies the proposed completion to the given document. The insertion
+     * has been triggered by entering the given character with a modifier at the given offset.
+     * This method assumes that {@link #validate(IDocument, int, DocumentEvent)}
+     * returns <code>true</code> if called for <code>offset</code>.
+     *
+     * @param viewer the text viewer into which to insert the proposed completion
+     * @param trigger the trigger to apply the completion
+     * @param stateMask the state mask of the modifiers
+     * @param offset the offset at which the trigger has been activated
+     */
+    void apply(ITextViewer viewer, char trigger, int stateMask, int offset);
+
+    /**
+     * Called when the proposal is selected.
+     *
+     * @param viewer the text viewer.
+     * @param smartToggle the smart toggle key was pressed
+     */
+    void selected(ITextViewer viewer, bool smartToggle);
+
+    /**
+     * Called when the proposal is unselected.
+     *
+     * @param viewer the text viewer.
+     */
+    void unselected(ITextViewer viewer);
+
+    /**
+     * Requests the proposal to be validated with respect to the document event.
+     * If the proposal cannot be validated, the methods returns <code>false</code>.
+     * If the document event was <code>null</code>, only the caret offset was changed, but not the document.
+     *
+     * This method replaces {@link ICompletionProposalExtension#isValidFor(IDocument, int)}
+     *
+     * @param document the document
+     * @param offset the caret offset
+     * @param event the document event, may be <code>null</code>
+     * @return bool
+     */
+    bool validate(IDocument document, int offset, DocumentEvent event);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension3.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.contentassist.ICompletionProposalExtension3;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IInformationControlCreator;
+
+
+/**
+ * Extends {@link org.eclipse.jface.text.contentassist.ICompletionProposal}
+ * with the following functions:
+ * <ul>
+ *  <li>provision of a custom information control creator</li>
+ *  <li>provide a custom completion text and offset for prefix completion</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public interface ICompletionProposalExtension3 {
+    /**
+     * Returns the information control creator of this completion proposal.
+     *
+     * @return the information control creator, or <code>null</code> if no custom control creator is available
+     */
+    IInformationControlCreator getInformationControlCreator();
+
+    /**
+     * Returns the string that would be inserted at the position returned from
+     * {@link #getPrefixCompletionStart(IDocument, int)} if this proposal was
+     * applied. If the replacement string cannot be determined,
+     * <code>null</code> may be returned.
+     *
+     * @param document the document that the receiver applies to
+     * @param completionOffset the offset into <code>document</code> where the
+     *        completion takes place
+     * @return the replacement string or <code>null</code> if it cannot be
+     *         determined
+     */
+    CharSequence getPrefixCompletionText(IDocument document, int completionOffset);
+
+    /**
+     * Returns the document offset at which the receiver would insert its
+     * proposal.
+     *
+     * @param document the document that the receiver applies to
+     * @param completionOffset the offset into <code>document</code> where the
+     *        completion takes place
+     * @return the offset at which the proposal would insert its proposal
+     */
+    int getPrefixCompletionStart(IDocument document, int completionOffset);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension4.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.contentassist.ICompletionProposalExtension4;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Extends
+ * {@link org.eclipse.jface.text.contentassist.ICompletionProposal} with
+ * the following functions:
+ * <ul>
+ *   <li>specify whether a proposal is automatically insertable</li>
+ * </ul>
+ * 
+ * @since 3.1
+ */
+public interface ICompletionProposalExtension4 {
+
+    /**
+     * Returns <code>true</code> if the proposal may be automatically
+     * inserted, <code>false</code> otherwise. Automatic insertion can
+     * happen if the proposal is the only one being proposed, in which
+     * case the content assistant may decide to not prompt the user with
+     * a list of proposals, but simply insert the single proposal. A
+     * proposal may veto this behavior by returning <code>false</code>
+     * to a call to this method.
+     * 
+     * @return <code>true</code> if the proposal may be inserted
+     *         automatically, <code>false</code> if not
+     */
+    bool isAutoInsertable();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension5.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.contentassist.ICompletionProposalExtension5;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Extends {@link org.eclipse.jface.text.contentassist.ICompletionProposal} with the following
+ * function:
+ * <ul>
+ * <li>Allow background computation of the additional info.</li>
+ * </ul>
+ * 
+ * @since 3.2
+ */
+public interface ICompletionProposalExtension5 {
+    /**
+     * Returns additional information about the proposal. The additional information will be
+     * presented to assist the user in deciding if the selected proposal is the desired choice.
+     * <p>
+     * This method may be called on a non-UI thread.
+     * </p>
+     * <p>
+     * By default, the returned information is converted to a string and displayed as text; if
+     * {@link ICompletionProposalExtension3#getInformationControlCreator()} is implemented, the
+     * information will be passed to a custom information control for display.
+     * </p>
+     * 
+     * @param monitor a monitor to report progress and to watch for
+     *        {@link IProgressMonitor#isCanceled() cancelation}.
+     * @return the additional information, <code>null</code> for no information
+     */
+    Object getAdditionalProposalInfo(IProgressMonitor monitor);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension6.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.contentassist.ICompletionProposalExtension6;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.StyledString;
+
+
+/**
+ * Extends {@link org.eclipse.jface.text.contentassist.ICompletionProposal} with the following
+ * function:
+ * <ul>
+ *  <li>Allow styled ranges in the display string.</li>
+ * </ul>
+ * 
+ * @since 3.4
+ */
+public interface ICompletionProposalExtension6 {
+    
+    /**
+     * Returns the styled string used to display this proposal in the list of completion proposals.
+     * This can for example be used to draw mixed colored labels.
+     * <p>
+     * <strong>Note:</strong> {@link ICompletionProposal#getDisplayString()} still needs to be
+     * correctly implemented as this method might be ignored in case of uninstalled owner draw
+     * support.
+     * </p>
+     * 
+     * @return the string builder used to display this proposal
+     */
+    StyledString getStyledDisplayString();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.contentassist.IContentAssistListener;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+
+
+
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.jface.text.IEventConsumer;
+
+
+/**
+ * An interface whereby listeners can not only receive key events,
+ * but can also consume them to prevent subsequent listeners from
+ * processing the event.
+ */
+interface IContentAssistListener : IEventConsumer {
+
+    /**
+     * Verifies the key event.
+     *
+     * @param event the verify event
+     * @return <code>true</code> if processing should be continued by additional listeners
+     * @see org.eclipse.swt.custom.VerifyKeyListener#verifyKey(VerifyEvent)
+     */
+    public bool verifyKey(VerifyEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistProcessor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.contentassist.IContentAssistProcessor;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * A content assist processor proposes completions and
+ * computes context information for a particular content type.
+ * A content assist processor is a {@link org.eclipse.jface.text.contentassist.IContentAssistant}
+ * plug-in.
+ * <p>
+ * This interface must be implemented by clients. Implementers should be
+ * registered with a content assistant in order to get involved in the
+ * assisting process.
+ * </p>
+*/
+public interface IContentAssistProcessor {
+
+    /**
+     * Returns a list of completion proposals based on the
+     * specified location within the document that corresponds
+     * to the current cursor position within the text viewer.
+     *
+     * @param viewer the viewer whose document is used to compute the proposals
+     * @param offset an offset within the document for which completions should be computed
+     * @return an array of completion proposals or <code>null</code> if no proposals are possible
+     */
+    ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset);
+
+    /**
+     * Returns information about possible contexts based on the
+     * specified location within the document that corresponds
+     * to the current cursor position within the text viewer.
+     *
+     * @param viewer the viewer whose document is used to compute the possible contexts
+     * @param offset an offset within the document for which context information should be computed
+     * @return an array of context information objects or <code>null</code> if no context could be found
+     */
+    IContextInformation[] computeContextInformation(ITextViewer viewer, int offset);
+
+    /**
+     * Returns the characters which when entered by the user should
+     * automatically trigger the presentation of possible completions.
+     *
+     * @return the auto activation characters for completion proposal or <code>null</code>
+     *      if no auto activation is desired
+     */
+    char[] getCompletionProposalAutoActivationCharacters();
+
+    /**
+     * Returns the characters which when entered by the user should
+     * automatically trigger the presentation of context information.
+     *
+     * @return the auto activation characters for presenting context information
+     *      or <code>null</code> if no auto activation is desired
+     */
+    char[] getContextInformationAutoActivationCharacters();
+
+    /**
+     * Returns the reason why this content assist processor
+     * was unable to produce any completion proposals or context information.
+     *
+     * @return an error message or <code>null</code> if no error occurred
+     */
+    String getErrorMessage();
+
+    /**
+     * Returns a validator used to determine when displayed context information
+     * should be dismissed. May only return <code>null</code> if the processor is
+     * incapable of computing context information. <p>
+     *
+     * @return a context information validator, or <code>null</code> if the processor
+     *          is incapable of computing context information
+     */
+    IContextInformationValidator getContextInformationValidator();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistant.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.contentassist.IContentAssistant;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * An <code>IContentAssistant</code> provides support on interactive content completion.
+ * The content assistant is a {@link org.eclipse.jface.text.ITextViewer} add-on. Its
+ * purpose is to propose, display, and insert completions of the content
+ * of the text viewer's document at the viewer's cursor position. In addition
+ * to handle completions, a content assistant can also be requested to provide
+ * context information. Context information is shown in a tool tip like popup.
+ * As it is not always possible to determine the exact context at a given
+ * document offset, a content assistant displays the possible contexts and requests
+ * the user to choose the one whose information should be displayed.
+ * <p>
+ * A content assistant has a list of {@link org.eclipse.jface.text.contentassist.IContentAssistProcessor}
+ * objects each of which is registered for a  particular document content
+ * type. The content assistant uses the processors to react on the request
+ * of completing documents or presenting context information.
+ * </p>
+ * <p>
+ * In order to provide backward compatibility for clients of <code>IContentAssistant</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.contentassist.IContentAssistantExtension} since version 3.0 introducing
+ *      the following functions:
+ *          <ul>
+ *              <li>handle documents with multiple partitions</li>
+ *              <li>insertion of common completion prefixes</li>
+ *          </ul>
+ * </li>
+ * <li>{@link org.eclipse.jface.text.contentassist.IContentAssistantExtension2} since version 3.2 introducing
+ *      the following functions:
+ *          <ul>
+ *              <li>repeated invocation (cycling) mode</li>
+ *              <li>completion listeners</li>
+ *              <li>a local status line for the completion popup</li>
+ *              <li>control over the behavior when no proposals are available</li>
+ *          </ul>
+ * </li>
+ * <li>{@link org.eclipse.jface.text.contentassist.IContentAssistantExtension3} since version 3.2 introducing
+ *      the following function:
+ *          <ul>
+ *              <li>a key-sequence to listen for in repeated invocation mode</li>
+ *          </ul>
+ * </li>
+ * <li>{@link org.eclipse.jface.text.contentassist.IContentAssistantExtension4} since version 3.4 introducing
+ *      the following function:
+ *          <ul>
+ *              <li>allows to get a handler for the given command identifier</li>
+ *          </ul>
+ * </li>
+ * </ul>
+ * </p>
+ * <p>
+ * The interface can be implemented by clients. By default, clients use
+ * {@link org.eclipse.jface.text.contentassist.ContentAssistant} as the standard
+ * implementer of this interface.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.ITextViewer
+ * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor
+ */
+ public interface IContentAssistant {
+
+    //------ proposal popup orientation styles ------------
+    /** The context info list will overlay the list of completion proposals. */
+    public const static int PROPOSAL_OVERLAY= 10;
+    /** The completion proposal list will be removed before the context info list will be shown. */
+    public const static int PROPOSAL_REMOVE=  11;
+    /** The context info list will be presented without hiding or overlapping the completion proposal list. */
+    public const static int PROPOSAL_STACKED= 12;
+
+    //------ context info box orientation styles ----------
+    /** Context info will be shown above the location it has been requested for without hiding the location. */
+    public const static int CONTEXT_INFO_ABOVE= 20;
+    /** Context info will be shown below the location it has been requested for without hiding the location. */
+    public const static int CONTEXT_INFO_BELOW= 21;
+
+
+    /**
+     * Installs content assist support on the given text viewer.
+     *
+     * @param textViewer the text viewer on which content assist will work
+     */
+    void install(ITextViewer textViewer);
+
+    /**
+     * Uninstalls content assist support from the text viewer it has
+     * previously be installed on.
+     */
+    void uninstall();
+
+    /**
+     * Shows all possible completions of the content at the viewer's cursor position.
+     *
+     * @return an optional error message if no proposals can be computed
+     */
+    String showPossibleCompletions();
+
+    /**
+     * Shows context information for the content at the viewer's cursor position.
+     *
+     * @return an optional error message if no context information can be computed
+     */
+    String showContextInformation();
+
+    /**
+     * Returns the content assist processor to be used for the given content type.
+     *
+     * @param contentType the type of the content for which this
+     *        content assistant is to be requested
+     * @return an instance content assist processor or
+     *         <code>null</code> if none exists for the specified content type
+     */
+    IContentAssistProcessor getContentAssistProcessor(String contentType);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistantExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.contentassist.IContentAssistantExtension;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Extends {@link org.eclipse.jface.text.contentassist.IContentAssistant}
+ * with the following functions:
+ * <ul>
+ *  <li>handle documents with multiple partitions</li>
+ *  <li>insertion of common completion prefixes</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public interface IContentAssistantExtension {
+
+    /**
+     * Returns the document partitioning this content assistant is using.
+     *
+     * @return the document partitioning this content assistant is using
+     */
+    String getDocumentPartitioning();
+
+    /**
+     * Inserts the common prefix of the available completions. If no common
+     * prefix can be computed it is identical to
+     * {@link IContentAssistant#showPossibleCompletions()}.
+     *
+     * @return an optional error message if no proposals can be computed
+     */
+    String completePrefix();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistantExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.contentassist.IContentAssistantExtension2;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Extends {@link org.eclipse.jface.text.contentassist.IContentAssistant} with the following
+ * functions:
+ * <ul>
+ * <li>completion listeners</li>
+ * <li>repeated invocation mode</li>
+ * <li>a local status line for the completion popup</li>
+ * <li>control over the behavior when no proposals are available</li>
+ * </ul>
+ * 
+ * @since 3.2
+ */
+public interface IContentAssistantExtension2 {
+
+    /**
+     * Adds a completion listener that will be informed before proposals are computed.
+     * 
+     * @param listener the listener
+     */
+    public void addCompletionListener(ICompletionListener listener);
+
+    /**
+     * Removes a completion listener.
+     * 
+     * @param listener the listener to remove
+     */
+    public void removeCompletionListener(ICompletionListener listener);
+
+    /**
+     * Enables repeated invocation mode, which will trigger re-computation of the proposals when
+     * code assist is executed repeatedly. The default is no <code>false</code>.
+     * 
+     * @param cycling <code>true</code> to enable repetition mode, <code>false</code> to disable
+     */
+    public void setRepeatedInvocationMode(bool cycling);
+
+    /**
+     * Enables displaying an empty completion proposal pop-up. The default is not to show an empty
+     * list.
+     * 
+     * @param showEmpty <code>true</code> to show empty lists
+     */
+    public void setShowEmptyList(bool showEmpty);
+
+    /**
+     * Enables displaying a status line below the proposal popup. The default is not to show the
+     * status line. The contents of the status line may be set via {@link #setStatusMessage(String)}.
+     * 
+     * @param show <code>true</code> to show a message line, <code>false</code> to not show one.
+     */
+    public void setStatusLineVisible(bool show);
+
+    /**
+     * Sets the caption message displayed at the bottom of the completion proposal popup.
+     * 
+     * @param message the message
+     */
+    public void setStatusMessage(String message);
+
+    /**
+     * Sets the text to be shown if no proposals are available and
+     * {@link #setShowEmptyList(bool) empty lists} are displayed.
+     * 
+     * @param message the text for the empty list
+     */
+    public void setEmptyMessage(String message);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistantExtension3.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.contentassist.IContentAssistantExtension3;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.bindings.keys.KeySequence;
+
+/**
+ * Extends {@link org.eclipse.jface.text.contentassist.IContentAssistant} with the following
+ * function:
+ * <ul>
+ * <li>a key-sequence to listen for in repeated invocation mode</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public interface IContentAssistantExtension3 {
+
+    /**
+     * Sets the key sequence to listen for in repeated invocation mode. If the key sequence is
+     * encountered, a step in the repetition iteration is triggered.
+     * 
+     * @param sequence the key sequence used for the repeated invocation mode or <code>null</code> if none
+     */
+    public void setRepeatedInvocationTrigger(KeySequence sequence);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistantExtension4.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.contentassist.IContentAssistantExtension4;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.core.commands.IHandler;
+
+
+/**
+ * Extends {@link org.eclipse.jface.text.contentassist.IContentAssistant} with
+ * the following function:
+ * <ul>
+ * <li>allows to get a handler for the given command identifier</li>
+ * </ul>
+ * 
+ * @since 3.4
+ */
+public interface IContentAssistantExtension4 {
+
+    /**
+     * Returns the handler for the given command identifier.
+     * <p>
+     * The same handler instance will be returned when called a more than once
+     * with the same command identifier.
+     * </p>
+     * 
+     * @param commandId the command identifier
+     * @return the handler for the given command identifier
+     * @throws IllegalArgumentException if the command is not supported by this
+     *             content assistant
+     * @throws IllegalStateException if called when this content assistant is
+     *             uninstalled
+     */
+    IHandler getHandler(String commandId);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.contentassist.IContextInformation;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Image;
+
+
+/**
+ * The interface of context information presented to the user and
+ * generated by content assist processors.
+ * <p>
+ * In order to provide backward compatibility for clients of
+ * <code>IContextInformation</code>, extension interfaces are used to
+ * provide a means of evolution. The following extension interfaces
+ * exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.contentassist.IContextInformationExtension}
+ * since version 2.0 introducing the ability to freely position the
+ * context information.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * The interface can be implemented by clients. By default, clients use
+ * {@link org.eclipse.jface.text.contentassist.ContextInformation} as
+ * the standard implementer of this interface.
+ * </p>
+ * 
+ * @see IContentAssistProcessor
+ */
+public interface IContextInformation {
+
+    /**
+     * Returns the string to be displayed in the list of contexts.
+     * This method is used to supply a unique presentation for
+     * situations where the context is ambiguous. These strings are
+     * used to allow the user to select the specific context.
+     *
+     * @return the string to be displayed for the context
+     */
+    String getContextDisplayString();
+
+    /**
+     * Returns the image for this context information.
+     * The image will be shown to the left of the display string.
+     *
+     * @return the image to be shown or <code>null</code> if no image is desired
+     */
+    Image getImage();
+
+    /**
+     * Returns the string to be displayed in the tool tip like information popup.
+     *
+     * @return the string to be displayed
+     */
+    String getInformationDisplayString();
+
+    /**
+     * Compares the given object with this receiver. Two context informations are
+     * equal if there information display strings and their context display strings
+     * are equal.
+     *
+     * @see Object#equals(Object)
+     */
+    bool equals(Object object);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.contentassist.IContextInformationExtension;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Extends {@link org.eclipse.jface.text.contentassist.IContextInformation} with
+ * the ability to freely position the context information.
+ *
+ * @since 2.0
+ */
+public interface IContextInformationExtension {
+
+    /**
+     * Returns the start offset of the range for which this context
+     * information is valid or <code>-1</code> if unknown.
+     * 
+     * @return the start offset of the range for which this context
+     *         information is valid
+     */
+    int getContextInformationPosition();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationPresenter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.contentassist.IContextInformationPresenter;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.TextPresentation;
+
+
+/**
+ * A context information presenter determines the presentation
+ * of context information depending on a given document position.
+ * <p>
+ * The interface can be implemented by clients.
+ * </p>
+ *
+ * @since 2.0
+ */
+public interface IContextInformationPresenter {
+
+    /**
+     * Installs this presenter for the given context information.
+     *
+     * @param info the context information which this presenter should style
+     * @param viewer the text viewer on which the information is presented
+     * @param offset the document offset for which the information has been computed
+     */
+    void install(IContextInformation info, ITextViewer viewer, int offset);
+
+    /**
+     * Updates the given presentation of the given context information
+     * at the given document position. Returns whether update changed the
+     * presentation.
+     *
+     * @param offset the current offset within the document
+     * @param presentation the presentation to be updated
+     * @return <code>true</code> if the given presentation has been changed
+     */
+    bool updatePresentation(int offset, TextPresentation presentation);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationValidator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.contentassist.IContextInformationValidator;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * A context information validator is used to determine if
+ * a displayed context information is still valid or should
+ * be dismissed.
+ * <p>
+ * The interface can be implemented by clients.
+ * </p>
+ *
+ * @see IContextInformationPresenter
+ */
+public interface IContextInformationValidator {
+
+    /**
+     * Installs this validator for the given context information.
+     *
+     * @param info the context information which this validator should check
+     * @param viewer the text viewer on which the information is presented
+     * @param offset the document offset for which the information has been computed
+     */
+    void install(IContextInformation info, ITextViewer viewer, int offset);
+
+    /**
+     * Returns whether the information this validator is installed on is still valid
+     * at the given document position.
+     *
+     * @param offset the current offset within the document
+     * @return <code>true</code> if the information also valid at the given document position
+     */
+    bool isContextInformationValid(int offset);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/JFaceTextMessages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.contentassist.JFaceTextMessages;
+
+import java.lang.all;
+
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+
+/**
+ * Helper class to get NLSed messages.
+ */
+class JFaceTextMessages {
+
+//     private static const String RESOURCE_BUNDLE= JFaceTextMessages.classinfo.getName();
+
+    private static ResourceBundle fgResourceBundle_;//= ResourceBundle.getBundle(RESOURCE_BUNDLE);
+    private static ResourceBundle fgResourceBundle(){
+        if( fgResourceBundle_ is null ){
+            synchronized(JFaceTextMessages.classinfo ){
+                if( fgResourceBundle_ is null ){
+                    fgResourceBundle_ = ResourceBundle.getBundle(
+                        getImportData!("org.eclipse.jface.text.contentassist.JFaceTextMessages.properties"));
+                }
+            }
+        }
+        return fgResourceBundle_;
+    }
+
+    private this() {
+    }
+
+    /**
+     * Gets a string from the resource bundle.
+     *
+     * @param key the string used to get the bundle value, must not be <code>null</code>
+     * @return the string from the resource bundle
+     */
+    public static String getString(String key) {
+        try {
+            return fgResourceBundle.getString(key);
+        } catch (MissingResourceException e) {
+            return "!" ~ key ~ "!";//$NON-NLS-2$ //$NON-NLS-1$
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/PopupCloser.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,308 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.contentassist.PopupCloser;
+
+import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
+import org.eclipse.jface.text.contentassist.Helper; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
+import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
+import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
+import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
+import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
+import org.eclipse.jface.text.contentassist.ContentAssistant; // packageimport
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
+import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.jface.internal.text.DelayedInputChangeListener;
+import org.eclipse.jface.internal.text.InformationControlReplacer;
+import org.eclipse.jface.text.IDelayedInputChangeProvider;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlExtension5;
+import org.eclipse.jface.text.IInputChangedListener;
+
+
+/**
+ * A generic closer class used to monitor various
+ * interface events in order to determine whether
+ * a content assistant should be terminated and all
+ * associated windows be closed.
+ */
+class PopupCloser : ShellAdapter , FocusListener, SelectionListener, Listener {
+
+    /** The content assistant to be monitored. */
+    private ContentAssistant fContentAssistant;
+    /** The table of a selector popup opened by the content assistant. */
+    private Table fTable;
+    /** The scroll bar of the table for the selector popup. */
+    private ScrollBar fScrollbar;
+    /** Indicates whether the scroll bar thumb has been grabbed. */
+    private bool fScrollbarClicked= false;
+    /**
+     * The shell on which some listeners are registered.
+     * @since 3.1
+     */
+    private Shell fShell;
+    /**
+     * The display on which some filters are registered.
+     * @since 3.4
+     */
+    private Display fDisplay;
+    /**
+     * The additional info controller, or <code>null</code>.
+     * @since 3.4
+     */
+    private AdditionalInfoController fAdditionalInfoController;
+
+    /**
+     * Installs this closer on the given table opened by the given content assistant.
+     *
+     * @param contentAssistant the content assistant
+     * @param table the table to be tracked
+     */
+    public void install(ContentAssistant contentAssistant, Table table) {
+        install(contentAssistant, table, null);
+    }
+
+    /**
+     * Installs this closer on the given table opened by the given content assistant.
+     *
+     * @param contentAssistant the content assistant
+     * @param table the table to be tracked
+     * @param additionalInfoController the additional info controller, or <code>null</code>
+     * @since 3.4
+     */
+    public void install(ContentAssistant contentAssistant, Table table, AdditionalInfoController additionalInfoController) {
+        fContentAssistant= contentAssistant;
+        fTable= table;
+        fAdditionalInfoController= additionalInfoController;
+
+        if (Helper.okToUse(fTable)) {
+            fShell= fTable.getShell();
+            fDisplay= fShell.getDisplay();
+
+            fShell.addShellListener(this);
+            fTable.addFocusListener(this);
+            fScrollbar= fTable.getVerticalBar();
+            if (fScrollbar !is null)
+                fScrollbar.addSelectionListener(this);
+
+            fDisplay.addFilter(SWT.Activate, this);
+            fDisplay.addFilter(SWT.MouseWheel, this);
+
+            fDisplay.addFilter(SWT.Deactivate, this);
+
+            fDisplay.addFilter(SWT.MouseUp, this);
+        }
+    }
+
+    /**
+     * Uninstalls this closer if previously installed.
+     */
+    public void uninstall() {
+        fContentAssistant= null;
+        if (Helper.okToUse(fShell))
+            fShell.removeShellListener(this);
+        fShell= null;
+        if (Helper.okToUse(fScrollbar))
+            fScrollbar.removeSelectionListener(this);
+        if (Helper.okToUse(fTable))
+            fTable.removeFocusListener(this);
+        if (fDisplay !is null && ! fDisplay.isDisposed()) {
+            fDisplay.removeFilter(SWT.Activate, this);
+            fDisplay.removeFilter(SWT.MouseWheel, this);
+
+            fDisplay.removeFilter(SWT.Deactivate, this);
+
+            fDisplay.removeFilter(SWT.MouseUp, this);
+        }
+    }
+
+    /*
+     * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+     */
+    public void widgetSelected(SelectionEvent e) {
+        fScrollbarClicked= true;
+    }
+
+    /*
+     * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
+     */
+    public void widgetDefaultSelected(SelectionEvent e) {
+        fScrollbarClicked= true;
+    }
+
+    /*
+     * @see org.eclipse.swt.events.FocusListener#focusGained(org.eclipse.swt.events.FocusEvent)
+     */
+    public void focusGained(FocusEvent e) {
+    }
+
+    /*
+     * @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent)
+     */
+    public void focusLost(FocusEvent e) {
+        fScrollbarClicked= false;
+        Display d= fTable.getDisplay();
+        d.asyncExec(dgRunnable((FocusEvent e_) {
+            if (Helper.okToUse(fTable) && !fTable.isFocusControl() && !fScrollbarClicked && fContentAssistant !is null)
+                fContentAssistant.popupFocusLost(e_);
+        }, e ));
+    }
+
+    /*
+     * @see org.eclipse.swt.events.ShellAdapter#shellDeactivated(org.eclipse.swt.events.ShellEvent)
+     * @since 3.1
+     */
+    public void shellDeactivated(ShellEvent e) {
+        if (fContentAssistant !is null && ! fContentAssistant.hasProposalPopupFocus())
+            fContentAssistant.hide_package();
+    }
+
+
+    /*
+     * @see org.eclipse.swt.events.ShellAdapter#shellClosed(org.eclipse.swt.events.ShellEvent)
+     * @since 3.1
+     */
+    public void shellClosed(ShellEvent e) {
+        if (fContentAssistant !is null)
+            fContentAssistant.hide_package();
+    }
+
+    /*
+     * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+     * @since 3.4
+     */
+    public void handleEvent(Event event) {
+        switch (event.type) {
+            case SWT.Activate:
+            case SWT.MouseWheel:
+                if (fAdditionalInfoController is null)
+                    return;
+                if (event.widget is fShell || event.widget is fTable || event.widget is fScrollbar)
+                    return;
+
+                if (fAdditionalInfoController.getInternalAccessor().getInformationControlReplacer() is null)
+                    fAdditionalInfoController.hideInformationControl_package();
+                else if (!fAdditionalInfoController.getInternalAccessor().isReplaceInProgress()) {
+                    IInformationControl infoControl= fAdditionalInfoController.getCurrentInformationControl2();
+                    // During isReplaceInProgress(), events can come from the replacing information control
+                    if (cast(Control)event.widget && cast(IInformationControlExtension5)infoControl ) {
+                        Control control= cast(Control) event.widget;
+                        IInformationControlExtension5 iControl5= cast(IInformationControlExtension5) infoControl;
+                        if (!(iControl5.containsControl(control)))
+                            fAdditionalInfoController.hideInformationControl_package();
+                        else if (event.type is SWT.MouseWheel)
+                            fAdditionalInfoController.getInternalAccessor().replaceInformationControl(false);
+                    } else if (infoControl !is null && infoControl.isFocusControl()) {
+                        fAdditionalInfoController.getInternalAccessor().replaceInformationControl(true);
+                    }
+                }
+                break;
+
+            case SWT.MouseUp:
+                if (fAdditionalInfoController is null || fAdditionalInfoController.getInternalAccessor().isReplaceInProgress())
+                    break;
+                if (cast(Control)event.widget) {
+                    Control control= cast(Control) event.widget;
+                    IInformationControl infoControl= fAdditionalInfoController.getCurrentInformationControl2();
+                    if ( cast(IInformationControlExtension5)infoControl ) {
+                        final IInformationControlExtension5 iControl5= cast(IInformationControlExtension5) infoControl;
+                        if (iControl5.containsControl(control)) {
+                            if ( cast(IDelayedInputChangeProvider)infoControl ) {
+                                final IDelayedInputChangeProvider delayedICP= cast(IDelayedInputChangeProvider) infoControl;
+                                final IInputChangedListener inputChangeListener= new DelayedInputChangeListener(delayedICP, fAdditionalInfoController.getInternalAccessor().getInformationControlReplacer());
+                                delayedICP.setDelayedInputChangeListener(inputChangeListener);
+                                // cancel automatic input updating after a small timeout:
+                                control.getShell().getDisplay().timerExec(1000, new class()  Runnable {
+                                    public void run() {
+                                        delayedICP.setDelayedInputChangeListener(null);
+                                    }
+                                });
+                            }
+
+                            // XXX: workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=212392 :
+                            control.getShell().getDisplay().asyncExec(new class()  Runnable {
+                                public void run() {
+                                    fAdditionalInfoController.getInternalAccessor().replaceInformationControl(true);
+                                }
+                            });
+                        }
+                    }
+                }
+                break;
+
+            case SWT.Deactivate:
+                if (fAdditionalInfoController is null)
+                    break;
+                InformationControlReplacer replacer= fAdditionalInfoController.getInternalAccessor().getInformationControlReplacer();
+                if (replacer !is null && fContentAssistant !is null) {
+                    IInformationControl iControl= replacer.getCurrentInformationControl2();
+                    if (cast(Control)event.widget  && cast(IInformationControlExtension5)iControl ) {
+                        Control control= cast(Control) event.widget;
+                        IInformationControlExtension5 iControl5= cast(IInformationControlExtension5) iControl;
+                        if (iControl5.containsControl(control)) {
+                            control.getDisplay().asyncExec(new class()  Runnable {
+                                public void run() {
+                                    if (fContentAssistant !is null && ! fContentAssistant.hasProposalPopupFocus())
+                                        fContentAssistant.hide_package();
+                                }
+                            });
+                        }
+                    }
+                }
+                break;
+            default:
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/ContentFormatter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,817 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.formatter.ContentFormatter;
+
+import org.eclipse.jface.text.formatter.MultiPassContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContext; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatterExtension; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategyExtension; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContextProperties; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingContext; // packageimport
+
+import java.lang.all;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+
+
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DefaultPositionUpdater;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension3;
+import org.eclipse.jface.text.IPositionUpdater;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.TypedPosition;
+
+
+/**
+ * Standard implementation of <code>IContentFormatter</code>.
+ * The formatter supports two operation modes: partition aware and
+ * partition unaware. <p>
+ * In the partition aware mode, the formatter determines the
+ * partitioning of the document region to be formatted. For each
+ * partition it determines all document positions  which are affected
+ * when text changes are applied to the partition. Those which overlap
+ * with the partition are remembered as character positions. These
+ * character positions are passed over to the formatting strategy
+ * registered for the partition's content type. The formatting strategy
+ * returns a string containing the formatted document partition as well
+ * as the adapted character positions. The formatted partition replaces
+ * the old content of the partition. The remembered document positions
+ * are updated with the adapted character positions. In addition, all
+ * other document positions are accordingly adapted to the formatting
+ * changes.<p>
+ * In the partition unaware mode, the document's partitioning is ignored
+ * and the document is considered consisting of only one partition of
+ * the content type <code>IDocument.DEFAULT_CONTENT_TYPE</code>. The
+ * formatting process is similar to the partition aware mode, with the
+ * exception of having only one partition.<p>
+ * Usually, clients instantiate this class and configure it before using it.
+ *
+ * @see IContentFormatter
+ * @see IDocument
+ * @see ITypedRegion
+ * @see Position
+ */
+public class ContentFormatter : IContentFormatter {
+
+    /**
+     * Defines a reference to either the offset or the end offset of
+     * a particular position.
+     */
+    static class PositionReference : Comparable {
+
+        /** The referenced position */
+        protected Position fPosition;
+        /** The reference to either the offset or the end offset */
+        protected bool fRefersToOffset;
+        /** The original category of the referenced position */
+        protected String fCategory;
+
+        /**
+         * Creates a new position reference.
+         *
+         * @param position the position to be referenced
+         * @param refersToOffset <code>true</code> if position offset should be referenced
+         * @param category the category the given position belongs to
+         */
+        protected this(Position position, bool refersToOffset, String category) {
+            fPosition= position;
+            fRefersToOffset= refersToOffset;
+            fCategory= category;
+        }
+
+        /**
+         * Returns the offset of the referenced position.
+         *
+         * @return the offset of the referenced position
+         */
+        protected int getOffset() {
+            return fPosition.getOffset();
+        }
+
+        /**
+         * Manipulates the offset of the referenced position.
+         *
+         * @param offset the new offset of the referenced position
+         */
+        protected void setOffset(int offset) {
+            fPosition.setOffset(offset);
+        }
+
+        /**
+         * Returns the length of the referenced position.
+         *
+         * @return the length of the referenced position
+         */
+        protected int getLength() {
+            return fPosition.getLength();
+        }
+
+        /**
+         * Manipulates the length of the referenced position.
+         *
+         * @param length the new length of the referenced position
+         */
+        protected void setLength(int length) {
+            fPosition.setLength(length);
+        }
+
+        /**
+         * Returns whether this reference points to the offset or end offset
+         * of the references position.
+         *
+         * @return <code>true</code> if the offset of the position is referenced, <code>false</code> otherwise
+         */
+        protected bool refersToOffset() {
+            return fRefersToOffset;
+        }
+
+        /**
+         * Returns the category of the referenced position.
+         *
+         * @return the category of the referenced position
+         */
+        protected String getCategory() {
+            return fCategory;
+        }
+
+        /**
+         * Returns the referenced position.
+         *
+         * @return the referenced position
+         */
+        protected Position getPosition() {
+            return fPosition;
+        }
+
+        /**
+         * Returns the referenced character position
+         *
+         * @return the referenced character position
+         */
+        protected int getCharacterPosition() {
+            if (fRefersToOffset)
+                return getOffset();
+            return getOffset() + getLength();
+        }
+
+        /*
+         * @see Comparable#compareTo(Object)
+         */
+        public int compareTo(Object obj) {
+
+            if ( cast(PositionReference)obj ) {
+                PositionReference r= cast(PositionReference) obj;
+                return getCharacterPosition() - r.getCharacterPosition();
+            }
+
+            throw new ClassCastException();
+        }
+    }
+
+    /**
+     * The position updater used to update the remembered partitions.
+     *
+     * @see IPositionUpdater
+     * @see DefaultPositionUpdater
+     */
+    class NonDeletingPositionUpdater : DefaultPositionUpdater {
+
+        /**
+         * Creates a new updater for the given category.
+         *
+         * @param category the category
+         */
+        protected this(String category) {
+            super(category);
+        }
+
+        /*
+         * @see DefaultPositionUpdater#notDeleted()
+         */
+        protected bool notDeleted() {
+            return true;
+        }
+    }
+
+    /**
+     * The position updater which runs as first updater on the document's positions.
+     * Used to remove all affected positions from their categories to avoid them
+     * from being regularly updated.
+     *
+     * @see IPositionUpdater
+     */
+    class RemoveAffectedPositions : IPositionUpdater {
+        /*
+         * @see IPositionUpdater#update(DocumentEvent)
+         */
+        public void update(DocumentEvent event) {
+            removeAffectedPositions(event.getDocument());
+        }
+    }
+
+    /**
+     * The position updater which runs as last updater on the document's positions.
+     * Used to update all affected positions and adding them back to their
+     * original categories.
+     *
+     * @see IPositionUpdater
+     */
+    class UpdateAffectedPositions : IPositionUpdater {
+
+        /** The affected positions */
+        private int[] fPositions;
+        /** The offset */
+        private int fOffset;
+
+        /**
+         * Creates a new updater.
+         *
+         * @param positions the affected positions
+         * @param offset the offset
+         */
+        public this(int[] positions, int offset) {
+            fPositions= positions;
+            fOffset= offset;
+        }
+
+        /*
+         * @see IPositionUpdater#update(DocumentEvent)
+         */
+        public void update(DocumentEvent event) {
+            updateAffectedPositions(event.getDocument(), fPositions, fOffset);
+        }
+    }
+
+
+    /** Internal position category used for the formatter partitioning */
+    private const static String PARTITIONING= "__formatter_partitioning"; //$NON-NLS-1$
+
+    /** The map of <code>IFormattingStrategy</code> objects */
+    private Map fStrategies;
+    /** The indicator of whether the formatter operates in partition aware mode or not */
+    private bool fIsPartitionAware= true;
+
+    /** The partition information managing document position categories */
+    private String[] fPartitionManagingCategories;
+    /** The list of references to offset and end offset of all overlapping positions */
+    private List fOverlappingPositionReferences;
+    /** Position updater used for partitioning positions */
+    private IPositionUpdater fPartitioningUpdater;
+    /**
+     * The document partitioning used by this formatter.
+     * @since 3.0
+     */
+    private String fPartitioning;
+    /**
+     * The document this formatter works on.
+     * @since 3.0
+     */
+    private IDocument fDocument;
+    /**
+     * The external partition managing categories.
+     * @since 3.0
+     */
+    private String[] fExternalPartitonManagingCategories;
+    /**
+     * Indicates whether <code>fPartitionManagingCategories</code> must be computed.
+     * @since 3.0
+     */
+    private bool fNeedsComputation= true;
+
+
+    /**
+     * Creates a new content formatter. The content formatter operates by default
+     * in the partition-aware mode. There are no preconfigured formatting strategies.
+     * Will use the default document partitioning if not further configured.
+     */
+    public this() {
+        fPartitioning= IDocumentExtension3.DEFAULT_PARTITIONING;
+    }
+
+    /**
+     * Registers a strategy for a particular content type. If there is already a strategy
+     * registered for this type, the new strategy is registered instead of the old one.
+     * If the given content type is <code>null</code> the given strategy is registered for
+     * all content types as is called only once per formatting session.
+     *
+     * @param strategy the formatting strategy to register, or <code>null</code> to remove an existing one
+     * @param contentType the content type under which to register
+     */
+    public void setFormattingStrategy(IFormattingStrategy strategy, String contentType) {
+
+        Assert.isNotNull(contentType);
+
+        if (fStrategies is null)
+            fStrategies= new HashMap();
+
+        if (strategy is null)
+            fStrategies.remove(contentType);
+        else
+            fStrategies.put(contentType, cast(Object)strategy);
+    }
+
+    /**
+     * Informs this content formatter about the names of those position categories
+     * which are used to manage the document's partitioning information and thus should
+     * be ignored when this formatter updates positions.
+     *
+     * @param categories the categories to be ignored
+     * @deprecated incompatible with an open set of document partitionings. The provided information is only used
+     *      if this formatter can not compute the partition managing position categories.
+     */
+    public void setPartitionManagingPositionCategories(String[] categories) {
+        fExternalPartitonManagingCategories= TextUtilities.copy(categories);
+    }
+
+    /**
+     * Sets the document partitioning to be used by this formatter.
+     *
+     * @param partitioning the document partitioning
+     * @since 3.0
+     */
+    public void setDocumentPartitioning(String partitioning) {
+        fPartitioning= partitioning;
+    }
+
+    /**
+     * Sets the formatter's operation mode.
+     *
+     * @param enable indicates whether the formatting process should be partition ware
+     */
+    public void enablePartitionAwareFormatting(bool enable) {
+        fIsPartitionAware= enable;
+    }
+
+    /*
+     * @see IContentFormatter#getFormattingStrategy(String)
+     */
+    public IFormattingStrategy getFormattingStrategy(String contentType) {
+
+        Assert.isNotNull(contentType);
+
+        if (fStrategies is null)
+            return null;
+
+        return cast(IFormattingStrategy) fStrategies.get(contentType);
+    }
+
+    /*
+     * @see IContentFormatter#format(IDocument, IRegion)
+     */
+    public void format(IDocument document, IRegion region) {
+        fNeedsComputation= true;
+        fDocument= document;
+        try {
+
+            if (fIsPartitionAware)
+                formatPartitions(region);
+            else
+                formatRegion(region);
+
+        } finally {
+            fNeedsComputation= true;
+            fDocument= null;
+        }
+    }
+
+    /**
+     * Determines the partitioning of the given region of the document.
+     * Informs the formatting strategies of each partition about the start,
+     * the process, and the termination of the formatting session.
+     *
+     * @param region the document region to be formatted
+     * @since 3.0
+     */
+    private void formatPartitions(IRegion region) {
+
+        addPartitioningUpdater();
+
+        try {
+
+            TypedPosition[] ranges= getPartitioning(region);
+            if (ranges !is null) {
+                start(ranges, getIndentation(region.getOffset()));
+                format(ranges);
+                stop(ranges);
+            }
+
+        } catch (BadLocationException x) {
+        }
+
+        removePartitioningUpdater();
+    }
+
+    /**
+     * Formats the given region with the strategy registered for the default
+     * content type. The strategy is informed about the start, the process, and
+     * the termination of the formatting session.
+     *
+     * @param region the region to be formatted
+     * @since 3.0
+     */
+    private void formatRegion(IRegion region) {
+
+        IFormattingStrategy strategy= getFormattingStrategy(IDocument.DEFAULT_CONTENT_TYPE);
+        if (strategy !is null) {
+            strategy.formatterStarts(getIndentation(region.getOffset()));
+            format(strategy, new TypedPosition(region.getOffset(), region.getLength(), IDocument.DEFAULT_CONTENT_TYPE));
+            strategy.formatterStops();
+        }
+    }
+
+    /**
+     * Returns the partitioning of the given region of the document to be formatted.
+     * As one partition after the other will be formatted and formatting will
+     * probably change the length of the formatted partition, it must be kept
+     * track of the modifications in order to submit the correct partition to all
+     * formatting strategies. For this, all partitions are remembered as positions
+     * in a dedicated position category. (As formatting strategies might rely on each
+     * other, calling them in reversed order is not an option.)
+     *
+     * @param region the region for which the partitioning must be determined
+     * @return the partitioning of the specified region
+     * @exception BadLocationException of region is invalid in the document
+     * @since 3.0
+     */
+    private TypedPosition[] getPartitioning(IRegion region)  {
+
+        ITypedRegion[] regions= TextUtilities.computePartitioning(fDocument, fPartitioning, region.getOffset(), region.getLength(), false);
+        TypedPosition[] positions= new TypedPosition[regions.length];
+
+        for (int i= 0; i < regions.length; i++) {
+            positions[i]= new TypedPosition(regions[i]);
+            try {
+                fDocument.addPosition(PARTITIONING, positions[i]);
+            } catch (BadPositionCategoryException x) {
+                // should not happen
+            }
+        }
+
+        return positions;
+    }
+
+    /**
+     * Fires <code>formatterStarts</code> to all formatter strategies
+     * which will be involved in the forthcoming formatting process.
+     *
+     * @param regions the partitioning of the document to be formatted
+     * @param indentation the initial indentation
+     */
+    private void start(TypedPosition[] regions, String indentation) {
+        for (int i= 0; i < regions.length; i++) {
+            IFormattingStrategy s= getFormattingStrategy(regions[i].getType());
+            if (s !is null)
+                s.formatterStarts(indentation);
+        }
+    }
+
+    /**
+     * Formats one partition after the other using the formatter strategy registered for
+     * the partition's content type.
+     *
+     * @param ranges the partitioning of the document region to be formatted
+     * @since 3.0
+     */
+    private void format(TypedPosition[] ranges) {
+        for (int i= 0; i < ranges.length; i++) {
+            IFormattingStrategy s= getFormattingStrategy(ranges[i].getType());
+            if (s !is null) {
+                format(s, ranges[i]);
+            }
+        }
+    }
+
+    /**
+     * Formats the given region of the document using the specified formatting
+     * strategy. In order to maintain positions correctly, first all affected
+     * positions determined, after all document listeners have been informed about
+     * the coming change, the affected positions are removed to avoid that they
+     * are regularly updated. After all position updaters have run, the affected
+     * positions are updated with the formatter's information and added back to
+     * their categories, right before the first document listener is informed about
+     * that a change happened.
+     *
+     * @param strategy the strategy to be used
+     * @param region the region to be formatted
+     * @since 3.0
+     */
+    private void format(IFormattingStrategy strategy, TypedPosition region) {
+        try {
+
+            final int offset= region.getOffset();
+            int length= region.getLength();
+
+            String content= fDocument.get(offset, length);
+            final int[] positions= getAffectedPositions(offset, length);
+            String formatted= strategy.format(content, isLineStart(offset), getIndentation(offset), positions);
+
+            if (formatted !is null && !formatted.equals(content)) {
+
+                IPositionUpdater first= new RemoveAffectedPositions();
+                fDocument.insertPositionUpdater(first, 0);
+                IPositionUpdater last= new UpdateAffectedPositions(positions, offset);
+                fDocument.addPositionUpdater(last);
+
+                fDocument.replace(offset, length, formatted);
+
+                fDocument.removePositionUpdater(first);
+                fDocument.removePositionUpdater(last);
+            }
+
+        } catch (BadLocationException x) {
+            // should not happen
+        }
+    }
+
+    /**
+     * Fires <code>formatterStops</code> to all formatter strategies which were
+     * involved in the formatting process which is about to terminate.
+     *
+     * @param regions the partitioning of the document which has been formatted
+     */
+    private void stop(TypedPosition[] regions) {
+        for (int i= 0; i < regions.length; i++) {
+            IFormattingStrategy s= getFormattingStrategy(regions[i].getType());
+            if (s !is null)
+                s.formatterStops();
+        }
+    }
+
+    /**
+     * Installs those updaters which the formatter needs to keep track of the partitions.
+     * @since 3.0
+     */
+    private void addPartitioningUpdater() {
+        fPartitioningUpdater= new NonDeletingPositionUpdater(PARTITIONING);
+        fDocument.addPositionCategory(PARTITIONING);
+        fDocument.addPositionUpdater(fPartitioningUpdater);
+    }
+
+    /**
+     * Removes the formatter's internal position updater and category.
+     *
+     * @since 3.0
+     */
+    private void removePartitioningUpdater() {
+
+        try {
+
+            fDocument.removePositionUpdater(fPartitioningUpdater);
+            fDocument.removePositionCategory(PARTITIONING);
+            fPartitioningUpdater= null;
+
+        } catch (BadPositionCategoryException x) {
+            // should not happen
+        }
+    }
+
+    /**
+     * Returns the partition managing position categories for the formatted document.
+     *
+     * @return the position managing position categories
+     * @since 3.0
+     */
+    private String[] getPartitionManagingCategories() {
+        if (fNeedsComputation) {
+            fNeedsComputation= false;
+            fPartitionManagingCategories= TextUtilities.computePartitionManagingCategories(fDocument);
+            if (fPartitionManagingCategories is null)
+                fPartitionManagingCategories= fExternalPartitonManagingCategories;
+        }
+        return fPartitionManagingCategories;
+    }
+
+    /**
+     * Determines whether the given document position category should be ignored
+     * by this formatter's position updating.
+     *
+     * @param category the category to check
+     * @return <code>true</code> if the category should be ignored, <code>false</code> otherwise
+     */
+    private bool ignoreCategory(String category) {
+
+        if (PARTITIONING.equals(category))
+            return true;
+
+        String[] categories= getPartitionManagingCategories();
+        if (categories !is null) {
+            for (int i= 0; i < categories.length; i++) {
+                if (categories[i].equals(category))
+                    return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Determines all embracing, overlapping, and follow up positions
+     * for the given region of the document.
+     *
+     * @param offset the offset of the document region to be formatted
+     * @param length the length of the document to be formatted
+     * @since 3.0
+     */
+    private void determinePositionsToUpdate(int offset, int length) {
+
+        String[] categories= fDocument.getPositionCategories();
+        if (categories !is null) {
+            for (int i= 0; i < categories.length; i++) {
+
+                if (ignoreCategory(categories[i]))
+                    continue;
+
+                try {
+
+                    Position[] positions= fDocument.getPositions(categories[i]);
+
+                    for (int j= 0; j < positions.length; j++) {
+
+                        Position p= positions[j];
+                        if (p.overlapsWith(offset, length)) {
+
+                            if (offset < p.getOffset())
+                                fOverlappingPositionReferences.add(new PositionReference(p, true, categories[i]));
+
+                            if (p.getOffset() + p.getLength() < offset + length)
+                                fOverlappingPositionReferences.add(new PositionReference(p, false, categories[i]));
+                        }
+                    }
+
+                } catch (BadPositionCategoryException x) {
+                    // can not happen
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns all offset and the end offset of all positions overlapping with the
+     * specified document range.
+     *
+     * @param offset the offset of the document region to be formatted
+     * @param length the length of the document to be formatted
+     * @return all character positions of the interleaving positions
+     * @since 3.0
+     */
+    private int[] getAffectedPositions(int offset, int length) {
+
+        fOverlappingPositionReferences= new ArrayList();
+
+        determinePositionsToUpdate(offset, length);
+
+        Collections.sort(fOverlappingPositionReferences);
+
+        int[] positions= new int[fOverlappingPositionReferences.size()];
+        for (int i= 0; i < positions.length; i++) {
+            PositionReference r= cast(PositionReference) fOverlappingPositionReferences.get(i);
+            positions[i]= r.getCharacterPosition() - offset;
+        }
+
+        return positions;
+    }
+
+    /**
+     * Removes the affected positions from their categories to avoid
+     * that they are invalidly updated.
+     *
+     * @param document the document
+     */
+    private void removeAffectedPositions(IDocument document) {
+        int size= fOverlappingPositionReferences.size();
+        for (int i= 0; i < size; i++) {
+            PositionReference r= cast(PositionReference) fOverlappingPositionReferences.get(i);
+            try {
+                document.removePosition(r.getCategory(), r.getPosition());
+            } catch (BadPositionCategoryException x) {
+                // can not happen
+            }
+        }
+    }
+
+    /**
+     * Updates all the overlapping positions. Note, all other positions are
+     * automatically updated by their document position updaters.
+     *
+     * @param document the document to has been formatted
+     * @param positions the adapted character positions to be used to update the document positions
+     * @param offset the offset of the document region that has been formatted
+     */
+    protected void updateAffectedPositions(IDocument document, int[] positions, int offset) {
+
+        if (document !is fDocument)
+            return;
+
+        if (positions.length is 0)
+            return;
+
+        for (int i= 0; i < positions.length; i++) {
+
+            PositionReference r= cast(PositionReference) fOverlappingPositionReferences.get(i);
+
+            if (r.refersToOffset())
+                r.setOffset(offset + positions[i]);
+            else
+                r.setLength((offset + positions[i]) - r.getOffset());
+
+            Position p= r.getPosition();
+            String category= r.getCategory();
+            if (!document.containsPosition(category, p.offset, p.length)) {
+                try {
+                    if (positionAboutToBeAdded(document, category, p))
+                        document.addPosition(r.getCategory(), p);
+                } catch (BadPositionCategoryException x) {
+                    // can not happen
+                } catch (BadLocationException x) {
+                    // should not happen
+                }
+            }
+
+        }
+
+        fOverlappingPositionReferences= null;
+    }
+
+    /**
+     * The given position is about to be added to the given position category of the given document. <p>
+     * This default implementation return <code>true</code>.
+     *
+     * @param document the document
+     * @param category the position category
+     * @param position the position that will be added
+     * @return <code>true</code> if the position can be added, <code>false</code> if it should be ignored
+     */
+    protected bool positionAboutToBeAdded(IDocument document, String category, Position position) {
+        return true;
+    }
+
+    /**
+     * Returns the indentation of the line of the given offset.
+     *
+     * @param offset the offset
+     * @return the indentation of the line of the offset
+     * @since 3.0
+     */
+    private String getIndentation(int offset) {
+
+        try {
+            int start= fDocument.getLineOfOffset(offset);
+            start= fDocument.getLineOffset(start);
+
+            int end= start;
+            char c= fDocument.getChar(end);
+            while ('\t' is c || ' ' is c)
+                c= fDocument.getChar(++end);
+
+            return fDocument.get(start, end - start);
+        } catch (BadLocationException x) {
+        }
+
+        return ""; //$NON-NLS-1$
+    }
+
+    /**
+     * Determines whether the offset is the beginning of a line in the given document.
+     *
+     * @param offset the offset
+     * @return <code>true</code> if offset is the beginning of a line
+     * @exception BadLocationException if offset is invalid in document
+     * @since 3.0
+     */
+    private bool isLineStart(int offset)  {
+        int start= fDocument.getLineOfOffset(offset);
+        start= fDocument.getLineOffset(start);
+        return (start is offset);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/ContextBasedFormattingStrategy.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy;
+
+import org.eclipse.jface.text.formatter.MultiPassContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContext; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatterExtension; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategyExtension; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContextProperties; // packageimport
+import org.eclipse.jface.text.formatter.ContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingContext; // packageimport
+
+import java.lang.all;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+
+
+
+/**
+ * Formatting strategy for context based content formatting. Retrieves the preferences
+ * set on the formatting context's {@link FormattingContextProperties#CONTEXT_PREFERENCES}
+ * property and makes them available to subclasses.
+ * <p>
+ *
+ * @since 3.0
+ */
+public abstract class ContextBasedFormattingStrategy : IFormattingStrategy, IFormattingStrategyExtension {
+
+    /** The current preferences for formatting */
+    private Map fCurrentPreferences= null;
+
+    /** The list of preferences for initiated the formatting steps */
+    private const LinkedList fPreferences;
+
+    this(){
+        fPreferences= new LinkedList();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingStrategyExtension#format()
+     */
+    public void format() {
+        fCurrentPreferences= cast(Map)fPreferences.removeFirst();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingStrategy#format(java.lang.String, bool, java.lang.String, int[])
+     */
+    public String format(String content, bool start, String indentation, int[] positions) {
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingStrategyExtension#formatterStarts(org.eclipse.jface.text.formatter.IFormattingContext)
+     */
+    public void formatterStarts(IFormattingContext context) {
+        fPreferences.addLast(context.getProperty(stringcast(FormattingContextProperties.CONTEXT_PREFERENCES)));
+    }
+
+    /*
+     * @see IFormattingStrategy#formatterStarts(String)
+     */
+    public void formatterStarts(String indentation) {
+        // Do nothing
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingStrategyExtension#formatterStops()
+     */
+    public void formatterStops() {
+        fPreferences.clear();
+
+        fCurrentPreferences= null;
+    }
+
+    /**
+     * Returns the preferences used for the current formatting step.
+     *
+     * @return The preferences for the current formatting step
+     */
+    public final Map getPreferences() {
+        return fCurrentPreferences;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/FormattingContext.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.formatter.FormattingContext;
+
+import org.eclipse.jface.text.formatter.MultiPassContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatterExtension; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategyExtension; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContextProperties; // packageimport
+import org.eclipse.jface.text.formatter.ContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingContext; // packageimport
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+
+
+import org.eclipse.jface.preference.IPreferenceStore;
+
+/**
+ * Default implementation of <code>IFormattingContext</code>.
+ *
+ * @since 3.0
+ */
+public class FormattingContext : IFormattingContext {
+
+    /** Map to store the properties */
+    private const Map fMap;
+
+    this(){
+        fMap= new HashMap();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingContext#dispose()
+     */
+    public void dispose() {
+        fMap.clear();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingContext#getPreferenceKeys()
+     */
+    public String[] getPreferenceKeys() {
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingContext#getProperty(java.lang.Object)
+     */
+    public Object getProperty(Object key) {
+        return fMap.get(key);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingContext#isBooleanPreference(java.lang.String)
+     */
+    public bool isBooleanPreference(String key) {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingContext#isDoublePreference(java.lang.String)
+     */
+    public bool isDoublePreference(String key) {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingContext#isFloatPreference(java.lang.String)
+     */
+    public bool isFloatPreference(String key) {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingContext#isIntegerPreference(java.lang.String)
+     */
+    public bool isIntegerPreference(String key) {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingContext#isLongPreference(java.lang.String)
+     */
+    public bool isLongPreference(String key) {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingContext#isStringPreference(java.lang.String)
+     */
+    public bool isStringPreference(String key) {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingContext#mapToStore(java.util.Map, org.eclipse.jface.preference.IPreferenceStore)
+     */
+    public void mapToStore(Map map, IPreferenceStore store) {
+
+        final String[] preferences= getPreferenceKeys();
+
+        String result= null;
+        String preference= null;
+
+        for (int index= 0; index < preferences.length; index++) {
+
+            preference= preferences[index];
+            result= stringcast(map.get(preference));
+
+            if (result !is null) {
+
+                try {
+                    if (isBooleanPreference(preference)) {
+                        store.setValue(preference, result.equals(IPreferenceStore.TRUE));
+                    } else if (isIntegerPreference(preference)) {
+                        store.setValue(preference, Integer.parseInt(result));
+                    } else if (isStringPreference(preference)) {
+                        store.setValue(preference, result);
+                    } else if (isDoublePreference(preference)) {
+                        store.setValue(preference, Double.parseDouble(result));
+                    } else if (isFloatPreference(preference)) {
+                        store.setValue(preference, Float.parseFloat(result));
+                    } else if (isLongPreference(preference)) {
+                        store.setValue(preference, Long.parseLong(result));
+                    }
+                } catch (NumberFormatException exception) {
+                    // Do nothing
+                }
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingContext#setProperty(java.lang.Object, java.lang.Object)
+     */
+    public void setProperty(Object key, Object property) {
+        fMap.put(key, property);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IFormattingContext#storeToMap(org.eclipse.jface.preference.IPreferenceStore, java.util.Map, bool)
+     */
+    public void storeToMap(IPreferenceStore store, Map map, bool useDefault) {
+
+        final String[] preferences= getPreferenceKeys();
+
+        String preference= null;
+        for (int index= 0; index < preferences.length; index++) {
+
+            preference= preferences[index];
+
+            if (isBooleanPreference(preference)) {
+                map.put(preference, (useDefault ? store.getDefaultBoolean(preference) : store.getBoolean(preference)) ? IPreferenceStore.TRUE : IPreferenceStore.FALSE);
+            } else if (isIntegerPreference(preference)) {
+                map.put(preference, String_valueOf(useDefault ? store.getDefaultInt(preference) : store.getInt(preference)));
+            } else if (isStringPreference(preference)) {
+                map.put(preference, useDefault ? store.getDefaultString(preference) : store.getString(preference));
+            } else if (isDoublePreference(preference)) {
+                map.put(preference, String_valueOf(useDefault ? store.getDefaultDouble(preference) : store.getDouble(preference)));
+            } else if (isFloatPreference(preference)) {
+                map.put(preference, String_valueOf(useDefault ? store.getDefaultFloat(preference) : store.getFloat(preference)));
+            } else if (isLongPreference(preference)) {
+                map.put(preference, String_valueOf(useDefault ? store.getDefaultLong(preference) : store.getLong(preference)));
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/FormattingContextProperties.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.formatter.FormattingContextProperties;
+
+import org.eclipse.jface.text.formatter.MultiPassContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContext; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatterExtension; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategyExtension; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.ContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingContext; // packageimport
+
+import java.lang.all;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Keys used by <code>IFormattingContext</code> objects to register specific
+ * properties needed during the formatting process of a content formatter
+ * implementing <code>IContentFormatterExtension</code>.
+ *
+ * @see IFormattingContext
+ * @see IFormattingStrategyExtension
+ * @see IContentFormatterExtension
+ * @since 3.0
+ */
+public class FormattingContextProperties {
+
+    /**
+     * Property key of the document property. The property must implement
+     * <code>java.lang#Boolean</code>. If set to <code>true</code> the whole
+     * document is formatted.
+     * <p>
+     * Value: <code>"formatting.context.document"</code>
+     */
+    public static const String CONTEXT_DOCUMENT= "formatting.context.document"; //$NON-NLS-1$
+
+    /**
+     * Property key of the partition property. The property must implement
+     * <code>org.eclipse.jface.text#TypedPosition</code>. The partition
+     * a context based formatting strategy should format.
+     * <p>
+     * Value: <code>"formatting.context.partition"</code>
+     */
+    public static const String CONTEXT_PARTITION= "formatting.context.partition"; //$NON-NLS-1$
+
+    /**
+     * Property key of the preferences property. The property must implement
+     * <code>java.util#Map</code>. The formatting preferences mapping preference
+     * keys to values.
+     * <p>
+     * Value: <code>"formatting.context.preferences"</code>
+     */
+    public static const String CONTEXT_PREFERENCES= "formatting.context.preferences"; //$NON-NLS-1$
+
+    /**
+     * Property key of the region property. The property must implement <code>org.eclipse.jface.text#IRegion</code>.
+     * The region to format. If set, {@link FormattingContextProperties#CONTEXT_DOCUMENT} should be <code>false</code>
+     * for this to take effect.
+     * <p>
+     * Value: <code>"formatting.context.region"</code>
+     */
+    public static const String CONTEXT_REGION= "formatting.context.region"; //$NON-NLS-1$
+
+    /**
+     * Property key of the medium property. The property must implement <code>org.eclipse.jface.text#IDocument</code>.
+     * The document to format.
+     * <p>
+     * Value: <code>"formatting.context.medium"</code>
+     */
+    public static const String CONTEXT_MEDIUM= "formatting.context.medium"; //$NON-NLS-1$
+
+    /**
+     * Ensure that this class cannot be instantiated.
+     */
+    private this() {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IContentFormatter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.formatter.IContentFormatter;
+
+import org.eclipse.jface.text.formatter.MultiPassContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContext; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatterExtension; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategyExtension; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContextProperties; // packageimport
+import org.eclipse.jface.text.formatter.ContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingContext; // packageimport
+
+
+import java.lang.all;
+
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+
+
+/**
+ * The interface of a document content formatter. The formatter formats ranges
+ * within documents. The documents are modified by the formatter.<p>
+ * The content formatter is assumed to determine the partitioning of the document
+ * range to be formatted. For each partition, the formatter determines based
+ * on the partition's content type the formatting strategy to be used. Before
+ * the first strategy is activated all strategies are informed about the
+ * start of the formatting process. After that, the formatting strategies are
+ * activated in the sequence defined by the partitioning of the document range to be
+ * formatted. It is assumed that a strategy must be finished before the next strategy
+ * can be activated. After the last strategy has been finished, all strategies are
+ * informed about the termination of the formatting process.</p>
+ * <p>
+ * The interface can be implemented by clients. By default, clients use <code>ContentFormatter</code>
+ * or <code>MultiPassContentFormatter</code> as the standard implementers of this interface.</p>
+ *
+ * @see IDocument
+ * @see IFormattingStrategy
+ */
+public interface IContentFormatter {
+
+    /**
+     * Formats the given region of the specified document.The formatter may safely
+     * assume that it is the only subject that modifies the document at this point in time.
+     *
+     * @param document the document to be formatted
+     * @param region the region within the document to be formatted
+     */
+    void format(IDocument document, IRegion region);
+
+    /**
+     * Returns the formatting strategy registered for the given content type.
+     *
+     * @param contentType the content type for which to look up the formatting strategy
+     * @return the formatting strategy for the given content type, or
+     *      <code>null</code> if there is no such strategy
+     */
+    IFormattingStrategy getFormattingStrategy(String contentType);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IContentFormatterExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.formatter.IContentFormatterExtension;
+
+import org.eclipse.jface.text.formatter.MultiPassContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContext; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategyExtension; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContextProperties; // packageimport
+import org.eclipse.jface.text.formatter.ContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingContext; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.IDocument;
+
+/**
+ * Extension interface for {@link IContentFormatter}.
+ * <p>
+ * Updates the content formatter to be able to pass {@link IFormattingContext}
+ * context objects to {@link IFormattingStrategyExtension} objects
+ * operating in context based mode.
+ * <p>
+ * Clients using context based formatting call the method
+ * <code>format(IDocument, IFormattingContext)</code> with a properly
+ * initialized formatting context.<br>
+ * The formatting context must be set up according to the desired formatting mode:
+ * <ul>
+ * <li>For whole document formatting set the property {@link FormattingContextProperties#CONTEXT_DOCUMENT}.
+ * This is equivalent to setting {@link FormattingContextProperties#CONTEXT_REGION} with a region spanning
+ * the whole document.</li>
+ * <li>For multiple region formatting set the property {@link FormattingContextProperties#CONTEXT_REGION}.
+ * Note that the content formatter automatically aligns the region to a block selected region,
+ * and if the region spans multiple partitions, it also completes eventual partitions covered only
+ * partially by the region.</li>
+ * </ul>
+ * Depending on the registered formatting strategies, more context information must
+ * be passed in the formatting context, like e.g. {@link FormattingContextProperties#CONTEXT_PREFERENCES}.
+ * <p>
+ * Note that in context based mode the content formatter is fully reentrant, but not
+ * thread-safe.
+ * <p>
+ *
+ * @see IFormattingContext
+ * @see FormattingContextProperties
+ * @since 3.0
+ */
+public interface IContentFormatterExtension {
+
+    /**
+     * Formats the given region of the specified document.
+     * <p>
+     * The formatter may safely assume that it is the only subject that
+     * modifies the document at this point in time. This method is fully
+     * reentrant, but not thread-safe.
+     * <p>
+     * The formatting process performed by <code>format(IDocument, IFormattingContext)</code>
+     * happens as follows:
+     * <ul>
+     * <li>In a first pass the content formatter formats the range of the
+     * document to be formatted by using the master formatting strategy. This
+     * happens regardless of the content type of the underlying partition.
+     * </li>
+     * <li>In the second pass, the range is formatted again, this time using
+     * the registered slave formatting strategies. For each partition contained
+     * in the range to be formatted, the content formatter determines its
+     * content type and formats the partition with the correct formatting
+     * strategy.
+     * </li>
+     *
+     * @param document
+     *                  The document to be formatted
+     * @param context
+     *                   The formatting context to pass to the formatting strategies.
+     *                   This argument must not be <code>null</code>.
+     */
+    void format(IDocument document, IFormattingContext context);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IFormattingContext.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.formatter.IFormattingContext;
+
+import org.eclipse.jface.text.formatter.MultiPassContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContext; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatterExtension; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategyExtension; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContextProperties; // packageimport
+import org.eclipse.jface.text.formatter.ContentFormatter; // packageimport
+
+import java.lang.all;
+import java.util.Map;
+import java.util.Set;
+
+
+import org.eclipse.jface.preference.IPreferenceStore;
+
+/**
+ * Formatting context used in formatting strategies implementing interface
+ * <code>IFormattingStrategyExtension</code>.
+ *
+ * @see IFormattingStrategyExtension
+ * @since 3.0
+ */
+public interface IFormattingContext {
+
+    /**
+     * Dispose of the formatting context.
+     * <p>
+     * Must be called after the formatting context has been used in a
+     * formatting process.
+     */
+    void dispose();
+
+    /**
+     * Returns the preference keys used for the retrieval of formatting
+     * preferences.
+     *
+     * @return The preference keys for formatting
+     */
+    String[] getPreferenceKeys();
+
+    /**
+     * Retrieves the property <code>key</code> from the formatting context
+     *
+     * @param key
+     *                  Key of the property to store in the context
+     * @return The property <code>key</code> if available, <code>null</code>
+     *               otherwise
+     */
+    Object getProperty(Object key);
+
+    /**
+     * Is this preference key for a bool preference?
+     *
+     * @param key
+     *                  The preference key to query its type
+     * @return <code>true</code> iff this key is for a bool preference,
+     *               <code>false</code> otherwise.
+     */
+    bool isBooleanPreference(String key);
+
+    /**
+     * Is this preference key for a double preference?
+     *
+     * @param key
+     *                  The preference key to query its type
+     * @return <code>true</code> iff this key is for a double preference,
+     *               <code>false</code> otherwise.
+     */
+    bool isDoublePreference(String key);
+
+    /**
+     * Is this preference key for a float preference?
+     *
+     * @param key
+     *                  The preference key to query its type
+     * @return <code>true</code> iff this key is for a float preference,
+     *               <code>false</code> otherwise.
+     */
+    bool isFloatPreference(String key);
+
+    /**
+     * Is this preference key for an integer preference?
+     *
+     * @param key
+     *                  The preference key to query its type
+     * @return <code>true</code> iff this key is for an integer preference,
+     *               <code>false</code> otherwise.
+     */
+    bool isIntegerPreference(String key);
+
+    /**
+     * Is this preference key for a long preference?
+     *
+     * @param key
+     *                  The preference key to query its type
+     * @return <code>true</code> iff this key is for a long preference,
+     *               <code>false</code> otherwise.
+     */
+    bool isLongPreference(String key);
+
+    /**
+     * Is this preference key for a string preference?
+     *
+     * @param key
+     *                  The preference key to query its type
+     * @return <code>true</code> iff this key is for a string preference,
+     *               <code>false</code> otherwise.
+     */
+    bool isStringPreference(String key);
+
+    /**
+     * Stores the preferences from a map to a preference store.
+     * <p>
+     * Note that the preference keys returned by
+     * {@link #getPreferenceKeys()} must not be used in the preference store.
+     * Otherwise the preferences are overwritten.
+     * </p>
+     *
+     * @param map
+     *                  Map to retrieve the preferences from
+     * @param store
+     *                  Preference store to store the preferences in
+     */
+    void mapToStore(Map map, IPreferenceStore store);
+
+    /**
+     * Stores the property <code>key</code> in the formatting context.
+     *
+     * @param key
+     *                  Key of the property to store in the context
+     * @param property
+     *                  Property to store in the context. If already present, the new
+     *                  property overwrites the present one.
+     */
+    void setProperty(Object key, Object property);
+
+    /**
+     * Retrieves the preferences from a preference store in a map.
+     * <p>
+     * Note that the preference keys returned by
+     * {@link #getPreferenceKeys()} must not be used in the map. Otherwise the
+     * preferences are overwritten.
+     * </p>
+     *
+     * @param store
+     *                  Preference store to retrieve the preferences from
+     * @param map
+     *                  Map to store the preferences in
+     * @param useDefault
+     *                  <code>true</code> if the default preferences should be
+     *                  used, <code>false</code> otherwise
+     */
+    void storeToMap(IPreferenceStore store, Map map, bool useDefault);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IFormattingStrategy.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.formatter.IFormattingStrategy;
+
+import org.eclipse.jface.text.formatter.MultiPassContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContext; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatterExtension; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategyExtension; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContextProperties; // packageimport
+import org.eclipse.jface.text.formatter.ContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingContext; // packageimport
+
+import java.lang.all;
+
+
+/**
+ * A formatting strategy is assumed to be specialized on formatting text
+ * of a particular content type. Each formatting process calls the strategy's
+ * methods in the following sequence:
+ * <ul>
+ * <li><code>formatterStarts</code>
+ * <li><code>format</code>
+ * <li><code>formatterStops</code>
+ * </ul>
+ * <p>
+ * This interface must be implemented by clients. Implementers should be registered with
+ * a content formatter in order get involved in the formatting process.</p>
+ */
+public interface IFormattingStrategy {
+
+    /**
+     * Informs the strategy about the start of a formatting process in which it will
+     * participate.
+     *
+     * @param initialIndentation the indent string of the first line at which the
+     *      overall formatting process starts.
+     */
+    void formatterStarts(String initialIndentation);
+
+    /**
+     * Formats the given string. During the formatting process this strategy must update
+     * the given character positions according to the changes applied to the given string.
+     *
+     * @param content the initial string to be formatted
+     * @param isLineStart indicates whether the beginning of content is a line start in its document
+     * @param indentation the indentation string to be used
+     * @param positions the character positions to be updated
+     * @return the formatted string
+     */
+    String format(String content, bool isLineStart, String indentation, int[] positions);
+
+    /**
+     * Informs the strategy that the formatting process in which it has participated
+     * has been finished.
+     */
+    void formatterStops();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IFormattingStrategyExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.formatter.IFormattingStrategyExtension;
+
+import org.eclipse.jface.text.formatter.MultiPassContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContext; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatterExtension; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContextProperties; // packageimport
+import org.eclipse.jface.text.formatter.ContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingContext; // packageimport
+
+import java.lang.all;
+
+/**
+ * Extension interface for <code>IFormattingStrategy</code>.
+ * <p>
+ * Updates formatting strategies to be able to receive a more general <code>IFormattingContext</code>
+ * object from its associated content formatters.
+ * <p>
+ * Each formatting process calls the strategy's methods in the following
+ * sequence:
+ * <ul>
+ * <li><code>formatterStarts</code>
+ * <li><code>format</code>
+ * <li><code>formatterStops</code>
+ * </ul>
+ * <p>
+ * Note that multiple calls to <code>formatterStarts</code> can be issued to
+ * a strategy before launching the formatting process with <code>format</code>.
+ * <p>
+ * This interface must be implemented by clients. Implementers should be
+ * registered with a content formatter in order get involved in the formatting
+ * process.
+ *
+ * @see IFormattingContext
+ * @since 3.0
+ */
+public interface IFormattingStrategyExtension {
+
+    /**
+     * Formats the region with the properties indicated in the formatting
+     * context previously supplied by <code>formatterStarts(IFormattingContext)</code>.
+     */
+    void format();
+
+    /**
+     * Informs the strategy about the start of a formatting process in which it
+     * will participate.
+     *
+     * @param context
+     *                  Formatting context used in the corresponding formatting
+     *                  process.
+     */
+    void formatterStarts(IFormattingContext context);
+
+    /**
+     * Informs the strategy that the formatting process in which it has
+     * participated has been finished.
+     */
+    void formatterStops();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/MultiPassContentFormatter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,335 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.formatter.MultiPassContentFormatter;
+
+import org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContext; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategy; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatterExtension; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingStrategyExtension; // packageimport
+import org.eclipse.jface.text.formatter.IContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.FormattingContextProperties; // packageimport
+import org.eclipse.jface.text.formatter.ContentFormatter; // packageimport
+import org.eclipse.jface.text.formatter.IFormattingContext; // packageimport
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DefaultPositionUpdater;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.TypedPosition;
+
+/**
+ * Content formatter for edit-based formatting strategies.
+ * <p>
+ * Two kinds of formatting strategies can be registered with this formatter:
+ * <ul>
+ * <li>one master formatting strategy for the default content type</li>
+ * <li>one formatting strategy for each non-default content type</li>
+ * </ul>
+ * The master formatting strategy always formats the whole region to be
+ * formatted in the first pass. In a second pass, all partitions of the region
+ * to be formatted that are not of master content type are formatted using the
+ * slave formatting strategy registered for the underlying content type. All
+ * formatting strategies must implement {@link IFormattingStrategyExtension}.
+ * <p>
+ * Regions to be formatted with the master formatting strategy always have
+ * an offset aligned to the line start. Regions to be formatted with slave formatting
+ * strategies are aligned on partition boundaries.
+ *
+ * @see IFormattingStrategyExtension
+ * @since 3.0
+ */
+public class MultiPassContentFormatter : IContentFormatter, IContentFormatterExtension {
+
+    /**
+     * Position updater that shifts otherwise deleted positions to the next
+     * non-whitespace character. The length of the positions are truncated to
+     * one if the position was shifted.
+     */
+    protected class NonDeletingPositionUpdater : DefaultPositionUpdater {
+
+        /**
+         * Creates a new non-deleting position updater.
+         *
+         * @param category The position category to update its positions
+         */
+        public this(String category) {
+            super(category);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.DefaultPositionUpdater#notDeleted()
+         */
+        protected final bool notDeleted() {
+
+            if (fOffset < fPosition.offset && (fPosition.offset + fPosition.length < fOffset + fLength)) {
+
+                int offset= fOffset + fLength;
+                if (offset < fDocument.getLength()) {
+
+                    try {
+
+                        bool moved= false;
+                        char character= fDocument.getChar(offset);
+
+                        while (offset < fDocument.getLength() && Character.isWhitespace(character)) {
+
+                            moved= true;
+                            character= fDocument.getChar(offset++);
+                        }
+
+                        if (moved)
+                            offset--;
+
+                    } catch (BadLocationException exception) {
+                        // Can not happen
+                    }
+
+                    fPosition.offset= offset;
+                    fPosition.length= 0;
+                }
+            }
+            return true;
+        }
+    }
+
+    /** The master formatting strategy */
+    private IFormattingStrategyExtension fMaster= null;
+    /** The partitioning of this content formatter */
+    private const String fPartitioning;
+    /** The slave formatting strategies */
+    private const Map fSlaves;
+    /** The default content type */
+    private const String fType;
+
+    /**
+     * Creates a new content formatter.
+     *
+     * @param partitioning the document partitioning for this formatter
+     * @param type the default content type
+     */
+    public this(String partitioning, String type) {
+        fSlaves= new HashMap();
+
+        fPartitioning= partitioning;
+        fType= type;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IContentFormatterExtension#format(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.formatter.IFormattingContext)
+     */
+    public final void format(IDocument medium, IFormattingContext context) {
+
+        context.setProperty(stringcast(FormattingContextProperties.CONTEXT_MEDIUM), cast(Object)medium);
+
+        final Boolean document= cast(Boolean)context.getProperty(stringcast(FormattingContextProperties.CONTEXT_DOCUMENT));
+        if (document is null || !document.booleanValue()) {
+
+            final IRegion region= cast(IRegion)context.getProperty(stringcast(FormattingContextProperties.CONTEXT_REGION));
+            if (region !is null) {
+                try {
+                    formatMaster(context, medium, region.getOffset(), region.getLength());
+                } finally {
+                    formatSlaves(context, medium, region.getOffset(), region.getLength());
+                }
+            }
+        } else {
+            try {
+                formatMaster(context, medium, 0, medium.getLength());
+            } finally {
+                formatSlaves(context, medium, 0, medium.getLength());
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IContentFormatter#format(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IRegion)
+     */
+    public final void format(IDocument medium, IRegion region) {
+
+        final FormattingContext context= new FormattingContext();
+
+        context.setProperty(stringcast(FormattingContextProperties.CONTEXT_DOCUMENT), Boolean.FALSE);
+        context.setProperty(stringcast(FormattingContextProperties.CONTEXT_REGION), cast(Object)region);
+
+        format(medium, context);
+    }
+
+    /**
+     * Formats the document specified in the formatting context with the master
+     * formatting strategy.
+     * <p>
+     * The master formatting strategy covers all regions of the document. The
+     * offset of the region to be formatted is aligned on line start boundaries,
+     * whereas the end index of the region remains the same. For this formatting
+     * type the document partitioning is not taken into account.
+     *
+     * @param context The formatting context to use
+     * @param document The document to operate on
+     * @param offset The offset of the region to format
+     * @param length The length of the region to format
+     */
+    protected void formatMaster(IFormattingContext context, IDocument document, int offset, int length) {
+
+        try {
+
+            final int delta= offset - document.getLineInformationOfOffset(offset).getOffset();
+            offset -= delta;
+            length += delta;
+
+        } catch (BadLocationException exception) {
+            // Do nothing
+        }
+
+        if (fMaster !is null) {
+
+            context.setProperty(stringcast(FormattingContextProperties.CONTEXT_PARTITION), new TypedPosition(offset, length, fType));
+
+            fMaster.formatterStarts(context);
+            fMaster.format();
+            fMaster.formatterStops();
+        }
+    }
+
+    /**
+     * Formats the document specified in the formatting context with the
+     * formatting strategy registered for the content type.
+     * <p>
+     * For this formatting type only slave strategies are used. The region to be
+     * formatted is aligned on partition boundaries of the underlying content
+     * type. The exact formatting strategy is determined by the underlying
+     * content type of the document partitioning.
+     *
+     * @param context The formatting context to use
+     * @param document The document to operate on
+     * @param offset The offset of the region to format
+     * @param length The length of the region to format
+     * @param type The content type of the region to format
+     */
+    protected void formatSlave(IFormattingContext context, IDocument document, int offset, int length, String type) {
+
+        final IFormattingStrategyExtension strategy= cast(IFormattingStrategyExtension)fSlaves.get(type);
+        if (strategy !is null) {
+
+            context.setProperty(stringcast(FormattingContextProperties.CONTEXT_PARTITION), new TypedPosition(offset, length, type));
+
+            strategy.formatterStarts(context);
+            strategy.format();
+            strategy.formatterStops();
+        }
+    }
+
+    /**
+     * Formats the document specified in the formatting context with the slave
+     * formatting strategies.
+     * <p>
+     * For each content type of the region to be formatted in the document
+     * partitioning, the registered slave formatting strategy is used to format
+     * that particular region. The region to be formatted is aligned on
+     * partition boundaries of the underlying content type. If the content type
+     * is the document's default content type, nothing happens.
+     *
+     * @param context The formatting context to use
+     * @param document The document to operate on
+     * @param offset The offset of the region to format
+     * @param length The length of the region to format
+     */
+    protected void formatSlaves(IFormattingContext context, IDocument document, int offset, int length) {
+
+        Map partitioners= new HashMap(0);
+        try {
+
+            final ITypedRegion[] partitions= TextUtilities.computePartitioning(document, fPartitioning, offset, length, false);
+
+            if (!fType.equals(partitions[0].getType()))
+                partitions[0]= TextUtilities.getPartition(document, fPartitioning, partitions[0].getOffset(), false);
+
+            if (partitions.length > 1) {
+
+                if (!fType.equals(partitions[partitions.length - 1].getType()))
+                    partitions[partitions.length - 1]= TextUtilities.getPartition(document, fPartitioning, partitions[partitions.length - 1].getOffset(), false);
+            }
+
+            String type= null;
+            ITypedRegion partition= null;
+
+            partitioners= TextUtilities.removeDocumentPartitioners(document);
+
+            for (int index= partitions.length - 1; index >= 0; index--) {
+
+                partition= partitions[index];
+                type= partition.getType();
+
+                if (!fType.equals(type))
+                    formatSlave(context, document, partition.getOffset(), partition.getLength(), type);
+            }
+
+        } catch (BadLocationException exception) {
+            // Should not happen
+        } finally {
+            TextUtilities.addDocumentPartitioners(document, partitioners);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.formatter.IContentFormatter#getFormattingStrategy(java.lang.String)
+     */
+    public final IFormattingStrategy getFormattingStrategy(String type) {
+        return null;
+    }
+
+    /**
+     * Registers a master formatting strategy.
+     * <p>
+     * The strategy may already be registered with a certain content type as
+     * slave strategy. The master strategy is registered for the default content
+     * type of documents. If a master strategy has already been registered, it
+     * is overridden by the new one.
+     *
+     * @param strategy The master formatting strategy, must implement
+     *  {@link IFormattingStrategyExtension}
+     */
+    public final void setMasterStrategy(IFormattingStrategy strategy) {
+        Assert.isTrue( null !is cast(IFormattingStrategyExtension)strategy );
+        fMaster= cast(IFormattingStrategyExtension) strategy;
+    }
+
+    /**
+     * Registers a slave formatting strategy for a certain content type.
+     * <p>
+     * The strategy may already be registered as master strategy. An
+     * already registered slave strategy for the specified content type
+     * will be replaced. However, the same strategy may be registered with
+     * several content types. Slave strategies cannot be registered for the
+     * default content type of documents.
+     *
+     * @param strategy The slave formatting strategy
+     * @param type The content type to register this strategy with,
+     *  must implement {@link IFormattingStrategyExtension}
+     */
+    public final void setSlaveStrategy(IFormattingStrategy strategy, String type) {
+        Assert.isTrue( null !is cast(IFormattingStrategyExtension)strategy );
+        if (!fType.equals(type))
+            fSlaves.put(type, cast(Object)strategy);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/AbstractHyperlinkDetector.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector;
+
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenterExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.MultipleHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkManager; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlink; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension2; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkMessages; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlink; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IAdaptable;
+
+
+/**
+ * A hyperlink detector that can provide adapters through
+ * a context that can be set by the creator of this hyperlink
+ * detector.
+ * <p>
+ * Clients may subclass.
+ * </p>
+ * 
+ * @since 3.3
+ */
+public abstract class AbstractHyperlinkDetector : IHyperlinkDetector, IHyperlinkDetectorExtension {
+
+    /**
+     * The context of this hyperlink detector.
+     */
+    private IAdaptable fContext;
+
+    /**
+     * Sets this hyperlink detector's context which
+     * is responsible to provide the adapters.
+     * 
+     * @param context the context for this hyperlink detector
+     * @throws IllegalArgumentException if the context is <code>null</code>
+     * @throws IllegalStateException if this method is called more than once
+     */
+    public final void setContext(IAdaptable context)  {
+        Assert.isLegal(context !is null);
+        if (fContext !is null)
+            throw new IllegalStateException();
+        fContext= context;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension#dispose()
+     */
+    public void dispose() {
+        fContext= null;
+    }
+
+    /**
+     * Returns an object which is an instance of the given class
+     * and provides additional context for this hyperlink detector.
+     *
+     * @param adapterClass the adapter class to look up
+     * @return an instance that can be cast to the given class, 
+     *          or <code>null</code> if this object does not
+     *          have an adapter for the given class
+     */
+    protected final Object getAdapter(ClassInfo adapterClass) {
+        Assert.isLegal(adapterClass !is null);
+        if (fContext !is null)
+            return fContext.getAdapter(adapterClass);
+        return null;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/DefaultHyperlinkPresenter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,397 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter;
+
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenterExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.MultipleHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkManager; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlink; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension2; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkMessages; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlink; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceConverter;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextInputListener;
+import org.eclipse.jface.text.ITextPresentationListener;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension2;
+import org.eclipse.jface.text.ITextViewerExtension4;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+
+/**
+ * The default hyperlink presenter underlines the
+ * link and colors the line and the text with
+ * the given color.
+ * <p>
+ * It can only be used together with the {@link HyperlinkManager#FIRST}
+ * or the {@link HyperlinkManager#LONGEST_REGION_FIRST} hyperlink strategy.
+ * </p>
+ *
+ * @since 3.1
+ */
+public class DefaultHyperlinkPresenter : IHyperlinkPresenter, IHyperlinkPresenterExtension, ITextPresentationListener, ITextInputListener, IDocumentListener, IPropertyChangeListener {
+
+    /**
+     * A named preference that holds the color used for hyperlinks.
+     * <p>
+     * Value is of type <code>String</code>. A RGB color value encoded as a string
+     * using class <code>PreferenceConverter</code>
+     * </p>
+     *
+     * @see org.eclipse.jface.resource.StringConverter
+     * @see org.eclipse.jface.preference.PreferenceConverter
+     */
+    public const static String HYPERLINK_COLOR= "hyperlinkColor"; //$NON-NLS-1$
+
+
+    /** The text viewer. */
+    private ITextViewer fTextViewer;
+    /** The hand cursor. */
+    private Cursor fCursor;
+    /** The link color. */
+    private Color fColor;
+    /** The link color specification. May be <code>null</code>. */
+    private RGB fRGB;
+    /** Tells whether to dispose the color on uninstall. */
+    private bool fDisposeColor;
+    /** The currently active region. */
+    private IRegion fActiveRegion;
+    /** The currently active style range as position. */
+    private Position fRememberedPosition;
+    /** The optional preference store. May be <code>null</code>. */
+    private IPreferenceStore fPreferenceStore;
+
+
+    /**
+     * Creates a new default hyperlink presenter which uses
+     * {@link #HYPERLINK_COLOR} to read the color from the given preference store.
+     *
+     * @param store the preference store
+     */
+    public this(IPreferenceStore store) {
+        fPreferenceStore= store;
+        fDisposeColor= true;
+    }
+
+    /**
+     * Creates a new default hyperlink presenter.
+     *
+     * @param color the hyperlink color, to be disposed by the caller
+     */
+    public this(Color color) {
+        fDisposeColor= false;
+        fColor= color;
+    }
+
+    /**
+     * Creates a new default hyperlink presenter.
+     *
+     * @param color the hyperlink color
+     */
+    public this(RGB color) {
+        fRGB= color;
+        fDisposeColor= true;
+    }
+
+    /*
+     * @see org.eclipse.jdt.internal.ui.javaeditor.IHyperlinkControl#canShowMultipleHyperlinks()
+     */
+    public bool canShowMultipleHyperlinks() {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jdt.internal.ui.javaeditor.IHyperlinkControl#activate(org.eclipse.jdt.internal.ui.javaeditor.IHyperlink[])
+     */
+    public void showHyperlinks(IHyperlink[] hyperlinks) {
+        Assert.isLegal(hyperlinks !is null && hyperlinks.length is 1);
+        highlightRegion(hyperlinks[0].getHyperlinkRegion());
+        activateCursor();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 3.4
+     */
+    public bool canHideHyperlinks() {
+        return true;
+    }
+
+    /*
+     * @see org.eclipse.jdt.internal.ui.javaeditor.IHyperlinkControl#deactivate()
+     */
+    public void hideHyperlinks() {
+        repairRepresentation();
+        fRememberedPosition= null;
+    }
+
+    /*
+     * @see org.eclipse.jdt.internal.ui.javaeditor.IHyperlinkControl#install(org.eclipse.jface.text.ITextViewer)
+     */
+    public void install(ITextViewer textViewer) {
+        Assert.isNotNull(cast(Object)textViewer);
+        fTextViewer= textViewer;
+        fTextViewer.addTextInputListener(this);
+        if ( cast(ITextViewerExtension4)fTextViewer )
+            (cast(ITextViewerExtension4)fTextViewer).addTextPresentationListener(this);
+
+        StyledText text= fTextViewer.getTextWidget();
+        if (text !is null && !text.isDisposed()) {
+            if (fPreferenceStore !is null)
+                fColor= createColor(fPreferenceStore, HYPERLINK_COLOR, text.getDisplay());
+            else if (fRGB !is null)
+                fColor= new Color(text.getDisplay(), fRGB);
+        }
+
+        if (fPreferenceStore !is null)
+            fPreferenceStore.addPropertyChangeListener(this);
+    }
+
+    /*
+     * @see org.eclipse.jdt.internal.ui.javaeditor.IHyperlinkControl#uninstall()
+     */
+    public void uninstall() {
+        fTextViewer.removeTextInputListener(this);
+        IDocument document= fTextViewer.getDocument();
+        if (document !is null)
+            document.removeDocumentListener(this);
+
+        if (fColor !is null) {
+            if (fDisposeColor)
+                fColor.dispose();
+            fColor= null;
+        }
+
+        if (fCursor !is null) {
+            fCursor.dispose();
+            fCursor= null;
+        }
+
+        if ( cast(ITextViewerExtension4)fTextViewer )
+            (cast(ITextViewerExtension4)fTextViewer).removeTextPresentationListener(this);
+        fTextViewer= null;
+
+        if (fPreferenceStore !is null)
+            fPreferenceStore.removePropertyChangeListener(this);
+    }
+
+    public void setColor(Color color) {
+        Assert.isNotNull(cast(Object)fTextViewer);
+        fColor= color;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextPresentationListener#applyTextPresentation(org.eclipse.jface.text.TextPresentation)
+     */
+    public void applyTextPresentation(TextPresentation textPresentation) {
+        if (fActiveRegion is null)
+            return;
+        IRegion region= textPresentation.getExtent();
+        if (fActiveRegion.getOffset() + fActiveRegion.getLength() >= region.getOffset() && region.getOffset() + region.getLength() > fActiveRegion.getOffset()) {
+            StyleRange styleRange= new StyleRange(fActiveRegion.getOffset(), fActiveRegion.getLength(), fColor, null);
+            styleRange.underline= true;
+            textPresentation.mergeStyleRange(styleRange);
+        }
+    }
+
+    private void highlightRegion(IRegion region) {
+
+        if ((cast(Object)region).opEquals(cast(Object)fActiveRegion))
+            return;
+
+        repairRepresentation();
+
+        StyledText text= fTextViewer.getTextWidget();
+        if (text is null || text.isDisposed())
+            return;
+
+        // Invalidate region is> apply text presentation
+        fActiveRegion= region;
+        if ( cast(ITextViewerExtension2)fTextViewer )
+            (cast(ITextViewerExtension2)fTextViewer).invalidateTextPresentation(region.getOffset(), region.getLength());
+        else
+            fTextViewer.invalidateTextPresentation();
+    }
+
+    private void activateCursor() {
+        StyledText text= fTextViewer.getTextWidget();
+        if (text is null || text.isDisposed())
+            return;
+        Display display= text.getDisplay();
+        if (fCursor is null)
+            fCursor= new Cursor(display, SWT.CURSOR_HAND);
+        text.setCursor(fCursor);
+    }
+
+    private void resetCursor() {
+        StyledText text= fTextViewer.getTextWidget();
+        if (text !is null && !text.isDisposed())
+            text.setCursor(null);
+
+        if (fCursor !is null) {
+            fCursor.dispose();
+            fCursor= null;
+        }
+    }
+
+    private void repairRepresentation() {
+
+        if (fActiveRegion is null)
+            return;
+
+        int offset= fActiveRegion.getOffset();
+        int length= fActiveRegion.getLength();
+        fActiveRegion= null;
+
+        resetCursor();
+
+        // Invalidate is> remove applied text presentation
+        if ( cast(ITextViewerExtension2)fTextViewer )
+            (cast(ITextViewerExtension2) fTextViewer).invalidateTextPresentation(offset, length);
+        else
+            fTextViewer.invalidateTextPresentation();
+
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
+     */
+    public void documentAboutToBeChanged(DocumentEvent event) {
+        if (fActiveRegion !is null) {
+            fRememberedPosition= new Position(fActiveRegion.getOffset(), fActiveRegion.getLength());
+            try {
+                event.getDocument().addPosition(fRememberedPosition);
+            } catch (BadLocationException x) {
+                fRememberedPosition= null;
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
+     */
+    public void documentChanged(DocumentEvent event) {
+        if (fRememberedPosition !is null) {
+            if (!fRememberedPosition.isDeleted()) {
+                event.getDocument().removePosition(fRememberedPosition);
+                fActiveRegion= new Region(fRememberedPosition.getOffset(), fRememberedPosition.getLength());
+            } else {
+                fActiveRegion= new Region(event.getOffset(), event.getLength());
+            }
+            fRememberedPosition= null;
+
+            StyledText widget= fTextViewer.getTextWidget();
+            if (widget !is null && !widget.isDisposed()) {
+                widget.getDisplay().asyncExec(new class()  Runnable {
+                    public void run() {
+                        hideHyperlinks();
+                    }
+                });
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
+     */
+    public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+        if (oldInput is null)
+            return;
+        hideHyperlinks();
+        oldInput.removeDocumentListener(this);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
+     */
+    public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
+        if (newInput is null)
+            return;
+        newInput.addDocumentListener(this);
+    }
+
+    /**
+     * Creates a color from the information stored in the given preference store.
+     *
+     * @param store the preference store
+     * @param key the key
+     * @param display the display
+     * @return the color or <code>null</code> if there is no such information available
+     */
+    private Color createColor(IPreferenceStore store, String key, Display display) {
+
+        RGB rgb= null;
+
+        if (store.contains(key)) {
+
+            if (store.isDefault(key))
+                rgb= PreferenceConverter.getDefaultColor(store, key);
+            else
+                rgb= PreferenceConverter.getColor(store, key);
+
+            if (rgb !is null)
+                return new Color(display, rgb);
+        }
+
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
+     */
+    public void propertyChange(PropertyChangeEvent event) {
+        if (!HYPERLINK_COLOR.equals(event.getProperty()))
+            return;
+
+        if (fDisposeColor && fColor !is null && !fColor.isDisposed())
+            fColor.dispose();
+        fColor= null;
+
+        StyledText textWidget= fTextViewer.getTextWidget();
+        if (textWidget !is null && !textWidget.isDisposed())
+            fColor= createColor(fPreferenceStore, HYPERLINK_COLOR, textWidget.getDisplay());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/HyperlinkManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,614 @@
+/*******************************************************************************
+ * 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
+ *     Steffen Pingel <steffen.pingel@tasktop.com> (Tasktop Technologies Inc.) - [navigation] hyperlink decoration is not erased when mouse is moved out of Text widget - https://bugs.eclipse.org/bugs/show_bug.cgi?id=100278
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.hyperlink.HyperlinkManager;
+
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenterExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.MultipleHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlink; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension2; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkMessages; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlink; // packageimport
+
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextEvent;
+
+
+/**
+ * Default implementation of a hyperlink manager.
+ *
+ * @since 3.1
+ */
+public class HyperlinkManager : ITextListener, Listener, KeyListener, MouseListener, MouseMoveListener, FocusListener, MouseTrackListener {
+
+    /**
+     * Detection strategy.
+     */
+    public static final class DETECTION_STRATEGY {
+
+        String fName;
+
+        private this(String name) {
+            fName= name;
+        }
+
+        /*
+         * @see java.lang.Object#toString()
+         */
+        public override String toString() {
+            return fName;
+        }
+    }
+
+
+    /**
+     * The first detected hyperlink is passed to the
+     * hyperlink presenter and no further detector
+     * is consulted.
+     */
+    private static DETECTION_STRATEGY FIRST_;
+    public static DETECTION_STRATEGY FIRST(){
+        if( FIRST_ is null ){
+            synchronized( HyperlinkManager.classinfo ){
+                if( FIRST_ is null ){
+                    FIRST_ = new DETECTION_STRATEGY("first"); //$NON-NLS-1$
+                }
+            }
+        }
+        return FIRST_;
+    }
+
+    /**
+     * All detected hyperlinks from all detectors are collected
+     * and passed to the hyperlink presenter.
+     * <p>
+     * This strategy is only allowed if {@link IHyperlinkPresenter#canShowMultipleHyperlinks()}
+     * returns <code>true</code>.
+     * </p>
+     */
+    private static DETECTION_STRATEGY ALL_;
+    public static DETECTION_STRATEGY ALL(){
+        if( ALL_ is null ){
+            synchronized( HyperlinkManager.classinfo ){
+                if( ALL_ is null ){
+                    ALL_ = new DETECTION_STRATEGY("all"); //$NON-NLS-1$
+                }
+            }
+        }
+        return ALL_;
+    }
+
+    /**
+     * All detected hyperlinks from all detectors are collected
+     * and all those with the longest region are passed to the
+     * hyperlink presenter.
+     * <p>
+     * This strategy is only allowed if {@link IHyperlinkPresenter#canShowMultipleHyperlinks()}
+     * returns <code>true</code>.
+     * </p>
+     */
+    private static DETECTION_STRATEGY LONGEST_REGION_ALL_;
+    public static DETECTION_STRATEGY LONGEST_REGION_ALL(){
+        if( LONGEST_REGION_ALL_ is null ){
+            synchronized( HyperlinkManager.classinfo ){
+                if( LONGEST_REGION_ALL_ is null ){
+                    LONGEST_REGION_ALL_ = new DETECTION_STRATEGY("all with same longest region"); //$NON-NLS-1$
+                }
+            }
+        }
+        return LONGEST_REGION_ALL_;
+    }
+
+    /**
+     * All detected hyperlinks from all detectors are collected
+     * and form all those with the longest region only the first
+     * one is passed to the hyperlink presenter.
+     */
+    private static DETECTION_STRATEGY LONGEST_REGION_FIRST_;
+    public static DETECTION_STRATEGY LONGEST_REGION_FIRST(){
+        if( LONGEST_REGION_FIRST_ is null ){
+            synchronized( HyperlinkManager.classinfo ){
+                if( LONGEST_REGION_FIRST_ is null ){
+                    LONGEST_REGION_FIRST_ = new DETECTION_STRATEGY("first with longest region"); //$NON-NLS-1$
+                }
+            }
+        }
+        return LONGEST_REGION_FIRST_;
+    }
+
+
+    /** The text viewer on which this hyperlink manager works. */
+    private ITextViewer fTextViewer;
+    /** The session is active. */
+    private bool fActive;
+    /** The key modifier mask. */
+    private int fHyperlinkStateMask;
+    /**
+     * The active key modifier mask.
+     * @since 3.3
+     */
+    private int fActiveHyperlinkStateMask;
+    /** The active hyperlinks. */
+    private IHyperlink[] fActiveHyperlinks;
+    /** The hyperlink detectors. */
+    private IHyperlinkDetector[] fHyperlinkDetectors;
+    /** The hyperlink presenter. */
+    private IHyperlinkPresenter fHyperlinkPresenter;
+    /** The detection strategy. */
+    private const DETECTION_STRATEGY fDetectionStrategy;
+
+
+    /**
+     * Creates a new hyperlink manager.
+     *
+     * @param detectionStrategy the detection strategy one of {{@link #ALL}, {@link #FIRST}, {@link #LONGEST_REGION_ALL}, {@link #LONGEST_REGION_FIRST}}
+     */
+    public this(DETECTION_STRATEGY detectionStrategy) {
+        Assert.isNotNull(detectionStrategy);
+        fDetectionStrategy= detectionStrategy;
+    }
+
+    /**
+     * Installs this hyperlink manager with the given arguments.
+     *
+     * @param textViewer the text viewer
+     * @param hyperlinkPresenter the hyperlink presenter
+     * @param hyperlinkDetectors the array of hyperlink detectors, must not be empty
+     * @param eventStateMask the SWT event state mask to activate hyperlink mode
+     */
+    public void install(ITextViewer textViewer, IHyperlinkPresenter hyperlinkPresenter, IHyperlinkDetector[] hyperlinkDetectors, int eventStateMask) {
+        Assert.isNotNull(cast(Object)textViewer);
+        Assert.isNotNull(cast(Object)hyperlinkPresenter);
+        fTextViewer= textViewer;
+        fHyperlinkPresenter= hyperlinkPresenter;
+        Assert.isLegal(fHyperlinkPresenter.canShowMultipleHyperlinks() || fDetectionStrategy is FIRST || fDetectionStrategy is LONGEST_REGION_FIRST);
+        setHyperlinkDetectors(hyperlinkDetectors);
+        setHyperlinkStateMask(eventStateMask);
+
+        StyledText text= fTextViewer.getTextWidget();
+        if (text is null || text.isDisposed())
+            return;
+
+        text.getDisplay().addFilter(SWT.KeyUp, this);
+        text.addKeyListener(this);
+        text.addMouseListener(this);
+        text.addMouseMoveListener(this);
+        text.addFocusListener(this);
+        text.addMouseTrackListener(this);
+
+        fTextViewer.addTextListener(this);
+
+        fHyperlinkPresenter.install(fTextViewer);
+    }
+
+    /**
+     * Sets the hyperlink detectors for this hyperlink manager.
+     * <p>
+     * It is allowed to call this method after this
+     * hyperlink manger has been installed.
+     * </p>
+     *
+     * @param hyperlinkDetectors and array of hyperlink detectors, must not be empty
+     */
+    public void setHyperlinkDetectors(IHyperlinkDetector[] hyperlinkDetectors) {
+        Assert.isTrue(hyperlinkDetectors !is null && hyperlinkDetectors.length > 0);
+        if (fHyperlinkDetectors is null){
+            fHyperlinkDetectors= hyperlinkDetectors;
+        }
+        else {
+            synchronized (/+fHyperlinkDetectors+/this) {
+                fHyperlinkDetectors= hyperlinkDetectors;
+            }
+        }
+    }
+
+    /**
+     * Sets the SWT event state mask which in combination
+     * with the left mouse button triggers the hyperlink mode.
+     * <p>
+     * It is allowed to call this method after this
+     * hyperlink manger has been installed.
+     * </p>
+     *
+     * @param eventStateMask the SWT event state mask to activate hyperlink mode
+     */
+    public void setHyperlinkStateMask(int eventStateMask) {
+        fHyperlinkStateMask= eventStateMask;
+    }
+
+    /**
+     * Uninstalls this hyperlink manager.
+     */
+    public void uninstall() {
+        deactivate();
+
+        StyledText text= fTextViewer.getTextWidget();
+        if (text !is null && !text.isDisposed()) {
+            text.removeKeyListener(this);
+            text.getDisplay().removeFilter(SWT.KeyUp, this);
+            text.removeMouseListener(this);
+            text.removeMouseMoveListener(this);
+            text.removeFocusListener(this);
+            text.removeMouseTrackListener(this);
+        }
+        fTextViewer.removeTextListener(this);
+
+        fHyperlinkPresenter.uninstall();
+
+        fHyperlinkPresenter= null;
+        fTextViewer= null;
+        fHyperlinkDetectors= null;
+    }
+
+    /**
+     * Deactivates the currently shown hyperlinks.
+     */
+    protected void deactivate() {
+        fHyperlinkPresenter.hideHyperlinks();
+        fActive= false;
+    }
+
+    /**
+     * Finds hyperlinks at the current offset.
+     *
+     * @return the hyperlinks or <code>null</code> if none.
+     */
+    protected IHyperlink[] findHyperlinks() {
+        int offset= getCurrentTextOffset();
+        if (offset is -1)
+            return null;
+
+        bool canShowMultipleHyperlinks= fHyperlinkPresenter.canShowMultipleHyperlinks();
+        IRegion region= new Region(offset, 0);
+        List allHyperlinks= new ArrayList(fHyperlinkDetectors.length * 2);
+        synchronized (/+fHyperlinkDetectors+/this) {
+            for (int i= 0, length= fHyperlinkDetectors.length; i < length; i++) {
+                IHyperlinkDetector detector= fHyperlinkDetectors[i];
+                if (detector is null)
+                    continue;
+
+                if ( cast(IHyperlinkDetectorExtension2)detector ) {
+                    int stateMask= (cast(IHyperlinkDetectorExtension2)detector).getStateMask();
+                    if (stateMask !is -1 && stateMask !is fActiveHyperlinkStateMask)
+                        continue;
+                    else if (stateMask is -1 && fActiveHyperlinkStateMask !is fHyperlinkStateMask)
+                    continue;
+                } else if (fActiveHyperlinkStateMask !is fHyperlinkStateMask)
+                    continue;
+
+                IHyperlink[] hyperlinks= detector.detectHyperlinks(fTextViewer, region, canShowMultipleHyperlinks);
+                if (hyperlinks is null)
+                    continue;
+
+                Assert.isLegal(hyperlinks.length > 0);
+
+                if (fDetectionStrategy is FIRST) {
+                    if (hyperlinks.length is 1)
+                        return hyperlinks;
+                    return [hyperlinks[0]];
+                }
+                allHyperlinks.addAll(Arrays.asList(arraycast!(Object)(hyperlinks)));
+            }
+        }
+
+        if (allHyperlinks.isEmpty())
+            return null;
+
+        if (fDetectionStrategy !is ALL) {
+            int maxLength= computeLongestHyperlinkLength(allHyperlinks);
+            Iterator iter= (new ArrayList(allHyperlinks)).iterator();
+            while (iter.hasNext()) {
+                IHyperlink hyperlink= cast(IHyperlink)iter.next();
+                if (hyperlink.getHyperlinkRegion().getLength() < maxLength)
+                    allHyperlinks.remove(cast(Object)hyperlink);
+            }
+        }
+
+        if (fDetectionStrategy is LONGEST_REGION_FIRST)
+            return [cast(IHyperlink)allHyperlinks.get(0)];
+
+        return arraycast!(IHyperlink)(allHyperlinks.toArray());
+
+    }
+
+    /**
+     * Computes the length of the longest detected
+     * hyperlink.
+     *
+     * @param hyperlinks
+     * @return the length of the longest detected
+     */
+    protected int computeLongestHyperlinkLength(List hyperlinks) {
+        Assert.isLegal(hyperlinks !is null && !hyperlinks.isEmpty());
+        Iterator iter= hyperlinks.iterator();
+        int length= Integer.MIN_VALUE;
+        while (iter.hasNext()) {
+            IRegion region= (cast(IHyperlink)iter.next()).getHyperlinkRegion();
+            if (region.getLength() < length)
+                continue;
+            length= region.getLength();
+        }
+        return length;
+    }
+
+    /**
+     * Returns the current text offset.
+     *
+     * @return the current text offset
+     */
+    protected int getCurrentTextOffset() {
+
+        try {
+            StyledText text= fTextViewer.getTextWidget();
+            if (text is null || text.isDisposed())
+                return -1;
+
+            Display display= text.getDisplay();
+            Point absolutePosition= display.getCursorLocation();
+            Point relativePosition= text.toControl(absolutePosition);
+
+            int widgetOffset= text.getOffsetAtLocation(relativePosition);
+            Point p= text.getLocationAtOffset(widgetOffset);
+            if (p.x > relativePosition.x)
+                widgetOffset--;
+
+            if ( cast(ITextViewerExtension5)fTextViewer ) {
+                ITextViewerExtension5 extension= cast(ITextViewerExtension5)fTextViewer;
+                return extension.widgetOffset2ModelOffset(widgetOffset);
+            }
+
+            return widgetOffset + fTextViewer.getVisibleRegion().getOffset();
+
+        } catch (IllegalArgumentException e) {
+            return -1;
+        }
+    }
+
+    /*
+     * @see org.eclipse.swt.events.KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)
+     */
+    public void keyPressed(KeyEvent event) {
+
+        if (fActive) {
+            deactivate();
+            return;
+        }
+
+        if (!isRegisteredStateMask(event.keyCode)) {
+            deactivate();
+            return;
+        }
+
+        fActive= true;
+        fActiveHyperlinkStateMask= event.keyCode;
+
+//          removed for #25871 (hyperlinks could interact with typing)
+//
+//          ITextViewer viewer= getSourceViewer();
+//          if (viewer is null)
+//              return;
+//
+//          IRegion region= getCurrentTextRegion(viewer);
+//          if (region is null)
+//              return;
+//
+//          highlightRegion(viewer, region);
+//          activateCursor(viewer);
+    }
+
+    /*
+     * @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent)
+     */
+    public void keyReleased(KeyEvent event) {
+    }
+
+    /*
+     * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
+     */
+    public void mouseDoubleClick(MouseEvent e) {
+
+    }
+
+    /*
+     * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
+     */
+    public void mouseDown(MouseEvent event) {
+
+        if (!fActive)
+            return;
+
+        if (event.stateMask !is fActiveHyperlinkStateMask) {
+            deactivate();
+            return;
+        }
+
+        if (event.button !is 1) {
+            deactivate();
+            return;
+        }
+    }
+
+    /*
+     * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
+     */
+    public void mouseUp(MouseEvent e) {
+
+        if (!fActive) {
+            fActiveHyperlinks= null;
+            return;
+        }
+
+        if (e.button !is 1)
+            fActiveHyperlinks= null;
+
+        deactivate();
+
+        if (fActiveHyperlinks !is null)
+            fActiveHyperlinks[0].open();
+    }
+
+    /*
+     * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
+     */
+    public void mouseMove(MouseEvent event) {
+        if ( cast(IHyperlinkPresenterExtension)fHyperlinkPresenter ) {
+            if (!(cast(IHyperlinkPresenterExtension)fHyperlinkPresenter).canHideHyperlinks())
+                return;
+        }
+
+        if (!isRegisteredStateMask(event.stateMask)) {
+            if (fActive)
+                deactivate();
+
+            return;
+        }
+
+        fActive= true;
+        fActiveHyperlinkStateMask= event.stateMask;
+
+        StyledText text= fTextViewer.getTextWidget();
+        if (text is null || text.isDisposed()) {
+            deactivate();
+            return;
+        }
+
+        if ((event.stateMask & SWT.BUTTON1) !is 0 && text.getSelectionCount() !is 0) {
+            deactivate();
+            return;
+        }
+
+        fActiveHyperlinks= findHyperlinks();
+        if (fActiveHyperlinks is null || fActiveHyperlinks.length is 0) {
+            fHyperlinkPresenter.hideHyperlinks();
+            return;
+        }
+
+        fHyperlinkPresenter.showHyperlinks(fActiveHyperlinks);
+
+    }
+
+    /**
+     * Checks whether the given state mask is registered.
+     *
+     * @param stateMask
+     * @return <code>true</code> if a detector is registered for the given state mask
+     * @since 3.3
+     */
+    private bool isRegisteredStateMask(int stateMask) {
+        if (stateMask is fHyperlinkStateMask)
+            return true;
+
+        synchronized (/+fHyperlinkDetectors+/this) {
+            for (int i= 0; i < fHyperlinkDetectors.length; i++) {
+                if (cast(IHyperlinkDetectorExtension2)fHyperlinkDetectors[i] ) {
+                    if (stateMask is (cast(IHyperlinkDetectorExtension2)fHyperlinkDetectors[i]).getStateMask())
+                        return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.swt.events.FocusListener#focusGained(org.eclipse.swt.events.FocusEvent)
+     */
+    public void focusGained(FocusEvent e) {}
+
+    /*
+     * @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent)
+     */
+    public void focusLost(FocusEvent event) {
+        deactivate();
+    }
+
+    /*
+     * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+     * @since 3.2
+     */
+    public void handleEvent(Event event) {
+        //key up
+        deactivate();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextListener#textChanged(TextEvent)
+     * @since 3.2
+     */
+    public void textChanged(TextEvent event) {
+        if (event.getDocumentEvent() !is null)
+            deactivate();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 3.4
+     */
+    public void mouseExit(MouseEvent e) {
+        if ( cast(IHyperlinkPresenterExtension)fHyperlinkPresenter ) {
+            if (!(cast(IHyperlinkPresenterExtension)fHyperlinkPresenter).canHideHyperlinks())
+                return;
+        }
+        deactivate();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 3.4
+     */
+    public void mouseEnter(MouseEvent e) {
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 3.4
+     */
+    public void mouseHover(MouseEvent e) {
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/HyperlinkMessages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.hyperlink.HyperlinkMessages;
+
+import java.lang.all;
+
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+
+
+/**
+ * Helper class to get NLSed messages.
+ *
+ * @since 3.4
+ */
+class HyperlinkMessages {
+    private static ResourceBundle RESOURCE_BUNDLE_;//= ResourceBundle.getBundle(BUNDLE_NAME);
+    private static ResourceBundle RESOURCE_BUNDLE(){
+        if( RESOURCE_BUNDLE_ is null ){
+            synchronized(HyperlinkMessages.classinfo ){
+                if( RESOURCE_BUNDLE_ is null ){
+                    RESOURCE_BUNDLE_ = ResourceBundle.getBundle(
+                        getImportData!("org.eclipse.jface.text.hyperlink.HyperlinkMessages.properties"));
+                }
+            }
+        }
+        return RESOURCE_BUNDLE_;
+    }
+
+    private this() {
+    }
+
+    /**
+     * Gets a string from the resource bundle.
+     *
+     * @param key the string used to get the bundle value, must not be
+     *            <code>null</code>
+     * @return the string from the resource bundle
+     */
+    public static String getString(String key) {
+        try {
+            return RESOURCE_BUNDLE.getString(key);
+        } catch (MissingResourceException e) {
+            return '!' ~ key ~ '!';
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlink.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.hyperlink.IHyperlink;
+
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenterExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.MultipleHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkManager; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlink; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension2; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkMessages; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.jface.text.IRegion;
+
+
+/**
+ * Represents a hyperlink.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IHyperlink {
+
+    /**
+     * The region covered by this type of hyperlink.
+     *
+     * @return the hyperlink region
+     */
+    IRegion getHyperlinkRegion();
+
+    /**
+     * Optional label for this type of hyperlink.
+     * <p>
+     * This type label can be used by {@link IHyperlinkPresenter}s
+     * which show several hyperlinks at once.
+     * </p>
+     *
+     * @return the type label or <code>null</code> if none
+     */
+    String getTypeLabel();
+
+    /**
+     * Optional text for this hyperlink.
+     * <p>
+     * This can be used in situations where there are
+     * several targets for the same hyperlink location.
+     * </p>
+     *
+     * @return the text or <code>null</code> if none
+     */
+    String getHyperlinkText();
+
+    /**
+     * Opens the given hyperlink.
+     */
+    void open();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlinkDetector.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
+
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenterExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.MultipleHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkManager; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlink; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension2; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkMessages; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlink; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.source.SourceViewerConfiguration;
+
+
+/**
+ * A hyperlink detector tries to find a hyperlink at
+ * a given location in a given text viewer.
+ * <p>
+ * In order to provide backward compatibility for clients of <code>IHyperlinkDetector</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link IHyperlinkDetectorExtension} since version 3.3,
+ *      adds the ability to dispose a hyperlink detector
+ * </li>
+ * <li>{@link IHyperlinkDetectorExtension2} since version 3.3,
+ *      adds the ability to specify the state mask of the modifier
+ *      keys that need to be pressed for this hyperlink detector
+ * </li>
+ * </ul></p>
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see SourceViewerConfiguration#getHyperlinkDetectors(org.eclipse.jface.text.source.ISourceViewer)
+ * @since 3.1
+ */
+public interface IHyperlinkDetector {
+
+    /**
+     * Tries to detect hyperlinks for the given region in
+     * the given text viewer and returns them.
+     * <p>
+     * In most of the cases only one hyperlink should be returned.
+     * </p>
+     * @param textViewer the text viewer on which the hover popup should be shown
+     * @param region the text range in the text viewer which is used to detect the hyperlinks
+     * @param canShowMultipleHyperlinks tells whether the caller is able to show multiple links
+     *                      to the user.
+     *                      If <code>true</code> {@link IHyperlink#open()} should directly open
+     *                          the link and not show any additional UI to select from a list.
+     *                      If <code>false</code> this method should only return one hyperlink
+     *                          which upon {@link IHyperlink#open()} may allow to select from a list.
+     * @return the hyperlinks or <code>null</code> if no hyperlink was detected
+     */
+    IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, bool canShowMultipleHyperlinks);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlinkDetectorExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension;
+
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenterExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.MultipleHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkManager; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlink; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension2; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkMessages; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlink; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Extends {@link IHyperlinkDetector} with ability
+ * to dispose a hyperlink detector.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ * 
+ * @since 3.3
+ */
+public interface IHyperlinkDetectorExtension {
+
+    /**
+     * Disposes this hyperlink detector.
+     */
+    void dispose();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlinkDetectorExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension2;
+
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenterExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.MultipleHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkManager; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlink; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkMessages; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlink; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Extends {@link IHyperlinkDetector} with ability
+ * to specify the state mask of the modifier keys that
+ * need to be pressed for this hyperlink detector.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ * 
+ * @since 3.3
+ */
+public interface IHyperlinkDetectorExtension2 {
+
+    /**
+     * Returns the state mask of the modifier keys that
+     * need to be pressed for this hyperlink detector.
+     * 
+     * @return the state mask
+     */
+    int getStateMask();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlinkPresenter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.hyperlink.IHyperlinkPresenter;
+
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenterExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.MultipleHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkManager; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlink; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension2; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkMessages; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlink; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * A hyperlink presenter shows hyperlinks on the installed text viewer
+ * and allows to pick one on of the hyperlinks.
+ * <p>
+ * In order to provide backward compatibility for clients of <code>IHyperlinkDetector</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link IHyperlinkPresenterExtension} since version 3.4,
+ *      adds the ability to query  whether the currently shown hyperlinks
+ *      can be hidden.
+ * </li>
+ * </ul></p>
+ * <p>
+ * Clients may implement this interface. A default implementation is provided
+ * through {@link org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter}.
+ * </p>
+ *
+ * @see IHyperlinkPresenterExtension
+ * @since 3.1
+ */
+public interface IHyperlinkPresenter {
+
+    /**
+     * Tells whether this presenter is able to handle
+     * more than one hyperlink.
+     *
+     * @return <code>true</code> if this presenter can handle more than one hyperlink
+     */
+    bool canShowMultipleHyperlinks();
+
+    /**
+     * Tells this hyperlink presenter to show the given
+     * hyperlinks on the installed text viewer.
+     *
+     * @param hyperlinks the hyperlinks to show
+     * @throws IllegalArgumentException if
+     *          <ul>
+     *              <li><code>hyperlinks</code> is empty</li>
+     *              <li>{@link #canShowMultipleHyperlinks()} returns <code>false</code> and <code>hyperlinks</code> contains more than one element</li>
+     *          </ul>
+     */
+    void showHyperlinks(IHyperlink[] hyperlinks) ;
+
+    /**
+     * Tells this hyperlink presenter to hide the hyperlinks
+     * requested to be shown by {@link #showHyperlinks(IHyperlink[])}.
+     */
+    void hideHyperlinks();
+
+    /**
+     * Installs this hyperlink presenter on the given text viewer.
+     *
+     * @param textViewer the text viewer
+     */
+    void install(ITextViewer textViewer);
+
+    /**
+     * Uninstalls this hyperlink presenter.
+     */
+    void uninstall();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/IHyperlinkPresenterExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.hyperlink.IHyperlinkPresenterExtension;
+
+import org.eclipse.jface.text.hyperlink.MultipleHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkManager; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlink; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension2; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkMessages; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlink; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Extends {@link IHyperlinkPresenter} with ability
+ * to query whether the currently shown hyperlinks
+ * can be hidden.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ * 
+ * @since 3.4
+ */
+public interface IHyperlinkPresenterExtension {
+
+    /**
+     * Tells whether the currently shown hyperlinks
+     * can be hidden.
+     * 
+     * @return <code>true</code> if the hyperlink manager can hide the current hyperlinks
+     */
+    bool canHideHyperlinks();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/MultipleHyperlinkPresenter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,756 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.hyperlink.MultipleHyperlinkPresenter;
+
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenterExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkManager; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlink; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension2; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkMessages; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlink; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.AbstractInformationControl;
+import org.eclipse.jface.text.AbstractInformationControlManager;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IInformationControlExtension2;
+import org.eclipse.jface.text.IInformationControlExtension3;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextHover;
+import org.eclipse.jface.text.ITextHoverExtension;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.IWidgetTokenKeeper;
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension;
+import org.eclipse.jface.text.IWidgetTokenOwner;
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension;
+import org.eclipse.jface.text.JFaceTextUtil;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.util.Geometry;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+
+
+/**
+ * A hyperlink presenter capable of showing multiple hyperlinks in a hover.
+ *
+ * @since 3.4
+ */
+public class MultipleHyperlinkPresenter : DefaultHyperlinkPresenter {
+
+    /**
+     * An information control capable of showing a list of hyperlinks. The hyperlinks can be opened.
+     */
+    private static class LinkListInformationControl : AbstractInformationControl , IInformationControlExtension2 {
+
+        private static final class LinkContentProvider : IStructuredContentProvider {
+
+            /*
+             * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
+             */
+            public Object[] getElements(Object inputElement) {
+                return arrayFromObject!(Object)( inputElement);
+            }
+
+            /*
+             * @see org.eclipse.jface.viewers.IContentProvider#dispose()
+             */
+            public void dispose() {
+            }
+
+            /*
+             * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
+             */
+            public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+            }
+        }
+
+        private static final class LinkLabelProvider : ColumnLabelProvider {
+            /*
+             * @see org.eclipse.jface.viewers.ColumnLabelProvider#getText(java.lang.Object)
+             */
+            public String getText(Object element) {
+                IHyperlink link= cast(IHyperlink)element;
+                String text= link.getHyperlinkText();
+                if (text !is null)
+                    return text;
+                return HyperlinkMessages.getString("LinkListInformationControl.unknownLink"); //$NON-NLS-1$
+            }
+        }
+
+        private const MultipleHyperlinkHoverManager fManager;
+
+        private IHyperlink[] fInput;
+        private Composite fParent;
+        private Table fTable;
+
+        private Color fForegroundColor;
+        private Color fBackgroundColor;
+
+
+        /**
+         * Creates a link list information control with the given shell as parent.
+         *
+         * @param parentShell the parent shell
+         * @param manager the hover manager
+         * @param foregroundColor the foreground color, must not be disposed
+         * @param backgroundColor the background color, must not be disposed
+         */
+        public this(Shell parentShell, MultipleHyperlinkHoverManager manager, Color foregroundColor, Color backgroundColor) {
+            super(parentShell, false);
+            fManager= manager;
+            fForegroundColor= foregroundColor;
+            fBackgroundColor= backgroundColor;
+            create();
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IInformationControl#setInformation(java.lang.String)
+         */
+        public void setInformation(String information) {
+            //replaced by IInformationControlExtension2#setInput(java.lang.Object)
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IInformationControlExtension2#setInput(java.lang.Object)
+         */
+        public void setInput(Object input) {
+            fInput= arrayFromObject!(IHyperlink)( input);
+            deferredCreateContent(fParent);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.AbstractInformationControl#createContent(org.eclipse.swt.widgets.Composite)
+         */
+        protected void createContent(Composite parent) {
+            fParent= parent;
+            if ("win32".equals(SWT.getPlatform())) { //$NON-NLS-1$
+                GridLayout layout= new GridLayout();
+                layout.marginWidth= 0;
+                layout.marginRight= 4;
+                fParent.setLayout(layout);
+            }
+            fParent.setForeground(fForegroundColor);
+            fParent.setBackground(fBackgroundColor);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.AbstractInformationControl#computeSizeHint()
+         */
+        public Point computeSizeHint() {
+            Point preferedSize= getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+
+            Point constraints= getSizeConstraints();
+            if (constraints is null)
+                return preferedSize;
+
+            if (fTable.getVerticalBar() is null || fTable.getHorizontalBar() is null)
+                return Geometry.min(constraints, preferedSize);
+
+            int scrollBarWidth= fTable.getVerticalBar().getSize().x;
+            int scrollBarHeight= fTable.getHorizontalBar().getSize().y;
+
+            int width;
+            if (preferedSize.y - scrollBarHeight <= constraints.y) {
+                width= preferedSize.x - scrollBarWidth;
+                fTable.getVerticalBar().setVisible(false);
+            } else {
+                width= Math.min(preferedSize.x, constraints.x);
+            }
+
+            int height;
+            if (preferedSize.x - scrollBarWidth <= constraints.x) {
+                height= preferedSize.y - scrollBarHeight;
+                fTable.getHorizontalBar().setVisible(false);
+            } else {
+                height= Math.min(preferedSize.y, constraints.y);
+            }
+
+            return new Point(width, height);
+        }
+
+        private void deferredCreateContent(Composite parent) {
+            fTable= new Table(parent, SWT.SINGLE | SWT.FULL_SELECTION);
+            fTable.setLinesVisible(false);
+            fTable.setHeaderVisible(false);
+            fTable.setForeground(fForegroundColor);
+            fTable.setBackground(fBackgroundColor);
+
+            final TableViewer viewer= new TableViewer(fTable);
+            viewer.setContentProvider(new LinkContentProvider());
+            viewer.setLabelProvider(new LinkLabelProvider());
+            viewer.setInput(new ArrayWrapperObject( arraycast!(Object)(fInput)));
+            fTable.setSelection(0);
+
+            registerTableListeners();
+
+            getShell().addShellListener(new class()  ShellAdapter {
+
+                /*
+                 * @see org.eclipse.swt.events.ShellAdapter#shellActivated(org.eclipse.swt.events.ShellEvent)
+                 */
+                public void shellActivated(ShellEvent e) {
+                    if (viewer.getTable().getSelectionCount() is 0) {
+                        viewer.getTable().setSelection(0);
+                    }
+
+                    viewer.getTable().setFocus();
+                }
+            });
+        }
+
+        private void registerTableListeners() {
+
+            fTable.addMouseMoveListener(new class()  MouseMoveListener {
+                TableItem fLastItem= null;
+
+                public void mouseMove(MouseEvent e) {
+                    if (fTable.opEquals(e.getSource())) {
+                        Object o= fTable.getItem(new Point(e.x, e.y));
+                        if ( cast(TableItem)o ) {
+                            TableItem item= cast(TableItem) o;
+                            if (!o.opEquals(fLastItem)) {
+                                fLastItem= cast(TableItem) o;
+                                fTable.setSelection([ fLastItem ]);
+                            } else if (e.y < fTable.getItemHeight() / 4) {
+                                // Scroll up
+                                int index= fTable.indexOf(item);
+                                if (index > 0) {
+                                    fLastItem= fTable.getItem(index - 1);
+                                    fTable.setSelection([ fLastItem ]);
+                                }
+                            } else if (e.y > fTable.getBounds().height - fTable.getItemHeight() / 4) {
+                                // Scroll down
+                                int index= fTable.indexOf(item);
+                                if (index < fTable.getItemCount() - 1) {
+                                    fLastItem= fTable.getItem(index + 1);
+                                    fTable.setSelection([ fLastItem ]);
+                                }
+                            }
+                        }
+                    }
+                }
+            });
+
+            fTable.addSelectionListener(new class()  SelectionAdapter {
+                public void widgetSelected(SelectionEvent e) {
+                    openSelectedLink();
+                }
+            });
+
+            fTable.addMouseListener(new class()  MouseAdapter {
+                public void mouseUp(MouseEvent e) {
+                    if (fTable.getSelectionCount() < 1)
+                        return;
+
+                    if (e.button !is 1)
+                        return;
+
+                    if (fTable.opEquals(e.getSource())) {
+                        Object o= fTable.getItem(new Point(e.x, e.y));
+                        TableItem selection= fTable.getSelection()[0];
+                        if (selection.opEquals(o))
+                            openSelectedLink();
+                    }
+                }
+            });
+
+            fTable.addKeyListener(new class()  KeyAdapter {
+                public void keyPressed(KeyEvent e) {
+                    if (e.keyCode is 0x0D) // return
+                        openSelectedLink();
+                }
+            });
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IInformationControlExtension#hasContents()
+         */
+        public bool hasContents() {
+            return true;
+        }
+
+        /**
+         * Opens the currently selected link.
+         */
+        private void openSelectedLink() {
+            TableItem selection= fTable.getSelection()[0];
+            IHyperlink link= cast(IHyperlink)selection.getData();
+            fManager.hideInformationControl();
+            link.open();
+        }
+    }
+
+    private class MultipleHyperlinkHover : ITextHover, ITextHoverExtension {
+
+        /**
+         * @see org.eclipse.jface.text.ITextHover#getHoverInfo(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion)
+         * @deprecated
+         */
+        public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) {
+            return null;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextHover#getHoverRegion(org.eclipse.jface.text.ITextViewer, int)
+         */
+        public IRegion getHoverRegion(ITextViewer textViewer, int offset) {
+            return fSubjectRegion;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextHoverExtension2#getHoverInfo2(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion)
+         */
+        public Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) {
+            return new ArrayWrapperObject( arraycast!(Object)(fHyperlinks));
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextHoverExtension#getHoverControlCreator()
+         */
+        public IInformationControlCreator getHoverControlCreator() {
+            return new class()  IInformationControlCreator {
+                public IInformationControl createInformationControl(Shell parent) {
+                    Color foregroundColor= fTextViewer.getTextWidget().getForeground();
+                    Color backgroundColor= fTextViewer.getTextWidget().getBackground();
+                    return new LinkListInformationControl(parent, fManager, foregroundColor, backgroundColor);
+                }
+            };
+        }
+    }
+
+    private static class MultipleHyperlinkHoverManager : AbstractInformationControlManager , IWidgetTokenKeeper, IWidgetTokenKeeperExtension {
+
+        private class Closer : IInformationControlCloser, Listener, KeyListener {
+
+            private Control fSubjectControl;
+            private Display fDisplay;
+            private IInformationControl fControl;
+            private Rectangle fSubjectArea;
+
+            /*
+             * @see org.eclipse.jface.text.AbstractInformationControlManager.IInformationControlCloser#setInformationControl(org.eclipse.jface.text.IInformationControl)
+             */
+            public void setInformationControl(IInformationControl control) {
+                fControl= control;
+            }
+
+            /*
+             * @see org.eclipse.jface.text.AbstractInformationControlManager.IInformationControlCloser#setSubjectControl(org.eclipse.swt.widgets.Control)
+             */
+            public void setSubjectControl(Control subject) {
+                fSubjectControl= subject;
+            }
+
+            /*
+             * @see org.eclipse.jface.text.AbstractInformationControlManager.IInformationControlCloser#start(org.eclipse.swt.graphics.Rectangle)
+             */
+            public void start(Rectangle subjectArea) {
+                fSubjectArea= subjectArea;
+
+                fDisplay= fSubjectControl.getDisplay();
+                if (!fDisplay.isDisposed()) {
+                    fDisplay.addFilter(SWT.FocusOut, this);
+                    fDisplay.addFilter(SWT.MouseMove, this);
+                    fTextViewer.getTextWidget().addKeyListener(this);
+                }
+            }
+
+            /*
+             * @see org.eclipse.jface.text.AbstractInformationControlManager.IInformationControlCloser#stop()
+             */
+            public void stop() {
+                if (fDisplay !is null && !fDisplay.isDisposed()) {
+                    fDisplay.removeFilter(SWT.FocusOut, this);
+                    fDisplay.removeFilter(SWT.MouseMove, this);
+                    fTextViewer.getTextWidget().removeKeyListener(this);
+                }
+
+                fSubjectArea= null;
+            }
+
+            /*
+             * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+             */
+            public void handleEvent(Event event) {
+                switch (event.type) {
+                    case SWT.FocusOut:
+                        if (!fControl.isFocusControl())
+                            disposeInformationControl();
+                        break;
+                    case SWT.MouseMove:
+                        handleMouseMove(event);
+                        break;
+                    default:
+                }
+            }
+
+            /**
+             * Handle mouse movement events.
+             *
+             * @param event the event
+             */
+            private void handleMouseMove(Event event) {
+                if (!(cast(Control)event.widget ))
+                    return;
+
+                if (fControl.isFocusControl())
+                    return;
+
+                Control eventControl= cast(Control) event.widget;
+
+                //transform coordinates to subject control:
+                Point mouseLoc= event.display.map(eventControl, fSubjectControl, event.x, event.y);
+
+                if (fSubjectArea.contains(mouseLoc))
+                    return;
+
+                if (inKeepUpZone(mouseLoc.x, mouseLoc.y, (cast(IInformationControlExtension3) fControl).getBounds()))
+                    return;
+
+                hideInformationControl();
+            }
+
+            /**
+             * Tests whether a given mouse location is within the keep-up zone.
+             * The hover should not be hidden as long as the mouse stays inside this zone.
+             *
+             * @param x the x coordinate, relative to the <em>subject control</em>
+             * @param y the y coordinate, relative to the <em>subject control</em>
+             * @param controlBounds the bounds of the current control
+             *
+             * @return <code>true</code> iff the mouse event occurred in the keep-up zone
+             */
+            private bool inKeepUpZone(int x, int y, Rectangle controlBounds) {
+                //  +-----------+
+                //  |subjectArea|
+                //  +-----------+
+                //  |also keepUp|
+                // ++-----------+-------+
+                // | totalBounds        |
+                // +--------------------+
+                if (fSubjectArea.contains(x, y))
+                    return true;
+
+                Rectangle iControlBounds= fSubjectControl.getDisplay().map(null, fSubjectControl, controlBounds);
+                Rectangle totalBounds= Geometry.copy(iControlBounds);
+                if (totalBounds.contains(x, y))
+                    return true;
+
+                int keepUpY= fSubjectArea.y + fSubjectArea.height;
+                Rectangle alsoKeepUp= new Rectangle(fSubjectArea.x, keepUpY, fSubjectArea.width, totalBounds.y - keepUpY);
+                return alsoKeepUp.contains(x, y);
+            }
+
+            /*
+             * @see org.eclipse.swt.events.KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)
+             */
+            public void keyPressed(KeyEvent e) {
+            }
+
+            /*
+             * @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent)
+             */
+            public void keyReleased(KeyEvent e) {
+                hideInformationControl();
+            }
+
+        }
+
+        /**
+         * Priority of the hover managed by this manager.
+         * Default value: One higher then for the hovers
+         * managed by TextViewerHoverManager.
+         */
+        private static const int WIDGET_TOKEN_PRIORITY= 1;
+
+        private const MultipleHyperlinkHover fHover;
+        private const ITextViewer fTextViewer;
+        private const MultipleHyperlinkPresenter fHyperlinkPresenter;
+        private Closer fCloser;
+        private bool fIsControlVisible;
+
+
+        /**
+         * Create a new MultipleHyperlinkHoverManager. The MHHM can show and hide
+         * the given MultipleHyperlinkHover inside the given ITextViewer.
+         *
+         * @param hover the hover to manage
+         * @param viewer the viewer to show the hover in
+         * @param hyperlinkPresenter the hyperlink presenter using this manager to present hyperlinks
+         */
+        public this(MultipleHyperlinkHover hover, ITextViewer viewer, MultipleHyperlinkPresenter hyperlinkPresenter) {
+            super(hover.getHoverControlCreator());
+
+            fHover= hover;
+            fTextViewer= viewer;
+            fHyperlinkPresenter= hyperlinkPresenter;
+
+            fCloser= new Closer();
+            setCloser(fCloser);
+            fIsControlVisible= false;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.AbstractInformationControlManager#computeInformation()
+         */
+        protected void computeInformation() {
+            IRegion region= fHover.getHoverRegion(fTextViewer, -1);
+            if (region is null) {
+                setInformation(cast(Object)null, cast(Rectangle)null);
+                return;
+            }
+
+            Rectangle area= JFaceTextUtil.computeArea(region, fTextViewer);
+            if (area is null || area.isEmpty()) {
+                setInformation(cast(Object)null, cast(Rectangle)null);
+                return;
+            }
+
+            Object information= fHover.getHoverInfo2(fTextViewer, region);
+            setCustomInformationControlCreator(fHover.getHoverControlCreator());
+            setInformation(information, area);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.AbstractInformationControlManager#computeInformationControlLocation(org.eclipse.swt.graphics.Rectangle, org.eclipse.swt.graphics.Point)
+         */
+        protected Point computeInformationControlLocation(Rectangle subjectArea, Point controlSize) {
+            Point result= super.computeInformationControlLocation(subjectArea, controlSize);
+
+            Point cursorLocation= fTextViewer.getTextWidget().getDisplay().getCursorLocation();
+            if (cursorLocation.x <= result.x + controlSize.x)
+                return result;
+
+            result.x= cursorLocation.x + 20 - controlSize.x;
+            return result;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.AbstractInformationControlManager#showInformationControl(org.eclipse.swt.graphics.Rectangle)
+         */
+        protected void showInformationControl(Rectangle subjectArea) {
+            if ( cast(IWidgetTokenOwnerExtension)fTextViewer ) {
+                if ((cast(IWidgetTokenOwnerExtension) fTextViewer).requestWidgetToken(this, WIDGET_TOKEN_PRIORITY))
+                    super.showInformationControl(subjectArea);
+            } else if ( cast(IWidgetTokenOwner)fTextViewer ) {
+                if ((cast(IWidgetTokenOwner) fTextViewer).requestWidgetToken(this))
+                    super.showInformationControl(subjectArea);
+            } else {
+                super.showInformationControl(subjectArea);
+            }
+
+            fIsControlVisible= true;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.AbstractInformationControlManager#hideInformationControl()
+         */
+        protected void hideInformationControl() {
+            super.hideInformationControl();
+
+            if ( cast(IWidgetTokenOwner)fTextViewer ) {
+                (cast(IWidgetTokenOwner) fTextViewer).releaseWidgetToken(this);
+            }
+
+            fIsControlVisible= false;
+            fHyperlinkPresenter.hideHyperlinks();
+        }
+
+        /*
+         * @see org.eclipse.jface.text.AbstractInformationControlManager#disposeInformationControl()
+         */
+        public void disposeInformationControl() {
+            super.disposeInformationControl();
+
+            if ( cast(IWidgetTokenOwner)fTextViewer ) {
+                (cast(IWidgetTokenOwner) fTextViewer).releaseWidgetToken(this);
+            }
+
+            fIsControlVisible= false;
+            fHyperlinkPresenter.hideHyperlinks();
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IWidgetTokenKeeper#requestWidgetToken(org.eclipse.jface.text.IWidgetTokenOwner)
+         */
+        public bool requestWidgetToken(IWidgetTokenOwner owner) {
+            hideInformationControl();
+            return true;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(org.eclipse.jface.text.IWidgetTokenOwner, int)
+         */
+        public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) {
+            if (priority < WIDGET_TOKEN_PRIORITY)
+                return false;
+
+            hideInformationControl();
+            return true;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#setFocus(org.eclipse.jface.text.IWidgetTokenOwner)
+         */
+        public bool setFocus(IWidgetTokenOwner owner) {
+            return false;
+        }
+
+        /**
+         * Returns <code>true</code> if the information control managed by
+         * this manager is visible, <code>false</code> otherwise.
+         *
+         * @return <code>true</code> if information control is visible
+         */
+        public bool isInformationControlVisible() {
+            return fIsControlVisible;
+        }
+    }
+
+    private ITextViewer fTextViewer;
+
+    private IHyperlink[] fHyperlinks;
+    private Region fSubjectRegion;
+    private MultipleHyperlinkHoverManager fManager;
+
+    /**
+     * Creates a new multiple hyperlink presenter which uses
+     * {@link #HYPERLINK_COLOR} to read the color from the given preference store.
+     *
+     * @param store the preference store
+     */
+    public this(IPreferenceStore store) {
+        super(store);
+    }
+
+    /**
+     * Creates a new multiple hyperlink presenter.
+     *
+     * @param color the hyperlink color, to be disposed by the caller
+     */
+    public this(RGB color) {
+        super(color);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter#install(org.eclipse.jface.text.ITextViewer)
+     */
+    public void install(ITextViewer viewer) {
+        super.install(viewer);
+        fTextViewer= viewer;
+
+        fManager= new MultipleHyperlinkHoverManager(new MultipleHyperlinkHover(), fTextViewer, this);
+        fManager.install(viewer.getTextWidget());
+        fManager.setSizeConstraints(100, 12, false, true);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter#uninstall()
+     */
+    public void uninstall() {
+        super.uninstall();
+
+        if (fTextViewer !is null) {
+            fManager.dispose();
+
+            fTextViewer= null;
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter#canShowMultipleHyperlinks()
+     */
+    public bool canShowMultipleHyperlinks() {
+        return true;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter#canHideHyperlinks()
+     */
+    public bool canHideHyperlinks() {
+        return !fManager.isInformationControlVisible();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter#hideHyperlinks()
+     */
+    public void hideHyperlinks() {
+        super.hideHyperlinks();
+
+        fHyperlinks= null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter#showHyperlinks(org.eclipse.jface.text.hyperlink.IHyperlink[])
+     */
+    public void showHyperlinks(IHyperlink[] hyperlinks) {
+        super.showHyperlinks([ hyperlinks[0] ]);
+
+        fSubjectRegion= null;
+        fHyperlinks= hyperlinks;
+
+        if (hyperlinks.length is 1)
+            return;
+
+        int start= hyperlinks[0].getHyperlinkRegion().getOffset();
+        int end= start + hyperlinks[0].getHyperlinkRegion().getLength();
+
+        for (int i= 1; i < hyperlinks.length; i++) {
+            int hstart= hyperlinks[i].getHyperlinkRegion().getOffset();
+            int hend= hstart + hyperlinks[i].getHyperlinkRegion().getLength();
+
+            start= Math.min(start, hstart);
+            end= Math.max(end, hend);
+        }
+
+        fSubjectRegion= new Region(start, end - start);
+
+        fManager.showInformation();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/URLHyperlink.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.hyperlink.URLHyperlink;
+
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenterExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.MultipleHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkManager; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension2; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkMessages; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlink; // packageimport
+
+
+import java.lang.all;
+import org.eclipse.dwtxhelper.MessageFormat;
+
+import org.eclipse.swt.program.Program;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.IRegion;
+
+
+
+
+
+/**
+ * URL hyperlink.
+ *
+ * @since 3.1
+ */
+public class URLHyperlink : IHyperlink {
+
+    private String fURLString;
+    private IRegion fRegion;
+
+    /**
+     * Creates a new URL hyperlink.
+     *
+     * @param region
+     * @param urlString
+     */
+    public this(IRegion region, String urlString) {
+        Assert.isNotNull(urlString);
+        Assert.isNotNull(cast(Object)region);
+
+        fRegion= region;
+        fURLString= urlString;
+    }
+
+    /*
+     * @see org.eclipse.jdt.internal.ui.javaeditor.IHyperlink#getHyperlinkRegion()
+     */
+    public IRegion getHyperlinkRegion() {
+        return fRegion;
+    }
+
+    /*
+     * @see org.eclipse.jdt.internal.ui.javaeditor.IHyperlink#open()
+     */
+    public void open() {
+        if (fURLString !is null) {
+            Program.launch(fURLString);
+            fURLString= null;
+            return;
+        }
+    }
+
+    /*
+     * @see org.eclipse.jdt.internal.ui.javaeditor.IHyperlink#getTypeLabel()
+     */
+    public String getTypeLabel() {
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jdt.internal.ui.javaeditor.IHyperlink#getHyperlinkText()
+     */
+    public String getHyperlinkText() {
+        return MessageFormat.format(HyperlinkMessages.getString("URLHyperlink.hyperlinkText"), stringcast(fURLString) ); //$NON-NLS-1$
+    }
+
+    /**
+     * Returns the URL string of this hyperlink.
+     *
+     * @return the URL string
+     * @since 3.2
+     */
+    public String getURLString() {
+        return fURLString;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/hyperlink/URLHyperlinkDetector.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * 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
+ *     Benjamin Muskalla <b.muskalla@gmx.net> - https://bugs.eclipse.org/bugs/show_bug.cgi?id=156433
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.hyperlink.URLHyperlinkDetector;
+
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenterExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.MultipleHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkManager; // packageimport
+import org.eclipse.jface.text.hyperlink.URLHyperlink; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension2; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter; // packageimport
+import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension; // packageimport
+import org.eclipse.jface.text.hyperlink.HyperlinkMessages; // packageimport
+import org.eclipse.jface.text.hyperlink.IHyperlink; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.dwtxhelper.MalformedURLException;
+import org.eclipse.dwtxhelper.URL;
+import org.eclipse.dwtxhelper.StringTokenizer;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Region;
+
+
+/**
+ * URL hyperlink detector.
+ *
+ * @since 3.1
+ */
+public class URLHyperlinkDetector : AbstractHyperlinkDetector {
+
+
+    /**
+     * Creates a new URL hyperlink detector.
+     *
+     * @since 3.2
+     */
+    public this() {
+    }
+
+    /**
+     * Creates a new URL hyperlink detector.
+     *
+     * @param textViewer the text viewer in which to detect the hyperlink
+     * @deprecated As of 3.2, replaced by {@link URLHyperlinkDetector}
+     */
+    public this(ITextViewer textViewer) {
+    }
+
+    /*
+     * @see org.eclipse.jface.text.hyperlink.IHyperlinkDetector#detectHyperlinks(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion, bool)
+     */
+    public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, bool canShowMultipleHyperlinks) {
+        if (region is null || textViewer is null)
+            return null;
+
+        IDocument document= textViewer.getDocument();
+
+        int offset= region.getOffset();
+
+        String urlString= null;
+        if (document is null)
+            return null;
+
+        IRegion lineInfo;
+        String line;
+        try {
+            lineInfo= document.getLineInformationOfOffset(offset);
+            line= document.get(lineInfo.getOffset(), lineInfo.getLength());
+        } catch (BadLocationException ex) {
+            return null;
+        }
+
+        int offsetInLine= offset - lineInfo.getOffset();
+
+        bool startDoubleQuote= false;
+        int urlOffsetInLine= 0;
+        int urlLength= 0;
+
+        int urlSeparatorOffset= line.indexOf("://"); //$NON-NLS-1$
+        while (urlSeparatorOffset >= 0) {
+
+            // URL protocol (left to "://")
+            urlOffsetInLine= urlSeparatorOffset;
+            char ch;
+            do {
+                urlOffsetInLine--;
+                ch= ' ';
+                if (urlOffsetInLine > -1)
+                    ch= line.charAt(urlOffsetInLine);
+                startDoubleQuote= ch is '"';
+            } while (Character.isUnicodeIdentifierStart(ch));
+            urlOffsetInLine++;
+
+            // Right to "://"
+            StringTokenizer tokenizer= new StringTokenizer(line.substring(urlSeparatorOffset + 3), " \t\n\r\f<>", false); //$NON-NLS-1$
+            if (!tokenizer.hasMoreTokens())
+                return null;
+
+            urlLength= tokenizer.nextToken().length() + 3 + urlSeparatorOffset - urlOffsetInLine;
+            if (offsetInLine >= urlOffsetInLine && offsetInLine <= urlOffsetInLine + urlLength)
+                break;
+
+            urlSeparatorOffset= line.indexOf("://", urlSeparatorOffset + 1); //$NON-NLS-1$
+        }
+
+        if (urlSeparatorOffset < 0)
+            return null;
+
+        if (startDoubleQuote) {
+            int endOffset= -1;
+            int nextDoubleQuote= line.indexOf('"', urlOffsetInLine);
+            int nextWhitespace= line.indexOf(' ', urlOffsetInLine);
+            if (nextDoubleQuote !is -1 && nextWhitespace !is -1)
+                endOffset= Math.min(nextDoubleQuote, nextWhitespace);
+            else if (nextDoubleQuote !is -1)
+                endOffset= nextDoubleQuote;
+            else if (nextWhitespace !is -1)
+                endOffset= nextWhitespace;
+            if (endOffset !is -1)
+                urlLength= endOffset - urlOffsetInLine;
+        }
+
+        // Set and validate URL string
+        try {
+            urlString= line.substring(urlOffsetInLine, urlOffsetInLine + urlLength);
+            new URL(urlString);
+        } catch (MalformedURLException ex) {
+            urlString= null;
+            return null;
+        }
+
+        IRegion urlRegion= new Region(lineInfo.getOffset() + urlOffsetInLine, urlLength);
+        return [new URLHyperlink(urlRegion, urlString)];
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationPresenter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.information.IInformationPresenter;
+
+import org.eclipse.jface.text.information.InformationPresenter; // packageimport
+import org.eclipse.jface.text.information.IInformationProvider; // packageimport
+import org.eclipse.jface.text.information.IInformationProviderExtension; // packageimport
+import org.eclipse.jface.text.information.IInformationPresenterExtension; // packageimport
+import org.eclipse.jface.text.information.IInformationProviderExtension2; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * An information presenter shows information available at the text viewer's
+ * current document position. An <code>IInformationPresenter</code> is a
+ * {@link org.eclipse.jface.text.ITextViewer} add-on.
+ * <p>
+ * An information presenters has a list of  {@link org.eclipse.jface.text.information.IInformationProvider} objects
+ * each of which is registered for a  particular document content type.
+ * The presenter uses the strategy objects to retrieve the information to present.
+ * </p>
+ * <p>
+ * In order to provide backward compatibility for clients of <code>IInformationPresenter</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link IInformationPresenterExtension} since version 3.0 introducing
+ *      the ability to handle documents with multiple partitions</li>
+ * </ul>
+ * </p>
+ * <p>
+ * The interface can be implemented by clients. By default, clients use
+ * {@link org.eclipse.jface.text.information.InformationPresenter} as the standard implementer of this interface.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.information.IInformationPresenterExtension
+ * @see org.eclipse.jface.text.ITextViewer
+ * @see org.eclipse.jface.text.information.IInformationProvider
+ * @since 2.0
+ */
+public interface IInformationPresenter {
+
+    /**
+     * Installs the information presenter on the given text viewer. After this method has been
+     * finished, the presenter is operational, i.e. the method {@link #showInformation()}
+     * can be called until {@link #uninstall()} is called.
+     *
+     * @param textViewer the viewer on which the presenter is installed
+     */
+    void install(ITextViewer textViewer);
+
+    /**
+     * Removes the information presenter from the text viewer it has previously been
+     * installed on.
+     */
+    void uninstall();
+
+    /**
+     * Shows information related to the cursor position of the text viewer
+     * this information presenter is installed on.
+     */
+    void showInformation();
+
+    /**
+     * Returns the information provider to be used for the given content type.
+     *
+     * @param contentType the type of the content for which information will be requested
+     * @return an information provider or <code>null</code> if none exists for the specified content type
+     */
+    IInformationProvider getInformationProvider(String contentType);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationPresenterExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.information.IInformationPresenterExtension;
+
+import org.eclipse.jface.text.information.InformationPresenter; // packageimport
+import org.eclipse.jface.text.information.IInformationProvider; // packageimport
+import org.eclipse.jface.text.information.IInformationProviderExtension; // packageimport
+import org.eclipse.jface.text.information.IInformationProviderExtension2; // packageimport
+import org.eclipse.jface.text.information.IInformationPresenter; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Extends {@link org.eclipse.jface.text.information.IInformationPresenter} with
+ * the ability to handle documents with multiple partitions.
+ *
+ * @see org.eclipse.jface.text.information.IInformationPresenter
+ * 
+ * @since 3.0
+ */
+public interface IInformationPresenterExtension {
+
+    /**
+     * Returns the document partitioning this information presenter is using.
+     *
+     * @return the document partitioning this information presenter is using
+     */
+    String getDocumentPartitioning();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.information.IInformationProvider;
+
+import org.eclipse.jface.text.information.InformationPresenter; // packageimport
+import org.eclipse.jface.text.information.IInformationProviderExtension; // packageimport
+import org.eclipse.jface.text.information.IInformationPresenterExtension; // packageimport
+import org.eclipse.jface.text.information.IInformationProviderExtension2; // packageimport
+import org.eclipse.jface.text.information.IInformationPresenter; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * Provides information related to the content of a text viewer.
+ * <p>
+ * In order to provide backward compatibility for clients of <code>IInformationProvider</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link IInformationProviderExtension} since version 2.1 introducing
+ *      the ability to provide the element for a given subject</li>
+ * <li>{@link IInformationProviderExtension2} since version 3.0 introducing
+ *      the ability to provide its own information control creator</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.information.IInformationProviderExtension
+ * @see org.eclipse.jface.text.information.IInformationProviderExtension2
+ * @see org.eclipse.jface.text.information.IInformationPresenter
+ * @see org.eclipse.jface.text.ITextViewer
+ * @since 2.0
+ */
+public interface IInformationProvider {
+
+    /**
+     * Returns the region of the text viewer's document close to the given
+     * offset that contains a subject about which information can be provided.<p>
+     * For example, if information can be provided on a per code block basis,
+     * the offset should be used to find the enclosing code block and the source
+     * range of the block should be returned.
+     *
+     * @param textViewer the text viewer in which information has been requested
+     * @param offset the offset at which information has been requested
+     * @return the region of the text viewer's document containing the information subject
+     */
+    IRegion getSubject(ITextViewer textViewer, int offset);
+
+    /**
+     * Returns the information about the given subject or <code>null</code> if
+     * no information is available. It depends on the concrete configuration in which
+     * format the information is to be provided. For example, information presented
+     * in an information control displaying HTML, should be provided in HTML.
+     *
+     * @param textViewer the viewer in whose document the subject is contained
+     * @param subject the text region constituting the information subject
+     * @return the information about the subject
+     * @see IInformationPresenter
+     * @deprecated As of 2.1, replaced by {@link IInformationProviderExtension#getInformation2(ITextViewer, IRegion)}
+     */
+    String getInformation(ITextViewer textViewer, IRegion subject);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationProviderExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.information.IInformationProviderExtension;
+
+import org.eclipse.jface.text.information.InformationPresenter; // packageimport
+import org.eclipse.jface.text.information.IInformationProvider; // packageimport
+import org.eclipse.jface.text.information.IInformationPresenterExtension; // packageimport
+import org.eclipse.jface.text.information.IInformationProviderExtension2; // packageimport
+import org.eclipse.jface.text.information.IInformationPresenter; // packageimport
+
+
+import java.lang.all;
+
+
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * Extends {@link org.eclipse.jface.text.information.IInformationProvider} with
+ * the ability to provide the element for a given subject.
+ *
+ * @see org.eclipse.jface.text.information.IInformationProvider
+ * @since 2.1
+ */
+public interface IInformationProviderExtension {
+
+    /**
+     * Returns the element for the given subject or <code>null</code> if
+     * no element is available.
+     * <p>
+     * Implementers should ignore the text returned by {@link IInformationProvider#getInformation(ITextViewer, IRegion)}.
+     * </p>
+     *
+     * @param textViewer the viewer in whose document the subject is contained
+     * @param subject the text region constituting the information subject
+     * @return the element for the subject
+     *
+     * @see IInformationProvider#getInformation(ITextViewer, IRegion)
+     * @see org.eclipse.jface.text.ITextViewer
+     */
+    Object getInformation2(ITextViewer textViewer, IRegion subject);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationProviderExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.information.IInformationProviderExtension2;
+
+import org.eclipse.jface.text.information.InformationPresenter; // packageimport
+import org.eclipse.jface.text.information.IInformationProvider; // packageimport
+import org.eclipse.jface.text.information.IInformationProviderExtension; // packageimport
+import org.eclipse.jface.text.information.IInformationPresenterExtension; // packageimport
+import org.eclipse.jface.text.information.IInformationPresenter; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.jface.text.IInformationControlCreator;
+
+/**
+ * Extends {@link org.eclipse.jface.text.information.IInformationProvider} with
+ * the ability to provide its own information presenter control creator.
+ *
+ * @see org.eclipse.jface.text.IInformationControlCreator
+ * @see org.eclipse.jface.text.information.IInformationProvider
+ * @since 3.0
+ */
+public interface IInformationProviderExtension2 {
+
+    /**
+     * Returns the information control creator of this information provider.
+     *
+     * @return the information control creator or <code>null</code> if none is available
+     */
+    IInformationControlCreator getInformationPresenterControlCreator();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/InformationPresenter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,506 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.information.InformationPresenter;
+
+import org.eclipse.jface.text.information.IInformationProvider; // packageimport
+import org.eclipse.jface.text.information.IInformationProviderExtension; // packageimport
+import org.eclipse.jface.text.information.IInformationPresenterExtension; // packageimport
+import org.eclipse.jface.text.information.IInformationProviderExtension2; // packageimport
+import org.eclipse.jface.text.information.IInformationPresenter; // packageimport
+
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.AbstractInformationControlManager;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocumentExtension3;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.IViewportListener;
+import org.eclipse.jface.text.IWidgetTokenKeeper;
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension;
+import org.eclipse.jface.text.IWidgetTokenOwner;
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
+
+
+/**
+ * Standard implementation of <code>IInformationPresenter</code>.
+ * This implementation extends <code>AbstractInformationControlManager</code>.
+ * The information control is made visible on request by calling
+ * {@link #showInformationControl(Rectangle)}.
+ * <p>
+ * Usually, clients instantiate this class and configure it before using it. The configuration
+ * must be consistent: This means the used {@link org.eclipse.jface.text.IInformationControlCreator}
+ * must create an information control expecting information in the same format the configured
+ * {@link org.eclipse.jface.text.information.IInformationProvider}s  use to encode the information they provide.
+ * </p>
+ *
+ * @since 2.0
+ */
+public class InformationPresenter : AbstractInformationControlManager , IInformationPresenter, IInformationPresenterExtension, IWidgetTokenKeeper, IWidgetTokenKeeperExtension {
+
+    alias AbstractInformationControlManager.install install;
+    public override void showInformation(){
+        super.showInformation();
+    }
+
+    /**
+     * Priority of the info controls managed by this information presenter.
+     * Default value: <code>5</code>.
+     *
+     * @since 3.0
+     */
+    /*
+     * 5 as value has been chosen in order to beat the hovers of {@link org.eclipse.jface.text.TextViewerHoverManager}
+     */
+    public static const int WIDGET_PRIORITY= 5;
+
+
+    /**
+     * Internal information control closer. Listens to several events issued by its subject control
+     * and closes the information control when necessary.
+     */
+    class Closer : IInformationControlCloser, ControlListener, MouseListener, FocusListener, IViewportListener, KeyListener {
+
+        /** The subject control. */
+        private Control fSubjectControl;
+        /** The information control. */
+        private IInformationControl fInformationControlToClose;
+        /** Indicates whether this closer is active. */
+        private bool fIsActive= false;
+
+        /*
+         * @see IInformationControlCloser#setSubjectControl(Control)
+         */
+        public void setSubjectControl(Control control) {
+            fSubjectControl= control;
+        }
+
+        /*
+         * @see IInformationControlCloser#setInformationControl(IInformationControl)
+         */
+        public void setInformationControl(IInformationControl control) {
+            fInformationControlToClose= control;
+        }
+
+        /*
+         * @see IInformationControlCloser#start(Rectangle)
+         */
+        public void start(Rectangle informationArea) {
+
+            if (fIsActive)
+                return;
+            fIsActive= true;
+
+            if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
+                fSubjectControl.addControlListener(this);
+                fSubjectControl.addMouseListener(this);
+                fSubjectControl.addFocusListener(this);
+                fSubjectControl.addKeyListener(this);
+            }
+
+            if (fInformationControlToClose !is null)
+                fInformationControlToClose.addFocusListener(this);
+
+            fTextViewer.addViewportListener(this);
+        }
+
+        /*
+         * @see IInformationControlCloser#stop()
+         */
+        public void stop() {
+
+            if (!fIsActive)
+                return;
+            fIsActive= false;
+
+            fTextViewer.removeViewportListener(this);
+
+            if (fInformationControlToClose !is null)
+                fInformationControlToClose.removeFocusListener(this);
+
+            if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
+                fSubjectControl.removeControlListener(this);
+                fSubjectControl.removeMouseListener(this);
+                fSubjectControl.removeFocusListener(this);
+                fSubjectControl.removeKeyListener(this);
+            }
+        }
+
+        /*
+         * @see ControlListener#controlResized(ControlEvent)
+         */
+         public void controlResized(ControlEvent e) {
+             hideInformationControl();
+        }
+
+        /*
+         * @see ControlListener#controlMoved(ControlEvent)
+         */
+         public void controlMoved(ControlEvent e) {
+             hideInformationControl();
+        }
+
+        /*
+         * @see MouseListener#mouseDown(MouseEvent)
+         */
+         public void mouseDown(MouseEvent e) {
+             hideInformationControl();
+        }
+
+        /*
+         * @see MouseListener#mouseUp(MouseEvent)
+         */
+        public void mouseUp(MouseEvent e) {
+        }
+
+        /*
+         * @see MouseListener#mouseDoubleClick(MouseEvent)
+         */
+        public void mouseDoubleClick(MouseEvent e) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see FocusListener#focusGained(FocusEvent)
+         */
+        public void focusGained(FocusEvent e) {
+        }
+
+        /*
+         * @see FocusListener#focusLost(FocusEvent)
+         */
+         public void focusLost(FocusEvent e) {
+            Display d= fSubjectControl.getDisplay();
+            d.asyncExec(new class()  Runnable {
+                // Without the asyncExec, mouse clicks to the workbench window are swallowed.
+                public void run() {
+                    if (fInformationControlToClose is null || !fInformationControlToClose.isFocusControl())
+                        hideInformationControl();
+                }
+            });
+        }
+
+        /*
+         * @see IViewportListenerListener#viewportChanged(int)
+         */
+        public void viewportChanged(int topIndex) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see KeyListener#keyPressed(KeyEvent)
+         */
+        public void keyPressed(KeyEvent e) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see KeyListener#keyReleased(KeyEvent)
+         */
+        public void keyReleased(KeyEvent e) {
+        }
+    }
+
+
+    /** The text viewer this information presenter works on */
+    private ITextViewer fTextViewer;
+    /** The map of <code>IInformationProvider</code> objects */
+    private Map fProviders;
+    /** The offset to override selection. */
+    private int fOffset= -1;
+    /**
+     * The document partitioning for this information presenter.
+     * @since 3.0
+     */
+    private String fPartitioning;
+
+    /**
+     * Creates a new information presenter that uses the given information control creator.
+     * The presenter is not installed on any text viewer yet. By default, an information
+     * control closer is set that closes the information control in the event of key strokes,
+     * resizing, moves, focus changes, mouse clicks, and disposal - all of those applied to
+     * the information control's parent control. Also, the setup ensures that the information
+     * control when made visible will request the focus. By default, the default document
+     * partitioning {@link IDocumentExtension3#DEFAULT_PARTITIONING} is used.
+     *
+     * @param creator the information control creator to be used
+     */
+    public this(IInformationControlCreator creator) {
+        super(creator);
+        setCloser(new Closer());
+        takesFocusWhenVisible(true);
+        fPartitioning= IDocumentExtension3.DEFAULT_PARTITIONING;
+    }
+
+    /**
+     * Sets the document partitioning to be used by this information presenter.
+     *
+     * @param partitioning the document partitioning to be used by this information presenter
+     * @since 3.0
+     */
+    public void setDocumentPartitioning(String partitioning) {
+        Assert.isNotNull(partitioning);
+        fPartitioning= partitioning;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.information.IInformationPresenterExtension#getDocumentPartitioning()
+     * @since 3.0
+     */
+    public String getDocumentPartitioning() {
+        return fPartitioning;
+    }
+
+    /**
+     * Registers a given information provider for a particular content type.
+     * If there is already a provider registered for this type, the new provider
+     * is registered instead of the old one.
+     *
+     * @param provider the information provider to register, or <code>null</code> to remove an existing one
+     * @param contentType the content type under which to register
+     */
+     public void setInformationProvider(IInformationProvider provider, String contentType) {
+
+        Assert.isNotNull(contentType);
+
+        if (fProviders is null)
+            fProviders= new HashMap();
+
+        if (provider is null)
+            fProviders.remove(contentType);
+        else
+            fProviders.put(contentType, cast(Object)provider);
+    }
+
+    /*
+     * @see IInformationPresenter#getInformationProvider(String)
+     */
+    public IInformationProvider getInformationProvider(String contentType) {
+        if (fProviders is null)
+            return null;
+
+        return cast(IInformationProvider) fProviders.get(contentType);
+    }
+
+    /**
+     * Sets a offset to override the selection. Setting the value to <code>-1</code> will disable
+     * overriding.
+     *
+     * @param offset the offset to override selection or <code>-1</code>
+     */
+    public void setOffset(int offset) {
+        fOffset= offset;
+    }
+
+    /*
+     * @see AbstractInformationControlManager#computeInformation()
+     */
+    protected void computeInformation() {
+
+        int offset= fOffset < 0 ? fTextViewer.getSelectedRange().x : fOffset;
+        if (offset is -1)
+            return;
+
+        fOffset= -1;
+
+        IInformationProvider provider= null;
+        try {
+            String contentType= TextUtilities.getContentType(fTextViewer.getDocument(), getDocumentPartitioning(), offset, true);
+            provider= getInformationProvider(contentType);
+        } catch (BadLocationException x) {
+        }
+        if (provider is null)
+            return;
+
+        IRegion subject= provider.getSubject(fTextViewer, offset);
+        if (subject is null)
+            return;
+
+        Object info;
+        if ( cast(IInformationProviderExtension)provider ) {
+            IInformationProviderExtension extension= cast(IInformationProviderExtension) provider;
+            info= extension.getInformation2(fTextViewer, subject);
+        } else {
+            // backward compatibility code
+            info= stringcast(provider.getInformation(fTextViewer, subject));
+        }
+
+        if ( cast(IInformationProviderExtension2)provider )
+            setCustomInformationControlCreator((cast(IInformationProviderExtension2) provider).getInformationPresenterControlCreator());
+        else
+            setCustomInformationControlCreator(null);
+
+        setInformation(info, computeArea(subject));
+    }
+
+    /**
+     * Determines the graphical area covered by the given text region.
+     *
+     * @param region the region whose graphical extend must be computed
+     * @return the graphical extend of the given region
+     */
+    private Rectangle computeArea(IRegion region) {
+
+        int start= 0;
+        int end= 0;
+
+        IRegion widgetRegion= modelRange2WidgetRange(region);
+        if (widgetRegion !is null) {
+            start= widgetRegion.getOffset();
+            end= widgetRegion.getOffset() + widgetRegion.getLength();
+        }
+
+        StyledText styledText= fTextViewer.getTextWidget();
+        Rectangle bounds;
+        if (end > 0 && start < end)
+            bounds= styledText.getTextBounds(start, end - 1);
+        else {
+            Point loc= styledText.getLocationAtOffset(start);
+            bounds= new Rectangle(loc.x, loc.y, 0, styledText.getLineHeight(start));
+        }
+
+        return bounds;
+    }
+
+    /**
+     * Translated the given range in the viewer's document into the corresponding
+     * range of the viewer's widget.
+     *
+     * @param region the range in the viewer's document
+     * @return the corresponding widget range
+     * @since 2.1
+     */
+    private IRegion modelRange2WidgetRange(IRegion region) {
+        if ( cast(ITextViewerExtension5)fTextViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) fTextViewer;
+            return extension.modelRange2WidgetRange(region);
+        }
+
+        IRegion visibleRegion= fTextViewer.getVisibleRegion();
+        int start= region.getOffset() - visibleRegion.getOffset();
+        int end= start + region.getLength();
+        if (end > visibleRegion.getLength())
+            end= visibleRegion.getLength();
+
+        return new Region(start, end - start);
+    }
+
+    /*
+     * @see IInformationPresenter#install(ITextViewer)
+     */
+    public void install(ITextViewer textViewer) {
+        fTextViewer= textViewer;
+        install(fTextViewer.getTextWidget());
+    }
+
+    /*
+     * @see IInformationPresenter#uninstall()
+     */
+    public void uninstall() {
+        dispose();
+    }
+
+    /*
+     * @see AbstractInformationControlManager#showInformationControl(Rectangle)
+     */
+    protected void showInformationControl(Rectangle subjectArea) {
+        if ( cast(IWidgetTokenOwnerExtension)fTextViewer  && cast(IWidgetTokenOwner)fTextViewer ) {
+            IWidgetTokenOwnerExtension extension= cast(IWidgetTokenOwnerExtension) fTextViewer;
+            if (extension.requestWidgetToken(this, WIDGET_PRIORITY))
+                super.showInformationControl(subjectArea);
+        } else if ( cast(IWidgetTokenOwner)fTextViewer ) {
+            IWidgetTokenOwner owner= cast(IWidgetTokenOwner) fTextViewer;
+            if (owner.requestWidgetToken(this))
+                super.showInformationControl(subjectArea);
+
+        } else
+            super.showInformationControl(subjectArea);
+    }
+
+    /*
+     * @see AbstractInformationControlManager#hideInformationControl()
+     */
+    protected void hideInformationControl() {
+        try {
+            super.hideInformationControl();
+        } finally {
+            if ( cast(IWidgetTokenOwner)fTextViewer ) {
+                IWidgetTokenOwner owner= cast(IWidgetTokenOwner) fTextViewer;
+                owner.releaseWidgetToken(this);
+            }
+        }
+    }
+
+    /*
+     * @see AbstractInformationControlManager#handleInformationControlDisposed()
+     */
+    protected void handleInformationControlDisposed() {
+        try {
+            super.handleInformationControlDisposed();
+        } finally {
+            if ( cast(IWidgetTokenOwner)fTextViewer ) {
+                IWidgetTokenOwner owner= cast(IWidgetTokenOwner) fTextViewer;
+                owner.releaseWidgetToken(this);
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner)
+     */
+    public bool requestWidgetToken(IWidgetTokenOwner owner) {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(org.eclipse.jface.text.IWidgetTokenOwner, int)
+     * @since 3.0
+     */
+    public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#setFocus(org.eclipse.jface.text.IWidgetTokenOwner)
+     * @since 3.0
+     */
+    public bool setFocus(IWidgetTokenOwner owner) {
+        return false;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/link/LinkedModeUI.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1358 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.link.LinkedModeUI;
+
+import org.eclipse.jface.text.link.LinkedModeModel; // packageimport
+import org.eclipse.jface.text.link.LinkedPosition; // packageimport
+import org.eclipse.jface.text.link.ILinkedModeListener; // packageimport
+import org.eclipse.jface.text.link.TabStopIterator; // packageimport
+import org.eclipse.jface.text.link.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionGroup; // packageimport
+import org.eclipse.jface.text.link.LinkedModeManager; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionAnnotations; // packageimport
+import org.eclipse.jface.text.link.ProposalPosition; // packageimport
+
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.HashSet;
+
+
+
+
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.custom.VerifyKeyListener;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.events.ShellListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.internal.text.link.contentassist.ContentAssistant2;
+import org.eclipse.jface.internal.text.link.contentassist.IProposalListener;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPartitioningException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DefaultPositionUpdater;
+import org.eclipse.jface.text.DocumentCommand;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IAutoEditStrategy;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension3;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IEditingSupport;
+import org.eclipse.jface.text.IEditingSupportRegistry;
+import org.eclipse.jface.text.IPositionUpdater;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.IRewriteTarget;
+import org.eclipse.jface.text.ITextInputListener;
+import org.eclipse.jface.text.ITextOperationTarget;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension;
+import org.eclipse.jface.text.ITextViewerExtension2;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.IAnnotationModelExtension;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+
+/**
+ * The UI for linked mode. Detects events that influence behavior of the linked mode
+ * UI and acts upon them.
+ * <p>
+ * <code>LinkedModeUI</code> relies on all added
+ * <code>LinkedModeUITarget</code>s to provide implementations of
+ * <code>ITextViewer</code> that implement <code>ITextViewerExtension</code>,
+ * and the documents being edited to implement <code>IDocumentExtension3</code>.
+ * </p>
+ * <p>
+ * Clients may instantiate and extend this class.
+ * </p>
+ *
+ * @since 3.0
+ */
+public class LinkedModeUI {
+
+    /* cycle constants */
+    /**
+     * Constant indicating that this UI should never cycle from the last
+     * position to the first and vice versa.
+     */
+    public static Object CYCLE_NEVER_;
+    public static Object CYCLE_NEVER(){
+        if( CYCLE_NEVER_ is null ){
+            synchronized( LinkedModeUI.classinfo ){
+                if( CYCLE_NEVER_ is null ){
+                    CYCLE_NEVER_ = new Object();
+                }
+            }
+        }
+        return CYCLE_NEVER_;
+    }
+    /**
+     * Constant indicating that this UI should always cycle from the last
+     * position to the first and vice versa.
+     */
+    public static Object CYCLE_ALWAYS_;
+    public static Object CYCLE_ALWAYS(){
+        if( CYCLE_ALWAYS_ is null ){
+            synchronized( LinkedModeUI.classinfo ){
+                if( CYCLE_ALWAYS_ is null ){
+                    CYCLE_ALWAYS_ = new Object();
+                }
+            }
+        }
+        return CYCLE_ALWAYS_;
+    }
+    /**
+     * Constant indicating that this UI should cycle from the last position to
+     * the first and vice versa if its model is not nested.
+     */
+    public static Object CYCLE_WHEN_NO_PARENT_;
+    public static Object CYCLE_WHEN_NO_PARENT(){
+        if( CYCLE_WHEN_NO_PARENT_ is null ){
+            synchronized( LinkedModeUI.classinfo ){
+                if( CYCLE_WHEN_NO_PARENT_ is null ){
+                    CYCLE_WHEN_NO_PARENT_ = new Object();
+                }
+            }
+        }
+        return CYCLE_WHEN_NO_PARENT_;
+    }
+
+    /**
+     * Listener that gets notified when the linked mode UI switches its focus position.
+     * <p>
+     * Clients may implement this interface.
+     * </p>
+     */
+    public interface ILinkedModeUIFocusListener {
+        /**
+         * Called when the UI for the linked mode leaves a linked position.
+         *
+         * @param position the position being left
+         * @param target the target where <code>position</code> resides in
+         */
+        void linkingFocusLost(LinkedPosition position, LinkedModeUITarget target);
+        /**
+         * Called when the UI for the linked mode gives focus to a linked position.
+         *
+         * @param position the position being entered
+         * @param target the target where <code>position</code> resides in
+         */
+        void linkingFocusGained(LinkedPosition position, LinkedModeUITarget target);
+    }
+
+    /**
+     * Null object implementation of focus listener.
+     */
+    private static final class EmtpyFocusListener : ILinkedModeUIFocusListener {
+
+        public void linkingFocusGained(LinkedPosition position, LinkedModeUITarget target) {
+            // ignore
+        }
+
+        public void linkingFocusLost(LinkedPosition position, LinkedModeUITarget target) {
+            // ignore
+        }
+    }
+
+    /**
+     * A link target consists of a viewer and gets notified if the linked mode UI on
+     * it is being shown.
+     * <p>
+     * Clients may extend this class.
+     * </p>
+     * @since 3.0
+     */
+    public static abstract class LinkedModeUITarget : ILinkedModeUIFocusListener {
+        /**
+         * Returns the viewer represented by this target, never <code>null</code>.
+         *
+         * @return the viewer associated with this target.
+         */
+        public abstract ITextViewer getViewer();
+
+        /**
+         * The viewer's text widget is initialized when the UI first connects
+         * to the viewer and never changed thereafter. This is to keep the
+         * reference of the widget that we have registered our listeners with,
+         * as the viewer, when it gets disposed, does not remember it, resulting
+         * in a situation where we cannot uninstall the listeners and a memory leak.
+         */
+        StyledText fWidget;
+
+        /** The cached shell - same reason as fWidget. */
+        Shell fShell;
+
+        /** The registered listener, or <code>null</code>. */
+        KeyListener fKeyListener;
+
+        /** The cached custom annotation model. */
+        LinkedPositionAnnotations fAnnotationModel;
+    }
+
+    private static final class EmptyTarget : LinkedModeUITarget {
+
+        private ITextViewer fTextViewer;
+
+        /**
+         * @param viewer the viewer
+         */
+        public this(ITextViewer viewer) {
+            Assert.isNotNull(cast(Object)viewer);
+            fTextViewer= viewer;
+        }
+
+        /*
+         * @see org.eclipse.jdt.internal.ui.text.link2.LinkedModeUI.ILinkedUITarget#getViewer()
+         */
+        public ITextViewer getViewer() {
+            return fTextViewer;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void linkingFocusLost(LinkedPosition position, LinkedModeUITarget target) {
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void linkingFocusGained(LinkedPosition position, LinkedModeUITarget target) {
+        }
+
+    }
+
+    /**
+     * Listens for state changes in the model.
+     */
+    private final class ExitListener : ILinkedModeListener {
+        public void left(LinkedModeModel model, int flags) {
+            leave(ILinkedModeListener.EXIT_ALL | flags);
+        }
+
+        public void suspend(LinkedModeModel model) {
+            disconnect();
+            redraw();
+        }
+
+        public void resume(LinkedModeModel model, int flags) {
+            if ((flags & ILinkedModeListener.EXIT_ALL) !is 0) {
+                leave(flags);
+            } else {
+                connect();
+                if ((flags & ILinkedModeListener.SELECT) !is 0)
+                    select();
+                ensureAnnotationModelInstalled();
+                redraw();
+            }
+        }
+    }
+
+    /**
+     * Exit flags returned if a custom exit policy wants to exit linked mode.
+     * <p>
+     * Clients may instantiate this class.
+     * </p>
+     */
+    public static class ExitFlags {
+        /** The flags to return in the <code>leave</code> method. */
+        public int flags;
+        /** The doit flag of the checked <code>VerifyKeyEvent</code>. */
+        public bool doit;
+        /**
+         * Creates a new instance.
+         *
+         * @param flags the exit flags
+         * @param doit the doit flag for the verify event
+         */
+        public this(int flags, bool doit) {
+            this.flags= flags;
+            this.doit= doit;
+        }
+    }
+
+    /**
+     * An exit policy can be registered by a caller to get custom exit
+     * behavior.
+     * <p>
+     * Clients may implement this interface.
+     * </p>
+     */
+    public interface IExitPolicy {
+        /**
+         * Checks whether the linked mode should be left after receiving the
+         * given <code>VerifyEvent</code> and selection. Note that the event
+         * carries widget coordinates as opposed to <code>offset</code> and
+         * <code>length</code> which are document coordinates.
+         *
+         * @param model the linked mode model
+         * @param event the verify event
+         * @param offset the offset of the current selection
+         * @param length the length of the current selection
+         * @return valid exit flags or <code>null</code> if no special action
+         *         should be taken
+         */
+        ExitFlags doExit(LinkedModeModel model, VerifyEvent event, int offset, int length);
+    }
+
+    /**
+     * A NullObject implementation of <code>IExitPolicy</code>.
+     */
+    private static class NullExitPolicy : IExitPolicy {
+        /*
+         * @see org.eclipse.jdt.internal.ui.text.link2.LinkedModeUI.IExitPolicy#doExit(org.eclipse.swt.events.VerifyEvent, int, int)
+         */
+        public ExitFlags doExit(LinkedModeModel model, VerifyEvent event, int offset, int length) {
+            return null;
+        }
+    }
+
+    /**
+     * Listens for shell events and acts upon them.
+     */
+    private class Closer : ShellListener, ITextInputListener {
+
+        public void shellActivated(ShellEvent e) {
+        }
+
+        public void shellClosed(ShellEvent e) {
+            leave(ILinkedModeListener.EXIT_ALL);
+        }
+
+        public void shellDeactivated(ShellEvent e) {
+//          TODO re-enable after debugging
+//          if (true) return;
+
+            // from LinkedPositionUI:
+
+            // don't deactivate on focus lost, since the proposal popups may take focus
+            // plus: it doesn't hurt if you can check with another window without losing linked mode
+            // since there is no intrusive popup sticking out.
+
+            // need to check first what happens on reentering based on an open action
+            // Seems to be no problem
+
+            // Better:
+            // Check with content assistant and only leave if its not the proposal shell that took the
+            // focus away.
+
+            StyledText text;
+            ITextViewer viewer;
+            Display display;
+
+            if (fCurrentTarget is null || (text= fCurrentTarget.fWidget) is null
+                    || text.isDisposed() || (display= text.getDisplay()) is null
+                    || display.isDisposed()
+                    || (viewer= fCurrentTarget.getViewer()) is null)
+            {
+                leave(ILinkedModeListener.EXIT_ALL);
+            }
+            else
+            {
+                // Post in UI thread since the assistant popup will only get the focus after we lose it.
+                display.asyncExec(dgRunnable( (ITextViewer viewer_) {
+                    if (fIsActive && cast(IEditingSupportRegistry)viewer_ ) {
+                        IEditingSupport[] helpers= (cast(IEditingSupportRegistry) viewer_).getRegisteredSupports();
+                        for (int i= 0; i < helpers.length; i++) {
+                            if (helpers[i].ownsFocusShell())
+                                return;
+                        }
+                    }
+
+                    // else
+                    leave(ILinkedModeListener.EXIT_ALL);
+
+                }, viewer ));
+            }
+        }
+
+        public void shellDeiconified(ShellEvent e) {
+        }
+
+        public void shellIconified(ShellEvent e) {
+            leave(ILinkedModeListener.EXIT_ALL);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
+         */
+        public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+            leave(ILinkedModeListener.EXIT_ALL);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
+         */
+        public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
+        }
+
+    }
+
+    /**
+     * @since 3.1
+     */
+    private class DocumentListener : IDocumentListener {
+        /*
+         * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
+         */
+        public void documentAboutToBeChanged(DocumentEvent event) {
+
+            // default behavior: any document change outside a linked position
+            // causes us to exit
+            int end= event.getOffset() + event.getLength();
+            for (int offset= event.getOffset(); offset <= end; offset++) {
+                if (!fModel.anyPositionContains(offset)) {
+                    ITextViewer viewer= fCurrentTarget.getViewer();
+                    if (fFramePosition !is null && cast(IEditingSupportRegistry)viewer ) {
+                        IEditingSupport[] helpers= (cast(IEditingSupportRegistry) viewer).getRegisteredSupports();
+                        for (int i= 0; i < helpers.length; i++) {
+                            if (helpers[i].isOriginator(null, new Region(fFramePosition.getOffset(), fFramePosition.getLength())))
+                                return;
+                        }
+                    }
+
+                    leave(ILinkedModeListener.EXTERNAL_MODIFICATION);
+                    return;
+                }
+            }
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
+         */
+        public void documentChanged(DocumentEvent event) {
+        }
+    }
+
+    /**
+     * Listens for key events, checks the exit policy for custom exit
+     * strategies but defaults to handling Tab, Enter, and Escape.
+     */
+    private class KeyListener : VerifyKeyListener {
+
+        private bool fIsEnabled= true;
+
+        public void verifyKey(VerifyEvent event) {
+
+            if (!event.doit || !fIsEnabled)
+                return;
+
+            Point selection= fCurrentTarget.getViewer().getSelectedRange();
+            int offset= selection.x;
+            int length= selection.y;
+
+            // if the custom exit policy returns anything, use that
+            ExitFlags exitFlags= fExitPolicy.doExit(fModel, event, offset, length);
+            if (exitFlags !is null) {
+                leave(exitFlags.flags);
+                event.doit= exitFlags.doit;
+                return;
+            }
+
+            // standard behavior:
+            // (Shift+)Tab: jumps from position to position, depending on cycle mode
+            // Enter:       accepts all entries and leaves all (possibly stacked) environments, the last sets the caret
+            // Esc:         accepts all entries and leaves all (possibly stacked) environments, the caret stays
+            // ? what do we do to leave one level of a cycling model that is stacked?
+            // -> This is only the case if the level was set up with forced cycling cast(CYCLE_ALWAYS), in which case
+            // the caller is sure that one does not need by-level exiting.
+            switch (event.character) {
+                // [SHIFT-]TAB = hop between edit boxes
+                case 0x09:
+                    if (!(fExitPosition !is null && fExitPosition.includes(offset)) && !fModel.anyPositionContains(offset)) {
+                        // outside any edit box -> leave (all? TODO should only leave the affected, level and forward to the next upper)
+                        leave(ILinkedModeListener.EXIT_ALL);
+                        break;
+                    }
+
+                    if (event.stateMask is SWT.SHIFT)
+                        previous();
+                    else
+                        next();
+
+                    event.doit= false;
+                    break;
+
+                // ENTER
+                case 0x0A:
+                // Ctrl+Enter on WinXP
+                case 0x0D:
+//                  if ((fExitPosition !is null && fExitPosition.includes(offset)) || !fModel.anyPositionContains(offset)) {
+                    if (!fModel.anyPositionContains(offset)) {
+//                  if ((fExitPosition is null || !fExitPosition.includes(offset)) && !fModel.anyPositionContains(offset)) {
+                        // outside any edit box or on exit position -> leave (all? TODO should only leave the affected, level and forward to the next upper)
+                        leave(ILinkedModeListener.EXIT_ALL);
+                        break;
+                    }
+
+                    // normal case: exit entire stack and put caret to final position
+                    leave(ILinkedModeListener.EXIT_ALL | ILinkedModeListener.UPDATE_CARET);
+                    event.doit= false;
+                    break;
+
+                // ESC
+                case 0x1B:
+                    // exit entire stack and leave caret
+                    leave(ILinkedModeListener.EXIT_ALL);
+                    event.doit= false;
+                    break;
+
+                default:
+                    if (event.character !is 0) {
+                        if (!controlUndoBehavior(offset, length)) {
+                            leave(ILinkedModeListener.EXIT_ALL);
+                            break;
+                        }
+                    }
+            }
+        }
+
+        private bool controlUndoBehavior(int offset, int length) {
+            LinkedPosition position= fModel.findPosition(new LinkedPosition(fCurrentTarget.getViewer().getDocument(), offset, length, LinkedPositionGroup.NO_STOP));
+            if (position !is null) {
+
+                // if the last position is not the same and there is an open change: close it.
+                if (!position.equals(fPreviousPosition))
+                    endCompoundChange();
+
+                beginCompoundChange();
+            }
+
+            fPreviousPosition= position;
+            return fPreviousPosition !is null;
+        }
+
+        /**
+         * @param enabled the new enabled state
+         */
+        public void setEnabled(bool enabled) {
+            fIsEnabled= enabled;
+        }
+
+    }
+
+    /**
+     * Installed as post selection listener on the watched viewer. Updates the
+     * linked position after cursor movement, even to positions not in the
+     * iteration list.
+     */
+    private class MySelectionListener : ISelectionChangedListener {
+
+        /*
+         * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
+         */
+        public void selectionChanged(SelectionChangedEvent event) {
+            ISelection selection= event.getSelection();
+            if ( cast(ITextSelection)selection ) {
+                ITextSelection textsel= cast(ITextSelection) selection;
+                if ( cast(ITextViewer)event.getSelectionProvider() ) {
+                    IDocument doc= (cast(ITextViewer) event.getSelectionProvider()).getDocument();
+                    if (doc !is null) {
+                        int offset= textsel.getOffset();
+                        int length= textsel.getLength();
+                        if (offset >= 0 && length >= 0) {
+                            LinkedPosition find= new LinkedPosition(doc, offset, length, LinkedPositionGroup.NO_STOP);
+                            LinkedPosition pos= fModel.findPosition(find);
+                            if (pos is null && fExitPosition !is null && fExitPosition.includes(find))
+                                pos= fExitPosition;
+
+                            if (pos !is null)
+                                switchPosition(pos, false, false);
+                        }
+                    }
+                }
+            }
+        }
+
+    }
+
+    private class ProposalListener : IProposalListener {
+
+        /*
+         * @see org.eclipse.jface.internal.text.link.contentassist.IProposalListener#proposalChosen(org.eclipse.jface.text.contentassist.ICompletionProposal)
+         */
+        public void proposalChosen(ICompletionProposal proposal) {
+            next();
+        }
+    }
+
+    /** The current viewer. */
+    private LinkedModeUITarget fCurrentTarget;
+    /**
+     * The manager of the linked positions we provide a UI for.
+     * @since 3.1
+     */
+    private LinkedModeModel fModel;
+    /** The set of viewers we manage. */
+    private LinkedModeUITarget[] fTargets;
+    /** The iterator over the tab stop positions. */
+    private TabStopIterator fIterator;
+
+    /* Our team of event listeners */
+    /** The shell listener. */
+    private Closer fCloser;
+    /** The linked mode listener. */
+    private ILinkedModeListener fLinkedListener;
+    /** The selection listener. */
+    private MySelectionListener fSelectionListener;
+    /** The content assist listener. */
+    private ProposalListener fProposalListener;
+    /**
+     * The document listener.
+     * @since 3.1
+     */
+    private IDocumentListener fDocumentListener;
+
+    /** The last caret position, used by fCaretListener. */
+    private const Position fCaretPosition;
+    /** The exit policy to control custom exit behavior */
+    private IExitPolicy fExitPolicy;
+    /** The current frame position shown in the UI, or <code>null</code>. */
+    private LinkedPosition fFramePosition;
+    /** The last visited position, used for undo / redo. */
+    private LinkedPosition fPreviousPosition;
+    /** The content assistant used to show proposals. */
+    private ContentAssistant2 fAssistant;
+    /** The exit position. */
+    private LinkedPosition fExitPosition;
+    /** State indicator to prevent multiple invocation of leave. */
+    private bool fIsActive= false;
+    /** The position updater for the exit position. */
+    private IPositionUpdater fPositionUpdater;
+    /** Whether to show context info. */
+    private bool fDoContextInfo= false;
+    /** Whether we have begun a compound change, but not yet closed. */
+    private bool fHasOpenCompoundChange= false;
+    /** The position listener. */
+    private ILinkedModeUIFocusListener fPositionListener;
+    private IAutoEditStrategy fAutoEditVetoer;
+    private void fAutoEditVetoer_init(){
+        fAutoEditVetoer = new class()  IAutoEditStrategy {
+            /*
+            * @see org.eclipse.jface.text.IAutoEditStrategy#customizeDocumentCommand(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.DocumentCommand)
+            */
+            public void customizeDocumentCommand(IDocument document, DocumentCommand command) {
+                // invalidate the change to ensure that the change is performed on the document only.
+                if (fModel.anyPositionContains(command.offset)) {
+                    command.doit= false;
+                    command.caretOffset= command.offset + command.length;
+                }
+
+            }
+        };
+    }
+
+    /** Whether this UI is in simple highlighting mode or not. */
+    private bool fSimple;
+
+    /**
+     * Tells whether colored labels support is enabled.
+     * @since 3.4
+     */
+    private bool fIsColoredLabelsSupportEnabled= false;
+
+    private this(){
+        fCloser= new Closer();
+        fLinkedListener= new ExitListener();
+        fSelectionListener= new MySelectionListener();
+        fProposalListener= new ProposalListener();
+        fDocumentListener= new DocumentListener();
+        fCaretPosition= new Position(0, 0);
+        fExitPolicy= new NullExitPolicy();
+        fPositionUpdater= new DefaultPositionUpdater(getCategory());
+        fPositionListener= new EmtpyFocusListener();
+        fAutoEditVetoer_init();
+    }
+
+    /**
+     * Creates a new UI on the given model and the set of viewers. The model
+     * must provide a tab stop sequence with a non-empty list of tab stops.
+     *
+     * @param model the linked mode model
+     * @param targets the non-empty list of targets upon which the linked mode
+     *        UI should act
+     */
+    public this(LinkedModeModel model, LinkedModeUITarget[] targets) {
+        this();
+        constructor(model, targets);
+    }
+
+    /**
+     * Convenience constructor for just one viewer.
+     *
+     * @param model the linked mode model
+     * @param viewer the viewer upon which the linked mode UI should act
+     */
+    public this(LinkedModeModel model, ITextViewer viewer) {
+        this();
+        constructor(model, [new EmptyTarget(viewer)]);
+    }
+
+    /**
+     * Convenience constructor for multiple viewers.
+     *
+     * @param model the linked mode model
+     * @param viewers the non-empty list of viewers upon which the linked mode
+     *        UI should act
+     */
+    public this(LinkedModeModel model, ITextViewer[] viewers) {
+        this();
+        LinkedModeUITarget[] array= new LinkedModeUITarget[viewers.length];
+        for (int i= 0; i < array.length; i++) {
+            array[i]= new EmptyTarget(viewers[i]);
+        }
+        constructor(model, array);
+    }
+
+    /**
+     * Convenience constructor for one target.
+     *
+     * @param model the linked mode model
+     * @param target the target upon which the linked mode UI should act
+     */
+    public this(LinkedModeModel model, LinkedModeUITarget target) {
+        this();
+        constructor(model, [target]);
+    }
+
+    /**
+     * This does the actual constructor work.
+     *
+     * @param model the linked mode model
+     * @param targets the non-empty array of targets upon which the linked mode UI
+     *        should act
+     */
+    private void constructor(LinkedModeModel model, LinkedModeUITarget[] targets) {
+        Assert.isNotNull(model);
+        //Assert.isNotNull(targets);
+        Assert.isTrue(targets.length > 0);
+        Assert.isTrue(model.getTabStopSequence().size() > 0);
+
+        fModel= model;
+        fTargets= targets;
+        fCurrentTarget= targets[0];
+        fIterator= new TabStopIterator(fModel.getTabStopSequence());
+        fIterator.setCycling(!fModel.isNested());
+        fModel.addLinkingListener(fLinkedListener);
+
+        fAssistant= new ContentAssistant2();
+        fAssistant.addProposalListener(fProposalListener);
+        // TODO find a way to set up content assistant.
+//      fAssistant.setDocumentPartitioning(IJavaPartitions.JAVA_PARTITIONING);
+        fAssistant.enableColoredLabels(fIsColoredLabelsSupportEnabled);
+        fCaretPosition.delete_();
+    }
+
+    /**
+     * Starts this UI on the first position.
+     */
+    public void enter() {
+        fIsActive= true;
+        connect();
+        next();
+    }
+
+    /**
+     * Sets an <code>IExitPolicy</code> to customize the exit behavior of
+     * this linked mode UI.
+     *
+     * @param policy the exit policy to use.
+     */
+    public void setExitPolicy(IExitPolicy policy) {
+        fExitPolicy= policy;
+    }
+
+    /**
+     * Sets the exit position to move the caret to when linked mode mode is
+     * exited.
+     *
+     * @param target the target where the exit position is located
+     * @param offset the offset of the exit position
+     * @param length the length of the exit position (in case there should be a
+     *        selection)
+     * @param sequence set to the tab stop position of the exit position, or
+     *        <code>LinkedPositionGroup.NO_STOP</code> if there should be no
+     *        tab stop.
+     * @throws BadLocationException if the position is not valid in the viewer's
+     *         document
+     */
+    public void setExitPosition(LinkedModeUITarget target, int offset, int length, int sequence)  {
+        // remove any existing exit position
+        if (fExitPosition !is null) {
+            fExitPosition.getDocument().removePosition(fExitPosition);
+            fIterator.removePosition(fExitPosition);
+            fExitPosition= null;
+        }
+
+        IDocument doc= target.getViewer().getDocument();
+        if (doc is null)
+            return;
+
+        fExitPosition= new LinkedPosition(doc, offset, length, sequence);
+        doc.addPosition(fExitPosition); // gets removed in leave()
+        if (sequence !is LinkedPositionGroup.NO_STOP)
+            fIterator.addPosition(fExitPosition);
+
+    }
+
+    /**
+     * Sets the exit position to move the caret to when linked mode is exited.
+     *
+     * @param viewer the viewer where the exit position is located
+     * @param offset the offset of the exit position
+     * @param length the length of the exit position (in case there should be a
+     *        selection)
+     * @param sequence set to the tab stop position of the exit position, or
+     *        <code>LinkedPositionGroup.NO_STOP</code> if there should be no tab stop.
+     * @throws BadLocationException if the position is not valid in the
+     *         viewer's document
+     */
+    public void setExitPosition(ITextViewer viewer, int offset, int length, int sequence)  {
+        setExitPosition(new EmptyTarget(viewer), offset, length, sequence);
+    }
+
+    /**
+     * Sets the cycling mode to either of <code>CYCLING_ALWAYS</code>,
+     * <code>CYCLING_NEVER</code>, or <code>CYCLING_WHEN_NO_PARENT</code>,
+     * which is the default.
+     *
+     * @param mode the new cycling mode.
+     */
+    public void setCyclingMode(Object mode) {
+        if (mode !is CYCLE_ALWAYS && mode !is CYCLE_NEVER && mode !is CYCLE_WHEN_NO_PARENT)
+            throw new IllegalArgumentException(null);
+
+        if (mode is CYCLE_ALWAYS || mode is CYCLE_WHEN_NO_PARENT && !fModel.isNested())
+            fIterator.setCycling(true);
+        else
+            fIterator.setCycling(false);
+    }
+
+    void next() {
+        if (fIterator.hasNext(fFramePosition)) {
+            switchPosition(fIterator.next(fFramePosition), true, true);
+            return;
+        }
+        leave(ILinkedModeListener.UPDATE_CARET);
+    }
+
+    void previous() {
+        if (fIterator.hasPrevious(fFramePosition)) {
+            switchPosition(fIterator.previous(fFramePosition), true, true);
+        } else
+            // dont't update caret, but rather select the current frame
+            leave(ILinkedModeListener.SELECT);
+    }
+
+    private void triggerContextInfo() {
+        ITextOperationTarget target= fCurrentTarget.getViewer().getTextOperationTarget();
+        if (target !is null) {
+            if (target.canDoOperation(ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION))
+                target.doOperation(ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION);
+        }
+    }
+
+    /** Trigger content assist on choice positions */
+    private void triggerContentAssist() {
+        if ( cast(ProposalPosition)fFramePosition ) {
+            ProposalPosition pp= cast(ProposalPosition) fFramePosition;
+            ICompletionProposal[] choices= pp.getChoices();
+            if (choices !is null && choices.length > 0) {
+                fAssistant.setCompletions(choices);
+                fAssistant.showPossibleCompletions();
+                return;
+            }
+        }
+
+        fAssistant.setCompletions(new ICompletionProposal[0]);
+        fAssistant.hidePossibleCompletions();
+    }
+
+    private void switchPosition(LinkedPosition pos, bool select_, bool showProposals) {
+        Assert.isNotNull(pos);
+        if (pos.equals(fFramePosition))
+            return;
+
+        if (fFramePosition !is null && fCurrentTarget !is null)
+            fPositionListener.linkingFocusLost(fFramePosition, fCurrentTarget);
+
+        // undo
+        endCompoundChange();
+
+        redraw(); // redraw current position being left - usually not needed
+        IDocument oldDoc= fFramePosition is null ? null : fFramePosition.getDocument();
+        IDocument newDoc= pos.getDocument();
+
+        switchViewer(oldDoc, newDoc, pos);
+        fFramePosition= pos;
+
+        if (select_)
+            select();
+        if (fFramePosition is fExitPosition && !fIterator.isCycling())
+            leave(ILinkedModeListener.NONE);
+        else {
+            redraw(); // redraw new position
+            ensureAnnotationModelInstalled();
+        }
+        if (showProposals)
+            triggerContentAssist();
+        if (fFramePosition !is fExitPosition && fDoContextInfo)
+            triggerContextInfo();
+
+        if (fFramePosition !is null && fCurrentTarget !is null)
+            fPositionListener.linkingFocusGained(fFramePosition, fCurrentTarget);
+
+    }
+
+    private void ensureAnnotationModelInstalled() {
+        LinkedPositionAnnotations lpa= fCurrentTarget.fAnnotationModel;
+        if (lpa !is null) {
+            ITextViewer viewer= fCurrentTarget.getViewer();
+            if ( cast(ISourceViewer)viewer ) {
+                ISourceViewer sv= cast(ISourceViewer) viewer;
+                IAnnotationModel model= sv.getAnnotationModel();
+                if ( cast(IAnnotationModelExtension)model ) {
+                    IAnnotationModelExtension ext= cast(IAnnotationModelExtension) model;
+                    IAnnotationModel ourModel= ext.getAnnotationModel(stringcast(getUniqueKey()));
+                    if (ourModel is null) {
+                        ext.addAnnotationModel(stringcast(getUniqueKey()), lpa);
+                    }
+                }
+            }
+        }
+    }
+
+    private void uninstallAnnotationModel(LinkedModeUITarget target) {
+        ITextViewer viewer= target.getViewer();
+        if ( cast(ISourceViewer)viewer ) {
+            ISourceViewer sv= cast(ISourceViewer) viewer;
+            IAnnotationModel model= sv.getAnnotationModel();
+            if ( cast(IAnnotationModelExtension)model ) {
+                IAnnotationModelExtension ext= cast(IAnnotationModelExtension) model;
+                ext.removeAnnotationModel(stringcast(getUniqueKey()));
+            }
+        }
+    }
+
+    private void switchViewer(IDocument oldDoc, IDocument newDoc, LinkedPosition pos) {
+        if (oldDoc !is newDoc) {
+
+            // redraw current document with new position before switching viewer
+            if (fCurrentTarget.fAnnotationModel !is null)
+                fCurrentTarget.fAnnotationModel.switchToPosition(fModel, pos);
+
+            LinkedModeUITarget target= null;
+            for (int i= 0; i < fTargets.length; i++) {
+                if (fTargets[i].getViewer().getDocument() is newDoc) {
+                    target= fTargets[i];
+                    break;
+                }
+            }
+            if (target !is fCurrentTarget) {
+                disconnect();
+                fCurrentTarget= target;
+                target.linkingFocusLost(fFramePosition, target);
+                connect();
+                ensureAnnotationModelInstalled();
+                if (fCurrentTarget !is null)
+                    fCurrentTarget.linkingFocusGained(pos, fCurrentTarget);
+            }
+        }
+    }
+
+    private void select() {
+        ITextViewer viewer= fCurrentTarget.getViewer();
+        if ( cast(ITextViewerExtension5)viewer ) {
+            ITextViewerExtension5 extension5= cast(ITextViewerExtension5) viewer;
+            extension5.exposeModelRange(new Region(fFramePosition.offset, fFramePosition.length));
+        } else if (!viewer.overlapsWithVisibleRegion(fFramePosition.offset, fFramePosition.length)) {
+            viewer.resetVisibleRegion();
+        }
+        viewer.revealRange(fFramePosition.offset, fFramePosition.length);
+        viewer.setSelectedRange(fFramePosition.offset, fFramePosition.length);
+    }
+
+    private void redraw() {
+        if (fCurrentTarget.fAnnotationModel !is null)
+            fCurrentTarget.fAnnotationModel.switchToPosition(fModel, fFramePosition);
+    }
+
+    private void connect() {
+        Assert.isNotNull(fCurrentTarget);
+        ITextViewer viewer= fCurrentTarget.getViewer();
+        Assert.isNotNull(cast(Object)viewer);
+        fCurrentTarget.fWidget= viewer.getTextWidget();
+        if (fCurrentTarget.fWidget is null)
+            leave(ILinkedModeListener.EXIT_ALL);
+
+        if (fCurrentTarget.fKeyListener is null) {
+            fCurrentTarget.fKeyListener= new KeyListener();
+            (cast(ITextViewerExtension) viewer).prependVerifyKeyListener(fCurrentTarget.fKeyListener);
+        } else
+            fCurrentTarget.fKeyListener.setEnabled(true);
+
+        registerAutoEditVetoer(viewer);
+
+        (cast(IPostSelectionProvider) viewer).addPostSelectionChangedListener(fSelectionListener);
+
+        createAnnotationModel();
+
+        showSelection();
+
+        fCurrentTarget.fShell= fCurrentTarget.fWidget.getShell();
+        if (fCurrentTarget.fShell is null)
+            leave(ILinkedModeListener.EXIT_ALL);
+        fCurrentTarget.fShell.addShellListener(fCloser);
+
+        fAssistant.install(viewer);
+
+        viewer.addTextInputListener(fCloser);
+
+        viewer.getDocument().addDocumentListener(fDocumentListener);
+    }
+
+    /**
+     * Reveals the selection on the current target's widget, if it is valid.
+     */
+    private void showSelection() {
+        final StyledText widget= fCurrentTarget.fWidget;
+        if (widget is null || widget.isDisposed())
+            return;
+
+        // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=132263
+        widget.getDisplay().asyncExec(new class()  Runnable {
+            public void run() {
+                if (!widget.isDisposed())
+                    try {
+                    widget.showSelection();
+                    } catch (IllegalArgumentException e) {
+                        /*
+                         * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=66914
+                         * if the StyledText is in setRedraw(false) mode, its
+                         * selection may not be up2date and calling showSelection
+                         * will throw an IAE.
+                         * We don't have means to find out whether the selection is valid
+                         * or whether the widget is redrawing or not therefore we try
+                         * and ignore an IAE.
+                         */
+                    }
+            }
+        });
+    }
+
+    /**
+     * Registers our auto edit vetoer with the viewer.
+     *
+     * @param viewer the viewer we want to veto ui-triggered changes within
+     *        linked positions
+     */
+    private void registerAutoEditVetoer(ITextViewer viewer) {
+        try {
+            String[] contentTypes= getContentTypes(viewer.getDocument());
+            if ( cast(ITextViewerExtension2)viewer ) {
+                ITextViewerExtension2 vExtension= (cast(ITextViewerExtension2) viewer);
+                for (int i= 0; i < contentTypes.length; i++) {
+                    vExtension.prependAutoEditStrategy(fAutoEditVetoer, contentTypes[i]);
+                }
+            } else {
+                Assert.isTrue(false);
+            }
+
+        } catch (BadPartitioningException e) {
+            leave(ILinkedModeListener.EXIT_ALL);
+        }
+    }
+
+    private void unregisterAutoEditVetoer(ITextViewer viewer) {
+        try {
+            String[] contentTypes= getContentTypes(viewer.getDocument());
+            if ( cast(ITextViewerExtension2)viewer ) {
+                ITextViewerExtension2 vExtension= (cast(ITextViewerExtension2) viewer);
+                for (int i= 0; i < contentTypes.length; i++) {
+                    vExtension.removeAutoEditStrategy(fAutoEditVetoer, contentTypes[i]);
+                }
+            } else {
+                Assert.isTrue(false);
+            }
+        } catch (BadPartitioningException e) {
+            leave(ILinkedModeListener.EXIT_ALL);
+        }
+    }
+
+    /**
+     * Returns all possible content types of <code>document</code>.
+     *
+     * @param document the document
+     * @return all possible content types of <code>document</code>
+     * @throws BadPartitioningException
+     * @since 3.1
+     */
+    private String[] getContentTypes(IDocument document)  {
+        if ( cast(IDocumentExtension3)document ) {
+            IDocumentExtension3 ext= cast(IDocumentExtension3) document;
+            String[] partitionings= ext.getPartitionings();
+            Set contentTypes= new HashSet(20);
+            for (int i= 0; i < partitionings.length; i++) {
+                contentTypes.addAll(Arrays.asList(stringcast(ext.getLegalContentTypes(partitionings[i]))));
+            }
+            contentTypes.add(IDocument.DEFAULT_CONTENT_TYPE);
+            return stringcast( contentTypes.toArray());
+        }
+        return document.getLegalContentTypes();
+    }
+
+    private void createAnnotationModel() {
+        if (fCurrentTarget.fAnnotationModel is null) {
+            LinkedPositionAnnotations lpa= new LinkedPositionAnnotations();
+            if (fSimple) {
+                lpa.markExitTarget(true);
+                lpa.markFocus(false);
+                lpa.markSlaves(false);
+                lpa.markTargets(false);
+            }
+            lpa.setTargets(fIterator.getPositions());
+            lpa.setExitTarget(fExitPosition);
+            lpa.connect(fCurrentTarget.getViewer().getDocument());
+            fCurrentTarget.fAnnotationModel= lpa;
+        }
+    }
+
+    private String getUniqueKey() {
+        return "linked.annotationmodelkey."~toString(); //$NON-NLS-1$
+    }
+
+    private void disconnect() {
+        Assert.isNotNull(fCurrentTarget);
+        ITextViewer viewer= fCurrentTarget.getViewer();
+        Assert.isNotNull(cast(Object)viewer);
+
+        viewer.getDocument().removeDocumentListener(fDocumentListener);
+
+        fAssistant.uninstall();
+        fAssistant.removeProposalListener(fProposalListener);
+
+        fCurrentTarget.fWidget= null;
+
+        Shell shell= fCurrentTarget.fShell;
+        fCurrentTarget.fShell= null;
+
+        if (shell !is null && !shell.isDisposed())
+            shell.removeShellListener(fCloser);
+
+        // this one is asymmetric: we don't install the model in
+        // connect, but leave it to its callers to ensure they
+        // have the model installed if they need it
+        uninstallAnnotationModel(fCurrentTarget);
+
+        unregisterAutoEditVetoer(viewer);
+
+        // don't remove the verify key listener to let it keep its position
+        // in the listener queue
+        if (fCurrentTarget.fKeyListener !is null)
+            fCurrentTarget.fKeyListener.setEnabled(false);
+
+        (cast(IPostSelectionProvider) viewer).removePostSelectionChangedListener(fSelectionListener);
+
+        redraw();
+    }
+
+    void leave(int flags) {
+        if (!fIsActive)
+            return;
+        fIsActive= false;
+
+        endCompoundChange();
+
+        Display display= null;
+        if (fCurrentTarget.fWidget !is null && !fCurrentTarget.fWidget.isDisposed())
+            display= fCurrentTarget.fWidget.getDisplay();
+
+        if (fCurrentTarget.fAnnotationModel !is null)
+            fCurrentTarget.fAnnotationModel.removeAllAnnotations();
+        disconnect();
+
+        for (int i= 0; i < fTargets.length; i++) {
+            LinkedModeUITarget target= fTargets[i];
+            ITextViewer viewer= target.getViewer();
+            if (target.fKeyListener !is null) {
+                (cast(ITextViewerExtension) viewer).removeVerifyKeyListener(target.fKeyListener);
+                target.fKeyListener= null;
+            }
+
+            viewer.removeTextInputListener(fCloser);
+        }
+
+        for (int i= 0; i < fTargets.length; i++) {
+
+            if (fTargets[i].fAnnotationModel !is null) {
+                fTargets[i].fAnnotationModel.removeAllAnnotations();
+                fTargets[i].fAnnotationModel.disconnect(fTargets[i].getViewer().getDocument());
+                fTargets[i].fAnnotationModel= null;
+            }
+
+            uninstallAnnotationModel(fTargets[i]);
+        }
+
+
+        if ((flags & ILinkedModeListener.UPDATE_CARET) !is 0 && fExitPosition !is null && fFramePosition !is fExitPosition && !fExitPosition.isDeleted())
+            switchPosition(fExitPosition, true, false);
+
+        final List docs= new ArrayList();
+        for (int i= 0; i < fTargets.length; i++) {
+            IDocument doc= fTargets[i].getViewer().getDocument();
+            if (doc !is null)
+                docs.add(cast(Object)doc);
+        }
+
+        fModel.stopForwarding(flags);
+
+        Runnable runnable= dgRunnable( (int flags_){
+            if (fExitPosition !is null)
+                fExitPosition.getDocument().removePosition(fExitPosition);
+
+            for (Iterator iter = docs.iterator(); iter.hasNext(); ) {
+                IDocument doc= cast(IDocument) iter.next();
+                doc.removePositionUpdater(fPositionUpdater);
+                bool uninstallCat= false;
+                String[] cats= doc.getPositionCategories();
+                for (int j= 0; j < cats.length; j++) {
+                    if (getCategory().equals(cats[j])) {
+                        uninstallCat= true;
+                        break;
+                    }
+                }
+                if (uninstallCat)
+                    try {
+                        doc.removePositionCategory(getCategory());
+                    } catch (BadPositionCategoryException e) {
+                        // ignore
+                    }
+            }
+            fModel.exit(flags_);
+        }, flags );
+
+        // remove positions (both exit positions AND linked positions in the
+        // model) asynchronously to make sure that the annotation painter
+        // gets correct document offsets.
+        if (display !is null)
+            display.asyncExec(runnable);
+        else
+            runnable.run();
+    }
+
+    private void endCompoundChange() {
+        if (fHasOpenCompoundChange) {
+            ITextViewerExtension extension= cast(ITextViewerExtension) fCurrentTarget.getViewer();
+            IRewriteTarget target= extension.getRewriteTarget();
+            target.endCompoundChange();
+            fHasOpenCompoundChange= false;
+        }
+    }
+
+    private void beginCompoundChange() {
+        if (!fHasOpenCompoundChange) {
+            ITextViewerExtension extension= cast(ITextViewerExtension) fCurrentTarget.getViewer();
+            IRewriteTarget target= extension.getRewriteTarget();
+            target.beginCompoundChange();
+            fHasOpenCompoundChange= true;
+        }
+    }
+
+    /**
+     * Returns the currently selected region or <code>null</code>.
+     *
+     * @return the currently selected region or <code>null</code>
+     */
+    public IRegion getSelectedRegion() {
+        if (fFramePosition !is null)
+            return new Region(fFramePosition.getOffset(), fFramePosition.getLength());
+        if (fExitPosition !is null)
+            return new Region(fExitPosition.getOffset(), fExitPosition.getLength());
+        return null;
+    }
+
+    private String getCategory() {
+        return toString();
+    }
+
+    /**
+     * Sets the context info property. If set to <code>true</code>, context
+     * info will be invoked on the current target's viewer whenever a position
+     * is switched.
+     *
+     * @param doContextInfo <code>true</code> if context information should be
+     *        displayed
+     */
+    public void setDoContextInfo(bool doContextInfo) {
+        fDoContextInfo= doContextInfo;
+    }
+
+    /**
+     * Sets the focus callback which will get informed when the focus of the
+     * linked mode UI changes.
+     * <p>
+     * If there is a listener installed already, it will be replaced.
+     * </p>
+     *
+     * @param listener the new listener, never <code>null</code>.
+     */
+    protected void setPositionListener(ILinkedModeUIFocusListener listener) {
+        Assert.isNotNull(cast(Object)listener);
+        fPositionListener= listener;
+    }
+
+    /**
+     * Sets the "simple" mode of the receiver. A linked mode UI in simple mode
+     * merely draws the exit position, but not the target, focus, and slave
+     * positions. Default is <code>false</code>. This method must be called
+     * before it is entered.
+     *
+     * @param simple <code>true</code> if the UI should be in simple mode.
+     */
+    public void setSimpleMode(bool simple) {
+        fSimple= simple;
+    }
+
+    /**
+     * Enables the support for colored labels in the proposal popup.
+     * <p>Completion proposals can implement {@link ICompletionProposalExtension6}
+     * to provide colored proposal labels.</p>
+     *
+     * @param isEnabled if <code>true</code> the support for colored labels is enabled in the proposal popup
+     * @since 3.4
+     */
+    public void enableColoredLabels(bool isEnabled) {
+        fIsColoredLabelsSupportEnabled= isEnabled;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/link/LinkedPositionAnnotations.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,337 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.link.LinkedPositionAnnotations;
+
+import org.eclipse.jface.text.link.LinkedModeModel; // packageimport
+import org.eclipse.jface.text.link.LinkedPosition; // packageimport
+import org.eclipse.jface.text.link.ILinkedModeListener; // packageimport
+import org.eclipse.jface.text.link.TabStopIterator; // packageimport
+import org.eclipse.jface.text.link.LinkedModeUI; // packageimport
+import org.eclipse.jface.text.link.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionGroup; // packageimport
+import org.eclipse.jface.text.link.LinkedModeManager; // packageimport
+import org.eclipse.jface.text.link.ProposalPosition; // packageimport
+
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+
+
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.AnnotationModel;
+
+/**
+ * Internal class.
+ *
+ * @since 3.0
+ */
+final class LinkedPositionAnnotations : AnnotationModel {
+
+    /* annotation types */
+    private static const String TARGET_ANNOTATION_TYPE= "org.eclipse.ui.internal.workbench.texteditor.link.target"; //$NON-NLS-1$
+    private static const String SLAVE_ANNOTATION_TYPE= "org.eclipse.ui.internal.workbench.texteditor.link.slave"; //$NON-NLS-1$
+    private static const String FOCUS_ANNOTATION_TYPE= "org.eclipse.ui.internal.workbench.texteditor.link.master"; //$NON-NLS-1$
+    private static const String EXIT_ANNOTATION_TYPE= "org.eclipse.ui.internal.workbench.texteditor.link.exit"; //$NON-NLS-1$
+
+    /* configuration */
+    private bool fMarkTargets= true;
+    private bool fMarkSlaves= true;
+    private bool fMarkFocus= true;
+    private bool fMarkExitTarget= true;
+
+    private Annotation fFocusAnnotation= null;
+    private Annotation fExitAnnotation= null;
+    private const Map fGroupAnnotations;
+    private const Map fTargetAnnotations;
+    private Position[] fTargets;
+    private LinkedPosition fExitPosition= null;
+
+    public this(){
+        fGroupAnnotations= new HashMap();
+        fTargetAnnotations= new HashMap();
+    }
+
+    /**
+     * Sets the position that should be highlighted as the focus position, i.e.
+     * as the position whose changes are propagated to all its linked positions
+     * by the linked environment.
+     *
+     * @param position the new focus position, or <code>null</code> if no focus is set.
+     * @throws BadLocationException if <code>position</code> is invalid
+     */
+    private void setFocusPosition(Position position)  {
+        if (fMarkFocus && getPosition(fFocusAnnotation) !is position) {
+            removeAnnotation(fFocusAnnotation, false);
+            if (position !is null) {
+                fFocusAnnotation= new Annotation(FOCUS_ANNOTATION_TYPE, false, ""); //$NON-NLS-1$
+                addAnnotation(fFocusAnnotation, position, false);
+            } else
+                fFocusAnnotation= null;
+        }
+    }
+
+    /**
+     * Sets the position that should be highlighted as the exit position, i.e.
+     * as the position whose changes are propagated to all its linked positions
+     * by the linked environment.
+     *
+     * @param position the new exit position, or <code>null</code> if no focus is set.
+     * @throws BadLocationException in case <code>position</code> is invalid
+     */
+    private void setExitPosition(Position position)  {
+        if (fMarkExitTarget && getPosition(fExitAnnotation) !is position) {
+            removeAnnotation(fExitAnnotation, false);
+            if (position !is null) {
+                fExitAnnotation= new Annotation(EXIT_ANNOTATION_TYPE, false, ""); //$NON-NLS-1$
+                addAnnotation(fExitAnnotation, position, false);
+            } else
+                fExitAnnotation= null;
+        }
+    }
+
+    /**
+     * Sets the positions that should be highlighted as the slave positions, i.e.
+     * as the positions that are linked to the focus position.
+     *
+     * @param positions the new slave positions, or <code>null</code> if no slave positions are to be set
+     * @throws BadLocationException in case any of the given positions is invalid
+     */
+    private void setGroupPositions(List positions)  {
+        if (!fMarkSlaves)
+            return;
+
+        // remove all positions which are already there
+        // Algorithm: toRemove contains all mappings at first, but all that are in
+        // positions get removed -> toRemove contains the difference set of previous - new
+        // toAdd are the new positions, which don't exist in previous = new - previous
+        List toRemove= new ArrayList(fGroupAnnotations.values());
+        Map toAdd= new HashMap();
+        if (positions !is null) {
+            for (Iterator iter= positions.iterator(); iter.hasNext();) {
+                Position p= cast(Position) iter.next();
+                if (fGroupAnnotations.containsKey(p)) {
+                    toRemove.remove(fGroupAnnotations.get(p));
+                } else {
+                    Annotation a= new Annotation(SLAVE_ANNOTATION_TYPE, false, ""); //$NON-NLS-1$
+                    toAdd.put(a, p);
+                    fGroupAnnotations.put(p, a);
+                }
+            }
+        }
+        fGroupAnnotations.values().removeAll(toRemove);
+
+        replaceAnnotations(arraycast!(Annotation)( toRemove.toArray()), toAdd, false);
+    }
+
+    /**
+     * Sets the positions that should be highlighted as the target positions, i.e.
+     * as the positions that can be jumped to in a linked set up.
+     *
+     * @param positions the new target positions, or <code>null</code> if no target positions are to be set
+     * @throws BadLocationException in case any of the given positions is invalid
+     */
+    private void setTargetPositions(List positions)  {
+        if (!fMarkTargets)
+            return;
+
+        // remove all positions which are already there
+        // Algorithm: toRemove contains all mappings at first, but all that are in
+        // positions get removed -> toRemove contains the difference set of previous - new
+        // toAdd are the new positions, which don't exist in previous = new - previous
+        List toRemove= new ArrayList(fTargetAnnotations.values());
+        Map toAdd= new HashMap();
+        if (positions !is null) {
+            for (Iterator iter= positions.iterator(); iter.hasNext();) {
+                Position p= cast(Position) iter.next();
+                if (fTargetAnnotations.containsKey(p)) {
+                    toRemove.remove(fTargetAnnotations.get(p));
+                } else {
+                    Annotation a= new Annotation(TARGET_ANNOTATION_TYPE, false, ""); //$NON-NLS-1$
+                    toAdd.put(a, p);
+                    fTargetAnnotations.put(p, a);
+                }
+            }
+        }
+        fTargetAnnotations.values().removeAll(toRemove);
+
+        replaceAnnotations(arraycast!(Annotation)( toRemove.toArray()), toAdd, false);
+    }
+
+    /**
+     * Switches the focus position to <code>position</code> given the
+     * <code>LinkedModeModel env</code>. The slave positions for <code>position</code>
+     * is extracted from the environment and set accordingly, the target positions
+     * are updated as well.
+     *
+     * @param env
+     * @param position
+     */
+    public void switchToPosition(LinkedModeModel env, LinkedPosition position) {
+        if (fDocument is null ||
+                (position !is null && getPosition(fFocusAnnotation) is position) ||
+                (position is null && fFocusAnnotation is null))
+            return;
+
+        LinkedPositionGroup linkedGroup= null;
+        if (position !is null)
+            linkedGroup= env.getGroupForPosition(position);
+
+        List targets= new ArrayList();
+        targets.addAll(Arrays.asList(fTargets));
+
+        List group;
+        if (linkedGroup !is null)
+            group= new ArrayList(Arrays.asList(linkedGroup.getPositions()));
+        else
+            group= new ArrayList();
+
+        if (position is null || !(cast(Object)fDocument).opEquals(cast(Object)position.getDocument()))
+            // position is not valid if not in this document
+            position= null;
+
+        LinkedPosition exit= fExitPosition;
+        if (exit is null || !(cast(Object)fDocument).opEquals(cast(Object)exit.getDocument()))
+            // position is not valid if not in this document
+            exit= null;
+
+
+        if (exit !is null) {
+            group.remove(exit);
+            targets.remove(exit);
+        }
+
+        group.removeAll(targets);
+        targets.remove(position);
+        group.remove(position);
+        prune(targets);
+        prune(group);
+
+        try {
+            setFocusPosition(position);
+            setExitPosition(exit);
+            setGroupPositions(group);
+            setTargetPositions(targets);
+        } catch (BadLocationException e) {
+            // will never happen as we don't actually add/remove positions from the document
+            // see the addPosition / removePosition methods
+            Assert.isTrue(false);
+        }
+        fireModelChanged();
+
+    }
+
+    /**
+     * Prune <code>list</code> of all <code>LinkedPosition</code>s that
+     * do not belong to this model's <code>IDocument</code>.
+     *
+     * @param list the list of positions to prune
+     */
+    private void prune(List list) {
+        for (Iterator iter= list.iterator(); iter.hasNext();) {
+            LinkedPosition pos= cast(LinkedPosition) iter.next();
+            if (!(cast(Object)pos.getDocument()).opEquals(cast(Object)fDocument))
+                iter.remove();
+        }
+    }
+
+    /**
+     * Sets the target positions
+     * @param positions
+     */
+    public void setTargets(Position[] positions) {
+        fTargets= positions;
+    }
+
+    /**
+     * Sets the exit position.
+     *
+     * @param position the new exit position, or <code>null</code> if no exit position should be set
+     */
+    public void setExitTarget(LinkedPosition position) {
+        fExitPosition = position;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.AnnotationModel#addPosition(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.Position)
+     */
+    protected void addPosition(IDocument document, Position position) {
+        // don't to anything as our positions are managed by custom
+        // position updaters
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.AnnotationModel#removePosition(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.Position)
+     */
+    protected void removePosition(IDocument document, Position pos) {
+        // don't to anything as our positions are managed by custom
+        // position updaters
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.AnnotationModel#fireModelChanged()
+     */
+    public void fireModelChanged() {
+        super.fireModelChanged();
+    }
+
+    /**
+     * Sets the drawing state for the exit target. Default is <code>true</code>.
+     *
+     * @param markExitTargets the new drawing state for exit targets
+     */
+    public void markExitTarget(bool markExitTargets) {
+        fMarkExitTarget= markExitTargets;
+    }
+
+    /**
+     * Sets the drawing state for the focus position. Default is <code>true</code>.
+     *
+     * @param markFocus the new drawing state for exit targets
+     */
+    public void markFocus(bool markFocus) {
+        fMarkFocus= markFocus;
+    }
+
+    /**
+     * Sets the drawing state for slave positions. Default is <code>true</code>.
+     *
+     * @param markSlaves the new drawing state for slaves
+     */
+    public void markSlaves(bool markSlaves) {
+        fMarkSlaves= markSlaves;
+    }
+
+    /**
+     * Sets the drawing state for targets. Default is <code>true</code>.
+     *
+     * @param markTargets the new drawing state for targets
+     */
+    public void markTargets(bool markTargets) {
+        fMarkTargets= markTargets;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/link/ProposalPosition.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.link.ProposalPosition;
+
+import org.eclipse.jface.text.link.LinkedModeModel; // packageimport
+import org.eclipse.jface.text.link.LinkedPosition; // packageimport
+import org.eclipse.jface.text.link.ILinkedModeListener; // packageimport
+import org.eclipse.jface.text.link.TabStopIterator; // packageimport
+import org.eclipse.jface.text.link.LinkedModeUI; // packageimport
+import org.eclipse.jface.text.link.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionGroup; // packageimport
+import org.eclipse.jface.text.link.LinkedModeManager; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionAnnotations; // packageimport
+
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.Set;
+
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+
+/**
+ * LinkedPosition with added completion proposals.
+ * <p>
+ * Clients may instantiate or extend this class.
+ * </p>
+ *
+ * @since 3.0
+ */
+public class ProposalPosition : LinkedPosition {
+
+    /**
+     * The proposals
+     */
+    private ICompletionProposal[] fProposals;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param document the document
+     * @param offset the offset of the position
+     * @param length the length of the position
+     * @param sequence the iteration sequence rank
+     * @param proposals the proposals to be shown when entering this position
+     */
+    public this(IDocument document, int offset, int length, int sequence, ICompletionProposal[] proposals) {
+        super(document, offset, length, sequence);
+        fProposals= copy(proposals);
+    }
+
+    /**
+     * Creates a new instance, with no sequence number.
+     *
+     * @param document the document
+     * @param offset the offset of the position
+     * @param length the length of the position
+     * @param proposals the proposals to be shown when entering this position
+     */
+    public this(IDocument document, int offset, int length, ICompletionProposal[] proposals) {
+        super(document, offset, length, LinkedPositionGroup.NO_STOP);
+        fProposals= copy(proposals);
+    }
+
+    /*
+     * @since 3.1
+     */
+    private ICompletionProposal[] copy(ICompletionProposal[] proposals) {
+        if (proposals !is null) {
+            ICompletionProposal[] copy= new ICompletionProposal[proposals.length];
+            SimpleType!(ICompletionProposal).arraycopy(proposals, 0, copy, 0, proposals.length);
+            return copy;
+        }
+        return null;
+    }
+
+    /*
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public override int opEquals(Object o) {
+        if ( cast(ProposalPosition)o ) {
+            if (super.opEquals(o)) {
+                return Arrays.equals(fProposals, (cast(ProposalPosition)o).fProposals);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the proposals attached to this position. The returned array is owned by
+     * this <code>ProposalPosition</code> and may not be modified by clients.
+     *
+     * @return an array of choices, including the initial one. Callers must not
+     *         modify it.
+     */
+    public ICompletionProposal[] getChoices() {
+        return fProposals;
+    }
+
+    /*
+     * @see org.eclipse.jdt.internal.ui.text.link.LinkedPosition#hashCode()
+     */
+    public override hash_t toHash() {
+        return super.toHash() | (fProposals is null ? 0 : (cast(hash_t)fProposals.ptr)/+.toHash()+/);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/link/TabStopIterator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.link.TabStopIterator;
+
+import org.eclipse.jface.text.link.LinkedModeModel; // packageimport
+import org.eclipse.jface.text.link.LinkedPosition; // packageimport
+import org.eclipse.jface.text.link.ILinkedModeListener; // packageimport
+import org.eclipse.jface.text.link.LinkedModeUI; // packageimport
+import org.eclipse.jface.text.link.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionGroup; // packageimport
+import org.eclipse.jface.text.link.LinkedModeManager; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionAnnotations; // packageimport
+import org.eclipse.jface.text.link.ProposalPosition; // packageimport
+
+
+import java.lang.all;
+import java.util.ListIterator;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import tango.core.Exception;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.Position;
+
+
+
+/**
+ * Iterator that leaps over the double occurrence of an element when switching from forward
+ * to backward iteration that is shown by <code>ListIterator</code>.
+ * <p>
+ * Package private, only for use by LinkedModeUI.
+ * </p>
+ * @since 3.0
+ */
+class TabStopIterator {
+    /**
+     * Comparator for <code>LinkedPosition</code>s. If the sequence number of two positions is equal, the
+     * offset is used.
+     */
+    private static class SequenceComparator : Comparator {
+
+        /**
+         * {@inheritDoc}
+         *
+         * <p><code>o1</code> and <code>o2</code> are required to be instances
+         * of <code>LinkedPosition</code>.</p>
+         */
+        public int compare(Object o1, Object o2) {
+            LinkedPosition p1= cast(LinkedPosition)o1;
+            LinkedPosition p2= cast(LinkedPosition)o2;
+            int i= p1.getSequenceNumber() - p2.getSequenceNumber();
+            if (i !is 0)
+                return i;
+            return p1.getOffset() - p2.getOffset();
+        }
+
+    }
+
+    /** The comparator to sort the list of positions. */
+    private static Comparator fComparator_;
+    private static Comparator fComparator(){
+        if( fComparator_ is null ){
+            synchronized( TabStopIterator.classinfo ){
+                if( fComparator_ is null ){
+                    fComparator_ = new SequenceComparator();
+                }
+            }
+        }
+        return fComparator_;
+    }
+
+    /** The iteration sequence. */
+    private const ArrayList fList;
+    /** The size of <code>fList</code>. */
+    private int fSize;
+    /** Index of the current element, to the first one initially. */
+    private int fIndex;
+    /** Cycling property. */
+    private bool fIsCycling= false;
+
+    this(List positionSequence) {
+        Assert.isNotNull(cast(Object)positionSequence);
+        fList= new ArrayList(positionSequence);
+        Collections.sort(fList, fComparator);
+        fSize= fList.size();
+        fIndex= -1;
+        Assert.isTrue(fSize > 0);
+    }
+
+    bool hasNext(LinkedPosition current) {
+        return getNextIndex(current) !is fSize;
+    }
+
+    private int getNextIndex(LinkedPosition current) {
+        if (current !is null && fList.get(fIndex) !is current)
+            return findNext(current);
+        else if (fIsCycling && fIndex is fSize - 1)
+            return 0;
+        else
+            // default: increase
+            return fIndex + 1;
+    }
+
+    /**
+     * Finds the closest position in the iteration set that follows after
+     * <code>current</code> and sets <code>fIndex</code> accordingly. If <code>current</code>
+     * is in the iteration set, the next in turn is chosen.
+     *
+     * @param current the current position
+     * @return <code>true</code> if there is a next position, <code>false</code> otherwise
+     */
+    private int findNext(LinkedPosition current) {
+        Assert.isNotNull(current);
+        // if the position is in the iteration set, jump to the next one
+        int index= fList.indexOf(current);
+        if (index !is -1) {
+            if (fIsCycling && index is fSize - 1)
+                return 0;
+            return index + 1;
+        }
+
+        // index is -1
+
+        // find the position that follows closest to the current position
+        LinkedPosition found= null;
+        for (Iterator it= fList.iterator(); it.hasNext(); ) {
+            LinkedPosition p= cast(LinkedPosition) it.next();
+            if (p.offset > current.offset)
+                if (found is null || found.offset > p.offset)
+                    found= p;
+        }
+
+        if (found !is null) {
+            return fList.indexOf(found);
+        } else if (fIsCycling) {
+            return 0;
+        } else
+            return fSize;
+    }
+
+    bool hasPrevious(LinkedPosition current) {
+        return getPreviousIndex(current) !is -1;
+    }
+
+    private int getPreviousIndex(LinkedPosition current) {
+        if (current !is null && fList.get(fIndex) !is current)
+            return findPrevious(current);
+        else if (fIsCycling && fIndex is 0)
+            return fSize - 1;
+        else
+            return fIndex - 1;
+    }
+
+    /**
+     * Finds the closest position in the iteration set that precedes
+     * <code>current</code>. If <code>current</code>
+     * is in the iteration set, the previous in turn is chosen.
+     *
+     * @param current the current position
+     * @return the index of the previous position
+     */
+    private int findPrevious(LinkedPosition current) {
+        Assert.isNotNull(current);
+        // if the position is in the iteration set, jump to the next one
+        int index= fList.indexOf(current);
+        if (index !is -1) {
+            if (fIsCycling && index is 0)
+                return fSize - 1;
+            return index - 1;
+        }
+
+        // index is -1
+
+        // find the position that follows closest to the current position
+        LinkedPosition found= null;
+        for (Iterator it= fList.iterator(); it.hasNext(); ) {
+            LinkedPosition p= cast(LinkedPosition) it.next();
+            if (p.offset < current.offset)
+                if (found is null || found.offset < p.offset)
+                    found= p;
+        }
+        if (found !is null) {
+            return fList.indexOf(found);
+        } else if (fIsCycling) {
+            return fSize - 1;
+        } else
+            return -1;
+    }
+
+    LinkedPosition next(LinkedPosition current) {
+        if (!hasNext(current))
+            throw new NoSuchElementException(null);
+        return cast(LinkedPosition) fList.get(fIndex= getNextIndex(current));
+    }
+
+    LinkedPosition previous(LinkedPosition current) {
+        if (!hasPrevious(current))
+            throw new NoSuchElementException(null);
+        return cast(LinkedPosition) fList.get(fIndex= getPreviousIndex(current));
+    }
+
+    void setCycling(bool mode) {
+        fIsCycling= mode;
+    }
+
+    void addPosition(Position position) {
+        fList.add(fSize++, position);
+        Collections.sort(fList, fComparator);
+    }
+
+    void removePosition(Position position) {
+        if (fList.remove(position))
+            fSize--;
+    }
+
+    /**
+     * @return Returns the isCycling.
+     */
+    bool isCycling() {
+        return fIsCycling;
+    }
+
+    LinkedPosition[] getPositions() {
+        return arraycast!(LinkedPosition)( fList.toArray());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationDamager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.presentation.IPresentationDamager;
+
+import org.eclipse.jface.text.presentation.IPresentationReconciler; // packageimport
+import org.eclipse.jface.text.presentation.PresentationReconciler; // packageimport
+import org.eclipse.jface.text.presentation.IPresentationRepairer; // packageimport
+import org.eclipse.jface.text.presentation.IPresentationReconcilerExtension; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+
+
+/**
+ * A presentation damager is a strategy used by a presentation reconciler to
+ * determine the region of the document's presentation which must be rebuilt
+ * because of a document change. A presentation damager is assumed to be
+ * specific for a particular document content type. A presentation damager is
+ * expected to return a damage region which is a valid input for a presentation
+ * repairer. I.e. having access to the damage region only the repairer must be
+ * able to derive all the information needed to successfully repair this region.
+ * <p>
+ * This interface must either be implemented by clients or clients use the
+ * rule-based default implementation
+ * {@link org.eclipse.jface.text.rules.DefaultDamagerRepairer}. Implementers
+ * should be registered with a presentation reconciler in order get involved in
+ * the reconciling process.</p>
+ *
+ * @see IPresentationReconciler
+ * @see IDocument
+ * @see DocumentEvent
+ * @see IPresentationRepairer
+ */
+public interface IPresentationDamager {
+
+    /**
+     * Tells the presentation damager on which document it will work.
+     *
+     * @param document the damager's working document
+     */
+    void setDocument(IDocument document);
+
+    /**
+     * Returns the damage in the document's presentation caused by the given document change.
+     * The damage is restricted to the specified partition for which the presentation damager is
+     * responsible. The damage may also depend on whether the document change also caused changes
+     * of the document's partitioning.
+     *
+     * @param partition the partition inside which the damage must be determined
+     * @param event the event describing the change whose damage must be determined
+     * @param documentPartitioningChanged indicates whether the given change changed the document's partitioning
+     * @return the computed damage
+     */
+    IRegion getDamageRegion(ITypedRegion partition, DocumentEvent event, bool documentPartitioningChanged);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationReconciler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.presentation.IPresentationReconciler;
+
+import org.eclipse.jface.text.presentation.IPresentationDamager; // packageimport
+import org.eclipse.jface.text.presentation.PresentationReconciler; // packageimport
+import org.eclipse.jface.text.presentation.IPresentationRepairer; // packageimport
+import org.eclipse.jface.text.presentation.IPresentationReconcilerExtension; // packageimport
+
+
+import java.lang.all;
+
+
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * An <code>IPresentationReconciler</code> defines and maintains the
+ * representation of a text viewer's document in the presence of changes applied
+ * to the document. An <code>IPresentationReconciler</code> is a
+ * <code>ITextViewer</code> add-on.
+ * <p>
+ * The presentation reconciler keeps track of changes applied to the text
+ * viewer. It sends each change to presentation damagers which are registered
+ * for the content types of the regions in which the change occurred. The
+ * presentation reconciler passes the computed damage to presentation repairer
+ * which construct text presentations. When applied to the presentation
+ * reconciler's text viewer, those text presentations bring the document's
+ * presentation in sync with the document's content and thus repair the damage.
+ * A presentation damager is expected to return damage which is a valid input
+ * for a presentation repairer registered for the same content type as the
+ * damager.
+ * </p>
+ * <p>
+ * A presentation reconciler should always be configured with a pair of
+ * damager/repairer strategies. I.e. for each damager there should be a
+ * corresponding repairer.
+ * </p>
+ * <p>
+ * The interface may be implemented by clients. Clients may use
+ * <code>PresentationReconciler</code> as the standard implementation of this
+ * interface.
+ * </p>
+ * <p>
+ * In order to provided backward compatibility for clients of
+ * <code>IPresentationReconciler</code>, extension interfaces are used to
+ * provide a means of evolution. The following extension interface exists:
+ * <ul>
+ * <li>
+ * {@link org.eclipse.jface.text.presentation.IPresentationReconcilerExtension}
+ * since version 3.0 adding support for documents with multiple partitionings.
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * @see org.eclipse.jface.text.presentation.IPresentationReconcilerExtension
+ * @see org.eclipse.jface.text.ITextViewer
+ * @see org.eclipse.jface.text.presentation.IPresentationDamager
+ * @see org.eclipse.jface.text.presentation.IPresentationRepairer
+ * @see org.eclipse.jface.text.TextPresentation
+ */
+public interface IPresentationReconciler {
+
+    /**
+     * Installs this presentation reconciler on the given text viewer. After
+     * this method has been finished, the reconciler is operational. I.e., it
+     * works without requesting further client actions until
+     * <code>uninstall</code> is called.
+     * <p>
+     * The <code>install</code> and <code>uninstall</code> methods must be
+     * called in sequence; i.e. repeatedly calling <code>install</code>
+     * without calling <code>uninstall</code> may throw an exception.
+     * </p>
+     *
+     * @param viewer the viewer on which this presentation reconciler is
+     *        installed
+     */
+    void install(ITextViewer viewer);
+
+    /**
+     * Removes the reconciler from the text viewer it has previously been
+     * installed on.
+     */
+    void uninstall();
+
+    /**
+     * Returns the presentation damager registered with this presentation reconciler
+     * for the specified content type.
+     *
+     * @param contentType the content type for which to determine the damager
+     * @return the presentation damager registered for the given content type, or
+     *      <code>null</code> if there is no damager
+     */
+    IPresentationDamager getDamager(String contentType);
+
+    /**
+     * Returns the presentation repairer registered with this presentation reconciler
+     * for the specified content type.
+     *
+     * @param contentType the content type for which to determine the repairer
+     * @return the presentation repairer registered for the given content type, or
+     *      <code>null</code> if there is no repairer
+     */
+    IPresentationRepairer getRepairer(String contentType);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationReconcilerExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.presentation.IPresentationReconcilerExtension;
+
+import org.eclipse.jface.text.presentation.IPresentationDamager; // packageimport
+import org.eclipse.jface.text.presentation.IPresentationReconciler; // packageimport
+import org.eclipse.jface.text.presentation.PresentationReconciler; // packageimport
+import org.eclipse.jface.text.presentation.IPresentationRepairer; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Extension interface for {@link IPresentationReconciler}. Adds awareness of
+ * documents with multiple partitions.
+ *
+ * @since 3.0
+ */
+public interface IPresentationReconcilerExtension {
+
+    /**
+     * Returns the document partitioning this presentation reconciler is using.
+     *
+     * @return the document partitioning this presentation reconciler is using
+     */
+    String getDocumentPartitioning();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationRepairer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.presentation.IPresentationRepairer;
+
+import org.eclipse.jface.text.presentation.IPresentationDamager; // packageimport
+import org.eclipse.jface.text.presentation.IPresentationReconciler; // packageimport
+import org.eclipse.jface.text.presentation.PresentationReconciler; // packageimport
+import org.eclipse.jface.text.presentation.IPresentationReconcilerExtension; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.TextPresentation;
+
+
+/**
+ * A presentation repairer is a strategy used by a presentation reconciler to
+ * rebuild a damaged region in a document's presentation. A presentation
+ * repairer is assumed to be specific for a particular document content type.
+ * The presentation repairer gets the region which it should repair and
+ * constructs a "repair description". The presentation repairer merges the steps
+ * contained within this description into the text presentation passed into
+ * <code>createPresentation</code>.
+ * <p>
+ * This interface may be implemented by clients. Alternatively, clients may use
+ * the rule-based default implementation
+ * {@link org.eclipse.jface.text.rules.DefaultDamagerRepairer}. Implementers
+ * should be registered with a presentation reconciler in order get involved in
+ * the reconciling process.
+ * </p>
+ *
+ * @see IPresentationReconciler
+ * @see IDocument
+ * @see org.eclipse.swt.custom.StyleRange
+ * @see TextPresentation
+ */
+public interface IPresentationRepairer {
+
+
+    /**
+     * Tells the presentation repairer on which document it will work.
+     *
+     * @param document the damager's working document
+     */
+    void setDocument(IDocument document);
+
+    /**
+     * Fills the given presentation with the style ranges which when applied to the
+     * presentation reconciler's text viewer repair the  presentation damage described by
+     * the given region.
+     *
+     * @param presentation the text presentation to be filled by this repairer
+     * @param damage the damage to be repaired
+     */
+    void createPresentation(TextPresentation presentation, ITypedRegion damage);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/PresentationReconciler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,606 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.presentation.PresentationReconciler;
+
+import org.eclipse.jface.text.presentation.IPresentationDamager; // packageimport
+import org.eclipse.jface.text.presentation.IPresentationReconciler; // packageimport
+import org.eclipse.jface.text.presentation.IPresentationRepairer; // packageimport
+import org.eclipse.jface.text.presentation.IPresentationReconcilerExtension; // packageimport
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+
+
+
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DefaultPositionUpdater;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension3;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IDocumentPartitioningListener;
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension;
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2;
+import org.eclipse.jface.text.IPositionUpdater;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextInputListener;
+import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextEvent;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.TypedPosition;
+
+
+
+/**
+ * Standard implementation of <code>IPresentationReconciler</code>. This
+ * implementation assumes that the tasks performed by its presentation damagers
+ * and repairers are lightweight and of low cost. This presentation reconciler
+ * runs in the UI thread and always repairs the complete damage caused by a
+ * document change rather than just the portion overlapping with the viewer's
+ * viewport.
+ * <p>
+ * Usually, clients instantiate this class and configure it before using it.
+ * </p>
+ */
+public class PresentationReconciler : IPresentationReconciler, IPresentationReconcilerExtension {
+
+    /** Prefix of the name of the position category for tracking damage regions. */
+    protected const static String TRACKED_PARTITION= "__reconciler_tracked_partition"; //$NON-NLS-1$
+
+
+    /**
+     * Internal listener class.
+     */
+    class InternalListener :
+            ITextInputListener, IDocumentListener, ITextListener,
+            IDocumentPartitioningListener, IDocumentPartitioningListenerExtension, IDocumentPartitioningListenerExtension2 {
+
+        /** Set to <code>true</code> if between a document about to be changed and a changed event. */
+        private bool fDocumentChanging= false;
+        /**
+         * The cached redraw state of the text viewer.
+         * @since 3.0
+         */
+        private bool fCachedRedrawState= true;
+
+        /*
+         * @see ITextInputListener#inputDocumentAboutToBeChanged(IDocument, IDocument)
+         */
+        public void inputDocumentAboutToBeChanged(IDocument oldDocument, IDocument newDocument) {
+            if (oldDocument !is null) {
+                try {
+
+                    fViewer.removeTextListener(this);
+                    oldDocument.removeDocumentListener(this);
+                    oldDocument.removeDocumentPartitioningListener(this);
+
+                    oldDocument.removePositionUpdater(fPositionUpdater);
+                    oldDocument.removePositionCategory(fPositionCategory);
+
+                } catch (BadPositionCategoryException x) {
+                    // should not happened for former input documents;
+                }
+            }
+        }
+
+        /*
+         * @see ITextInputListener#inputDocumenChanged(IDocument, IDocument)
+         */
+        public void inputDocumentChanged(IDocument oldDocument, IDocument newDocument) {
+
+            fDocumentChanging= false;
+            fCachedRedrawState= true;
+
+            if (newDocument !is null) {
+
+                newDocument.addPositionCategory(fPositionCategory);
+                newDocument.addPositionUpdater(fPositionUpdater);
+
+                newDocument.addDocumentPartitioningListener(this);
+                newDocument.addDocumentListener(this);
+                fViewer.addTextListener(this);
+
+                setDocumentToDamagers(newDocument);
+                setDocumentToRepairers(newDocument);
+                processDamage(new Region(0, newDocument.getLength()), newDocument);
+            }
+        }
+
+        /*
+         * @see IDocumentPartitioningListener#documentPartitioningChanged(IDocument)
+         */
+        public void documentPartitioningChanged(IDocument document) {
+            if (!fDocumentChanging && fCachedRedrawState)
+                processDamage(new Region(0, document.getLength()), document);
+            else
+                fDocumentPartitioningChanged= true;
+        }
+
+        /*
+         * @see IDocumentPartitioningListenerExtension#documentPartitioningChanged(IDocument, IRegion)
+         * @since 2.0
+         */
+        public void documentPartitioningChanged(IDocument document, IRegion changedRegion) {
+            if (!fDocumentChanging && fCachedRedrawState) {
+                processDamage(new Region(changedRegion.getOffset(), changedRegion.getLength()), document);
+            } else {
+                fDocumentPartitioningChanged= true;
+                fChangedDocumentPartitions= changedRegion;
+            }
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IDocumentPartitioningListenerExtension2#documentPartitioningChanged(org.eclipse.jface.text.DocumentPartitioningChangedEvent)
+         * @since 3.0
+         */
+        public void documentPartitioningChanged(DocumentPartitioningChangedEvent event) {
+            IRegion changedRegion= event.getChangedRegion(getDocumentPartitioning());
+            if (changedRegion !is null)
+                documentPartitioningChanged(event.getDocument(), changedRegion);
+        }
+
+        /*
+         * @see IDocumentListener#documentAboutToBeChanged(DocumentEvent)
+         */
+        public void documentAboutToBeChanged(DocumentEvent e) {
+
+            fDocumentChanging= true;
+            if (fCachedRedrawState) {
+                try {
+                    int offset= e.getOffset() + e.getLength();
+                    ITypedRegion region= getPartition(e.getDocument(), offset);
+                    fRememberedPosition= new TypedPosition(region);
+                    e.getDocument().addPosition(fPositionCategory, fRememberedPosition);
+                } catch (BadLocationException x) {
+                    // can not happen
+                } catch (BadPositionCategoryException x) {
+                    // should not happen on input elements
+                }
+            }
+        }
+
+        /*
+         * @see IDocumentListener#documentChanged(DocumentEvent)
+         */
+        public void documentChanged(DocumentEvent e) {
+            if (fCachedRedrawState) {
+                try {
+                    e.getDocument().removePosition(fPositionCategory, fRememberedPosition);
+                } catch (BadPositionCategoryException x) {
+                    // can not happen on input documents
+                }
+            }
+            fDocumentChanging= false;
+        }
+
+        /*
+         * @see ITextListener#textChanged(TextEvent)
+         */
+        public void textChanged(TextEvent e) {
+
+            fCachedRedrawState= e.getViewerRedrawState();
+            if (!fCachedRedrawState)
+                return;
+
+            IRegion damage= null;
+            IDocument document= null;
+
+            if (e.getDocumentEvent() is null) {
+                document= fViewer.getDocument();
+                if (document !is null)  {
+                    if (e.getOffset() is 0 && e.getLength() is 0 && e.getText() is null) {
+                        // redraw state change, damage the whole document
+                        damage= new Region(0, document.getLength());
+                    } else {
+                        IRegion region= widgetRegion2ModelRegion(e);
+                        try {
+                            String text= document.get(region.getOffset(), region.getLength());
+                            DocumentEvent de= new DocumentEvent(document, region.getOffset(), region.getLength(), text);
+                            damage= getDamage(de, false);
+                        } catch (BadLocationException x) {
+                        }
+                    }
+                }
+            } else  {
+                DocumentEvent de= e.getDocumentEvent();
+                document= de.getDocument();
+                damage= getDamage(de, true);
+            }
+
+            if (damage !is null && document !is null)
+                processDamage(damage, document);
+
+            fDocumentPartitioningChanged= false;
+            fChangedDocumentPartitions= null;
+        }
+
+        /**
+         * Translates the given text event into the corresponding range of the viewer's document.
+         *
+         * @param e the text event
+         * @return the widget region corresponding the region of the given event
+         * @since 2.1
+         */
+        protected IRegion widgetRegion2ModelRegion(TextEvent e) {
+
+            String text= e.getText();
+            int length= text is null ? 0 : text.length();
+
+            if ( cast(ITextViewerExtension5)fViewer ) {
+                ITextViewerExtension5 extension= cast(ITextViewerExtension5) fViewer;
+                return extension.widgetRange2ModelRange(new Region(e.getOffset(), length));
+            }
+
+            IRegion visible= fViewer.getVisibleRegion();
+            IRegion region= new Region(e.getOffset() + visible.getOffset(), length);
+            return region;
+        }
+    }
+
+    /** The map of presentation damagers. */
+    private Map fDamagers;
+    /** The map of presentation repairers. */
+    private Map fRepairers;
+    /** The target viewer. */
+    private ITextViewer fViewer;
+    /** The internal listener. */
+    private InternalListener fInternalListener;
+    /** The name of the position category to track damage regions. */
+    private String fPositionCategory;
+    /** The position updated for the damage regions' position category. */
+    private IPositionUpdater fPositionUpdater;
+    /** The positions representing the damage regions. */
+    private TypedPosition fRememberedPosition;
+    /** Flag indicating the receipt of a partitioning changed notification. */
+    private bool fDocumentPartitioningChanged= false;
+    /** The range covering the changed partitioning. */
+    private IRegion fChangedDocumentPartitions= null;
+    /**
+     * The partitioning used by this presentation reconciler.
+     * @since 3.0
+     */
+    private String fPartitioning;
+
+    /**
+     * Creates a new presentation reconciler. There are no damagers or repairers
+     * registered with this reconciler by default. The default partitioning
+     * <code>IDocumentExtension3.DEFAULT_PARTITIONING</code> is used.
+     */
+    public this() {
+        fInternalListener= new InternalListener();
+//         super();
+        fPartitioning= IDocumentExtension3.DEFAULT_PARTITIONING;
+        fPositionCategory= TRACKED_PARTITION ~ Integer.toString(toHash());
+        fPositionUpdater= new DefaultPositionUpdater(fPositionCategory);
+    }
+
+    /**
+     * Sets the document partitioning for this presentation reconciler.
+     *
+     * @param partitioning the document partitioning for this presentation reconciler.
+     * @since 3.0
+     */
+    public void setDocumentPartitioning(String partitioning) {
+        Assert.isNotNull(partitioning);
+        fPartitioning= partitioning;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.presentation.IPresentationReconcilerExtension#geDocumenttPartitioning()
+     * @since 3.0
+     */
+    public String getDocumentPartitioning() {
+        return fPartitioning;
+    }
+
+    /**
+     * Registers the given presentation damager for a particular content type.
+     * If there is already a damager registered for this type, the old damager
+     * is removed first.
+     *
+     * @param damager the presentation damager to register, or <code>null</code> to remove an existing one
+     * @param contentType the content type under which to register
+     */
+    public void setDamager(IPresentationDamager damager, String contentType) {
+
+        Assert.isNotNull(contentType);
+
+        if (fDamagers is null)
+            fDamagers= new HashMap();
+
+        if (damager is null)
+            fDamagers.remove(contentType);
+        else
+            fDamagers.put(stringcast(contentType), cast(Object)damager);
+    }
+
+    /**
+     * Registers the given presentation repairer for a particular content type.
+     * If there is already a repairer registered for this type, the old repairer
+     * is removed first.
+     *
+     * @param repairer the presentation repairer to register, or <code>null</code> to remove an existing one
+     * @param contentType the content type under which to register
+     */
+    public void setRepairer(IPresentationRepairer repairer, String contentType) {
+
+        Assert.isNotNull(contentType);
+
+        if (fRepairers is null)
+            fRepairers= new HashMap();
+
+        if (repairer is null)
+            fRepairers.remove(contentType);
+        else
+            fRepairers.put(stringcast(contentType), cast(Object)repairer);
+    }
+
+    /*
+     * @see IPresentationReconciler#install(ITextViewer)
+     */
+    public void install(ITextViewer viewer) {
+        Assert.isNotNull(cast(Object)viewer);
+
+        fViewer= viewer;
+        fViewer.addTextInputListener(fInternalListener);
+
+        IDocument document= viewer.getDocument();
+        if (document !is null)
+            fInternalListener.inputDocumentChanged(null, document);
+    }
+
+    /*
+     * @see IPresentationReconciler#uninstall()
+     */
+    public void uninstall() {
+        fViewer.removeTextInputListener(fInternalListener);
+
+        // Ensure we uninstall all listeners
+        fInternalListener.inputDocumentAboutToBeChanged(fViewer.getDocument(), null);
+    }
+
+    /*
+     * @see IPresentationReconciler#getDamager(String)
+     */
+    public IPresentationDamager getDamager(String contentType) {
+
+        if (fDamagers is null)
+            return null;
+
+        return cast(IPresentationDamager) fDamagers.get(contentType);
+    }
+
+    /*
+     * @see IPresentationReconciler#getRepairer(String)
+     */
+    public IPresentationRepairer getRepairer(String contentType) {
+
+        if (fRepairers is null)
+            return null;
+
+        return cast(IPresentationRepairer) fRepairers.get(contentType);
+    }
+
+    /**
+     * Informs all registered damagers about the document on which they will work.
+     *
+     * @param document the document on which to work
+     */
+    protected void setDocumentToDamagers(IDocument document) {
+        if (fDamagers !is null) {
+            Iterator e= fDamagers.values().iterator();
+            while (e.hasNext()) {
+                IPresentationDamager damager= cast(IPresentationDamager) e.next();
+                damager.setDocument(document);
+            }
+        }
+    }
+
+    /**
+     * Informs all registered repairers about the document on which they will work.
+     *
+     * @param document the document on which to work
+     */
+    protected void setDocumentToRepairers(IDocument document) {
+        if (fRepairers !is null) {
+            Iterator e= fRepairers.values().iterator();
+            while (e.hasNext()) {
+                IPresentationRepairer repairer= cast(IPresentationRepairer) e.next();
+                repairer.setDocument(document);
+            }
+        }
+    }
+
+    /**
+     * Constructs a "repair description" for the given damage and returns this
+     * description as a text presentation. For this, it queries the partitioning
+     * of the damage region and asks the appropriate presentation repairer for
+     * each partition to construct the "repair description" for this partition.
+     *
+     * @param damage the damage to be repaired
+     * @param document the document whose presentation must be repaired
+     * @return the presentation repair description as text presentation or
+     *         <code>null</code> if the partitioning could not be computed
+     */
+    protected TextPresentation createPresentation(IRegion damage, IDocument document) {
+        try {
+            if (fRepairers is null || fRepairers.isEmpty()) {
+                TextPresentation presentation= new TextPresentation(damage, 100);
+                presentation.setDefaultStyleRange(new StyleRange(damage.getOffset(), damage.getLength(), null, null));
+                return presentation;
+            }
+
+            TextPresentation presentation= new TextPresentation(damage, 1000);
+
+            ITypedRegion[] partitioning= TextUtilities.computePartitioning(document, getDocumentPartitioning(), damage.getOffset(), damage.getLength(), false);
+            for (int i= 0; i < partitioning.length; i++) {
+                ITypedRegion r= partitioning[i];
+                IPresentationRepairer repairer= getRepairer(r.getType());
+                if (repairer !is null)
+                    repairer.createPresentation(presentation, r);
+            }
+
+            return presentation;
+
+        } catch (BadLocationException x) {
+            return null;
+        }
+    }
+
+
+    /**
+     * Checks for the first and the last affected partition affected by a
+     * document event and calls their damagers. Invalidates everything from the
+     * start of the damage for the first partition until the end of the damage
+     * for the last partition.
+     *
+     * @param e the event describing the document change
+     * @param optimize <code>true</code> if partition changes should be
+     *        considered for optimization
+     * @return the damaged caused by the change or <code>null</code> if
+     *         computing the partitioning failed
+     * @since 3.0
+     */
+    private IRegion getDamage(DocumentEvent e, bool optimize) {
+        int length= e.getText() is null ? 0 : e.getText().length();
+
+        if (fDamagers is null || fDamagers.isEmpty()) {
+            length= Math.max(e.getLength(), length);
+            length= Math.min(e.getDocument().getLength() - e.getOffset(), length);
+            return new Region(e.getOffset(), length);
+        }
+
+        bool isDeletion= length is 0;
+        IRegion damage= null;
+        try {
+            int offset= e.getOffset();
+            if (isDeletion)
+                offset= Math.max(0, offset - 1);
+            ITypedRegion partition= getPartition(e.getDocument(), offset);
+            IPresentationDamager damager= getDamager(partition.getType());
+            if (damager is null)
+                return null;
+
+            IRegion r= damager.getDamageRegion(partition, e, fDocumentPartitioningChanged);
+
+            if (!fDocumentPartitioningChanged && optimize && !isDeletion) {
+                damage= r;
+            } else {
+
+                int damageEnd= getDamageEndOffset(e);
+
+                int parititionDamageEnd= -1;
+                if (fChangedDocumentPartitions !is null)
+                    parititionDamageEnd= fChangedDocumentPartitions.getOffset() + fChangedDocumentPartitions.getLength();
+
+                int end= Math.max(damageEnd, parititionDamageEnd);
+
+                damage= end is -1 ? r : new Region(r.getOffset(), end - r.getOffset());
+            }
+
+        } catch (BadLocationException x) {
+        }
+
+        return damage;
+    }
+
+    /**
+     * Returns the end offset of the damage. If a partition has been split by
+     * the given document event also the second half of the original
+     * partition must be considered. This is achieved by using the remembered
+     * partition range.
+     *
+     * @param e the event describing the change
+     * @return the damage end offset (excluding)
+     * @exception BadLocationException if method accesses invalid offset
+     */
+    private int getDamageEndOffset(DocumentEvent e)  {
+
+        IDocument d= e.getDocument();
+
+        int length= 0;
+        if (e.getText() !is null) {
+            length= e.getText().length;
+            if (length > 0)
+                -- length;
+        }
+
+        ITypedRegion partition= getPartition(d, e.getOffset() + length);
+        int endOffset= partition.getOffset() + partition.getLength();
+        if (endOffset is e.getOffset())
+            return -1;
+
+        int end= fRememberedPosition is null ? -1 : fRememberedPosition.getOffset() + fRememberedPosition.getLength();
+        if (endOffset < end && end < d.getLength())
+            partition= getPartition(d, end);
+
+        IPresentationDamager damager= getDamager(partition.getType());
+        if (damager is null)
+            return -1;
+
+        IRegion r= damager.getDamageRegion(partition, e, fDocumentPartitioningChanged);
+
+        return r.getOffset() + r.getLength();
+    }
+
+    /**
+     * Processes the given damage.
+     * @param damage the damage to be repaired
+     * @param document the document whose presentation must be repaired
+     */
+    private void processDamage(IRegion damage, IDocument document) {
+        if (damage !is null && damage.getLength() > 0) {
+            TextPresentation p= createPresentation(damage, document);
+            if (p !is null)
+                applyTextRegionCollection(p);
+        }
+    }
+
+    /**
+     * Applies the given text presentation to the text viewer the presentation
+     * reconciler is installed on.
+     *
+     * @param presentation the text presentation to be applied to the text viewer
+     */
+    private void applyTextRegionCollection(TextPresentation presentation) {
+        fViewer.changeTextPresentation(presentation, false);
+    }
+
+    /**
+     * Returns the partition for the given offset in the given document.
+     *
+     * @param document the document
+     * @param offset the offset
+     * @return the partition
+     * @throws BadLocationException if offset is invalid in the given document
+     * @since 3.0
+     */
+    private ITypedRegion getPartition(IDocument document, int offset)  {
+        return TextUtilities.getPartition(document, getDocumentPartitioning(), offset, false);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/quickassist/IQuickAssistAssistant.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
+
+import org.eclipse.jface.text.quickassist.QuickAssistAssistant; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistantExtension; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickFixableAnnotation; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.jface.preference.JFacePreferences;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.contentassist.ICompletionListener;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.text.source.ISourceViewerExtension3;
+
+
+/**
+ * An <code>IQuickAssistAssistant</code> provides support for quick fixes and quick
+ * assists.
+ * The quick assist assistant is a {@link ISourceViewer} add-on. Its
+ * purpose is to propose, display, and insert quick assists and quick fixes
+ * available at the current source viewer's quick assist invocation context.
+ * <p>
+ * The quick assist assistant can be configured with a {@link IQuickAssistProcessor}
+ * which provides the possible quick assist and quick fix completions.
+ * </p>
+ * In order to provide backward compatibility for clients of
+ * <code>IQuickAssistAssistant</code>, extension interfaces are used to
+ * provide a means of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link IQuickAssistAssistantExtension} since version 3.4 introducing the
+ * following function:
+ * <ul>
+ *  <li>allows to get a handler for the given command identifier</li>
+ *  <li>allows to enable support for colored labels in the proposal popup</li>
+ * </ul>
+ * </li>
+ * </p>
+ * <p>
+ * The interface can be implemented by clients. By default, clients use
+ * {@link QuickAssistAssistant} as the standard
+ * implementer of this interface.
+ * </p>
+ *
+ * @see ISourceViewer
+ * @see IQuickAssistProcessor
+ * @see IQuickAssistAssistantExtension
+ * @since 3.2
+ */
+ public interface IQuickAssistAssistant {
+
+    /**
+     * Installs quick assist support on the given source viewer.
+     * <p>
+     * <strong>Note:</strong> This quick assist assistant will only be able to query the invocation context
+     * if <code>sourceViewer</code> also implements {@link ISourceViewerExtension3}.
+     * </p>
+     *
+     * @param sourceViewer the source viewer on which quick assist will work
+     */
+    void install(ISourceViewer sourceViewer);
+
+    /**
+     * Sets the information control creator for the additional information control.
+     *
+     * @param creator the information control creator for the additional information control
+     */
+    void setInformationControlCreator(IInformationControlCreator creator);
+
+    /**
+     * Uninstalls quick assist support from the source viewer it has
+     * previously be installed on.
+     */
+    void uninstall();
+
+    /**
+     * Shows all possible quick fixes and quick assists at the viewer's cursor position.
+     *
+     * @return an optional error message if no proposals can be computed
+     */
+    String showPossibleQuickAssists();
+    
+    /**
+     * Registers a given quick assist processor for a particular content type. If there is already
+     * a processor registered, the new processor is registered instead of the old one.
+     *
+     * @param processor the quick assist processor to register, or <code>null</code> to remove
+     *        an existing one
+     */
+    void setQuickAssistProcessor(IQuickAssistProcessor processor);
+    
+    /**
+     * Returns the quick assist processor to be used for the given content type.
+     *
+     * @return the quick assist processor or <code>null</code> if none exists
+     */
+    IQuickAssistProcessor getQuickAssistProcessor();
+    
+    /**
+     * Tells whether this assistant has a fix for the given annotation.
+     * <p>
+     * <strong>Note:</strong> This test must be fast and optimistic i.e. it is OK to return
+     * <code>true</code> even though there might be no quick fix.
+     * </p>
+     * 
+     * @param annotation the annotation
+     * @return <code>true</code> if the assistant has a fix for the given annotation
+     */
+    bool canFix(Annotation annotation);
+    
+    /**
+     * Tells whether this assistant has assists for the given invocation context.
+     * 
+     * @param invocationContext the invocation context
+     * @return <code>true</code> if the assistant has a fix for the given annotation
+     */
+    bool canAssist(IQuickAssistInvocationContext invocationContext);
+    
+    /**
+     * Sets the proposal selector's background color.
+     * <p>
+     * <strong>Note:</strong> As of 3.4, you should only call this
+     * method if you want to override the {@link JFacePreferences#CONTENT_ASSIST_BACKGROUND_COLOR}.
+     * </p>
+     *
+     * @param background the background color
+     */
+    void setProposalSelectorBackground(Color background);
+
+    /**
+     * Sets the proposal's foreground color.
+     * <p>
+     * <strong>Note:</strong> As of 3.4, you should only call this
+     * method if you want to override the {@link JFacePreferences#CONTENT_ASSIST_FOREGROUND_COLOR}.
+     * </p>
+     *
+     * @param foreground the foreground color
+     */
+    void setProposalSelectorForeground(Color foreground);
+    
+    /**
+     * Adds a completion listener that will be informed before proposals are computed.
+     * 
+     * @param listener the listener
+     */
+    void addCompletionListener(ICompletionListener listener);
+
+    /**
+     * Removes a completion listener.
+     * 
+     * @param listener the listener to remove
+     */
+    void removeCompletionListener(ICompletionListener listener);
+    
+    /**
+     * Enables displaying a status line below the proposal popup. The default is not to show the
+     * status line. The contents of the status line may be set via {@link #setStatusMessage(String)}.
+     * 
+     * @param show <code>true</code> to show a message line, <code>false</code> to not show one.
+     */
+    public void setStatusLineVisible(bool show);
+
+    /**
+     * Sets the caption message displayed at the bottom of the completion proposal popup.
+     * 
+     * @param message the message
+     */
+    public void setStatusMessage(String message);
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/quickassist/IQuickAssistAssistantExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 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 org.eclipse.jface.text.quickassist.IQuickAssistAssistantExtension;
+
+import org.eclipse.jface.text.quickassist.QuickAssistAssistant; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistant; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickFixableAnnotation; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; // packageimport
+
+
+import java.lang.all;
+
+
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6;
+
+
+/**
+ * Extends {@link IQuickAssistAssistant} with the following function:
+ * <ul>
+ *  <li>allows to get a handler for the given command identifier</li>
+ *  <li>allows to enable support for colored labels in the proposal popup</li>
+ * </ul>
+ * 
+ * @since 3.4
+ */
+public interface IQuickAssistAssistantExtension {
+
+    /**
+     * Returns the handler for the given command identifier.
+     * <p>
+     * The same handler instance will be returned when called a more than once
+     * with the same command identifier.
+     * </p>
+     * 
+     * @param commandId the command identifier
+     * @return the handler for the given command identifier
+     * @throws IllegalArgumentException if the command is not supported by this
+     *             content assistant
+     * @throws IllegalStateException if called when this content assistant is
+     *             uninstalled
+     */
+    IHandler getHandler(String commandId);
+
+    /**
+     * Enables the support for colored labels in the proposal popup.
+     * <p>Completion proposals can implement {@link ICompletionProposalExtension6}
+     * to provide colored proposal labels.</p>
+     * 
+     * @param isEnabled if <code>true</code> the support for colored labels is enabled in the proposal popup
+     * @since 3.4
+     */
+    void enableColoredLabels(bool isEnabled);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/quickassist/IQuickAssistInvocationContext.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
+
+import org.eclipse.jface.text.quickassist.QuickAssistAssistant; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistant; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistantExtension; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickFixableAnnotation; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.source.ISourceViewer;
+
+
+/**
+ * Context information for quick fix and quick assist processors.
+ * <p>
+ * This interface can be implemented by clients.</p>
+ * 
+ * @since 3.2
+ */
+public interface IQuickAssistInvocationContext {
+
+    /**
+     * Returns the offset where quick assist was invoked.
+     * 
+     * @return the invocation offset or <code>-1</code> if unknown
+     */
+    int getOffset();
+
+    /**
+     * Returns the length of the selection at the invocation offset.
+     * 
+     * @return the length of the current selection or <code>-1</code> if none or unknown
+     */
+    int getLength();
+    
+    /**
+     * Returns the viewer for this context.
+     * 
+     * @return the viewer or <code>null</code> if not available
+     */
+    ISourceViewer getSourceViewer();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/quickassist/IQuickAssistProcessor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.text.quickassist.IQuickAssistProcessor;
+
+import org.eclipse.jface.text.quickassist.QuickAssistAssistant; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistant; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistantExtension; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickFixableAnnotation; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.source.Annotation;
+
+
+/**
+ * Quick assist processor for quick fixes and quick assists.
+ * <p>
+ * A processor can provide just quick fixes, just quick assists
+ * or both.
+ * </p>
+ * <p>
+ * This interface can be implemented by clients.</p>
+ * 
+ * @since 3.2
+ */
+public interface IQuickAssistProcessor {
+
+    /**
+     * Returns the reason why this quick assist processor
+     * was unable to produce any completion proposals.
+     *
+     * @return an error message or <code>null</code> if no error occurred
+     */
+    String getErrorMessage();
+
+    /**
+     * Tells whether this processor has a fix for the given annotation.
+     * <p>
+     * <strong>Note:</strong> This test must be fast and optimistic i.e. it is OK to return
+     * <code>true</code> even though there might be no quick fix.
+     * </p>
+     * 
+     * @param annotation the annotation
+     * @return <code>true</code> if the assistant has a fix for the given annotation
+     */
+    bool canFix(Annotation annotation);
+    
+    /**
+     * Tells whether this assistant has assists for the given invocation context.
+     * 
+     * @param invocationContext the invocation context
+     * @return <code>true</code> if the assistant has a fix for the given annotation
+     */
+    bool canAssist(IQuickAssistInvocationContext invocationContext);
+    
+    /**
+     * Returns a list of quick assist and quick fix proposals for the
+     * given invocation context.
+     *
+     * @param invocationContext the invocation context
+     * @return an array of completion proposals or <code>null</code> if no proposals are available
+     */
+    ICompletionProposal[] computeQuickAssistProposals(IQuickAssistInvocationContext invocationContext);
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/quickassist/IQuickFixableAnnotation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.text.quickassist.IQuickFixableAnnotation;
+
+import org.eclipse.jface.text.quickassist.QuickAssistAssistant; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistant; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistantExtension; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.AssertionFailedException;
+import org.eclipse.jface.text.source.Annotation;
+
+
+/**
+ * Allows an annotation to tell whether there are quick fixes
+ * for it and to cache that state.
+ * <p>
+ * Caching the state is important to improve overall performance as calling
+ * {@link org.eclipse.jface.text.quickassist.IQuickAssistAssistant#canFix(Annotation)}
+ * can be expensive.
+ * </p>
+ * <p>
+ * This interface can be implemented by clients.</p>
+ * 
+ * @since 3.2
+ */
+public interface IQuickFixableAnnotation {
+
+    /**
+     * Sets whether there are quick fixes available for
+     * this annotation.
+     * 
+     * @param state <code>true</code> if there are quick fixes available, false otherwise
+     */
+    void setQuickFixable(bool state);
+
+    /**
+     * Tells whether the quick fixable state has been set.
+     * <p>
+     * Normally this means {@link #setQuickFixable(bool)} has been
+     * called at least once but it can also be hard-coded, e.g. always
+     * return <code>true</code>.
+     * </p>
+     * 
+     * @return <code>true</code> if the state has been set
+     */
+    bool isQuickFixableStateSet();
+
+    /**
+     * Tells whether there are quick fixes for this annotation.
+     * <p>
+     * <strong>Note:</strong> This method must only be called
+     * if {@link #isQuickFixableStateSet()} returns <code>true</code>.</p>
+     * 
+     * @return <code>true</code> if this annotation offers quick fixes
+     * @throws AssertionFailedException if called when {@link #isQuickFixableStateSet()} is <code>false</code>
+     */
+    bool isQuickFixable() ;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/quickassist/QuickAssistAssistant.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,271 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.text.quickassist.QuickAssistAssistant;
+
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistant; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistantExtension; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickFixableAnnotation; // packageimport
+import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.contentassist.ContentAssistant;
+import org.eclipse.jface.text.contentassist.ICompletionListener;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.contentassist.IContextInformationValidator;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.text.source.TextInvocationContext;
+
+
+/**
+ * Default implementation of <code>IQuickAssistAssistant</code>.
+ *
+ * @since 3.2
+ */
+public class QuickAssistAssistant : IQuickAssistAssistant, IQuickAssistAssistantExtension {
+
+
+    private static final class QuickAssistAssistantImpl : ContentAssistant {
+        /*
+         * @see org.eclipse.jface.text.contentassist.ContentAssistant#possibleCompletionsClosed()
+         */
+        public void possibleCompletionsClosed() {
+            super.possibleCompletionsClosed();
+        }
+
+        /*
+         * @see org.eclipse.jface.text.contentassist.ContentAssistant#hide()
+         * @since 3.4
+         */
+        protected void hide() {
+            super.hide();
+        }
+    }
+
+
+    private static final class ContentAssistProcessor : IContentAssistProcessor {
+
+        private IQuickAssistProcessor fQuickAssistProcessor;
+
+        this(IQuickAssistProcessor processor) {
+            fQuickAssistProcessor= processor;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
+         */
+        public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
+            // panic code - should not happen
+            if (!( cast(ISourceViewer)viewer ))
+                return null;
+
+            return fQuickAssistProcessor.computeQuickAssistProposals(new TextInvocationContext(cast(ISourceViewer)viewer, offset, -1));
+        }
+
+        /*
+         * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.ITextViewer, int)
+         */
+        public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
+            return null;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
+         */
+        public char[] getCompletionProposalAutoActivationCharacters() {
+            return null;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters()
+         */
+        public char[] getContextInformationAutoActivationCharacters() {
+            return null;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getErrorMessage()
+         */
+        public String getErrorMessage() {
+            return null;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator()
+         */
+        public IContextInformationValidator getContextInformationValidator() {
+            return null;
+        }
+
+    }
+
+    private QuickAssistAssistantImpl fQuickAssistAssistantImpl;
+    private IQuickAssistProcessor fQuickAssistProcessor;
+
+    public this() {
+        fQuickAssistAssistantImpl= new QuickAssistAssistantImpl();
+        fQuickAssistAssistantImpl.enableAutoActivation(false);
+        fQuickAssistAssistantImpl.enableAutoInsert(false);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistAssistant#showPossibleQuickAssists()
+     */
+    public String showPossibleQuickAssists() {
+        return fQuickAssistAssistantImpl.showPossibleCompletions();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistAssistant#getQuickAssistProcessor(java.lang.String)
+     */
+    public IQuickAssistProcessor getQuickAssistProcessor() {
+        return fQuickAssistProcessor;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistAssistant#setQuickAssistProcessor(org.eclipse.jface.text.quickassist.IQuickAssistProcessor)
+     */
+    public void setQuickAssistProcessor(IQuickAssistProcessor processor) {
+        fQuickAssistProcessor= processor;
+        fQuickAssistAssistantImpl.setDocumentPartitioning("__" ~ this.classinfo.name ~ "_partitioning"); //$NON-NLS-1$ //$NON-NLS-2$
+        fQuickAssistAssistantImpl.setContentAssistProcessor(new ContentAssistProcessor(processor), IDocument.DEFAULT_CONTENT_TYPE);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistAssistant#canFix(org.eclipse.jface.text.source.Annotation)
+     */
+    public bool canFix(Annotation annotation) {
+        return fQuickAssistProcessor !is null && fQuickAssistProcessor.canFix(annotation);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistAssistant#canAssist(org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext)
+     */
+    public bool canAssist(IQuickAssistInvocationContext invocationContext) {
+        return fQuickAssistProcessor !is null && fQuickAssistProcessor.canAssist(invocationContext);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistAssistant#install(org.eclipse.jface.text.ITextViewer)
+     */
+    public void install(ISourceViewer sourceViewer) {
+        fQuickAssistAssistantImpl.install(sourceViewer);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistAssistant#setInformationControlCreator(org.eclipse.jface.text.IInformationControlCreator)
+     */
+    public void setInformationControlCreator(IInformationControlCreator creator) {
+        fQuickAssistAssistantImpl.setInformationControlCreator(creator);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistAssistant#uninstall()
+     */
+    public void uninstall() {
+        fQuickAssistAssistantImpl.uninstall();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistAssistant#setProposalSelectorBackground(org.eclipse.swt.graphics.Color)
+     */
+    public void setProposalSelectorBackground(Color background) {
+        fQuickAssistAssistantImpl.setProposalSelectorBackground(background);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistAssistant#setProposalSelectorForeground(org.eclipse.swt.graphics.Color)
+     */
+    public void setProposalSelectorForeground(Color foreground) {
+        fQuickAssistAssistantImpl.setProposalSelectorForeground(foreground);
+    }
+
+    /**
+     * Callback to signal this quick assist assistant that the presentation of the
+     * possible completions has been stopped.
+     */
+    protected void possibleCompletionsClosed() {
+        fQuickAssistAssistantImpl.possibleCompletionsClosed();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistAssistant#addCompletionListener(org.eclipse.jface.text.contentassist.ICompletionListener)
+     */
+    public void addCompletionListener(ICompletionListener listener) {
+        fQuickAssistAssistantImpl.addCompletionListener(listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistAssistant#removeCompletionListener(org.eclipse.jface.text.contentassist.ICompletionListener)
+     */
+    public void removeCompletionListener(ICompletionListener listener) {
+        fQuickAssistAssistantImpl.removeCompletionListener(listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistAssistant#setStatusLineVisible(bool)
+     */
+    public void setStatusLineVisible(bool show) {
+        fQuickAssistAssistantImpl.setStatusLineVisible(show);
+
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistAssistant#setStatusMessage(java.lang.String)
+     */
+    public void setStatusMessage(String message) {
+        fQuickAssistAssistantImpl.setStatusMessage(message);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 3.4
+     */
+    public final IHandler getHandler(String commandId) {
+        return fQuickAssistAssistantImpl.getHandler(commandId);
+    }
+
+    /**
+     * Hides any open pop-ups.
+     *
+     * @since 3.4
+     */
+    protected void hide() {
+        fQuickAssistAssistantImpl.hide();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 3.4
+     */
+    public void enableColoredLabels(bool isEnabled) {
+        fQuickAssistAssistantImpl.enableColoredLabels(isEnabled);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/AbstractReconcileStep.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.reconciler.AbstractReconcileStep;
+
+import org.eclipse.jface.text.reconciler.IReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegionQueue; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategy; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; // packageimport
+import org.eclipse.jface.text.reconciler.MonoReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.Reconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilableModel; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegion; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileResult; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilerExtension; // packageimport
+
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.IRegion;
+
+
+/**
+ * Abstract implementation of a reconcile step.
+ *
+ * @since 3.0
+ */
+public abstract class AbstractReconcileStep : IReconcileStep {
+
+    private IReconcileStep fNextStep;
+    private IReconcileStep fPreviousStep;
+    private IProgressMonitor fProgressMonitor;
+    protected IReconcilableModel fInputModel;
+
+    /**
+     * Creates an intermediate reconcile step which adds
+     * the given step to the pipe.
+     *
+     * @param step the reconcile step
+     */
+    public this(IReconcileStep step) {
+        Assert.isNotNull(cast(Object)step);
+        fNextStep= step;
+        fNextStep.setPreviousStep(this);
+    }
+
+    /**
+     * Creates the last reconcile step of the pipe.
+     */
+    public this() {
+    }
+
+    public bool isLastStep() {
+        return fNextStep is null;
+    }
+
+    public bool isFirstStep() {
+        return fPreviousStep is null;
+    }
+
+    /*
+     * @see org.eclipse.text.reconcilerpipe.IReconcilerResultCollector#setProgressMonitor(org.eclipse.core.runtime.IProgressMonitor)
+     */
+    public void setProgressMonitor(IProgressMonitor monitor) {
+        fProgressMonitor= monitor;
+
+        if (!isLastStep())
+            fNextStep.setProgressMonitor(monitor);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.reconciler.IReconcileStep#getProgressMonitor()
+     */
+    public IProgressMonitor getProgressMonitor() {
+        return fProgressMonitor;
+    }
+
+    /*
+     * @see IReconcileStep#reconcile(IRegion)
+     */
+    public final IReconcileResult[] reconcile(IRegion partition) {
+        IReconcileResult[] result= reconcileModel(null, partition);
+        if (!isLastStep()) {
+            fNextStep.setInputModel(getModel());
+            IReconcileResult[] nextResult= fNextStep.reconcile(partition);
+            return merge(result, convertToInputModel(nextResult));
+        }
+        return result;
+    }
+
+    /*
+     * @see IReconcileStep#reconcile(org.eclipse.jface.text.reconciler.DirtyRegion, org.eclipse.jface.text.IRegion)
+     */
+    public final IReconcileResult[] reconcile(DirtyRegion dirtyRegion, IRegion subRegion) {
+        IReconcileResult[] result= reconcileModel(dirtyRegion, subRegion);
+        if (!isLastStep()) {
+            fNextStep.setInputModel(getModel());
+            IReconcileResult[] nextResult= fNextStep.reconcile(dirtyRegion, subRegion);
+            return merge(result, convertToInputModel(nextResult));
+        }
+        return result;
+    }
+
+
+    /**
+     * Reconciles the model of this reconcile step. The
+     * result is based on the input model.
+     *
+     * @param dirtyRegion the document region which has been changed
+     * @param subRegion the sub region in the dirty region which should be reconciled
+     * @return an array with reconcile results
+     */
+    abstract protected IReconcileResult[] reconcileModel(DirtyRegion dirtyRegion, IRegion subRegion);
+
+    /**
+     * Adapts the given an array with reconcile results to
+     * this step's input model and returns it.
+     *
+     * @param inputResults an array with reconcile results
+     * @return an array with the reconcile results adapted to the input model
+     */
+    protected IReconcileResult[] convertToInputModel(IReconcileResult[] inputResults) {
+        return inputResults;
+    }
+
+    /**
+     * Merges the two reconcile result arrays.
+     *
+     * @param results1 an array with reconcile results
+     * @param results2 an array with reconcile results
+     * @return an array with the merged reconcile results
+     */
+    private IReconcileResult[] merge(IReconcileResult[] results1, IReconcileResult[] results2) {
+        if (results1 is null)
+            return results2;
+
+        if (results2 is null)
+            return results1;
+
+        // XXX: not yet performance optimized
+        Collection collection= new ArrayList(Arrays.asList(arraycast!(Object)(results1)));
+        collection.addAll(Arrays.asList(arraycast!(Object)(results2)));
+        return arraycast!(IReconcileResult)(collection.toArray());
+    }
+
+    /*
+     * @see IProgressMonitor#isCanceled()
+     */
+    protected final bool isCanceled() {
+        return fProgressMonitor !is null && fProgressMonitor.isCanceled();
+    }
+
+    /*
+     * @see IReconcileStep#setPreviousStep(IReconcileStep)
+     */
+    public void setPreviousStep(IReconcileStep step) {
+        Assert.isNotNull(cast(Object)step);
+        Assert.isTrue(fPreviousStep is null);
+        fPreviousStep= step;
+    }
+
+    /*
+     * @see IReconcileStep#setInputModel(Object)
+     */
+    public void setInputModel(IReconcilableModel inputModel) {
+        fInputModel= inputModel;
+
+        if (!isLastStep())
+            fNextStep.setInputModel(getModel());
+    }
+
+    /**
+     * Returns the reconcilable input model.
+     *
+     * @return the reconcilable input model.
+     */
+    public IReconcilableModel getInputModel() {
+        return fInputModel;
+    }
+
+    /**
+     * Returns the reconcilable model.
+     *
+     * @return the reconcilable model
+     */
+    abstract public IReconcilableModel getModel();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/AbstractReconciler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,636 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.reconciler.AbstractReconciler;
+
+import org.eclipse.jface.text.reconciler.IReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegionQueue; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategy; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; // packageimport
+import org.eclipse.jface.text.reconciler.MonoReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.Reconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilableModel; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegion; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileResult; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilerExtension; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+import java.lang.JThread;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.ITextInputListener;
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * Abstract implementation of {@link IReconciler}. The reconciler
+ * listens to input document changes as well as changes of
+ * the input document of the text viewer it is installed on. Depending on
+ * its configuration it manages the received change notifications in a
+ * queue folding neighboring or overlapping changes together. The reconciler
+ * processes the dirty regions as a background activity after having waited for further
+ * changes for the configured duration of time. A reconciler is started using the
+ * {@link #install(ITextViewer)} method.  As a first step {@link #initialProcess()} is
+ * executed in the background. Then, the reconciling thread waits for changes that
+ * need to be reconciled. A reconciler can be resumed by calling {@link #forceReconciling()}
+ * independent from the existence of actual changes. This mechanism is for subclasses only.
+ * It is the clients responsibility to stop a reconciler using its {@link #uninstall()}
+ * method. Unstopped reconcilers do not free their resources.
+ * <p>
+ * It is subclass responsibility to specify how dirty regions are processed.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.IDocumentListener
+ * @see org.eclipse.jface.text.ITextInputListener
+ * @see org.eclipse.jface.text.reconciler.DirtyRegion
+ * @since 2.0
+ */
+abstract public class AbstractReconciler : IReconciler {
+
+
+    /**
+     * Background thread for the reconciling activity.
+     */
+    class BackgroundThread {
+        JThread thread;
+
+        /** Has the reconciler been canceled. */
+        private bool fCanceled= false;
+        /** Has the reconciler been reset. */
+        private bool fReset= false;
+        /** Some changes need to be processed. */
+        private bool fIsDirty= false;
+        /** Is a reconciling strategy active. */
+        private bool fIsActive= false;
+
+        /**
+         * Creates a new background thread. The thread
+         * runs with minimal priority.
+         *
+         * @param name the thread's name
+         */
+        public this(String name) {
+            thread = new JThread( &run );
+            thread.setName( name );
+            thread.setPriority( JThread.MIN_PRIORITY );
+            thread.setDaemon(true);
+        }
+
+        public void start(){
+            thread.start();
+        }
+        public bool isAlive(){
+            return thread.isAlive();
+        }
+        public JThread getThread(){
+            return thread;
+        }
+        /**
+         * Returns whether a reconciling strategy is active right now.
+         *
+         * @return <code>true</code> if a activity is active
+         */
+        public bool isActive() {
+            return fIsActive;
+        }
+
+        /**
+         * Returns whether some changes need to be processed.
+         *
+         * @return <code>true</code> if changes wait to be processed
+         * @since 3.0
+         */
+        public synchronized bool isDirty() {
+            return fIsDirty;
+        }
+
+        /**
+         * Cancels the background thread.
+         */
+        public void cancel() {
+            fCanceled= true;
+            IProgressMonitor pm= fProgressMonitor;
+            if (pm !is null)
+                pm.setCanceled(true);
+            synchronized (fDirtyRegionQueue) {
+                fDirtyRegionQueue.notifyAll();
+            }
+        }
+
+        /**
+         * Suspends the caller of this method until this background thread has
+         * emptied the dirty region queue.
+         */
+        public void suspendCallerWhileDirty() {
+            bool isDirty;
+            do {
+                synchronized (fDirtyRegionQueue) {
+                    isDirty= fDirtyRegionQueue.getSize() > 0;
+                    if (isDirty) {
+                        try {
+                            fDirtyRegionQueue.wait();
+                        } catch (InterruptedException x) {
+                        }
+                    }
+                }
+            } while (isDirty);
+        }
+
+        /**
+         * Reset the background thread as the text viewer has been changed,
+         */
+        public void reset() {
+
+            if (fDelay > 0) {
+
+                synchronized (this) {
+                    fIsDirty= true;
+                    fReset= true;
+                }
+
+            } else {
+
+                synchronized (this) {
+                    fIsDirty= true;
+                }
+
+                synchronized (fDirtyRegionQueue) {
+                    fDirtyRegionQueue.notifyAll();
+                }
+            }
+
+            reconcilerReset();
+        }
+
+        /**
+         * The background activity. Waits until there is something in the
+         * queue managing the changes that have been applied to the text viewer.
+         * Removes the first change from the queue and process it.
+         * <p>
+         * Calls {@link AbstractReconciler#initialProcess()} on entrance.
+         * </p>
+         */
+        public void run() {
+
+            synchronized (fDirtyRegionQueue) {
+                try {
+                    fDirtyRegionQueue.wait(fDelay);
+                } catch (InterruptedException x) {
+                }
+            }
+
+            if (fCanceled)
+                return;
+
+            initialProcess();
+
+            while (!fCanceled) {
+
+                synchronized (fDirtyRegionQueue) {
+                    try {
+                        fDirtyRegionQueue.wait(fDelay);
+                    } catch (InterruptedException x) {
+                    }
+                }
+
+                if (fCanceled)
+                    break;
+
+                if (!isDirty())
+                    continue;
+
+                synchronized (this) {
+                    if (fReset) {
+                        fReset= false;
+                        continue;
+                    }
+                }
+
+                DirtyRegion r= null;
+                synchronized (fDirtyRegionQueue) {
+                    r= fDirtyRegionQueue.removeNextDirtyRegion();
+                }
+
+                fIsActive= true;
+
+                fProgressMonitor.setCanceled(false);
+
+                process(r);
+
+                synchronized (fDirtyRegionQueue) {
+                    if (0 is fDirtyRegionQueue.getSize()) {
+                        synchronized (this) {
+                            fIsDirty= fProgressMonitor.isCanceled();
+                        }
+                        fDirtyRegionQueue.notifyAll();
+                    }
+                }
+
+                fIsActive= false;
+            }
+        }
+    }
+
+    /**
+     * Internal document listener and text input listener.
+     */
+    class Listener : IDocumentListener, ITextInputListener {
+
+        /*
+         * @see IDocumentListener#documentAboutToBeChanged(DocumentEvent)
+         */
+        public void documentAboutToBeChanged(DocumentEvent e) {
+        }
+
+        /*
+         * @see IDocumentListener#documentChanged(DocumentEvent)
+         */
+        public void documentChanged(DocumentEvent e) {
+
+            if (!fThread.isDirty() && fThread.isAlive()) {
+                if (!fIsAllowedToModifyDocument && JThread.currentThread() is fThread.getThread())
+                    throw new UnsupportedOperationException("The reconciler thread is not allowed to modify the document"); //$NON-NLS-1$
+                aboutToBeReconciled();
+            }
+
+            /*
+             * The second OR condition handles the case when the document
+             * gets changed while still inside initialProcess().
+             */
+            if (fThread.isActive() || fThread.isDirty() && fThread.isAlive())
+                fProgressMonitor.setCanceled(true);
+
+            if (fIsIncrementalReconciler)
+                createDirtyRegion(e);
+
+            fThread.reset();
+
+        }
+
+        /*
+         * @see ITextInputListener#inputDocumentAboutToBeChanged(IDocument, IDocument)
+         */
+        public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+
+            if (oldInput is fDocument) {
+
+                if (fDocument !is null)
+                    fDocument.removeDocumentListener(this);
+
+                if (fIsIncrementalReconciler) {
+                    synchronized (fDirtyRegionQueue) {
+                        fDirtyRegionQueue.purgeQueue();
+                    }
+                    if (fDocument !is null && fDocument.getLength() > 0 && fThread.isDirty() && fThread.isAlive()) {
+                        DocumentEvent e= new DocumentEvent(fDocument, 0, fDocument.getLength(), ""); //$NON-NLS-1$
+                        createDirtyRegion(e);
+                        fThread.reset();
+                        fThread.suspendCallerWhileDirty();
+                    }
+                }
+
+                fDocument= null;
+            }
+        }
+
+        /*
+         * @see ITextInputListener#inputDocumentChanged(IDocument, IDocument)
+         */
+        public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
+
+            fDocument= newInput;
+            if (fDocument is null)
+                return;
+
+
+            reconcilerDocumentChanged(fDocument);
+
+            fDocument.addDocumentListener(this);
+
+            if (!fThread.isDirty())
+                aboutToBeReconciled();
+
+            startReconciling();
+        }
+    }
+
+    /** Queue to manage the changes applied to the text viewer. */
+    private DirtyRegionQueue fDirtyRegionQueue;
+    /** The background thread. */
+    private BackgroundThread fThread;
+    /** Internal document and text input listener. */
+    private Listener fListener;
+    /** The background thread delay. */
+    private int fDelay= 500;
+    /** Are there incremental reconciling strategies? */
+    private bool fIsIncrementalReconciler= true;
+    /** The progress monitor used by this reconciler. */
+    private IProgressMonitor fProgressMonitor;
+    /**
+     * Tells whether this reconciler is allowed to modify the document.
+     * @since 3.2
+     */
+    private bool fIsAllowedToModifyDocument= true;
+
+
+    /** The text viewer's document. */
+    private IDocument fDocument;
+    /** The text viewer */
+    private ITextViewer fViewer;
+
+
+    /**
+     * Processes a dirty region. If the dirty region is <code>null</code> the whole
+     * document is consider being dirty. The dirty region is partitioned by the
+     * document and each partition is handed over to a reconciling strategy registered
+     * for the partition's content type.
+     *
+     * @param dirtyRegion the dirty region to be processed
+     */
+    abstract protected void process(DirtyRegion dirtyRegion);
+
+    /**
+     * Hook called when the document whose contents should be reconciled
+     * has been changed, i.e., the input document of the text viewer this
+     * reconciler is installed on. Usually, subclasses use this hook to
+     * inform all their reconciling strategies about the change.
+     *
+     * @param newDocument the new reconciler document
+     */
+    abstract protected void reconcilerDocumentChanged(IDocument newDocument);
+
+
+    /**
+     * Creates a new reconciler without configuring it.
+     */
+    protected this() {
+        fProgressMonitor= new NullProgressMonitor();
+    }
+
+    /**
+     * Tells the reconciler how long it should wait for further text changes before
+     * activating the appropriate reconciling strategies.
+     *
+     * @param delay the duration in milliseconds of a change collection period.
+     */
+    public void setDelay(int delay) {
+        fDelay= delay;
+    }
+
+    /**
+     * Tells the reconciler whether any of the available reconciling strategies
+     * is interested in getting detailed dirty region information or just in the
+     * fact that the document has been changed. In the second case, the reconciling
+     * can not incrementally be pursued.
+     *
+     * @param isIncremental indicates whether this reconciler will be configured with
+     *      incremental reconciling strategies
+     *
+     * @see DirtyRegion
+     * @see IReconcilingStrategy
+     */
+    public void setIsIncrementalReconciler(bool isIncremental) {
+        fIsIncrementalReconciler= isIncremental;
+    }
+
+    /**
+     * Tells the reconciler whether it is allowed to change the document
+     * inside its reconciler thread.
+     * <p>
+     * If this is set to <code>false</code> an {@link UnsupportedOperationException}
+     * will be thrown when this restriction will be violated.
+     * </p>
+     *
+     * @param isAllowedToModify indicates whether this reconciler is allowed to modify the document
+     * @since 3.2
+     */
+    public void setIsAllowedToModifyDocument(bool isAllowedToModify) {
+        fIsAllowedToModifyDocument= isAllowedToModify;
+    }
+
+    /**
+     * Sets the progress monitor of this reconciler.
+     *
+     * @param monitor the monitor to be used
+     */
+    public void setProgressMonitor(IProgressMonitor monitor) {
+        Assert.isLegal(monitor !is null);
+        fProgressMonitor= monitor;
+    }
+
+    /**
+     * Returns whether any of the reconciling strategies is interested in
+     * detailed dirty region information.
+     *
+     * @return whether this reconciler is incremental
+     *
+     * @see IReconcilingStrategy
+     */
+    protected bool isIncrementalReconciler() {
+        return fIsIncrementalReconciler;
+    }
+
+    /**
+     * Returns the input document of the text viewer this reconciler is installed on.
+     *
+     * @return the reconciler document
+     */
+    protected IDocument getDocument() {
+        return fDocument;
+    }
+
+    /**
+     * Returns the text viewer this reconciler is installed on.
+     *
+     * @return the text viewer this reconciler is installed on
+     */
+    protected ITextViewer getTextViewer() {
+        return fViewer;
+    }
+
+    /**
+     * Returns the progress monitor of this reconciler.
+     *
+     * @return the progress monitor of this reconciler
+     */
+    protected IProgressMonitor getProgressMonitor() {
+        return fProgressMonitor;
+    }
+
+    /*
+     * @see IReconciler#install(ITextViewer)
+     */
+    public void install(ITextViewer textViewer) {
+
+        Assert.isNotNull(cast(Object)textViewer);
+        fViewer= textViewer;
+
+        synchronized (this) {
+            if (fThread !is null)
+                return;
+            fThread= new BackgroundThread(this.classinfo.name);
+        }
+
+        fDirtyRegionQueue= new DirtyRegionQueue();
+
+        fListener= new Listener();
+        fViewer.addTextInputListener(fListener);
+
+        // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=67046
+        // if the reconciler gets installed on a viewer that already has a document
+        // (e.g. when reusing editors), we force the listener to register
+        // itself as document listener, because there will be no input change
+        // on the viewer.
+        // In order to do that, we simulate an input change.
+        IDocument document= textViewer.getDocument();
+        if (document !is null) {
+            fListener.inputDocumentAboutToBeChanged(fDocument, document);
+            fListener.inputDocumentChanged(fDocument, document);
+        }
+    }
+
+    /*
+     * @see IReconciler#uninstall()
+     */
+    public void uninstall() {
+        if (fListener !is null) {
+
+            fViewer.removeTextInputListener(fListener);
+            if (fDocument !is null) {
+                fListener.inputDocumentAboutToBeChanged(fDocument, null);
+                fListener.inputDocumentChanged(fDocument, null);
+            }
+            fListener= null;
+
+            synchronized (this) {
+                // http://dev.eclipse.org/bugs/show_bug.cgi?id=19135
+                BackgroundThread bt= fThread;
+                fThread= null;
+                bt.cancel();
+            }
+        }
+    }
+
+    /**
+     * Creates a dirty region for a document event and adds it to the queue.
+     *
+     * @param e the document event for which to create a dirty region
+     */
+    private void createDirtyRegion(DocumentEvent e) {
+        synchronized (fDirtyRegionQueue) {
+            if (e.getLength() is 0 && e.getText() !is null) {
+                // Insert
+                fDirtyRegionQueue.addDirtyRegion(new DirtyRegion(e.getOffset(), e.getText().length(), DirtyRegion.INSERT, e.getText()));
+
+            } else if (e.getText() is null || e.getText().length() is 0) {
+                // Remove
+                fDirtyRegionQueue.addDirtyRegion(new DirtyRegion(e.getOffset(), e.getLength(), DirtyRegion.REMOVE, null));
+
+            } else {
+                // Replace (Remove + Insert)
+                fDirtyRegionQueue.addDirtyRegion(new DirtyRegion(e.getOffset(), e.getLength(), DirtyRegion.REMOVE, null));
+                fDirtyRegionQueue.addDirtyRegion(new DirtyRegion(e.getOffset(), e.getText().length(), DirtyRegion.INSERT, e.getText()));
+            }
+        }
+    }
+
+    /**
+     * Hook for subclasses which want to perform some
+     * action as soon as reconciliation is needed.
+     * <p>
+     * Default implementation is to do nothing.
+     * </p>
+     *
+     * @since 3.0
+     */
+    protected void aboutToBeReconciled() {
+    }
+
+    /**
+     * This method is called on startup of the background activity. It is called only
+     * once during the life time of the reconciler. Clients may reimplement this method.
+     */
+    protected void initialProcess() {
+    }
+
+    /**
+     * Forces the reconciler to reconcile the structure of the whole document.
+     * Clients may extend this method.
+     */
+    protected void forceReconciling() {
+
+        if (fDocument !is null) {
+
+            if (!fThread.isDirty()&& fThread.isAlive())
+                aboutToBeReconciled();
+
+            if (fThread.isActive())
+                fProgressMonitor.setCanceled(true);
+
+            if (fIsIncrementalReconciler) {
+                DocumentEvent e= new DocumentEvent(fDocument, 0, fDocument.getLength(), fDocument.get());
+                createDirtyRegion(e);
+            }
+
+            startReconciling();
+        }
+    }
+
+    /**
+     * Starts the reconciler to reconcile the queued dirty-regions.
+     * Clients may extend this method.
+     */
+    protected synchronized void startReconciling() {
+        if (fThread is null)
+            return;
+
+        if (!fThread.isAlive()) {
+// SWT
+//             try {
+                fThread.start();
+//             } catch (IllegalThreadStateException e) {
+                // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=40549
+                // This is the only instance where the thread is started; since
+                // we checked that it is not alive, it must be dead already due
+                // to a run-time exception or error. Exit.
+//             }
+        } else {
+            fThread.reset();
+        }
+    }
+
+    /**
+     * Hook that is called after the reconciler thread has been reset.
+     */
+    protected void reconcilerReset() {
+    }
+
+    /**
+     * Tells whether the code is running in this reconciler's
+     * background thread.
+     *
+     * @return <code>true</code> if running in this reconciler's background thread
+     * @since 3.4
+     */
+    protected bool isRunningInReconcilerThread() {
+        return JThread.currentThread() is fThread.getThread();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/DirtyRegion.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.reconciler.DirtyRegion;
+
+import org.eclipse.jface.text.reconciler.IReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegionQueue; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategy; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; // packageimport
+import org.eclipse.jface.text.reconciler.MonoReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.Reconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilableModel; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileResult; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilerExtension; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.ITypedRegion;
+
+
+/**
+ * A dirty region describes a document range which has been changed.
+ */
+public class DirtyRegion : ITypedRegion {
+
+    /**
+     * Identifies an insert operation.
+     */
+    final static public String INSERT= "__insert"; //$NON-NLS-1$
+    /**
+     * Identifies a remove operation.
+     */
+    final static public String REMOVE= "__remove"; //$NON-NLS-1$
+
+    /** The region's offset. */
+    private int fOffset;
+    /** The region's length. */
+    private int fLength;
+    /** Indicates the type of the applied change. */
+    private String fType;
+    /** The text which has been inserted. */
+    private String fText;
+
+    /**
+     * Creates a new dirty region.
+     *
+     * @param offset the offset within the document where the change occurred
+     * @param length the length of the text within the document that changed
+     * @param type the type of change that this region represents: {@link #INSERT} {@link #REMOVE}
+     * @param text the substitution text
+     */
+    public this(int offset, int length, String type, String text) {
+        fOffset= offset;
+        fLength= length;
+        fType= normalizeTypeValue(type);
+        fText= text;
+    }
+
+    /**
+     * Computes the normalized type value to ensure that the implementation can use object identity rather
+     * than equality.
+     *
+     * @param type the type value
+     * @return the normalized type value or <code>null</code>
+     * @since 3.1
+     */
+    private String normalizeTypeValue(String type) {
+        if (INSERT.equals(type))
+            return INSERT;
+        if (REMOVE.equals(type))
+            return REMOVE;
+        return null;
+    }
+
+    /*
+     * @see ITypedRegion#getOffset()
+     */
+    public int getOffset() {
+        return fOffset;
+    }
+
+    /*
+     * @see ITypedRegion#getLength()
+     */
+    public int getLength() {
+        return fLength;
+    }
+
+    /*
+     * @see ITypedRegion#getType
+     */
+    public String getType() {
+        return fType;
+    }
+
+    /**
+     * Returns the text that changed as part of the region change.
+     *
+     * @return the changed text
+     */
+    public String getText() {
+        return fText;
+    }
+
+    /**
+     * Modify the receiver so that it encompasses the region specified by the dirty region.
+     *
+     * @param dr the dirty region with which to merge
+     */
+    void mergeWith(DirtyRegion dr) {
+        int start= Math.min(fOffset, dr.fOffset);
+        int end= Math.max(fOffset + fLength, dr.fOffset + dr.fLength);
+        fOffset= start;
+        fLength= end - start;
+        fText= (dr.fText is null ? fText : (fText is null) ? dr.fText : fText ~ dr.fText);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/DirtyRegionQueue.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.reconciler.DirtyRegionQueue;
+
+import org.eclipse.jface.text.reconciler.IReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategy; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; // packageimport
+import org.eclipse.jface.text.reconciler.MonoReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.Reconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilableModel; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegion; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileResult; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilerExtension; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+import tango.core.sync.Mutex;
+import tango.core.sync.Condition;
+
+/**
+ * Queue used by {@link org.eclipse.jface.text.reconciler.AbstractReconciler} to manage
+ * dirty regions. When a dirty region is inserted into the queue, the queue tries
+ * to fold it into the neighboring dirty region.
+ *
+ * @see org.eclipse.jface.text.reconciler.AbstractReconciler
+ * @see org.eclipse.jface.text.reconciler.DirtyRegion
+ */
+class DirtyRegionQueue : Mutex {
+
+    /** The list of dirty regions. */
+    private List fDirtyRegions;
+    private Condition cond;
+    /**
+     * Creates a new empty dirty region.
+     */
+    public this() {
+        //super();
+        fDirtyRegions= new ArrayList();
+        cond = new Condition(this);
+    }
+
+    public void wait(){
+        cond.wait();
+    }
+    public void wait(int delay){
+        cond.wait(delay/1000.0);
+    }
+    public void notifyAll(){
+        cond.notifyAll();
+    }
+
+    /**
+     * Adds a dirty region to the end of the dirty-region queue.
+     *
+     * @param dr the dirty region to add
+     */
+    public void addDirtyRegion(DirtyRegion dr) {
+        // If the dirty region being added is directly after the last dirty
+        // region on the queue then merge the two dirty regions together.
+        DirtyRegion lastDR= getLastDirtyRegion();
+        bool wasMerged= false;
+        if (lastDR !is null)
+            if (lastDR.getType() is dr.getType())
+                if (lastDR.getType() is DirtyRegion.INSERT) {
+                    if (lastDR.getOffset() + lastDR.getLength() is dr.getOffset()) {
+                        lastDR.mergeWith(dr);
+                        wasMerged= true;
+                    }
+                } else if (lastDR.getType() is DirtyRegion.REMOVE) {
+                    if (dr.getOffset() + dr.getLength() is lastDR.getOffset()) {
+                        lastDR.mergeWith(dr);
+                        wasMerged= true;
+                    }
+                }
+
+        if (!wasMerged)
+            // Don't merge- just add the new one onto the queue.
+            fDirtyRegions.add(dr);
+    }
+
+    /**
+     * Returns the last dirty region that was added to the queue.
+     *
+     * @return the last DirtyRegion on the queue
+     */
+    private DirtyRegion getLastDirtyRegion() {
+        int size= fDirtyRegions.size();
+        return (size is 0 ? null : cast(DirtyRegion) fDirtyRegions.get(size - 1));
+    }
+
+    /**
+     * Returns the number of regions in the queue.
+     *
+     * @return the dirty-region queue-size
+     */
+    public int getSize() {
+        return fDirtyRegions.size();
+    }
+
+    /**
+     * Throws away all entries in the queue.
+     */
+    public void purgeQueue() {
+        fDirtyRegions.clear();
+    }
+
+    /**
+     * Removes and returns the first dirty region in the queue
+     *
+     * @return the next dirty region on the queue
+     */
+    public DirtyRegion removeNextDirtyRegion() {
+        if (fDirtyRegions.size() is 0)
+            return null;
+        DirtyRegion dr= cast(DirtyRegion) fDirtyRegions.get(0);
+        fDirtyRegions.remove(0);
+        return dr;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilableModel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.reconciler.IReconcilableModel;
+
+import org.eclipse.jface.text.reconciler.IReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegionQueue; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategy; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; // packageimport
+import org.eclipse.jface.text.reconciler.MonoReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.Reconciler; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegion; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileResult; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilerExtension; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Tagging interface for a model that can get reconciled during a
+ * {@linkplain org.eclipse.jface.text.reconciler.IReconcileStep reconcile step}.
+ * <p>
+ * This model is not directly used by a {@linkplain org.eclipse.jface.text.reconciler.IReconciler reconciler}
+ * or a {@linkplain org.eclipse.jface.text.reconciler.IReconcilingStrategy reconciling strategy}.
+ * </p>
+ *
+ * <p>
+ * This interface must be implemented by clients that want to use one of
+ * their models as a reconcile step's input model.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.reconciler.IReconcileStep#setInputModel(IReconcilableModel)
+ * @since 3.0
+ */
+public interface IReconcilableModel {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcileResult.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.reconciler.IReconcileResult;
+
+import org.eclipse.jface.text.reconciler.IReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegionQueue; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategy; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; // packageimport
+import org.eclipse.jface.text.reconciler.MonoReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.Reconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilableModel; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegion; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilerExtension; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Tagging interface for the {@linkplain org.eclipse.jface.text.reconciler.IReconcileStep reconcile step}
+ * result's array element type.
+ * <p>
+ * This interface must be implemented by clients that want to
+ * let one of their model elements be part of a reconcile step result.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.reconciler.IReconcileStep
+ * @since 3.0
+ */
+public interface IReconcileResult {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcileStep.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.reconciler.IReconcileStep;
+
+import org.eclipse.jface.text.reconciler.IReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegionQueue; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategy; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; // packageimport
+import org.eclipse.jface.text.reconciler.MonoReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.Reconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilableModel; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegion; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileResult; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilerExtension; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.IRegion;
+
+
+/**
+ * A reconcile step is one of several steps of a
+ * {@linkplain org.eclipse.jface.text.reconciler.IReconcilingStrategy reconcile strategy}
+ * that consists of several steps. This relationship is not coded into an interface but
+ * should be used by clients who's reconcile strategy consists of several steps.
+ * <p>
+ * If a reconcile step has an {@linkplain org.eclipse.jface.text.reconciler.IReconcilableModel input model}
+ * it will compute the correct model for the next step in the chain and set the next steps
+ * input model before <code>reconcile</code> gets called on that next step. After the last
+ * step has reconciled the {@linkplain org.eclipse.jface.text.reconciler.IReconcileResult reconcile result}
+ * array gets returned to the previous step. Each step in the chain adapts the result to its
+ * input model and returns it to its previous step.
+ * </p>
+ * <p>
+ * Example: Assume a strategy consists of steps A, B and C. And the main model is M.
+ * The strategy will set M to be A's input model. What will happen is:
+ * <ol>
+ *  <li>A.setInputModel(M)</li>
+ *  <li>A.reconcile: A reconciles M</li>
+ *  <li>A computes the model for B =&gt; MB</li>
+ *  <li>B.setInputModel(MB)</li>
+ *  <li>B.reconcile: B reconciles MB</li>
+ *  <li>B computes the model for C =&gt; MC</li>
+ *  <li>C.setInputModel(MC)</li>
+ *  <li>C.reconcile: C reconciles MC</li>
+ *  <li>C returns result RC to step B</li>
+ *  <li>B adapts the RC to MB and merges with its own results</li>
+ *  <li>B returns result RB to step A</li>
+ *  <li>A adapts the result to M and merges with its own results</li>
+ *  <li>A returns the result to the reconcile strategy</li>
+ * </ol>
+ * </p>
+ * <p>
+ * This interface must be implemented by clients.
+ * </p>
+ * @since 3.0
+ */
+public interface IReconcileStep {
+
+    /**
+     * Returns whether this is the last reconcile step or not.
+     *
+     * @return <code>true</code> iff this is the last reconcile step
+     */
+    bool isLastStep();
+
+    /**
+     * Returns whether this is the first reconcile step or not.
+     *
+     * @return <code>true</code> iff this is the first reconcile step
+     */
+    bool isFirstStep();
+
+    /**
+     * Sets the step which is in front of this step in the pipe.
+     * <p>
+     * Note: This method must be called at most once per reconcile step.
+     * </p>
+     *
+     * @param step the previous step
+     * @throws RuntimeException if called more than once
+     */
+    void setPreviousStep(IReconcileStep step);
+
+    /**
+     * Activates incremental reconciling of the specified dirty region.
+     * As a dirty region might span multiple content types, the segment of the
+     * dirty region which should be investigated is also provided to this
+     * reconciling strategy. The given regions refer to the document passed into
+     * the most recent call of {@link IReconcilingStrategy#setDocument(org.eclipse.jface.text.IDocument)}.
+     *
+     * @param dirtyRegion the document region which has been changed
+     * @param subRegion the sub region in the dirty region which should be reconciled
+     * @return an array with reconcile results
+     */
+    IReconcileResult[] reconcile(DirtyRegion dirtyRegion, IRegion subRegion);
+
+    /**
+     * Activates non-incremental reconciling. The reconciling strategy is just told
+     * that there are changes and that it should reconcile the given partition of the
+     * document most recently passed into {@link IReconcilingStrategy#setDocument(org.eclipse.jface.text.IDocument)}.
+     *
+     * @param partition the document partition to be reconciled
+     * @return an array with reconcile results
+     */
+    IReconcileResult[] reconcile(IRegion partition);
+
+    /**
+     * Sets the progress monitor for this reconcile step.
+     *
+     * @param monitor the progress monitor to be used
+     */
+    void setProgressMonitor(IProgressMonitor monitor);
+
+    /**
+     * Returns the progress monitor used to report progress.
+     *
+     * @return a progress monitor or <code>null</code> if no progress monitor is available
+     */
+    public IProgressMonitor getProgressMonitor();
+
+    /**
+     * Tells this reconcile step on which model it will
+     * work. This method will be called before any other method
+     * and can be called multiple times. The regions passed to the
+     * other methods always refer to the most recent model
+     * passed into this method.
+     *
+     * @param inputModel the model on which this step will work
+     */
+    void setInputModel(IReconcilableModel inputModel);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconciler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.reconciler.IReconciler;
+
+import org.eclipse.jface.text.reconciler.DirtyRegionQueue; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategy; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; // packageimport
+import org.eclipse.jface.text.reconciler.MonoReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.Reconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilableModel; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegion; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileResult; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilerExtension; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * An <code>IReconciler</code> defines and maintains a model of the content
+ * of the text  viewer's document in the presence of changes applied to this
+ * document. An <code>IReconciler</code> is a {@link org.eclipse.jface.text.ITextViewer} add-on.
+ * <p>
+ * Reconcilers are assumed to be asynchronous, i.e. they allow a certain
+ * temporal window of inconsistency between the document and the model of
+ * the content of this document.
+ * </p>
+ * <p>
+ * Reconcilers have a list of {@link org.eclipse.jface.text.reconciler.IReconcilingStrategy}
+ * objects each of which is registered for a  particular document content type.
+ * The reconciler uses the strategy objects to react on the changes applied
+ * to the text viewer's document.
+ *</p>
+ * <p>
+ * In order to provide backward compatibility for clients of <code>IReconciler</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.reconciler.IReconcilerExtension} since version 3.0 introducing
+ *      the ability to be aware of documents with multiple partitionings.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * The interface can be implemented by clients. By default, clients use
+ * {@link org.eclipse.jface.text.reconciler.MonoReconciler} or
+ * {@link org.eclipse.jface.text.reconciler.Reconciler} as the standard
+ * implementers of this interface.
+ * </p>
+ *
+ * @see ITextViewer
+ * @see IReconcilingStrategy
+ */
+public interface IReconciler {
+
+    /**
+     * Installs the reconciler on the given text viewer. After this method has been
+     * finished, the reconciler is operational, i.e., it works without requesting
+     * further client actions until <code>uninstall</code> is called.
+     *
+     * @param textViewer the viewer on which the reconciler is installed
+     */
+    void install(ITextViewer textViewer);
+
+    /**
+     * Removes the reconciler from the text viewer it has
+     * previously been installed on.
+     */
+    void uninstall();
+
+    /**
+     * Returns the reconciling strategy registered with the reconciler
+     * for the specified content type.
+     *
+     * @param contentType the content type for which to determine the reconciling strategy
+     * @return the reconciling strategy registered for the given content type, or
+     *      <code>null</code> if there is no such strategy
+     */
+    IReconcilingStrategy getReconcilingStrategy(String contentType);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilerExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.reconciler.IReconcilerExtension;
+
+import org.eclipse.jface.text.reconciler.IReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegionQueue; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategy; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; // packageimport
+import org.eclipse.jface.text.reconciler.MonoReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.Reconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilableModel; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegion; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileResult; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Extends {@link org.eclipse.jface.text.reconciler.IReconciler} with
+ * the ability to be aware of documents with multiple partitionings.
+ *
+ * @since 3.0
+ */
+public interface IReconcilerExtension {
+
+    /**
+     * Returns the partitioning this reconciler is using.
+     *
+     * @return the partitioning this reconciler is using
+     */
+    String getDocumentPartitioning();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilingStrategy.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.reconciler.IReconcilingStrategy;
+
+import org.eclipse.jface.text.reconciler.IReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegionQueue; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; // packageimport
+import org.eclipse.jface.text.reconciler.MonoReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.Reconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilableModel; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegion; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileResult; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilerExtension; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+
+
+/**
+ * A reconciling strategy is used by an reconciler to reconcile a model
+ * based on text of a particular content type. It provides methods for
+ * incremental as well as non-incremental reconciling.
+ * <p>
+ * If a reconcile strategy consists of several steps between which
+ * model transformation is desired the each step should implement
+ * {@link org.eclipse.jface.text.reconciler.IReconcileStep}.
+ * </p>
+ * <p>
+ * In order to provide backward compatibility for clients of <code>IReconcilingStrategy</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension} since version 2.0 introducing
+ *      the following functions:
+ *          <ul>
+ *              <li>usage of a progress monitor</li>
+ *              <li>initial reconciling step: if a reconciler runs as periodic activity in the background, this
+ *                  methods offers the reconciler a chance for initializing its strategies and achieving a
+ *                  reconciled state before the periodic activity starts.</li>
+ *          </ul>
+ * </li>
+ * </ul>
+ * </p>
+ * <p>
+ * This interface must be implemented by clients. Implementers should be
+ * registered with a reconciler in order get involved in the reconciling
+ * process.
+ * </p>
+ */
+public interface IReconcilingStrategy {
+
+    /**
+     * Tells this reconciling strategy on which document it will
+     * work. This method will be called before any other method
+     * and can be called multiple times. The regions passed to the
+     * other methods always refer to the most recent document
+     * passed into this method.
+     *
+     * @param document the document on which this strategy will work
+     */
+    void setDocument(IDocument document);
+
+    /**
+     * Activates incremental reconciling of the specified dirty region.
+     * As a dirty region might span multiple content types, the segment of the
+     * dirty region which should be investigated is also provided to this
+     * reconciling strategy. The given regions refer to the document passed into
+     * the most recent call of {@link #setDocument(IDocument)}.
+     *
+     * @param dirtyRegion the document region which has been changed
+     * @param subRegion the sub region in the dirty region which should be reconciled
+     */
+    void reconcile(DirtyRegion dirtyRegion, IRegion subRegion);
+
+    /**
+     * Activates non-incremental reconciling. The reconciling strategy is just told
+     * that there are changes and that it should reconcile the given partition of the
+     * document most recently passed into {@link #setDocument(IDocument)}.
+     *
+     * @param partition the document partition to be reconciled
+     */
+    void reconcile(IRegion partition);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilingStrategyExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension;
+
+import org.eclipse.jface.text.reconciler.IReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegionQueue; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategy; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.MonoReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.Reconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilableModel; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegion; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileResult; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilerExtension; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+
+/**
+ * Extends {@link org.eclipse.jface.text.reconciler.IReconcilingStrategy}
+ * with the following functions:
+ * <ul>
+ *  <li>usage of a progress monitor</li>
+ *  <li>initial reconciling step: if a reconciler runs as periodic activity in the background, this
+ *      methods offers the reconciler a chance for initializing its strategies and achieving a
+ *      reconciled state before the periodic activity starts.</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public interface IReconcilingStrategyExtension {
+
+    /**
+     * Tells this reconciling strategy with which progress monitor
+     * it will work. This method will be called before any other
+     * method and can be called multiple times.
+     *
+     * @param monitor the progress monitor with which this strategy will work
+     */
+    void setProgressMonitor(IProgressMonitor monitor);
+
+    /**
+     * Called only once in the life time of this reconciling strategy.
+     */
+    void initialReconcile();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/MonoReconciler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.reconciler.MonoReconciler;
+
+import org.eclipse.jface.text.reconciler.IReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegionQueue; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategy; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.Reconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilableModel; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegion; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileResult; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilerExtension; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.Region;
+
+
+/**
+ * Standard implementation of {@link org.eclipse.jface.text.reconciler.IReconciler}.
+ * The reconciler is configured with a single {@linkplain org.eclipse.jface.text.reconciler.IReconcilingStrategy reconciling strategy}
+ * that is used independently from where a dirty region is located in the reconciler's
+ * document.
+ * <p>
+ * Usually, clients instantiate this class and configure it before using it.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.IDocumentListener
+ * @see org.eclipse.jface.text.ITextInputListener
+ * @see org.eclipse.jface.text.reconciler.DirtyRegion
+ * @since 2.0
+ */
+public class MonoReconciler : AbstractReconciler {
+
+
+    /** The reconciling strategy. */
+    private IReconcilingStrategy fStrategy;
+
+
+    /**
+     * Creates a new reconciler that uses the same reconciling strategy to
+     * reconcile its document independent of the type of the document's contents.
+     *
+     * @param strategy the reconciling strategy to be used
+     * @param isIncremental the indication whether strategy is incremental or not
+     */
+    public this(IReconcilingStrategy strategy, bool isIncremental) {
+        Assert.isNotNull(cast(Object)strategy);
+        fStrategy= strategy;
+        if ( cast(IReconcilingStrategyExtension)fStrategy ) {
+            IReconcilingStrategyExtension extension= cast(IReconcilingStrategyExtension)fStrategy;
+            extension.setProgressMonitor(getProgressMonitor());
+        }
+
+        setIsIncrementalReconciler(isIncremental);
+    }
+
+    /*
+     * @see IReconciler#getReconcilingStrategy(String)
+     */
+    public IReconcilingStrategy getReconcilingStrategy(String contentType) {
+        Assert.isNotNull(contentType);
+        return fStrategy;
+    }
+
+    /*
+     * @see AbstractReconciler#process(DirtyRegion)
+     */
+    protected void process(DirtyRegion dirtyRegion) {
+
+        if(dirtyRegion !is null)
+            fStrategy.reconcile(dirtyRegion, dirtyRegion);
+        else {
+            IDocument document= getDocument();
+            if (document !is null)
+                fStrategy.reconcile(new Region(0, document.getLength()));
+        }
+    }
+
+    /*
+     * @see AbstractReconciler#reconcilerDocumentChanged(IDocument)
+     */
+    protected void reconcilerDocumentChanged(IDocument document) {
+        fStrategy.setDocument(document);
+    }
+
+    /*
+     * @see AbstractReconciler#setProgressMonitor(IProgressMonitor)
+     */
+    public void setProgressMonitor(IProgressMonitor monitor) {
+        super.setProgressMonitor(monitor);
+        if ( cast(IReconcilingStrategyExtension)fStrategy ) {
+            IReconcilingStrategyExtension extension= cast(IReconcilingStrategyExtension) fStrategy;
+            extension.setProgressMonitor(monitor);
+        }
+    }
+
+    /*
+     * @see AbstractReconciler#initialProcess()
+     */
+    protected void initialProcess() {
+        if ( cast(IReconcilingStrategyExtension)fStrategy ) {
+            IReconcilingStrategyExtension extension= cast(IReconcilingStrategyExtension) fStrategy;
+            extension.initialReconcile();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/Reconciler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,248 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.reconciler.Reconciler;
+
+import org.eclipse.jface.text.reconciler.IReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegionQueue; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategy; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; // packageimport
+import org.eclipse.jface.text.reconciler.MonoReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileStep; // packageimport
+import org.eclipse.jface.text.reconciler.AbstractReconciler; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilableModel; // packageimport
+import org.eclipse.jface.text.reconciler.DirtyRegion; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcileResult; // packageimport
+import org.eclipse.jface.text.reconciler.IReconcilerExtension; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension3;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.TypedRegion;
+
+
+/**
+ * Standard implementation of {@link org.eclipse.jface.text.reconciler.IReconciler}.
+ * The reconciler is configured with a set of {@linkplain org.eclipse.jface.text.reconciler.IReconcilingStrategy reconciling strategies}
+ * each of which is responsible for a particular content type.
+ * <p>
+ * Usually, clients instantiate this class and configure it before using it.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.IDocumentListener
+ * @see org.eclipse.jface.text.ITextInputListener
+ * @see org.eclipse.jface.text.reconciler.DirtyRegion
+ */
+public class Reconciler : AbstractReconciler , IReconcilerExtension {
+
+    /** The map of reconciling strategies. */
+    private Map fStrategies;
+
+    /**
+     * The partitioning this reconciler uses.
+     *@since 3.0
+     */
+    private String fPartitioning;
+
+    /**
+     * Creates a new reconciler with the following configuration: it is
+     * an incremental reconciler with a standard delay of 500 milliseconds. There
+     * are no predefined reconciling strategies. The partitioning it uses
+     * is the default partitioning {@link IDocumentExtension3#DEFAULT_PARTITIONING}.
+     */
+    public this() {
+        super();
+        fPartitioning= IDocumentExtension3.DEFAULT_PARTITIONING;
+    }
+
+    /**
+     * Sets the document partitioning for this reconciler.
+     *
+     * @param partitioning the document partitioning for this reconciler
+     * @since 3.0
+     */
+    public void setDocumentPartitioning(String partitioning) {
+        Assert.isNotNull(partitioning);
+        fPartitioning= partitioning;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.reconciler.IReconcilerExtension#getDocumentPartitioning()
+     * @since 3.0
+     */
+    public String getDocumentPartitioning() {
+        return fPartitioning;
+    }
+
+    /**
+     * Registers a given reconciling strategy for a particular content type.
+     * If there is already a strategy registered for this type, the new strategy
+     * is registered instead of the old one.
+     *
+     * @param strategy the reconciling strategy to register, or <code>null</code> to remove an existing one
+     * @param contentType the content type under which to register
+     */
+    public void setReconcilingStrategy(IReconcilingStrategy strategy, String contentType) {
+
+        Assert.isNotNull(contentType);
+
+        if (fStrategies is null)
+            fStrategies= new HashMap();
+
+        if (strategy is null)
+            fStrategies.remove(contentType);
+        else {
+            fStrategies.put(contentType, cast(Object)strategy);
+            if (cast(IReconcilingStrategyExtension )strategy && getProgressMonitor() !is null) {
+                IReconcilingStrategyExtension extension= cast(IReconcilingStrategyExtension) strategy;
+                extension.setProgressMonitor(getProgressMonitor());
+            }
+        }
+    }
+
+    /*
+     * @see IReconciler#getReconcilingStrategy(String)
+     */
+    public IReconcilingStrategy getReconcilingStrategy(String contentType) {
+
+        Assert.isNotNull(contentType);
+
+        if (fStrategies is null)
+            return null;
+
+        return cast(IReconcilingStrategy) fStrategies.get(contentType);
+    }
+
+    /**
+     * Processes a dirty region. If the dirty region is <code>null</code> the whole
+     * document is consider being dirty. The dirty region is partitioned by the
+     * document and each partition is handed over to a reconciling strategy registered
+     * for the partition's content type.
+     *
+     * @param dirtyRegion the dirty region to be processed
+     * @see AbstractReconciler#process(DirtyRegion)
+     */
+    protected void process(DirtyRegion dirtyRegion) {
+
+        IRegion region= dirtyRegion;
+
+        if (region is null)
+            region= new Region(0, getDocument().getLength());
+
+        ITypedRegion[] regions= computePartitioning(region.getOffset(), region.getLength());
+
+        for (int i= 0; i < regions.length; i++) {
+            ITypedRegion r= regions[i];
+            IReconcilingStrategy s= getReconcilingStrategy(r.getType());
+            if (s is null)
+                continue;
+
+            if(dirtyRegion !is null)
+                s.reconcile(dirtyRegion, r);
+            else
+                s.reconcile(r);
+        }
+    }
+
+    /*
+     * @see AbstractReconciler#reconcilerDocumentChanged(IDocument)
+     * @since 2.0
+     */
+    protected void reconcilerDocumentChanged(IDocument document) {
+        if (fStrategies !is null) {
+            Iterator e= fStrategies.values().iterator();
+            while (e.hasNext()) {
+                IReconcilingStrategy strategy= cast(IReconcilingStrategy) e.next();
+                strategy.setDocument(document);
+            }
+        }
+    }
+
+    /*
+     * @see AbstractReconciler#setProgressMonitor(IProgressMonitor)
+     * @since 2.0
+     */
+    public void setProgressMonitor(IProgressMonitor monitor) {
+        super.setProgressMonitor(monitor);
+
+        if (fStrategies !is null) {
+            Iterator e= fStrategies.values().iterator();
+            while (e.hasNext()) {
+                IReconcilingStrategy strategy= cast(IReconcilingStrategy) e.next();
+                if ( cast(IReconcilingStrategyExtension)strategy ) {
+                    IReconcilingStrategyExtension extension= cast(IReconcilingStrategyExtension) strategy;
+                    extension.setProgressMonitor(monitor);
+                }
+            }
+        }
+    }
+
+    /*
+     * @see AbstractReconciler#initialProcess()
+     * @since 2.0
+     */
+    protected void initialProcess() {
+        ITypedRegion[] regions= computePartitioning(0, getDocument().getLength());
+        List contentTypes= new ArrayList(regions.length);
+        for (int i= 0; i < regions.length; i++) {
+            String contentType= regions[i].getType();
+            if( contentTypes.contains(contentType))
+                continue;
+            contentTypes.add(contentType);
+            IReconcilingStrategy s= getReconcilingStrategy(contentType);
+            if ( cast(IReconcilingStrategyExtension)s ) {
+                IReconcilingStrategyExtension e= cast(IReconcilingStrategyExtension) s;
+                e.initialReconcile();
+            }
+        }
+    }
+
+    /**
+     * Computes and returns the partitioning for the given region of the input document
+     * of the reconciler's connected text viewer.
+     *
+     * @param offset the region offset
+     * @param length the region length
+     * @return the computed partitioning
+     * @since 3.0
+     */
+    private ITypedRegion[] computePartitioning(int offset, int length) {
+        ITypedRegion[] regions= null;
+        try {
+            regions= TextUtilities.computePartitioning(getDocument(), getDocumentPartitioning(), offset, length, false);
+        } catch (BadLocationException x) {
+            regions= new ITypedRegion[0];
+        }
+        return regions;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/revisions/IRevisionListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.text.revisions.IRevisionListener;
+
+import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension; // packageimport
+import org.eclipse.jface.text.revisions.RevisionRange; // packageimport
+import org.eclipse.jface.text.revisions.IRevisionRulerColumn; // packageimport
+import org.eclipse.jface.text.revisions.RevisionEvent; // packageimport
+import org.eclipse.jface.text.revisions.RevisionInformation; // packageimport
+import org.eclipse.jface.text.revisions.Revision; // packageimport
+
+
+import java.lang.all;
+
+
+/** 
+ * A listener which is notified when revision information changes.
+ *
+ * @see RevisionInformation
+ * @see IRevisionRulerColumnExtension
+ * @since 3.3
+ */
+public interface IRevisionListener {
+    /**
+     * Notifies the receiver that the revision information has been updated. This typically occurs
+     * when revision information is being displayed in an editor and the annotated document is
+     * modified.
+     * 
+     * @param e the revision event describing the change
+     */
+    void revisionInformationChanged(RevisionEvent e);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/revisions/IRevisionRulerColumn.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.revisions.IRevisionRulerColumn;
+
+import org.eclipse.jface.text.revisions.IRevisionListener; // packageimport
+import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension; // packageimport
+import org.eclipse.jface.text.revisions.RevisionRange; // packageimport
+import org.eclipse.jface.text.revisions.RevisionEvent; // packageimport
+import org.eclipse.jface.text.revisions.RevisionInformation; // packageimport
+import org.eclipse.jface.text.revisions.Revision; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.source.IVerticalRulerColumn;
+import org.eclipse.jface.text.source.IVerticalRulerInfo;
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension;
+
+/**
+ * A vertical ruler column capable of displaying revision (annotate) information.
+ *
+ * In order to provide backward compatibility for clients of
+ * <code>IRevisionRulerColumn</code>, extension interfaces are used as a means
+ * of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link IRevisionRulerColumnExtension} since
+ * version 3.3 allowing to register a selection listener on revisions and a configurable rendering mode.
+ * </li>
+ * </ul>
+ * 
+ * @since 3.2
+ * @see RevisionInformation
+ * @see IRevisionRulerColumnExtension
+ */
+public interface IRevisionRulerColumn : IVerticalRulerColumn, IVerticalRulerInfo, IVerticalRulerInfoExtension {
+    /**
+     * Sets the revision information.
+     * 
+     * @param info the new revision information, or <code>null</code> to reset the ruler
+     */
+    void setRevisionInformation(RevisionInformation info);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/revisions/IRevisionRulerColumnExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension;
+
+import org.eclipse.jface.text.revisions.IRevisionListener; // packageimport
+import org.eclipse.jface.text.revisions.RevisionRange; // packageimport
+import org.eclipse.jface.text.revisions.IRevisionRulerColumn; // packageimport
+import org.eclipse.jface.text.revisions.RevisionEvent; // packageimport
+import org.eclipse.jface.text.revisions.RevisionInformation; // packageimport
+import org.eclipse.jface.text.revisions.Revision; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.viewers.ISelectionProvider;
+
+    static this(){
+        IRevisionRulerColumnExtension_AUTHOR= new IRevisionRulerColumnExtension_RenderingMode("Author"); //$NON-NLS-1$
+        IRevisionRulerColumnExtension_AGE= new IRevisionRulerColumnExtension_RenderingMode("Age"); //$NON-NLS-1$
+        IRevisionRulerColumnExtension_AUTHOR_SHADED_BY_AGE= new IRevisionRulerColumnExtension_RenderingMode("Both"); //$NON-NLS-1$
+    }
+
+    /**
+     * Rendering mode type-safe enum.
+     */
+    final class RenderingMode {
+        private const String fName;
+        private this(String name) {
+            Assert.isLegal(name !is null);
+            fName= name;
+        }
+        /**
+         * Returns the name of the rendering mode.
+         * @return the name of the rendering mode
+         */
+        public String name() {
+            return fName;
+        }
+    }
+    alias RenderingMode IRevisionRulerColumnExtension_RenderingMode;
+
+
+    /**
+     * Rendering mode that assigns a unique color to each revision author.
+     */
+    static const RenderingMode IRevisionRulerColumnExtension_AUTHOR;
+    /**
+     * Rendering mode that assigns colors to revisions by their age.
+     * <p>
+     * Currently the most recent revision is red, the oldest is a faint yellow.
+     * The coloring scheme can change in future releases.
+     * </p>
+     */
+    static const RenderingMode IRevisionRulerColumnExtension_AGE;
+    /**
+     * Rendering mode that assigns unique colors per revision author and
+     * uses different color intensity depending on the age.
+     * <p>
+     * Currently it selects lighter colors for older revisions and more intense
+     * colors for more recent revisions.
+     * The coloring scheme can change in future releases.
+     * </p>
+     */
+    static const RenderingMode IRevisionRulerColumnExtension_AUTHOR_SHADED_BY_AGE;
+
+/**
+ * Extension interface for {@link IRevisionRulerColumn}.
+ * <p>
+ * Introduces the ability to register a selection listener on revisions and configurable rendering
+ * modes.
+ * </p>
+ *
+ * @see IRevisionRulerColumn
+ * @since 3.3
+ */
+public interface IRevisionRulerColumnExtension {
+
+    /**
+     * Changes the rendering mode and triggers redrawing if needed.
+     *
+     * @param mode the rendering mode
+     */
+    void setRevisionRenderingMode(RenderingMode mode);
+
+    /**
+     * Enables showing the revision id.
+     *
+     * @param show <code>true</code> to show the revision, <code>false</code> to hide it
+     */
+    void showRevisionId(bool show);
+
+    /**
+     * Enables showing the revision author.
+     *
+     * @param show <code>true</code> to show the author, <code>false</code> to hide it
+     */
+    void showRevisionAuthor(bool show);
+
+    /**
+     * Returns the revision selection provider.
+     *
+     * @return the revision selection provider
+     */
+    ISelectionProvider getRevisionSelectionProvider();
+
+    /**
+     * Adds a revision listener that will be notified when the displayed revision information
+     * changes.
+     *
+     * @param listener the listener to add
+     */
+    void addRevisionListener(IRevisionListener listener);
+
+    /**
+     * Removes a previously registered revision listener; nothing happens if <code>listener</code>
+     * was not registered with the receiver.
+     *
+     * @param listener the listener to remove
+     */
+    void removeRevisionListener(IRevisionListener listener);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/revisions/Revision.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,174 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.revisions.Revision;
+
+import org.eclipse.jface.text.revisions.IRevisionListener; // packageimport
+import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension; // packageimport
+import org.eclipse.jface.text.revisions.RevisionRange; // packageimport
+import org.eclipse.jface.text.revisions.IRevisionRulerColumn; // packageimport
+import org.eclipse.jface.text.revisions.RevisionEvent; // packageimport
+import org.eclipse.jface.text.revisions.RevisionInformation; // packageimport
+
+
+import java.lang.all;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import org.eclipse.dwtxhelper.Date;
+
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.jface.internal.text.revisions.ChangeRegion;
+import org.eclipse.jface.internal.text.revisions.Hunk;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.source.ILineRange;
+
+/**
+ * Describes a revision of a document. A revision consists of one ore more {@link ILineRange}s.
+ * <p>
+ * Clients may subclass.
+ * </p>
+ *
+ * @since 3.2
+ */
+public abstract class Revision {
+    /** The original list of change regions, element type: {@link ChangeRegion}. */
+    private const List fChangeRegions;
+    /**
+     * The cached list of adjusted ranges, element type: {@link RevisionRange}. <code>null</code>
+     * if the list must be re-computed. Unmodifiable.
+     *
+     * @since 3.3
+     */
+    private List fRanges= null;
+
+    /**
+     * Creates a new revision.
+     */
+    protected this() {
+        fChangeRegions= new ArrayList();
+    }
+
+    /**
+     * Adds a line range to this revision. The range must be non-empty and have a legal start line
+     * (not -1).
+     *
+     * @param range a line range that was changed with this revision
+     * @throws IndexOutOfBoundsException if the line range is empty or has a negative start line
+     */
+    public final void addRange(ILineRange range)  {
+        fChangeRegions.add(new ChangeRegion(this, range));
+    }
+
+    /**
+     * Returns the contained {@link RevisionRange}s adapted to the current diff state. The returned
+     * information is only valid at the moment it is returned, and may change as the annotated
+     * document is modified.
+     *
+     * @return an unmodifiable view of the contained ranges (element type: {@link RevisionRange})
+     */
+    public final List getRegions() {
+        if (fRanges is null) {
+            List ranges= new ArrayList(fChangeRegions.size());
+            for (Iterator it= fChangeRegions.iterator(); it.hasNext();) {
+                ChangeRegion region= cast(ChangeRegion) it.next();
+                for (Iterator inner= region.getAdjustedRanges().iterator(); inner.hasNext();) {
+                    ILineRange range= cast(ILineRange) inner.next();
+                    ranges.add(new RevisionRange(this, range));
+                }
+            }
+            fRanges= Collections.unmodifiableList(ranges);
+        }
+        return fRanges;
+    }
+
+    /**
+     * Adjusts the revision information to the given diff information. Any previous diff information
+     * is discarded.
+     *
+     * @param hunks the diff hunks to adjust the revision information to
+     * @since 3.3
+     */
+    final void applyDiff(Hunk[] hunks) {
+        fRanges= null; // mark for recomputation
+        for (Iterator regions= fChangeRegions.iterator(); regions.hasNext();) {
+            ChangeRegion region= cast(ChangeRegion) regions.next();
+            region.clearDiff();
+            for (int i= 0; i < hunks.length; i++) {
+                Hunk hunk= hunks[i];
+                region.adjustTo(hunk);
+            }
+        }
+    }
+
+    /**
+     * Returns the hover information that will be shown when the user hovers over the a change
+     * region of this revision.
+     * <p>
+     * <strong>Note:</strong> The hover information control which is used to display the information
+     * must be able process the given object. If the default information control creator is used
+     * the supported format is simple text, full HTML or an HTML fragment.
+     * </p>
+     *
+     * @return the hover information for this revision or <code>null</code> for no hover
+     * @see RevisionInformation#setHoverControlCreator(IInformationControlCreator)
+     */
+    public abstract Object getHoverInfo();
+
+    /**
+     * Returns the author color for this revision. This color can be used to visually distinguish
+     * one revision from another, for example as background color.
+     * <p>
+     * Revisions from the same author must return the same color and revisions from different authors
+     * must return distinct colors.</p>
+     *
+     * @return the RGB color for this revision's author
+     */
+    public abstract RGB getColor();
+
+    /**
+     * Returns the unique (within the document) id of this revision. This may be the version string
+     * or a different identifier.
+     *
+     * @return the id of this revision
+     */
+    public abstract String getId();
+
+    /**
+     * Returns the modification date of this revision.
+     *
+     * @return the modification date of this revision
+     */
+    public abstract Date getDate();
+
+    /*
+     * @see java.lang.Object#toString()
+     */
+    public override String toString() {
+        return "Revision " ~ getId(); //$NON-NLS-1$
+    }
+
+    /**
+     * Returns the display string for the author of this revision.
+     * <p>
+     * Subclasses should replace - the default implementation returns the empty string.
+     * </p>
+     *
+     * @return the author name
+     * @since 3.3
+     */
+    public String getAuthor() {
+        return ""; //$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/revisions/RevisionEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.text.revisions.RevisionEvent;
+
+import org.eclipse.jface.text.revisions.IRevisionListener; // packageimport
+import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension; // packageimport
+import org.eclipse.jface.text.revisions.RevisionRange; // packageimport
+import org.eclipse.jface.text.revisions.IRevisionRulerColumn; // packageimport
+import org.eclipse.jface.text.revisions.RevisionInformation; // packageimport
+import org.eclipse.jface.text.revisions.Revision; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * Informs about a change of revision information.
+ * <p>
+ * Clients may use but not instantiate this class.
+ * </p>
+ * 
+ * @since 3.3
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public final class RevisionEvent {
+    
+    private const RevisionInformation fInformation;
+
+    /**
+     * Creates a new event.
+     * 
+     * @param information the revision info
+     */
+    public this(RevisionInformation information) {
+        Assert.isLegal(information !is null);
+        fInformation= information;
+    }
+
+    /**
+     * Returns the revision information that has changed.
+     * 
+     * @return the revision information that has changed
+     */
+    public RevisionInformation getRevisionInformation() {
+        return fInformation;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/revisions/RevisionInformation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.revisions.RevisionInformation;
+
+import org.eclipse.jface.text.revisions.IRevisionListener; // packageimport
+import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension; // packageimport
+import org.eclipse.jface.text.revisions.RevisionRange; // packageimport
+import org.eclipse.jface.text.revisions.IRevisionRulerColumn; // packageimport
+import org.eclipse.jface.text.revisions.RevisionEvent; // packageimport
+import org.eclipse.jface.text.revisions.Revision; // packageimport
+
+
+import java.lang.all;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.internal.text.revisions.Hunk;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.ITextHoverExtension;
+import org.eclipse.jface.text.information.IInformationProviderExtension2;
+
+/**
+ * Encapsulates revision information for one line-based document.
+ * <p>
+ * Clients may instantiate.
+ * </p>
+ *
+ * @since 3.2
+ * @see Revision
+ */
+public final class RevisionInformation : ITextHoverExtension, IInformationProviderExtension2 {
+    /** The revisions, element type: {@link Revision}. */
+    private const List fRevisions;
+    /** A unmodifiable view of <code>fRevisions</code>. */
+    private const List fRORevisions;
+    /**
+     * The flattened list of {@link RevisionRange}s, unmodifiable. <code>null</code> if the list
+     * must be re-computed.
+     *
+     * @since 3.3
+     */
+    private List fRanges= null;
+
+    /**
+     * The hover control creator. Can be <code>null</code>.
+     *
+     * @since 3.3
+     */
+    private IInformationControlCreator fHoverControlCreator;
+
+    /**
+     * The information presenter control creator. Can be <code>null</code>.
+     *
+     * @since 3.3
+     */
+    private IInformationControlCreator fInformationPresenterControlCreator;
+
+    /**
+     * Creates a new revision information model.
+     */
+    public this() {
+        fRevisions= new ArrayList();
+        fRORevisions= Collections.unmodifiableList(fRevisions);
+    }
+
+    /**
+     * Adds a revision.
+     *
+     * @param revision a revision
+     */
+    public void addRevision(Revision revision) {
+        Assert.isLegal(revision !is null);
+        fRevisions.add(revision);
+    }
+
+    /**
+     * Returns the contained revisions.
+     *
+     * @return an unmodifiable view of the contained revisions (element type: {@link Revision})
+     */
+    public List getRevisions() {
+        return fRORevisions;
+    }
+
+    /**
+     * Returns the line ranges of this revision information. The returned information is only valid
+     * at the moment it is returned, and may change as the annotated document is modified. See
+     * {@link IRevisionListener} for a way to be informed when the revision information changes. The
+     * returned list is sorted by document offset.
+     *
+     * @return an unmodifiable view of the line ranges (element type: {@link RevisionRange})
+     * @see IRevisionListener
+     * @since 3.3
+     */
+    public List getRanges() {
+        if (fRanges is null) {
+            List ranges= new ArrayList(fRevisions.size() * 2); // wild size guess
+            for (Iterator it= fRevisions.iterator(); it.hasNext();) {
+                Revision revision= cast(Revision) it.next();
+                ranges.addAll(revision.getRegions());
+            }
+
+            // sort by start line
+            Collections.sort(ranges, new class()  Comparator {
+                public int compare(Object o1, Object o2) {
+                    RevisionRange r1= cast(RevisionRange) o1;
+                    RevisionRange r2= cast(RevisionRange) o2;
+
+                    return r1.getStartLine() - r2.getStartLine();
+                }
+            });
+
+            fRanges= Collections.unmodifiableList(ranges);
+        }
+        return fRanges;
+    }
+
+    /**
+     * Adjusts the revision information to the given diff information. Any previous diff information is discarded. <strong>Note</strong>: This is an internal framework method and must not be called by clients.
+     *
+     * @param hunks the diff hunks to adjust the revision information to
+     * @since 3.3
+     * @noreference This method is not intended to be referenced by clients.
+     */
+    public void applyDiff(Hunk[] hunks) {
+        fRanges= null; // mark for recomputation
+        for (Iterator revisions= getRevisions().iterator(); revisions.hasNext();)
+            (cast(Revision) revisions.next()).applyDiff(hunks);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextHoverExtension#getHoverControlCreator()
+     * @since 3.3
+     */
+    public IInformationControlCreator getHoverControlCreator() {
+        return fHoverControlCreator;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return the information control creator or <code>null</code>
+     * @since 3.3
+     */
+    public IInformationControlCreator getInformationPresenterControlCreator() {
+        return fInformationPresenterControlCreator;
+    }
+
+    /**
+     * Sets the hover control creator.
+     * <p>
+     * <strong>Note:</strong> The created information control must be able to display the object
+     * returned by the concrete implementation of {@link Revision#getHoverInfo()}.
+     * </p>
+     *
+     * @param creator the control creator
+     * @since 3.3
+     */
+    public void setHoverControlCreator(IInformationControlCreator creator) {
+        fHoverControlCreator= creator;
+    }
+
+    /**
+     * Sets the information presenter control creator.
+     *
+     * @param creator the control creator
+     * @since 3.3
+     */
+    public void setInformationPresenterControlCreator(IInformationControlCreator creator) {
+        fInformationPresenterControlCreator= creator;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/revisions/RevisionRange.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.text.revisions.RevisionRange;
+
+import org.eclipse.jface.text.revisions.IRevisionListener; // packageimport
+import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension; // packageimport
+import org.eclipse.jface.text.revisions.IRevisionRulerColumn; // packageimport
+import org.eclipse.jface.text.revisions.RevisionEvent; // packageimport
+import org.eclipse.jface.text.revisions.RevisionInformation; // packageimport
+import org.eclipse.jface.text.revisions.Revision; // packageimport
+
+
+import java.lang.all;
+import tango.text.convert.Format;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.source.ILineRange;
+
+
+/**
+ * An unmodifiable line range that belongs to a {@link Revision}.
+ *
+ * @since 3.3
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public final class RevisionRange : ILineRange {
+    private const Revision fRevision;
+    private const int fStartLine;
+    private const int fNumberOfLines;
+
+    this(Revision revision, ILineRange range) {
+        Assert.isLegal(revision !is null);
+        fRevision= revision;
+        fStartLine= range.getStartLine();
+        fNumberOfLines= range.getNumberOfLines();
+    }
+
+    /**
+     * Returns the revision that this range belongs to.
+     *
+     * @return the revision that this range belongs to
+     */
+    public Revision getRevision() {
+        return fRevision;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.ILineRange#getStartLine()
+     */
+    public int getStartLine() {
+        return fStartLine;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.ILineRange#getNumberOfLines()
+     */
+    public int getNumberOfLines() {
+        return fNumberOfLines;
+    }
+
+    /*
+     * @see java.lang.Object#toString()
+     */
+    public override String toString() {
+        return Format("RevisionRange [{}, [{}+{})]", fRevision.toString(), getStartLine(), getNumberOfLines()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/BufferedRuleBasedScanner.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.rules.BufferedRuleBasedScanner;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+/**
+ * A buffered rule based scanner. The buffer always contains a section
+ * of a fixed size of the document to be scanned. Completely adheres to
+ * the contract of <code>RuleBasedScanner</code>.
+ */
+public class BufferedRuleBasedScanner : RuleBasedScanner {
+
+    /** The default buffer size. Value = 500 */
+    private const static int DEFAULT_BUFFER_SIZE= 500;
+    /** The actual size of the buffer. Initially set to <code>DEFAULT_BUFFER_SIZE</code> */
+    private int fBufferSize= DEFAULT_BUFFER_SIZE;
+    /** The buffer */
+    private char[] fBuffer;
+    /** The offset of the document at which the buffer starts */
+    private int fStart;
+    /** The offset of the document at which the buffer ends */
+    private int fEnd;
+    /** The cached length of the document */
+    private int fDocumentLength;
+
+
+    /**
+     * Creates a new buffered rule based scanner which does
+     * not have any rule and a default buffer size of 500 characters.
+     */
+    protected this() {
+        super();
+        fBuffer= new char[DEFAULT_BUFFER_SIZE];
+        fBuffer[] = 0;
+    }
+
+    /**
+     * Creates a new buffered rule based scanner which does
+     * not have any rule. The buffer size is set to the given
+     * number of characters.
+     *
+     * @param size the buffer size
+     */
+    public this(int size) {
+        super();
+        fBuffer= new char[DEFAULT_BUFFER_SIZE];
+        fBuffer[] = 0;
+        setBufferSize(size);
+    }
+
+    /**
+     * Sets the buffer to the given number of characters.
+     *
+     * @param size the buffer size
+     */
+    protected void setBufferSize(int size) {
+        Assert.isTrue(size > 0);
+        fBufferSize= size;
+        fBuffer= new char[size];
+        fBuffer[] = 0;
+    }
+
+    /**
+     * Shifts the buffer so that the buffer starts at the
+     * given document offset.
+     *
+     * @param offset the document offset at which the buffer starts
+     */
+    private void shiftBuffer(int offset) {
+
+        fStart= offset;
+        fEnd= fStart + fBufferSize;
+        if (fEnd > fDocumentLength)
+            fEnd= fDocumentLength;
+
+        try {
+
+            String content= fDocument.get(fStart, fEnd - fStart);
+            content.getChars(0, fEnd - fStart, fBuffer, 0);
+
+        } catch (BadLocationException x) {
+        }
+    }
+
+    /*
+     * @see RuleBasedScanner#setRange(IDocument, int, int)
+     */
+    public void setRange(IDocument document, int offset, int length) {
+
+        super.setRange(document, offset, length);
+
+        fDocumentLength= document.getLength();
+        shiftBuffer(offset);
+    }
+
+    /*
+     * @see RuleBasedScanner#read()
+     */
+    public int read() {
+        fColumn= UNDEFINED;
+        if (fOffset >= fRangeEnd) {
+            ++ fOffset;
+            return EOF;
+        }
+
+        if (fOffset is fEnd)
+            shiftBuffer(fEnd);
+        else if (fOffset < fStart || fEnd < fOffset)
+            shiftBuffer(fOffset);
+
+        return fBuffer[fOffset++ - fStart];
+    }
+
+    /*
+     * @see RuleBasedScanner#unread()
+     */
+    public void unread() {
+
+        if (fOffset is fStart)
+            shiftBuffer(Math.max(0, fStart - (fBufferSize / 2)));
+
+        --fOffset;
+        fColumn= UNDEFINED;
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/DefaultDamagerRepairer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,259 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.rules.DefaultDamagerRepairer;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextAttribute;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.jface.text.presentation.IPresentationDamager;
+import org.eclipse.jface.text.presentation.IPresentationRepairer;
+
+
+/**
+ * A standard implementation of a syntax driven presentation damager
+ * and presentation repairer. It uses a token scanner to scan
+ * the document and to determine its damage and new text presentation.
+ * The tokens returned by the scanner are supposed to return text attributes
+ * as their data.
+ *
+ * @see ITokenScanner
+ * @since 2.0
+ */
+public class DefaultDamagerRepairer : IPresentationDamager, IPresentationRepairer {
+
+
+    /** The document this object works on */
+    protected IDocument fDocument;
+    /** The scanner it uses */
+    protected ITokenScanner fScanner;
+    /** The default text attribute if non is returned as data by the current token */
+    protected TextAttribute fDefaultTextAttribute;
+
+    /**
+     * Creates a damager/repairer that uses the given scanner and returns the given default
+     * text attribute if the current token does not carry a text attribute.
+     *
+     * @param scanner the token scanner to be used
+     * @param defaultTextAttribute the text attribute to be returned if non is specified by the current token,
+     *          may not be <code>null</code>
+     *
+     * @deprecated use DefaultDamagerRepairer(ITokenScanner) instead
+     */
+    public this(ITokenScanner scanner, TextAttribute defaultTextAttribute) {
+
+        Assert.isNotNull(defaultTextAttribute);
+
+        fScanner= scanner;
+        fDefaultTextAttribute= defaultTextAttribute;
+    }
+
+    /**
+     * Creates a damager/repairer that uses the given scanner. The scanner may not be <code>null</code>
+     * and is assumed to return only token that carry text attributes.
+     *
+     * @param scanner the token scanner to be used, may not be <code>null</code>
+     */
+    public this(ITokenScanner scanner) {
+
+        Assert.isNotNull(cast(Object)scanner);
+
+        fScanner= scanner;
+        fDefaultTextAttribute= new TextAttribute(null);
+    }
+
+    /*
+     * @see IPresentationDamager#setDocument(IDocument)
+     * @see IPresentationRepairer#setDocument(IDocument)
+     */
+    public void setDocument(IDocument document) {
+        fDocument= document;
+    }
+
+
+    //---- IPresentationDamager
+
+    /**
+     * Returns the end offset of the line that contains the specified offset or
+     * if the offset is inside a line delimiter, the end offset of the next line.
+     *
+     * @param offset the offset whose line end offset must be computed
+     * @return the line end offset for the given offset
+     * @exception BadLocationException if offset is invalid in the current document
+     */
+    protected int endOfLineOf(int offset)  {
+
+        IRegion info= fDocument.getLineInformationOfOffset(offset);
+        if (offset <= info.getOffset() + info.getLength())
+            return info.getOffset() + info.getLength();
+
+        int line= fDocument.getLineOfOffset(offset);
+        try {
+            info= fDocument.getLineInformation(line + 1);
+            return info.getOffset() + info.getLength();
+        } catch (BadLocationException x) {
+            return fDocument.getLength();
+        }
+    }
+
+    /*
+     * @see IPresentationDamager#getDamageRegion(ITypedRegion, DocumentEvent, bool)
+     */
+    public IRegion getDamageRegion(ITypedRegion partition, DocumentEvent e, bool documentPartitioningChanged) {
+
+        if (!documentPartitioningChanged) {
+            try {
+
+                IRegion info= fDocument.getLineInformationOfOffset(e.getOffset());
+                int start= Math.max(partition.getOffset(), info.getOffset());
+
+                int end= e.getOffset() + (e.getText() is null ? e.getLength() : e.getText().length());
+
+                if (info.getOffset() <= end && end <= info.getOffset() + info.getLength()) {
+                    // optimize the case of the same line
+                    end= info.getOffset() + info.getLength();
+                } else
+                    end= endOfLineOf(end);
+
+                end= Math.min(partition.getOffset() + partition.getLength(), end);
+                return new Region(start, end - start);
+
+            } catch (BadLocationException x) {
+            }
+        }
+
+        return partition;
+    }
+
+    //---- IPresentationRepairer
+
+    /*
+     * @see IPresentationRepairer#createPresentation(TextPresentation, ITypedRegion)
+     */
+    public void createPresentation(TextPresentation presentation, ITypedRegion region) {
+
+        if (fScanner is null) {
+            // will be removed if deprecated constructor will be removed
+            addRange(presentation, region.getOffset(), region.getLength(), fDefaultTextAttribute);
+            return;
+        }
+
+        int lastStart= region.getOffset();
+        int length= 0;
+        bool firstToken= true;
+        IToken lastToken= Token.UNDEFINED;
+        TextAttribute lastAttribute= getTokenTextAttribute(lastToken);
+
+        fScanner.setRange(fDocument, lastStart, region.getLength());
+
+        while (true) {
+            IToken token= fScanner.nextToken();
+            if (token.isEOF())
+                break;
+
+            TextAttribute attribute= getTokenTextAttribute(token);
+            if (lastAttribute !is null && lastAttribute.equals(attribute)) {
+                length += fScanner.getTokenLength();
+                firstToken= false;
+            } else {
+                if (!firstToken)
+                    addRange(presentation, lastStart, length, lastAttribute);
+                firstToken= false;
+                lastToken= token;
+                lastAttribute= attribute;
+                lastStart= fScanner.getTokenOffset();
+                length= fScanner.getTokenLength();
+            }
+        }
+
+        addRange(presentation, lastStart, length, lastAttribute);
+    }
+
+    /**
+     * Returns a text attribute encoded in the given token. If the token's
+     * data is not <code>null</code> and a text attribute it is assumed that
+     * it is the encoded text attribute. It returns the default text attribute
+     * if there is no encoded text attribute found.
+     *
+     * @param token the token whose text attribute is to be determined
+     * @return the token's text attribute
+     */
+    protected TextAttribute getTokenTextAttribute(IToken token) {
+        Object data= token.getData();
+        if ( cast(TextAttribute)data )
+            return cast(TextAttribute) data;
+        return fDefaultTextAttribute;
+    }
+
+    /**
+     * Adds style information to the given text presentation.
+     *
+     * @param presentation the text presentation to be extended
+     * @param offset the offset of the range to be styled
+     * @param length the length of the range to be styled
+     * @param attr the attribute describing the style of the range to be styled
+     */
+    protected void addRange(TextPresentation presentation, int offset, int length, TextAttribute attr) {
+        if (attr !is null) {
+            int style= attr.getStyle();
+            int fontStyle= style & (SWT.ITALIC | SWT.BOLD | SWT.NORMAL);
+            StyleRange styleRange= new StyleRange(offset, length, attr.getForeground(), attr.getBackground(), fontStyle);
+            styleRange.strikeout= (style & TextAttribute.STRIKETHROUGH) !is 0;
+            styleRange.underline= (style & TextAttribute.UNDERLINE) !is 0;
+            styleRange.font= attr.getFont();
+            presentation.addStyleRange(styleRange);
+        }
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/DefaultPartitioner.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,761 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.rules.DefaultPartitioner;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DefaultPositionUpdater;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.DocumentRewriteSession;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentPartitioner;
+import org.eclipse.jface.text.IDocumentPartitionerExtension;
+import org.eclipse.jface.text.IDocumentPartitionerExtension2;
+import org.eclipse.jface.text.IDocumentPartitionerExtension3;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.TypedPosition;
+import org.eclipse.jface.text.TypedRegion;
+
+
+
+/**
+ * A standard implementation of a document partitioner. It uses a partition
+ * token scanner to scan the document and to determine the document's
+ * partitioning. The tokens returned by the scanner are supposed to return the
+ * partition type as their data. The partitioner remembers the document's
+ * partitions in the document itself rather than maintaining its own data
+ * structure.
+ *
+ * @see IPartitionTokenScanner
+ * @since 2.0
+ * @deprecated As of 3.1, replaced by {@link org.eclipse.jface.text.rules.FastPartitioner} instead
+ */
+public class DefaultPartitioner : IDocumentPartitioner, IDocumentPartitionerExtension, IDocumentPartitionerExtension2, IDocumentPartitionerExtension3 {
+
+    /**
+     * The position category this partitioner uses to store the document's partitioning information.
+     * @deprecated As of 3.0, use <code>getManagingPositionCategories()</code> instead.
+     */
+    public const static String CONTENT_TYPES_CATEGORY= "__content_types_category"; //$NON-NLS-1$
+
+
+    /** The partitioner's scanner */
+    protected IPartitionTokenScanner fScanner;
+    /** The legal content types of this partitioner */
+    protected String[] fLegalContentTypes;
+    /** The partitioner's document */
+    protected IDocument fDocument;
+    /** The document length before a document change occurred */
+    protected int fPreviousDocumentLength;
+    /** The position updater used to for the default updating of partitions */
+    protected DefaultPositionUpdater fPositionUpdater;
+    /** The offset at which the first changed partition starts */
+    protected int fStartOffset;
+    /** The offset at which the last changed partition ends */
+    protected int fEndOffset;
+    /**The offset at which a partition has been deleted */
+    protected int fDeleteOffset;
+    /**
+     * The position category this partitioner uses to store the document's partitioning information.
+     * @since 3.0
+     */
+    private String fPositionCategory;
+    /**
+     * The active document rewrite session.
+     * @since 3.1
+     */
+    private DocumentRewriteSession fActiveRewriteSession;
+    /**
+     * Flag indicating whether this partitioner has been initialized.
+     * @since 3.1
+     */
+    private bool fIsInitialized= false;
+
+    /**
+     * Creates a new partitioner that uses the given scanner and may return
+     * partitions of the given legal content types.
+     *
+     * @param scanner the scanner this partitioner is supposed to use
+     * @param legalContentTypes the legal content types of this partitioner
+     */
+    public this(IPartitionTokenScanner scanner, String[] legalContentTypes) {
+        fScanner= scanner;
+        fLegalContentTypes= TextUtilities.copy(legalContentTypes);
+        fPositionCategory= CONTENT_TYPES_CATEGORY ~ Integer.toString(toHash());
+        fPositionUpdater= new DefaultPositionUpdater(fPositionCategory);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitionerExtension2#getManagingPositionCategories()
+     * @since 3.0
+     */
+    public String[] getManagingPositionCategories() {
+        return [ fPositionCategory ];
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitioner#connect(org.eclipse.jface.text.IDocument)
+     */
+    public void connect(IDocument document) {
+        connect(document, false);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitionerExtension3#connect(org.eclipse.jface.text.IDocument, bool)
+     * @since 3.1
+     */
+    public void connect(IDocument document, bool delayInitialization) {
+        Assert.isNotNull(cast(Object)document);
+        Assert.isTrue(!document.containsPositionCategory(fPositionCategory));
+
+        fDocument= document;
+        fDocument.addPositionCategory(fPositionCategory);
+
+        fIsInitialized= false;
+        if (!delayInitialization)
+            checkInitialization();
+    }
+
+    /*
+     * @since 3.1
+     */
+    protected final void checkInitialization() {
+        if (!fIsInitialized)
+            initialize();
+    }
+
+    /**
+     * Performs the initial partitioning of the partitioner's document.
+     */
+    protected void initialize() {
+        fIsInitialized= true;
+        fScanner.setRange(fDocument, 0, fDocument.getLength());
+
+        try {
+            IToken token= fScanner.nextToken();
+            while (!token.isEOF()) {
+
+                String contentType= getTokenContentType(token);
+
+                if (isSupportedContentType(contentType)) {
+                    TypedPosition p= new TypedPosition(fScanner.getTokenOffset(), fScanner.getTokenLength(), contentType);
+                    fDocument.addPosition(fPositionCategory, p);
+                }
+
+                token= fScanner.nextToken();
+            }
+        } catch (BadLocationException x) {
+            // cannot happen as offsets come from scanner
+        } catch (BadPositionCategoryException x) {
+            // cannot happen if document has been connected before
+        }
+    }
+
+    /*
+     * @see IDocumentPartitioner#disconnect()
+     */
+    public void disconnect() {
+
+        Assert.isTrue(fDocument.containsPositionCategory(fPositionCategory));
+
+        try {
+            fDocument.removePositionCategory(fPositionCategory);
+        } catch (BadPositionCategoryException x) {
+            // can not happen because of Assert
+        }
+    }
+
+    /*
+     * @see IDocumentPartitioner#documentAboutToBeChanged(DocumentEvent)
+     */
+    public void documentAboutToBeChanged(DocumentEvent e) {
+        if (fIsInitialized) {
+
+            Assert.isTrue(e.getDocument() is fDocument);
+
+            fPreviousDocumentLength= e.getDocument().getLength();
+            fStartOffset= -1;
+            fEndOffset= -1;
+            fDeleteOffset= -1;
+        }
+    }
+
+    /*
+     * @see IDocumentPartitioner#documentChanged(DocumentEvent)
+     */
+    public bool documentChanged(DocumentEvent e) {
+        if (fIsInitialized) {
+            IRegion region= documentChanged2(e);
+            return (region !is null);
+        }
+        return false;
+    }
+
+    /**
+     * Helper method for tracking the minimal region containing all partition changes.
+     * If <code>offset</code> is smaller than the remembered offset, <code>offset</code>
+     * will from now on be remembered. If <code>offset  + length</code> is greater than
+     * the remembered end offset, it will be remembered from now on.
+     *
+     * @param offset the offset
+     * @param length the length
+     */
+    private void rememberRegion(int offset, int length) {
+        // remember start offset
+        if (fStartOffset is -1)
+            fStartOffset= offset;
+        else if (offset < fStartOffset)
+            fStartOffset= offset;
+
+        // remember end offset
+        int endOffset= offset + length;
+        if (fEndOffset is -1)
+            fEndOffset= endOffset;
+        else if (endOffset > fEndOffset)
+            fEndOffset= endOffset;
+    }
+
+    /**
+     * Remembers the given offset as the deletion offset.
+     *
+     * @param offset the offset
+     */
+    private void rememberDeletedOffset(int offset) {
+        fDeleteOffset= offset;
+    }
+
+    /**
+     * Creates the minimal region containing all partition changes using the
+     * remembered offset, end offset, and deletion offset.
+     *
+     * @return the minimal region containing all the partition changes
+     */
+    private IRegion createRegion() {
+        if (fDeleteOffset is -1) {
+            if (fStartOffset is -1 || fEndOffset is -1)
+                return null;
+            return new Region(fStartOffset, fEndOffset - fStartOffset);
+        } else if (fStartOffset is -1 || fEndOffset is -1) {
+            return new Region(fDeleteOffset, 0);
+        } else {
+            int offset= Math.min(fDeleteOffset, fStartOffset);
+            int endOffset= Math.max(fDeleteOffset, fEndOffset);
+            return new Region(offset, endOffset - offset);
+        }
+    }
+
+    /*
+     * @see IDocumentPartitionerExtension#documentChanged2(DocumentEvent)
+     * @since 2.0
+     */
+    public IRegion documentChanged2(DocumentEvent e) {
+
+        if (!fIsInitialized)
+            return null;
+
+        try {
+
+            IDocument d= e.getDocument();
+            Position[] category= d.getPositions(fPositionCategory);
+            IRegion line= d.getLineInformationOfOffset(e.getOffset());
+            int reparseStart= line.getOffset();
+            int partitionStart= -1;
+            String contentType= null;
+            int newLength= e.getText() is null ? 0 : e.getText().length();
+
+            int first= d.computeIndexInCategory(fPositionCategory, reparseStart);
+            if (first > 0)  {
+                TypedPosition partition= cast(TypedPosition) category[first - 1];
+                if (partition.includes(reparseStart)) {
+                    partitionStart= partition.getOffset();
+                    contentType= partition.getType();
+                    if (e.getOffset() is partition.getOffset() + partition.getLength())
+                        reparseStart= partitionStart;
+                    -- first;
+                } else if (reparseStart is e.getOffset() && reparseStart is partition.getOffset() + partition.getLength()) {
+                    partitionStart= partition.getOffset();
+                    contentType= partition.getType();
+                    reparseStart= partitionStart;
+                    -- first;
+                } else {
+                    partitionStart= partition.getOffset() + partition.getLength();
+                    contentType= IDocument.DEFAULT_CONTENT_TYPE;
+                }
+            }
+
+            fPositionUpdater.update(e);
+            for (int i= first; i < category.length; i++) {
+                Position p= category[i];
+                if (p.isDeleted) {
+                    rememberDeletedOffset(e.getOffset());
+                    break;
+                }
+            }
+            category= d.getPositions(fPositionCategory);
+
+            fScanner.setPartialRange(d, reparseStart, d.getLength() - reparseStart, contentType, partitionStart);
+
+            int lastScannedPosition= reparseStart;
+            IToken token= fScanner.nextToken();
+
+            while (!token.isEOF()) {
+
+                contentType= getTokenContentType(token);
+
+                if (!isSupportedContentType(contentType)) {
+                    token= fScanner.nextToken();
+                    continue;
+                }
+
+                int start= fScanner.getTokenOffset();
+                int length= fScanner.getTokenLength();
+
+                lastScannedPosition= start + length - 1;
+
+                // remove all affected positions
+                while (first < category.length) {
+                    TypedPosition p= cast(TypedPosition) category[first];
+                    if (lastScannedPosition >= p.offset + p.length ||
+                            (p.overlapsWith(start, length) &&
+                                (!d.containsPosition(fPositionCategory, start, length) ||
+                                 !contentType.equals(p.getType())))) {
+
+                        rememberRegion(p.offset, p.length);
+                        d.removePosition(fPositionCategory, p);
+                        ++ first;
+
+                    } else
+                        break;
+                }
+
+                // if position already exists and we have scanned at least the
+                // area covered by the event, we are done
+                if (d.containsPosition(fPositionCategory, start, length)) {
+                    if (lastScannedPosition >= e.getOffset() + newLength)
+                        return createRegion();
+                    ++ first;
+                } else {
+                    // insert the new type position
+                    try {
+                        d.addPosition(fPositionCategory, new TypedPosition(start, length, contentType));
+                        rememberRegion(start, length);
+                    } catch (BadPositionCategoryException x) {
+                    } catch (BadLocationException x) {
+                    }
+                }
+
+                token= fScanner.nextToken();
+            }
+
+
+            // remove all positions behind lastScannedPosition since there aren't any further types
+            if (lastScannedPosition !is reparseStart) {
+                // if this condition is not met, nothing has been scanned because of a deletion
+                ++ lastScannedPosition;
+            }
+            first= d.computeIndexInCategory(fPositionCategory, lastScannedPosition);
+            category= d.getPositions(fPositionCategory);
+
+            TypedPosition p;
+            while (first < category.length) {
+                p= cast(TypedPosition) category[first++];
+                d.removePosition(fPositionCategory, p);
+                rememberRegion(p.offset, p.length);
+            }
+
+        } catch (BadPositionCategoryException x) {
+            // should never happen on connected documents
+        } catch (BadLocationException x) {
+        }
+
+        return createRegion();
+    }
+
+
+    /**
+     * Returns the position in the partitoner's position category which is
+     * close to the given offset. This is, the position has either an offset which
+     * is the same as the given offset or an offset which is smaller than the given
+     * offset. This method profits from the knowledge that a partitioning is
+     * a ordered set of disjoint position.
+     *
+     * @param offset the offset for which to search the closest position
+     * @return the closest position in the partitioner's category
+     */
+    protected TypedPosition findClosestPosition(int offset) {
+
+        try {
+
+            int index= fDocument.computeIndexInCategory(fPositionCategory, offset);
+            Position[] category= fDocument.getPositions(fPositionCategory);
+
+            if (category.length is 0)
+                return null;
+
+            if (index < category.length) {
+                if (offset is category[index].offset)
+                    return cast(TypedPosition) category[index];
+            }
+
+            if (index > 0)
+                index--;
+
+            return cast(TypedPosition) category[index];
+
+        } catch (BadPositionCategoryException x) {
+        } catch (BadLocationException x) {
+        }
+
+        return null;
+    }
+
+
+    /*
+     * @see IDocumentPartitioner#getContentType(int)
+     */
+    public String getContentType(int offset) {
+        checkInitialization();
+
+        TypedPosition p= findClosestPosition(offset);
+        if (p !is null && p.includes(offset))
+            return p.getType();
+
+        return IDocument.DEFAULT_CONTENT_TYPE;
+    }
+
+    /*
+     * @see IDocumentPartitioner#getPartition(int)
+     */
+    public ITypedRegion getPartition(int offset) {
+        checkInitialization();
+
+        try {
+
+            Position[] category = fDocument.getPositions(fPositionCategory);
+
+            if (category is null || category.length is 0)
+                return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE);
+
+            int index= fDocument.computeIndexInCategory(fPositionCategory, offset);
+
+            if (index < category.length) {
+
+                TypedPosition next= cast(TypedPosition) category[index];
+
+                if (offset is next.offset)
+                    return new TypedRegion(next.getOffset(), next.getLength(), next.getType());
+
+                if (index is 0)
+                    return new TypedRegion(0, next.offset, IDocument.DEFAULT_CONTENT_TYPE);
+
+                TypedPosition previous= cast(TypedPosition) category[index - 1];
+                if (previous.includes(offset))
+                    return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType());
+
+                int endOffset= previous.getOffset() + previous.getLength();
+                return new TypedRegion(endOffset, next.getOffset() - endOffset, IDocument.DEFAULT_CONTENT_TYPE);
+            }
+
+            TypedPosition previous= cast(TypedPosition) category[category.length - 1];
+            if (previous.includes(offset))
+                return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType());
+
+            int endOffset= previous.getOffset() + previous.getLength();
+            return new TypedRegion(endOffset, fDocument.getLength() - endOffset, IDocument.DEFAULT_CONTENT_TYPE);
+
+        } catch (BadPositionCategoryException x) {
+        } catch (BadLocationException x) {
+        }
+
+        return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE);
+    }
+
+    /*
+     * @see IDocumentPartitioner#computePartitioning(int, int)
+     */
+    public ITypedRegion[] computePartitioning(int offset, int length) {
+        return computePartitioning(offset, length, false);
+    }
+
+    /*
+     * @see IDocumentPartitioner#getLegalContentTypes()
+     */
+    public String[] getLegalContentTypes() {
+        return TextUtilities.copy(fLegalContentTypes);
+    }
+
+    /**
+     * Returns whether the given type is one of the legal content types.
+     *
+     * @param contentType the content type to check
+     * @return <code>true</code> if the content type is a legal content type
+     */
+    protected bool isSupportedContentType(String contentType) {
+        if (contentType !is null) {
+            for (int i= 0; i < fLegalContentTypes.length; i++) {
+                if (fLegalContentTypes[i].equals(contentType))
+                    return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns a content type encoded in the given token. If the token's
+     * data is not <code>null</code> and a string it is assumed that
+     * it is the encoded content type.
+     *
+     * @param token the token whose content type is to be determined
+     * @return the token's content type
+     */
+    protected String getTokenContentType(IToken token) {
+        Object data= token.getData();
+        if ( auto str = cast(ArrayWrapperString)data )
+            return str.array;
+        return null;
+    }
+
+    /* zero-length partition support */
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitionerExtension2#getContentType(int)
+     * @since 3.0
+     */
+    public String getContentType(int offset, bool preferOpenPartitions) {
+        return getPartition(offset, preferOpenPartitions).getType();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitionerExtension2#getPartition(int)
+     * @since 3.0
+     */
+    public ITypedRegion getPartition(int offset, bool preferOpenPartitions) {
+        ITypedRegion region= getPartition(offset);
+        if (preferOpenPartitions) {
+            if (region.getOffset() is offset && !region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE)) {
+                if (offset > 0) {
+                    region= getPartition(offset - 1);
+                    if (region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE))
+                        return region;
+                }
+                return new TypedRegion(offset, 0, IDocument.DEFAULT_CONTENT_TYPE);
+            }
+        }
+        return region;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitionerExtension2#computePartitioning(int, int, bool)
+     * @since 3.0
+     */
+    public ITypedRegion[] computePartitioning(int offset, int length, bool includeZeroLengthPartitions) {
+        checkInitialization();
+        List list= new ArrayList();
+
+        try {
+
+            int endOffset= offset + length;
+
+            Position[] category= fDocument.getPositions(fPositionCategory);
+
+            TypedPosition previous= null, current= null;
+            int start, end, gapOffset;
+            Position gap= new Position(0);
+
+            int startIndex= getFirstIndexEndingAfterOffset(category, offset);
+            int endIndex= getFirstIndexStartingAfterOffset(category, endOffset);
+            for (int i= startIndex; i < endIndex; i++) {
+
+                current= cast(TypedPosition) category[i];
+
+                gapOffset= (previous !is null) ? previous.getOffset() + previous.getLength() : 0;
+                gap.setOffset(gapOffset);
+                gap.setLength(current.getOffset() - gapOffset);
+                if ((includeZeroLengthPartitions && overlapsOrTouches(gap, offset, length)) ||
+                        (gap.getLength() > 0 && gap.overlapsWith(offset, length))) {
+                    start= Math.max(offset, gapOffset);
+                    end= Math.min(endOffset, gap.getOffset() + gap.getLength());
+                    list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE));
+                }
+
+                if (current.overlapsWith(offset, length)) {
+                    start= Math.max(offset, current.getOffset());
+                    end= Math.min(endOffset, current.getOffset() + current.getLength());
+                    list.add(new TypedRegion(start, end - start, current.getType()));
+                }
+
+                previous= current;
+            }
+
+            if (previous !is null) {
+                gapOffset= previous.getOffset() + previous.getLength();
+                gap.setOffset(gapOffset);
+                gap.setLength(fDocument.getLength() - gapOffset);
+                if ((includeZeroLengthPartitions && overlapsOrTouches(gap, offset, length)) ||
+                        (gap.getLength() > 0 && gap.overlapsWith(offset, length))) {
+                    start= Math.max(offset, gapOffset);
+                    end= Math.min(endOffset, fDocument.getLength());
+                    list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE));
+                }
+            }
+
+            if (list.isEmpty())
+                list.add(new TypedRegion(offset, length, IDocument.DEFAULT_CONTENT_TYPE));
+
+        } catch (BadPositionCategoryException x) {
+        }
+
+        return arraycast!(ITypedRegion)(list.toArray());
+    }
+
+    /**
+     * Returns <code>true</code> if the given ranges overlap with or touch each other.
+     *
+     * @param gap the first range
+     * @param offset the offset of the second range
+     * @param length the length of the second range
+     * @return <code>true</code> if the given ranges overlap with or touch each other
+     * @since 3.0
+     */
+    private bool overlapsOrTouches(Position gap, int offset, int length) {
+        return gap.getOffset() <= offset + length && offset <= gap.getOffset() + gap.getLength();
+    }
+
+    /**
+     * Returns the index of the first position which ends after the given offset.
+     *
+     * @param positions the positions in linear order
+     * @param offset the offset
+     * @return the index of the first position which ends after the offset
+     *
+     * @since 3.0
+     */
+    private int getFirstIndexEndingAfterOffset(Position[] positions, int offset) {
+        int i= -1, j= positions.length;
+        while (j - i > 1) {
+            int k= (i + j) >> 1;
+            Position p= positions[k];
+            if (p.getOffset() + p.getLength() > offset)
+                j= k;
+            else
+                i= k;
+        }
+        return j;
+    }
+
+    /**
+     * Returns the index of the first position which starts at or after the given offset.
+     *
+     * @param positions the positions in linear order
+     * @param offset the offset
+     * @return the index of the first position which starts after the offset
+     *
+     * @since 3.0
+     */
+    private int getFirstIndexStartingAfterOffset(Position[] positions, int offset) {
+        int i= -1, j= positions.length;
+        while (j - i > 1) {
+            int k= (i + j) >> 1;
+            Position p= positions[k];
+            if (p.getOffset() >= offset)
+                j= k;
+            else
+                i= k;
+        }
+        return j;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitionerExtension3#startRewriteSession(org.eclipse.jface.text.DocumentRewriteSession)
+     * @since 3.1
+     */
+    public void startRewriteSession(DocumentRewriteSession session)  {
+        if (fActiveRewriteSession !is null)
+            throw new IllegalStateException();
+        fActiveRewriteSession= session;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitionerExtension3#stopRewriteSession(org.eclipse.jface.text.DocumentRewriteSession)
+     * @since 3.1
+     */
+    public void stopRewriteSession(DocumentRewriteSession session) {
+        if (fActiveRewriteSession is session)
+            flushRewriteSession();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitionerExtension3#getActiveRewriteSession()
+     * @since 3.1
+     */
+    public DocumentRewriteSession getActiveRewriteSession() {
+        return fActiveRewriteSession;
+    }
+
+    /**
+     * Flushes the active rewrite session.
+     *
+     * @since 3.1
+     */
+    protected final void flushRewriteSession() {
+        fActiveRewriteSession= null;
+
+        // remove all position belonging to the partitioner position category
+        try {
+            fDocument.removePositionCategory(fPositionCategory);
+        } catch (BadPositionCategoryException x) {
+        }
+        fDocument.addPositionCategory(fPositionCategory);
+
+        fIsInitialized= false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/EndOfLineRule.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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
+ *     Christopher Lenz (cmlenz@gmx.de) - support for line continuation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.rules.EndOfLineRule;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * A specific configuration of a single line rule
+ * whereby the pattern begins with a specific sequence but
+ * is only ended by a line delimiter.
+ */
+public class EndOfLineRule : SingleLineRule {
+
+    /**
+     * Creates a rule for the given starting sequence
+     * which, if detected, will return the specified token.
+     *
+     * @param startSequence the pattern's start sequence
+     * @param token the token to be returned on success
+     */
+    public this(String startSequence, IToken token) {
+        this(startSequence, token, cast(wchar) 0);
+    }
+
+    /**
+     * Creates a rule for the given starting sequence
+     * which, if detected, will return the specified token.
+     * Any character which follows the given escape character
+     * will be ignored.
+     *
+     * @param startSequence the pattern's start sequence
+     * @param token the token to be returned on success
+     * @param escapeCharacter the escape character
+     */
+    public this(String startSequence, IToken token, char escapeCharacter) {
+        super(startSequence, null, token, escapeCharacter, true);
+    }
+
+    /**
+     * Creates a rule for the given starting sequence
+     * which, if detected, will return the specified token.
+     * Any character which follows the given escape character
+     * will be ignored. In addition, an escape character
+     * immediately before an end of line can be set to continue
+     * the line.
+     *
+     * @param startSequence the pattern's start sequence
+     * @param token the token to be returned on success
+     * @param escapeCharacter the escape character
+     * @param escapeContinuesLine indicates whether the specified escape
+     *        character is used for line continuation, so that an end of
+     *        line immediately after the escape character does not
+     *        terminate the line, even if <code>breakOnEOL</code> is true
+     * @since 3.0
+     */
+    public this(String startSequence, IToken token, char escapeCharacter, bool escapeContinuesLine) {
+        super(startSequence, null, token, escapeCharacter, true, escapeContinuesLine);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/FastPartitioner.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,859 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.rules.FastPartitioner;
+
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import tango.text.convert.Format;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DefaultPositionUpdater;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.DocumentRewriteSession;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentPartitioner;
+import org.eclipse.jface.text.IDocumentPartitionerExtension;
+import org.eclipse.jface.text.IDocumentPartitionerExtension2;
+import org.eclipse.jface.text.IDocumentPartitionerExtension3;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.TypedPosition;
+import org.eclipse.jface.text.TypedRegion;
+
+
+
+/**
+ * A standard implementation of a document partitioner. It uses an
+ * {@link IPartitionTokenScanner} to scan the document and to determine the
+ * document's partitioning. The tokens returned by the scanner must return the
+ * partition type as their data. The partitioner remembers the document's
+ * partitions in the document itself rather than maintaining its own data
+ * structure.
+ * <p>
+ * To reduce array creations in {@link IDocument#getPositions(String)}, the
+ * positions get cached. The cache is cleared after updating the positions in
+ * {@link #documentChanged2(DocumentEvent)}. Subclasses need to call
+ * {@link #clearPositionCache()} after modifying the partitioner's positions.
+ * The cached positions may be accessed through {@link #getPositions()}.
+ * </p>
+ *
+ * @see IPartitionTokenScanner
+ * @since 3.1
+ */
+public class FastPartitioner : IDocumentPartitioner, IDocumentPartitionerExtension, IDocumentPartitionerExtension2, IDocumentPartitionerExtension3 {
+
+    /**
+     * The position category this partitioner uses to store the document's partitioning information.
+     */
+    private static const String CONTENT_TYPES_CATEGORY= "__content_types_category"; //$NON-NLS-1$
+    /** The partitioner's scanner */
+    protected final IPartitionTokenScanner fScanner;
+    /** The legal content types of this partitioner */
+    protected final String[] fLegalContentTypes;
+    /** The partitioner's document */
+    protected IDocument fDocument;
+    /** The document length before a document change occurred */
+    protected int fPreviousDocumentLength;
+    /** The position updater used to for the default updating of partitions */
+    protected final DefaultPositionUpdater fPositionUpdater;
+    /** The offset at which the first changed partition starts */
+    protected int fStartOffset;
+    /** The offset at which the last changed partition ends */
+    protected int fEndOffset;
+    /**The offset at which a partition has been deleted */
+    protected int fDeleteOffset;
+    /**
+     * The position category this partitioner uses to store the document's partitioning information.
+     */
+    private const String fPositionCategory;
+    /**
+     * The active document rewrite session.
+     */
+    private DocumentRewriteSession fActiveRewriteSession;
+    /**
+     * Flag indicating whether this partitioner has been initialized.
+     */
+    private bool fIsInitialized= false;
+    /**
+     * The cached positions from our document, so we don't create a new array every time
+     * someone requests partition information.
+     */
+    private Position[] fCachedPositions= null;
+    /** Debug option for cache consistency checking. */
+    private static bool CHECK_CACHE_CONSISTENCY_;
+    private static bool CHECK_CACHE_CONSISTENCY_init;
+    private static bool CHECK_CACHE_CONSISTENCY(){
+        if( !CHECK_CACHE_CONSISTENCY_init ){
+            synchronized(FastPartitioner.classinfo ){
+                if( !CHECK_CACHE_CONSISTENCY_init ){
+                    CHECK_CACHE_CONSISTENCY_init = true;
+                    CHECK_CACHE_CONSISTENCY_ = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.jface.text/debug/FastPartitioner/PositionCache"));  //$NON-NLS-1$//$NON-NLS-2$;
+                }
+            }
+        }
+        return CHECK_CACHE_CONSISTENCY_;
+    }
+
+    /**
+     * Creates a new partitioner that uses the given scanner and may return
+     * partitions of the given legal content types.
+     *
+     * @param scanner the scanner this partitioner is supposed to use
+     * @param legalContentTypes the legal content types of this partitioner
+     */
+    public this(IPartitionTokenScanner scanner, String[] legalContentTypes) {
+        fScanner= scanner;
+        fLegalContentTypes= TextUtilities.copy(legalContentTypes);
+        fPositionCategory= CONTENT_TYPES_CATEGORY ~ Integer.toString(toHash());
+        fPositionUpdater= new DefaultPositionUpdater(fPositionCategory);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitionerExtension2#getManagingPositionCategories()
+     */
+    public String[] getManagingPositionCategories() {
+        return [ fPositionCategory ];
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitioner#connect(org.eclipse.jface.text.IDocument)
+     */
+    public final void connect(IDocument document) {
+        connect(document, false);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * May be extended by subclasses.
+     * </p>
+     */
+    public void connect(IDocument document, bool delayInitialization) {
+        Assert.isNotNull(cast(Object)document);
+        Assert.isTrue(!document.containsPositionCategory(fPositionCategory));
+
+        fDocument= document;
+        fDocument.addPositionCategory(fPositionCategory);
+
+        fIsInitialized= false;
+        if (!delayInitialization)
+            checkInitialization();
+    }
+
+    /**
+     * Calls {@link #initialize()} if the receiver is not yet initialized.
+     */
+    protected final void checkInitialization() {
+        if (!fIsInitialized)
+            initialize();
+    }
+
+    /**
+     * Performs the initial partitioning of the partitioner's document.
+     * <p>
+     * May be extended by subclasses.
+     * </p>
+     */
+    protected void initialize() {
+        fIsInitialized= true;
+        clearPositionCache();
+        fScanner.setRange(fDocument, 0, fDocument.getLength());
+
+        try {
+            IToken token= fScanner.nextToken();
+            while (!token.isEOF()) {
+
+                String contentType= getTokenContentType(token);
+
+                if (isSupportedContentType(contentType)) {
+                    TypedPosition p= new TypedPosition(fScanner.getTokenOffset(), fScanner.getTokenLength(), contentType);
+                    fDocument.addPosition(fPositionCategory, p);
+                }
+
+                token= fScanner.nextToken();
+            }
+        } catch (BadLocationException x) {
+            // cannot happen as offsets come from scanner
+        } catch (BadPositionCategoryException x) {
+            // cannot happen if document has been connected before
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * May be extended by subclasses.
+     * </p>
+     */
+    public void disconnect() {
+
+        Assert.isTrue(fDocument.containsPositionCategory(fPositionCategory));
+
+        try {
+            fDocument.removePositionCategory(fPositionCategory);
+        } catch (BadPositionCategoryException x) {
+            // can not happen because of Assert
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * May be extended by subclasses.
+     * </p>
+     */
+    public void documentAboutToBeChanged(DocumentEvent e) {
+        if (fIsInitialized) {
+
+            Assert.isTrue(e.getDocument() is fDocument);
+
+            fPreviousDocumentLength= e.getDocument().getLength();
+            fStartOffset= -1;
+            fEndOffset= -1;
+            fDeleteOffset= -1;
+        }
+    }
+
+    /*
+     * @see IDocumentPartitioner#documentChanged(DocumentEvent)
+     */
+    public final bool documentChanged(DocumentEvent e) {
+        if (fIsInitialized) {
+            IRegion region= documentChanged2(e);
+            return (region !is null);
+        }
+        return false;
+    }
+
+    /**
+     * Helper method for tracking the minimal region containing all partition changes.
+     * If <code>offset</code> is smaller than the remembered offset, <code>offset</code>
+     * will from now on be remembered. If <code>offset  + length</code> is greater than
+     * the remembered end offset, it will be remembered from now on.
+     *
+     * @param offset the offset
+     * @param length the length
+     */
+    private void rememberRegion(int offset, int length) {
+        // remember start offset
+        if (fStartOffset is -1)
+            fStartOffset= offset;
+        else if (offset < fStartOffset)
+            fStartOffset= offset;
+
+        // remember end offset
+        int endOffset= offset + length;
+        if (fEndOffset is -1)
+            fEndOffset= endOffset;
+        else if (endOffset > fEndOffset)
+            fEndOffset= endOffset;
+    }
+
+    /**
+     * Remembers the given offset as the deletion offset.
+     *
+     * @param offset the offset
+     */
+    private void rememberDeletedOffset(int offset) {
+        fDeleteOffset= offset;
+    }
+
+    /**
+     * Creates the minimal region containing all partition changes using the
+     * remembered offset, end offset, and deletion offset.
+     *
+     * @return the minimal region containing all the partition changes
+     */
+    private IRegion createRegion() {
+        if (fDeleteOffset is -1) {
+            if (fStartOffset is -1 || fEndOffset is -1)
+                return null;
+            return new Region(fStartOffset, fEndOffset - fStartOffset);
+        } else if (fStartOffset is -1 || fEndOffset is -1) {
+            return new Region(fDeleteOffset, 0);
+        } else {
+            int offset= Math.min(fDeleteOffset, fStartOffset);
+            int endOffset= Math.max(fDeleteOffset, fEndOffset);
+            return new Region(offset, endOffset - offset);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * May be extended by subclasses.
+     * </p>
+     */
+    public IRegion documentChanged2(DocumentEvent e) {
+
+        if (!fIsInitialized)
+            return null;
+
+        try {
+            Assert.isTrue(e.getDocument() is fDocument);
+
+            Position[] category= getPositions();
+            IRegion line= fDocument.getLineInformationOfOffset(e.getOffset());
+            int reparseStart= line.getOffset();
+            int partitionStart= -1;
+            String contentType= null;
+            int newLength= e.getText() is null ? 0 : e.getText().length();
+
+            int first= fDocument.computeIndexInCategory(fPositionCategory, reparseStart);
+            if (first > 0)  {
+                TypedPosition partition= cast(TypedPosition) category[first - 1];
+                if (partition.includes(reparseStart)) {
+                    partitionStart= partition.getOffset();
+                    contentType= partition.getType();
+                    if (e.getOffset() is partition.getOffset() + partition.getLength())
+                        reparseStart= partitionStart;
+                    -- first;
+                } else if (reparseStart is e.getOffset() && reparseStart is partition.getOffset() + partition.getLength()) {
+                    partitionStart= partition.getOffset();
+                    contentType= partition.getType();
+                    reparseStart= partitionStart;
+                    -- first;
+                } else {
+                    partitionStart= partition.getOffset() + partition.getLength();
+                    contentType= IDocument.DEFAULT_CONTENT_TYPE;
+                }
+            }
+
+            fPositionUpdater.update(e);
+            for (int i= first; i < category.length; i++) {
+                Position p= category[i];
+                if (p.isDeleted) {
+                    rememberDeletedOffset(e.getOffset());
+                    break;
+                }
+            }
+            clearPositionCache();
+            category= getPositions();
+
+            fScanner.setPartialRange(fDocument, reparseStart, fDocument.getLength() - reparseStart, contentType, partitionStart);
+
+            int behindLastScannedPosition= reparseStart;
+            IToken token= fScanner.nextToken();
+
+            while (!token.isEOF()) {
+
+                contentType= getTokenContentType(token);
+
+                if (!isSupportedContentType(contentType)) {
+                    token= fScanner.nextToken();
+                    continue;
+                }
+
+                int start= fScanner.getTokenOffset();
+                int length= fScanner.getTokenLength();
+
+                behindLastScannedPosition= start + length;
+                int lastScannedPosition= behindLastScannedPosition - 1;
+
+                // remove all affected positions
+                while (first < category.length) {
+                    TypedPosition p= cast(TypedPosition) category[first];
+                    if (lastScannedPosition >= p.offset + p.length ||
+                            (p.overlapsWith(start, length) &&
+                                (!fDocument.containsPosition(fPositionCategory, start, length) ||
+                                 !contentType.equals(p.getType())))) {
+
+                        rememberRegion(p.offset, p.length);
+                        fDocument.removePosition(fPositionCategory, p);
+                        ++ first;
+
+                    } else
+                        break;
+                }
+
+                // if position already exists and we have scanned at least the
+                // area covered by the event, we are done
+                if (fDocument.containsPosition(fPositionCategory, start, length)) {
+                    if (lastScannedPosition >= e.getOffset() + newLength)
+                        return createRegion();
+                    ++ first;
+                } else {
+                    // insert the new type position
+                    try {
+                        fDocument.addPosition(fPositionCategory, new TypedPosition(start, length, contentType));
+                        rememberRegion(start, length);
+                    } catch (BadPositionCategoryException x) {
+                    } catch (BadLocationException x) {
+                    }
+                }
+
+                token= fScanner.nextToken();
+            }
+
+            first= fDocument.computeIndexInCategory(fPositionCategory, behindLastScannedPosition);
+
+            clearPositionCache();
+            category= getPositions();
+            TypedPosition p;
+            while (first < category.length) {
+                p= cast(TypedPosition) category[first++];
+                fDocument.removePosition(fPositionCategory, p);
+                rememberRegion(p.offset, p.length);
+            }
+
+        } catch (BadPositionCategoryException x) {
+            // should never happen on connected documents
+        } catch (BadLocationException x) {
+        } finally {
+            clearPositionCache();
+        }
+
+        return createRegion();
+    }
+
+    /**
+     * Returns the position in the partitoner's position category which is
+     * close to the given offset. This is, the position has either an offset which
+     * is the same as the given offset or an offset which is smaller than the given
+     * offset. This method profits from the knowledge that a partitioning is
+     * a ordered set of disjoint position.
+     * <p>
+     * May be extended or replaced by subclasses.
+     * </p>
+     * @param offset the offset for which to search the closest position
+     * @return the closest position in the partitioner's category
+     */
+    protected TypedPosition findClosestPosition(int offset) {
+
+        try {
+
+            int index= fDocument.computeIndexInCategory(fPositionCategory, offset);
+            Position[] category= getPositions();
+
+            if (category.length is 0)
+                return null;
+
+            if (index < category.length) {
+                if (offset is category[index].offset)
+                    return cast(TypedPosition) category[index];
+            }
+
+            if (index > 0)
+                index--;
+
+            return cast(TypedPosition) category[index];
+
+        } catch (BadPositionCategoryException x) {
+        } catch (BadLocationException x) {
+        }
+
+        return null;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * May be replaced or extended by subclasses.
+     * </p>
+     */
+    public String getContentType(int offset) {
+        checkInitialization();
+
+        TypedPosition p= findClosestPosition(offset);
+        if (p !is null && p.includes(offset))
+            return p.getType();
+
+        return IDocument.DEFAULT_CONTENT_TYPE;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * May be replaced or extended by subclasses.
+     * </p>
+     */
+    public ITypedRegion getPartition(int offset) {
+        checkInitialization();
+
+        try {
+
+            Position[] category = getPositions();
+
+            if (category is null || category.length is 0)
+                return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE);
+
+            int index= fDocument.computeIndexInCategory(fPositionCategory, offset);
+
+            if (index < category.length) {
+
+                TypedPosition next= cast(TypedPosition) category[index];
+
+                if (offset is next.offset)
+                    return new TypedRegion(next.getOffset(), next.getLength(), next.getType());
+
+                if (index is 0)
+                    return new TypedRegion(0, next.offset, IDocument.DEFAULT_CONTENT_TYPE);
+
+                TypedPosition previous= cast(TypedPosition) category[index - 1];
+                if (previous.includes(offset))
+                    return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType());
+
+                int endOffset= previous.getOffset() + previous.getLength();
+                return new TypedRegion(endOffset, next.getOffset() - endOffset, IDocument.DEFAULT_CONTENT_TYPE);
+            }
+
+            TypedPosition previous= cast(TypedPosition) category[category.length - 1];
+            if (previous.includes(offset))
+                return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType());
+
+            int endOffset= previous.getOffset() + previous.getLength();
+            return new TypedRegion(endOffset, fDocument.getLength() - endOffset, IDocument.DEFAULT_CONTENT_TYPE);
+
+        } catch (BadPositionCategoryException x) {
+        } catch (BadLocationException x) {
+        }
+
+        return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE);
+    }
+
+    /*
+     * @see IDocumentPartitioner#computePartitioning(int, int)
+     */
+    public final ITypedRegion[] computePartitioning(int offset, int length) {
+        return computePartitioning(offset, length, false);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * May be replaced or extended by subclasses.
+     * </p>
+     */
+    public String[] getLegalContentTypes() {
+        return TextUtilities.copy(fLegalContentTypes);
+    }
+
+    /**
+     * Returns whether the given type is one of the legal content types.
+     * <p>
+     * May be extended by subclasses.
+     * </p>
+     *
+     * @param contentType the content type to check
+     * @return <code>true</code> if the content type is a legal content type
+     */
+    protected bool isSupportedContentType(String contentType) {
+        if (contentType !is null) {
+            for (int i= 0; i < fLegalContentTypes.length; i++) {
+                if (fLegalContentTypes[i].equals(contentType))
+                    return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns a content type encoded in the given token. If the token's
+     * data is not <code>null</code> and a string it is assumed that
+     * it is the encoded content type.
+     * <p>
+     * May be replaced or extended by subclasses.
+     * </p>
+     *
+     * @param token the token whose content type is to be determined
+     * @return the token's content type
+     */
+    protected String getTokenContentType(IToken token) {
+        Object data= token.getData();
+        if ( auto str = cast(ArrayWrapperString)data )
+            return str.array;
+        return null;
+    }
+
+    /* zero-length partition support */
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * May be replaced or extended by subclasses.
+     * </p>
+     */
+    public String getContentType(int offset, bool preferOpenPartitions) {
+        return getPartition(offset, preferOpenPartitions).getType();
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * May be replaced or extended by subclasses.
+     * </p>
+     */
+    public ITypedRegion getPartition(int offset, bool preferOpenPartitions) {
+        ITypedRegion region= getPartition(offset);
+        if (preferOpenPartitions) {
+            if (region.getOffset() is offset && !region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE)) {
+                if (offset > 0) {
+                    region= getPartition(offset - 1);
+                    if (region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE))
+                        return region;
+                }
+                return new TypedRegion(offset, 0, IDocument.DEFAULT_CONTENT_TYPE);
+            }
+        }
+        return region;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * May be replaced or extended by subclasses.
+     * </p>
+     */
+    public ITypedRegion[] computePartitioning(int offset, int length, bool includeZeroLengthPartitions) {
+        checkInitialization();
+        List list= new ArrayList();
+
+        try {
+
+            int endOffset= offset + length;
+
+            Position[] category= getPositions();
+
+            TypedPosition previous= null, current= null;
+            int start, end, gapOffset;
+            Position gap= new Position(0);
+
+            int startIndex= getFirstIndexEndingAfterOffset(category, offset);
+            int endIndex= getFirstIndexStartingAfterOffset(category, endOffset);
+            for (int i= startIndex; i < endIndex; i++) {
+
+                current= cast(TypedPosition) category[i];
+
+                gapOffset= (previous !is null) ? previous.getOffset() + previous.getLength() : 0;
+                gap.setOffset(gapOffset);
+                gap.setLength(current.getOffset() - gapOffset);
+                if ((includeZeroLengthPartitions && overlapsOrTouches(gap, offset, length)) ||
+                        (gap.getLength() > 0 && gap.overlapsWith(offset, length))) {
+                    start= Math.max(offset, gapOffset);
+                    end= Math.min(endOffset, gap.getOffset() + gap.getLength());
+                    list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE));
+                }
+
+                if (current.overlapsWith(offset, length)) {
+                    start= Math.max(offset, current.getOffset());
+                    end= Math.min(endOffset, current.getOffset() + current.getLength());
+                    list.add(new TypedRegion(start, end - start, current.getType()));
+                }
+
+                previous= current;
+            }
+
+            if (previous !is null) {
+                gapOffset= previous.getOffset() + previous.getLength();
+                gap.setOffset(gapOffset);
+                gap.setLength(fDocument.getLength() - gapOffset);
+                if ((includeZeroLengthPartitions && overlapsOrTouches(gap, offset, length)) ||
+                        (gap.getLength() > 0 && gap.overlapsWith(offset, length))) {
+                    start= Math.max(offset, gapOffset);
+                    end= Math.min(endOffset, fDocument.getLength());
+                    list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE));
+                }
+            }
+
+            if (list.isEmpty())
+                list.add(new TypedRegion(offset, length, IDocument.DEFAULT_CONTENT_TYPE));
+
+        } catch (BadPositionCategoryException ex) {
+            // Make sure we clear the cache
+            clearPositionCache();
+        } catch (RuntimeException ex) {
+            // Make sure we clear the cache
+            clearPositionCache();
+            throw ex;
+        }
+
+        return arraycast!(ITypedRegion)(list.toArray());
+    }
+
+    /**
+     * Returns <code>true</code> if the given ranges overlap with or touch each other.
+     *
+     * @param gap the first range
+     * @param offset the offset of the second range
+     * @param length the length of the second range
+     * @return <code>true</code> if the given ranges overlap with or touch each other
+     */
+    private bool overlapsOrTouches(Position gap, int offset, int length) {
+        return gap.getOffset() <= offset + length && offset <= gap.getOffset() + gap.getLength();
+    }
+
+    /**
+     * Returns the index of the first position which ends after the given offset.
+     *
+     * @param positions the positions in linear order
+     * @param offset the offset
+     * @return the index of the first position which ends after the offset
+     */
+    private int getFirstIndexEndingAfterOffset(Position[] positions, int offset) {
+        int i= -1, j= positions.length;
+        while (j - i > 1) {
+            int k= (i + j) >> 1;
+            Position p= positions[k];
+            if (p.getOffset() + p.getLength() > offset)
+                j= k;
+            else
+                i= k;
+        }
+        return j;
+    }
+
+    /**
+     * Returns the index of the first position which starts at or after the given offset.
+     *
+     * @param positions the positions in linear order
+     * @param offset the offset
+     * @return the index of the first position which starts after the offset
+     */
+    private int getFirstIndexStartingAfterOffset(Position[] positions, int offset) {
+        int i= -1, j= positions.length;
+        while (j - i > 1) {
+            int k= (i + j) >> 1;
+            Position p= positions[k];
+            if (p.getOffset() >= offset)
+                j= k;
+            else
+                i= k;
+        }
+        return j;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitionerExtension3#startRewriteSession(org.eclipse.jface.text.DocumentRewriteSession)
+     */
+    public void startRewriteSession(DocumentRewriteSession session)  {
+        if (fActiveRewriteSession !is null)
+            throw new IllegalStateException();
+        fActiveRewriteSession= session;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * May be extended by subclasses.
+     * </p>
+     */
+    public void stopRewriteSession(DocumentRewriteSession session) {
+        if (fActiveRewriteSession is session)
+            flushRewriteSession();
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * May be extended by subclasses.
+     * </p>
+     */
+    public DocumentRewriteSession getActiveRewriteSession() {
+        return fActiveRewriteSession;
+    }
+
+    /**
+     * Flushes the active rewrite session.
+     */
+    protected final void flushRewriteSession() {
+        fActiveRewriteSession= null;
+
+        // remove all position belonging to the partitioner position category
+        try {
+            fDocument.removePositionCategory(fPositionCategory);
+        } catch (BadPositionCategoryException x) {
+        }
+        fDocument.addPositionCategory(fPositionCategory);
+
+        fIsInitialized= false;
+    }
+
+    /**
+     * Clears the position cache. Needs to be called whenever the positions have
+     * been updated.
+     */
+    protected final void clearPositionCache() {
+        if (fCachedPositions !is null) {
+            fCachedPositions= null;
+        }
+    }
+
+    /**
+     * Returns the partitioners positions.
+     *
+     * @return the partitioners positions
+     * @throws BadPositionCategoryException if getting the positions from the
+     *         document fails
+     */
+    protected final Position[] getPositions()  {
+        if (fCachedPositions is null) {
+            fCachedPositions= fDocument.getPositions(fPositionCategory);
+        } else if (CHECK_CACHE_CONSISTENCY) {
+            Position[] positions= fDocument.getPositions(fPositionCategory);
+            int len= Math.min(positions.length, fCachedPositions.length);
+            for (int i= 0; i < len; i++) {
+                if (!positions[i].opEquals(fCachedPositions[i]))
+                    System.err.println(Format("FastPartitioner.getPositions(): cached position is not up to date: from document: {} in cache: {}", toString(positions[i]), toString(fCachedPositions[i]))); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+            for (int i= len; i < positions.length; i++)
+                System.err.println(Format("FastPartitioner.getPositions(): new position in document: {}", toString(positions[i]))); //$NON-NLS-1$
+            for (int i= len; i < fCachedPositions.length; i++)
+                System.err.println(Format("FastPartitioner.getPositions(): stale position in cache: {}", toString(fCachedPositions[i]))); //$NON-NLS-1$
+        }
+        return fCachedPositions;
+    }
+
+    /**
+     * Pretty print a <code>Position</code>.
+     *
+     * @param position the position to format
+     * @return a formatted string
+     */
+    private override String toString(Position position) {
+        return Format("P[{}+{}]", position.getOffset(), position.getLength() ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/ICharacterScanner.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.rules.ICharacterScanner;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Defines the interface of a character scanner used by rules.
+ * Rules may request the next character or ask the character
+ * scanner to unread the last read character.
+ */
+public interface ICharacterScanner {
+
+    /**
+     * The value returned when this scanner has read EOF.
+     */
+    public static const int EOF= -1;
+
+    /**
+     * Provides rules access to the legal line delimiters. The returned
+     * object may not be modified by clients.
+     *
+     * @return the legal line delimiters
+     */
+    char[][] getLegalLineDelimiters();
+
+    /**
+     * Returns the column of the character scanner.
+     *
+     * @return the column of the character scanner
+     */
+    int getColumn();
+
+    /**
+     * Returns the next character or EOF if end of file has been reached
+     *
+     * @return the next character or EOF
+     */
+    int read();
+
+    /**
+     * Rewinds the scanner before the last read character.
+     */
+    void unread();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IPartitionTokenScanner.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.rules.IPartitionTokenScanner;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * A partition token scanner returns tokens that represent partitions. For that reason,
+ * a partition token scanner is vulnerable in respect to the document offset it starts
+ * scanning. In a simple case, a partition token scanner must always start at a partition
+ * boundary. A partition token scanner can also start in the middle of a partition,
+ * if it knows the type of the partition.
+ *
+ * @since 2.0
+ */
+public interface IPartitionTokenScanner  : ITokenScanner {
+
+    /**
+     * Configures the scanner by providing access to the document range that should be scanned.
+     * The range may no only contain complete partitions but starts at the beginning of a line in the
+     * middle of a partition of the given content type. This requires that a partition delimiter can not
+     * contain a line delimiter.
+     *
+     * @param document the document to scan
+     * @param offset the offset of the document range to scan
+     * @param length the length of the document range to scan
+     * @param contentType the content type at the given offset
+     * @param partitionOffset the offset at which the partition of the given offset starts
+     */
+    void setPartialRange(IDocument document, int offset, int length, String contentType, int partitionOffset);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IPredicateRule.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.rules.IPredicateRule;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Defines the interface for a rule used in the scanning of text for the purpose of
+ * document partitioning or text styling. A predicate rule can only return one single
+ * token after having successfully detected content. This token is called success token.
+ * Also, it also returns a token indicating that this rule has not been successful.
+ *
+ * @see ICharacterScanner
+ * @since 2.0
+ */
+public interface IPredicateRule : IRule {
+
+    /**
+     * Returns the success token of this predicate rule.
+     *
+     * @return the success token of this rule
+     */
+    IToken getSuccessToken();
+
+    /**
+     * Evaluates the rule by examining the characters available from
+     * the provided character scanner. The token returned by this rule
+     * returns <code>true</code> when calling <code>isUndefined</code>,
+     * if the text that the rule investigated does not match the rule's requirements. Otherwise,
+     * this method returns this rule's success token. If this rules relies on a text pattern
+     * comprising a opening and a closing character sequence this method can also be called
+     * when the scanner is positioned already between the opening and the closing sequence.
+     * In this case, <code>resume</code> must be set to <code>true</code>.
+     *
+     * @param scanner the character scanner to be used by this rule
+     * @param resume indicates that the rule starts working between the opening and the closing character sequence
+     * @return the token computed by the rule
+     */
+    IToken evaluate(ICharacterScanner scanner, bool resume);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IRule.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.rules.IRule;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Defines the interface for a rule used in the
+ * scanning of text for the purpose of document
+ * partitioning or text styling.
+ *
+ * @see ICharacterScanner
+ */
+public interface IRule {
+
+    /**
+     * Evaluates the rule by examining the characters available from
+     * the provided character scanner. The token returned by this rule
+     * returns <code>true</code> when calling <code>isUndefined</code>,
+     * if the text that the rule investigated does not match the rule's requirements
+     *
+     * @param scanner the character scanner to be used by this rule
+     * @return the token computed by the rule
+     */
+    IToken evaluate(ICharacterScanner scanner);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IToken.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.rules.IToken;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * A token to be returned by a rule.
+ */
+public interface IToken {
+
+    /**
+     * Return whether this token is undefined.
+     *
+     * @return <code>true</code>if this token is undefined
+     */
+    bool isUndefined();
+
+    /**
+     * Return whether this token represents a whitespace.
+     *
+     * @return <code>true</code>if this token represents a whitespace
+     */
+    bool isWhitespace();
+
+    /**
+     * Return whether this token represents End Of File.
+     *
+     * @return <code>true</code>if this token represents EOF
+     */
+    bool isEOF();
+
+    /**
+     * Return whether this token is neither undefined, nor whitespace, nor EOF.
+     *
+     * @return <code>true</code>if this token is not undefined, not a whitespace, and not EOF
+     */
+    bool isOther();
+
+    /**
+     * Return a data attached to this token. The semantics of this data kept undefined by this interface.
+     *
+     * @return the data attached to this token.
+     */
+    Object getData();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/ITokenScanner.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.rules.ITokenScanner;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * A token scanner scans a range of a document and reports about the token it finds.
+ * A scanner has state. When asked, the scanner returns the offset and the length of the
+ * last found token.
+ *
+ * @see org.eclipse.jface.text.rules.IToken
+ * @since 2.0
+ */
+public interface ITokenScanner {
+
+    /**
+     * Configures the scanner by providing access to the document range that should
+     * be scanned.
+     *
+     * @param document the document to scan
+     * @param offset the offset of the document range to scan
+     * @param length the length of the document range to scan
+     */
+    void setRange(IDocument document, int offset, int length);
+
+    /**
+     * Returns the next token in the document.
+     *
+     * @return the next token in the document
+     */
+    IToken nextToken();
+
+    /**
+     * Returns the offset of the last token read by this scanner.
+     *
+     * @return the offset of the last token read by this scanner
+     */
+    int getTokenOffset();
+
+    /**
+     * Returns the length of the last token read by this scanner.
+     *
+     * @return the length of the last token read by this scanner
+     */
+    int getTokenLength();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IWhitespaceDetector.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.rules.IWhitespaceDetector;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Defines the interface by which <code>WhitespaceRule</code>
+ * determines whether a given character is to be considered
+ * whitespace in the current context.
+ */
+public interface IWhitespaceDetector {
+
+    /**
+     * Returns whether the specified character is whitespace.
+     *
+     * @param c the character to be checked
+     * @return <code>true</code> if the specified character is a whitespace char
+     */
+    bool isWhitespace(char c);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IWordDetector.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.rules.IWordDetector;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Defines the interface by which <code>WordRule</code>
+ * determines whether a given character is valid as part
+ * of a word in the current context.
+ */
+public interface IWordDetector {
+
+    /**
+     * Returns whether the specified character is
+     * valid as the first character in a word.
+     *
+     * @param c the character to be checked
+     * @return <code>true</code> is a valid first character in a word, <code>false</code> otherwise
+     */
+    bool isWordStart(dchar c);
+
+    /**
+     * Returns whether the specified character is
+     * valid as a subsequent character in a word.
+     *
+     * @param c the character to be checked
+     * @return <code>true</code> if the character is a valid word part, <code>false</code> otherwise
+     */
+    bool isWordPart(dchar c);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/MultiLineRule.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.rules.MultiLineRule;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * A rule for detecting patterns which begin with a given
+ * sequence and may end with a given sequence thereby spanning
+ * multiple lines.
+ */
+public class MultiLineRule : PatternRule {
+
+    /**
+     * Creates a rule for the given starting and ending sequence
+     * which, if detected, will return the specified token.
+     *
+     * @param startSequence the pattern's start sequence
+     * @param endSequence the pattern's end sequence
+     * @param token the token to be returned on success
+     */
+    public this(String startSequence, String endSequence, IToken token) {
+        this(startSequence, endSequence, token, cast(wchar) 0);
+    }
+
+    /**
+     * Creates a rule for the given starting and ending sequence
+     * which, if detected, will return the specific token.
+     * Any character which follows the given escape character will be ignored.
+     *
+     * @param startSequence the pattern's start sequence
+     * @param endSequence the pattern's end sequence
+     * @param token the token to be returned on success
+     * @param escapeCharacter the escape character
+     */
+    public this(String startSequence, String endSequence, IToken token, char escapeCharacter) {
+        this(startSequence, endSequence, token, escapeCharacter, false);
+    }
+
+    /**
+     * Creates a rule for the given starting and ending sequence
+     * which, if detected, will return the specific token. Any character that follows the
+     * given escape character will be ignored. <code>breakOnEOF</code> indicates whether
+     * EOF is equivalent to detecting the <code>endSequence</code>.
+     *
+     * @param startSequence the pattern's start sequence
+     * @param endSequence the pattern's end sequence
+     * @param token the token to be returned on success
+     * @param escapeCharacter the escape character
+     * @param breaksOnEOF indicates whether the end of the file terminates this rule successfully
+     * @since 2.1
+     */
+    public this(String startSequence, String endSequence, IToken token, char escapeCharacter, bool breaksOnEOF) {
+        super(startSequence, endSequence, token, escapeCharacter, false, breaksOnEOF);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/NumberRule.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.rules.NumberRule;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * An implementation of <code>IRule</code> detecting a numerical value.
+ */
+public class NumberRule : IRule {
+
+    /** Internal setting for the un-initialized column constraint */
+    protected static const int UNDEFINED= -1;
+    /** The token to be returned when this rule is successful */
+    protected IToken fToken;
+    /** The column constraint */
+    protected int fColumn= UNDEFINED;
+
+    /**
+     * Creates a rule which will return the specified
+     * token when a numerical sequence is detected.
+     *
+     * @param token the token to be returned
+     */
+    public this(IToken token) {
+        Assert.isNotNull(cast(Object)token);
+        fToken= token;
+    }
+
+    /**
+     * Sets a column constraint for this rule. If set, the rule's token
+     * will only be returned if the pattern is detected starting at the
+     * specified column. If the column is smaller then 0, the column
+     * constraint is considered removed.
+     *
+     * @param column the column in which the pattern starts
+     */
+    public void setColumnConstraint(int column) {
+        if (column < 0)
+            column= UNDEFINED;
+        fColumn= column;
+    }
+
+    /*
+     * @see IRule#evaluate(ICharacterScanner)
+     */
+    public IToken evaluate(ICharacterScanner scanner) {
+        int c= scanner.read();
+        if (Character.isDigit(cast(char)c)) {
+            if (fColumn is UNDEFINED || (fColumn is scanner.getColumn() - 1)) {
+                do {
+                    c= scanner.read();
+                } while (Character.isDigit(cast(char) c));
+                scanner.unread();
+                return fToken;
+            }
+        }
+
+        scanner.unread();
+        return Token.UNDEFINED;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/PatternRule.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,361 @@
+/*******************************************************************************
+ * 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
+ *     Christopher Lenz (cmlenz@gmx.de) - support for line continuation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.rules.PatternRule;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.Set;
+
+
+
+import org.eclipse.core.runtime.Assert;
+
+
+
+
+
+/**
+ * Standard implementation of <code>IPredicateRule</code>.
+ * Is is capable of detecting a pattern which begins with a given start
+ * sequence and ends with a given end sequence. If the end sequence is
+ * not specified, it can be either end of line, end or file, or both. Additionally,
+ * the pattern can be constrained to begin in a certain column. The rule can also
+ * be used to check whether the text to scan covers half of the pattern, i.e. contains
+ * the end sequence required by the rule.
+ */
+public class PatternRule : IPredicateRule {
+
+    /**
+     * Comparator that orders <code>char[]</code> in decreasing array lengths.
+     *
+     * @since 3.1
+     */
+    private static class DecreasingCharArrayLengthComparator : Comparator {
+        public int compare(Object o1, Object o2) {
+            return stringcast( o2).length - stringcast( o1).length;
+        }
+    }
+
+    /** Internal setting for the un-initialized column constraint */
+    protected static final int UNDEFINED= -1;
+
+    /** The token to be returned on success */
+    protected IToken fToken;
+    /** The pattern's start sequence */
+    protected char[] fStartSequence;
+    /** The pattern's end sequence */
+    protected char[] fEndSequence;
+    /** The pattern's column constrain */
+    protected int fColumn;
+    /** The pattern's escape character */
+    protected char fEscapeCharacter;
+    /**
+     * Indicates whether the escape character continues a line
+     * @since 3.0
+     */
+    protected bool fEscapeContinuesLine;
+    /** Indicates whether end of line terminates the pattern */
+    protected bool fBreaksOnEOL;
+    /** Indicates whether end of file terminates the pattern */
+    protected bool fBreaksOnEOF;
+
+    /**
+     * Line delimiter comparator which orders according to decreasing delimiter length.
+     * @since 3.1
+     */
+    private Comparator fLineDelimiterComparator;
+    /**
+     * Cached line delimiters.
+     * @since 3.1
+     */
+    private char[][] fLineDelimiters;
+    /**
+     * Cached sorted {@linkplain #fLineDelimiters}.
+     * @since 3.1
+     */
+    private char[][] fSortedLineDelimiters;
+
+    /**
+     * Creates a rule for the given starting and ending sequence.
+     * When these sequences are detected the rule will return the specified token.
+     * Alternatively, the sequence can also be ended by the end of the line.
+     * Any character which follows the given escapeCharacter will be ignored.
+     *
+     * @param startSequence the pattern's start sequence
+     * @param endSequence the pattern's end sequence, <code>null</code> is a legal value
+     * @param token the token which will be returned on success
+     * @param escapeCharacter any character following this one will be ignored
+     * @param breaksOnEOL indicates whether the end of the line also terminates the pattern
+     */
+    public this(String startSequence, String endSequence, IToken token, char escapeCharacter, bool breaksOnEOL) {
+        fColumn= UNDEFINED;
+        fLineDelimiterComparator= new DecreasingCharArrayLengthComparator();
+
+        Assert.isTrue(startSequence !is null && startSequence.length() > 0);
+        Assert.isTrue(endSequence !is null || breaksOnEOL);
+        Assert.isNotNull(cast(Object)token);
+
+        fStartSequence= startSequence.toCharArray();
+        fEndSequence= (endSequence is null ? new char[0] : endSequence.toCharArray());
+        fToken= token;
+        fEscapeCharacter= escapeCharacter;
+        fBreaksOnEOL= breaksOnEOL;
+    }
+
+    /**
+     * Creates a rule for the given starting and ending sequence.
+     * When these sequences are detected the rule will return the specified token.
+     * Alternatively, the sequence can also be ended by the end of the line or the end of the file.
+     * Any character which follows the given escapeCharacter will be ignored.
+     *
+     * @param startSequence the pattern's start sequence
+     * @param endSequence the pattern's end sequence, <code>null</code> is a legal value
+     * @param token the token which will be returned on success
+     * @param escapeCharacter any character following this one will be ignored
+     * @param breaksOnEOL indicates whether the end of the line also terminates the pattern
+     * @param breaksOnEOF indicates whether the end of the file also terminates the pattern
+     * @since 2.1
+     */
+    public this(String startSequence, String endSequence, IToken token, char escapeCharacter, bool breaksOnEOL, bool breaksOnEOF) {
+        this(startSequence, endSequence, token, escapeCharacter, breaksOnEOL);
+        fBreaksOnEOF= breaksOnEOF;
+    }
+
+    /**
+     * Creates a rule for the given starting and ending sequence.
+     * When these sequences are detected the rule will return the specified token.
+     * Alternatively, the sequence can also be ended by the end of the line or the end of the file.
+     * Any character which follows the given escapeCharacter will be ignored. An end of line
+     * immediately after the given <code>lineContinuationCharacter</code> will not cause the
+     * pattern to terminate even if <code>breakOnEOL</code> is set to true.
+     *
+     * @param startSequence the pattern's start sequence
+     * @param endSequence the pattern's end sequence, <code>null</code> is a legal value
+     * @param token the token which will be returned on success
+     * @param escapeCharacter any character following this one will be ignored
+     * @param breaksOnEOL indicates whether the end of the line also terminates the pattern
+     * @param breaksOnEOF indicates whether the end of the file also terminates the pattern
+     * @param escapeContinuesLine indicates whether the specified escape character is used for line
+     *        continuation, so that an end of line immediately after the escape character does not
+     *        terminate the pattern, even if <code>breakOnEOL</code> is set
+     * @since 3.0
+     */
+    public this(String startSequence, String endSequence, IToken token, char escapeCharacter, bool breaksOnEOL, bool breaksOnEOF, bool escapeContinuesLine) {
+        this(startSequence, endSequence, token, escapeCharacter, breaksOnEOL, breaksOnEOF);
+        fEscapeContinuesLine= escapeContinuesLine;
+    }
+
+    /**
+     * Sets a column constraint for this rule. If set, the rule's token
+     * will only be returned if the pattern is detected starting at the
+     * specified column. If the column is smaller then 0, the column
+     * constraint is considered removed.
+     *
+     * @param column the column in which the pattern starts
+     */
+    public void setColumnConstraint(int column) {
+        if (column < 0)
+            column= UNDEFINED;
+        fColumn= column;
+    }
+
+
+    /**
+     * Evaluates this rules without considering any column constraints.
+     *
+     * @param scanner the character scanner to be used
+     * @return the token resulting from this evaluation
+     */
+    protected IToken doEvaluate(ICharacterScanner scanner) {
+        return doEvaluate(scanner, false);
+    }
+
+    /**
+     * Evaluates this rules without considering any column constraints. Resumes
+     * detection, i.e. look sonly for the end sequence required by this rule if the
+     * <code>resume</code> flag is set.
+     *
+     * @param scanner the character scanner to be used
+     * @param resume <code>true</code> if detection should be resumed, <code>false</code> otherwise
+     * @return the token resulting from this evaluation
+     * @since 2.0
+     */
+    protected IToken doEvaluate(ICharacterScanner scanner, bool resume) {
+
+        if (resume) {
+
+            if (endSequenceDetected(scanner))
+                return fToken;
+
+        } else {
+
+            int c= scanner.read();
+            if (c is fStartSequence[0]) {
+                if (sequenceDetected(scanner, fStartSequence, false)) {
+                    if (endSequenceDetected(scanner))
+                        return fToken;
+                }
+            }
+        }
+
+        scanner.unread();
+        return Token.UNDEFINED;
+    }
+
+    /*
+     * @see IRule#evaluate(ICharacterScanner)
+     */
+    public IToken evaluate(ICharacterScanner scanner) {
+        return evaluate(scanner, false);
+    }
+
+    /**
+     * Returns whether the end sequence was detected. As the pattern can be considered
+     * ended by a line delimiter, the result of this method is <code>true</code> if the
+     * rule breaks on the end of the line, or if the EOF character is read.
+     *
+     * @param scanner the character scanner to be used
+     * @return <code>true</code> if the end sequence has been detected
+     */
+    protected bool endSequenceDetected(ICharacterScanner scanner) {
+
+        char[][] originalDelimiters= scanner.getLegalLineDelimiters();
+        int count= originalDelimiters.length;
+        if (fLineDelimiters is null || originalDelimiters.length !is count) {
+            fSortedLineDelimiters= new char[][](count);
+        } else {
+            while (count > 0 && fLineDelimiters[count-1] is originalDelimiters[count-1])
+                count--;
+        }
+        if (count !is 0) {
+            fLineDelimiters= originalDelimiters;
+            System.arraycopy(fLineDelimiters, 0, fSortedLineDelimiters, 0, fLineDelimiters.length);
+            Arrays.sort(fSortedLineDelimiters, fLineDelimiterComparator);
+        }
+
+        int readCount= 1;
+        int c;
+        while ((c= scanner.read()) !is ICharacterScanner.EOF) {
+            if (c is fEscapeCharacter) {
+                // Skip escaped character(s)
+                if (fEscapeContinuesLine) {
+                    c= scanner.read();
+                    for (int i= 0; i < fSortedLineDelimiters.length; i++) {
+                        if (c is fSortedLineDelimiters[i][0] && sequenceDetected(scanner, fSortedLineDelimiters[i], true))
+                            break;
+                    }
+                } else
+                    scanner.read();
+
+            } else if (fEndSequence.length > 0 && c is fEndSequence[0]) {
+                // Check if the specified end sequence has been found.
+                if (sequenceDetected(scanner, fEndSequence, true))
+                    return true;
+            } else if (fBreaksOnEOL) {
+                // Check for end of line since it can be used to terminate the pattern.
+                for (int i= 0; i < fSortedLineDelimiters.length; i++) {
+                    if (c is fSortedLineDelimiters[i][0] && sequenceDetected(scanner, fSortedLineDelimiters[i], true))
+                        return true;
+                }
+            }
+            readCount++;
+        }
+
+        if (fBreaksOnEOF)
+            return true;
+
+        for (; readCount > 0; readCount--)
+            scanner.unread();
+
+        return false;
+    }
+
+    /**
+     * Returns whether the next characters to be read by the character scanner
+     * are an exact match with the given sequence. No escape characters are allowed
+     * within the sequence. If specified the sequence is considered to be found
+     * when reading the EOF character.
+     *
+     * @param scanner the character scanner to be used
+     * @param sequence the sequence to be detected
+     * @param eofAllowed indicated whether EOF terminates the pattern
+     * @return <code>true</code> if the given sequence has been detected
+     */
+    protected bool sequenceDetected(ICharacterScanner scanner, char[] sequence, bool eofAllowed) {
+        for (int i= 1; i < sequence.length; i++) {
+            int c= scanner.read();
+            if (c is ICharacterScanner.EOF && eofAllowed) {
+                return true;
+            } else if (c !is sequence[i]) {
+                // Non-matching character detected, rewind the scanner back to the start.
+                // Do not unread the first character.
+                scanner.unread();
+                for (int j= i-1; j > 0; j--)
+                    scanner.unread();
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /*
+     * @see IPredicateRule#evaluate(ICharacterScanner, bool)
+     * @since 2.0
+     */
+    public IToken evaluate(ICharacterScanner scanner, bool resume) {
+        if (fColumn is UNDEFINED)
+            return doEvaluate(scanner, resume);
+
+        int c= scanner.read();
+        scanner.unread();
+        if (c is fStartSequence[0])
+            return (fColumn is scanner.getColumn() ? doEvaluate(scanner, resume) : Token.UNDEFINED);
+        return Token.UNDEFINED;
+    }
+
+    /*
+     * @see IPredicateRule#getSuccessToken()
+     * @since 2.0
+     */
+    public IToken getSuccessToken() {
+        return fToken;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/RuleBasedDamagerRepairer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.rules.RuleBasedDamagerRepairer;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+import java.lang.all;
+
+
+import org.eclipse.jface.text.TextAttribute;
+
+
+/**
+ * @deprecated use <code>DefaultDamagerRepairer</code>
+ */
+public class RuleBasedDamagerRepairer : DefaultDamagerRepairer {
+
+    /**
+     * Creates a damager/repairer that uses the given scanner and returns the given default
+     * text attribute if the current token does not carry a text attribute.
+     *
+     * @param scanner the rule based scanner to be used
+     * @param defaultTextAttribute the text attribute to be returned if none is specified by the current token,
+     *          may not be <code>null</code>
+     *
+     * @deprecated use RuleBasedDamagerRepairer(RuleBasedScanner) instead
+     */
+    public this(RuleBasedScanner scanner, TextAttribute defaultTextAttribute) {
+        super(scanner, defaultTextAttribute);
+    }
+
+    /**
+     * Creates a damager/repairer that uses the given scanner. The scanner may not be <code>null</code>
+     * and is assumed to return only token that carry text attributes.
+     *
+     * @param scanner the rule based scanner to be used, may not be <code>null</code>
+     * @since 2.0
+     */
+    public this(RuleBasedScanner scanner) {
+        super(scanner);
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/RuleBasedPartitionScanner.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.rules.RuleBasedPartitionScanner;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * Scanner that exclusively uses predicate rules.
+ * @since 2.0
+ */
+public class RuleBasedPartitionScanner : BufferedRuleBasedScanner , IPartitionTokenScanner {
+
+    /** The content type of the partition in which to resume scanning. */
+    protected String fContentType;
+    /** The offset of the partition inside which to resume. */
+    protected int fPartitionOffset;
+
+
+    /**
+     * Disallow setting the rules since this scanner
+     * exclusively uses predicate rules.
+     *
+     * @param rules the sequence of rules controlling this scanner
+     */
+    public void setRules(IRule[] rules) {
+        throw new UnsupportedOperationException();
+    }
+
+    /*
+     * @see RuleBasedScanner#setRules(IRule[])
+     */
+    public void setPredicateRules(IPredicateRule[] rules) {
+        super.setRules(rules);
+    }
+
+    /*
+     * @see ITokenScanner#setRange(IDocument, int, int)
+     */
+    public void setRange(IDocument document, int offset, int length) {
+        setPartialRange(document, offset, length, null, -1);
+    }
+
+    /*
+     * @see IPartitionTokenScanner#setPartialRange(IDocument, int, int, String, int)
+     */
+    public void setPartialRange(IDocument document, int offset, int length, String contentType, int partitionOffset) {
+        fContentType= contentType;
+        fPartitionOffset= partitionOffset;
+        if (partitionOffset > -1) {
+            int delta= offset - partitionOffset;
+            if (delta > 0) {
+                super.setRange(document, partitionOffset, length + delta);
+                fOffset= offset;
+                return;
+            }
+        }
+        super.setRange(document, offset, length);
+    }
+
+    /*
+     * @see ITokenScanner#nextToken()
+     */
+    public IToken nextToken() {
+
+
+        if (fContentType is null || fRules is null) {
+            //don't try to resume
+            return super.nextToken();
+        }
+
+        // inside a partition
+
+        fColumn= UNDEFINED;
+        bool resume= (fPartitionOffset > -1 && fPartitionOffset < fOffset);
+        fTokenOffset= resume ? fPartitionOffset : fOffset;
+
+        IPredicateRule rule;
+        IToken token;
+
+        for (int i= 0; i < fRules.length; i++) {
+            rule= cast(IPredicateRule) fRules[i];
+            token= rule.getSuccessToken();
+            if (fContentType.equals(stringcast(token.getData()))) {
+                token= rule.evaluate(this, resume);
+                if (!token.isUndefined()) {
+                    fContentType= null;
+                    return token;
+                }
+            }
+        }
+
+        // haven't found any rule for this type of partition
+        fContentType= null;
+        if (resume)
+            fOffset= fPartitionOffset;
+        return super.nextToken();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/RuleBasedPartitioner.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,617 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.rules.RuleBasedPartitioner;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DefaultPositionUpdater;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentPartitioner;
+import org.eclipse.jface.text.IDocumentPartitionerExtension;
+import org.eclipse.jface.text.IDocumentPartitionerExtension2;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.TypedPosition;
+import org.eclipse.jface.text.TypedRegion;
+
+
+
+/**
+ * A standard implementation of a syntax driven document partitioner.
+ * It uses a rule based scanner to scan the document and to determine
+ * the document's partitioning. The tokens returned by the rules the
+ * scanner is configured with are supposed to return the partition type
+ * as their data. The partitioner remembers the document's partitions
+ * in the document itself rather than maintaining its own data structure.
+ *
+ * @see IRule
+ * @see RuleBasedScanner
+ *
+ * @deprecated use <code>FastPartitioner</code> instead
+ */
+public class RuleBasedPartitioner : IDocumentPartitioner, IDocumentPartitionerExtension, IDocumentPartitionerExtension2 {
+
+    /**
+     * The position category this partitioner uses to store the document's partitioning information
+     * @deprecated As of 3.0, use <code>getManagingPositionCategories()</code>.
+     */
+    public const static String CONTENT_TYPES_CATEGORY= "__content_types_category"; //$NON-NLS-1$
+
+
+    /** The partitioner's scanner */
+    protected RuleBasedScanner fScanner;
+    /** The legal content types of this partitioner */
+    protected String[] fLegalContentTypes;
+    /** The partitioner's document */
+    protected IDocument fDocument;
+    /** The document length before a document change occurred */
+    protected int fPreviousDocumentLength;
+    /** The position updater used to for the default updating of partitions */
+    protected DefaultPositionUpdater fPositionUpdater;
+    /** The offset at which the first changed partition starts */
+    protected int fStartOffset;
+    /** The offset at which the last changed partition ends */
+    protected int fEndOffset;
+    /**The offset at which a partition has been deleted */
+    protected int fDeleteOffset;
+    /**
+     * The position category for managing partitioning information.
+     * @since 3.0
+     */
+    private String fPositionCategory;
+
+
+    /**
+     * Creates a new partitioner that uses the given scanner and may return
+     * partitions of the given legal content types.
+     *
+     * @param scanner the scanner this partitioner is supposed to use
+     * @param legalContentTypes the legal content types of this partitioner
+     */
+    public this(RuleBasedScanner scanner, String[] legalContentTypes) {
+        fScanner= scanner;
+        fLegalContentTypes= TextUtilities.copy(legalContentTypes);
+        fPositionCategory= CONTENT_TYPES_CATEGORY ~ Integer.toString(toHash());
+        fPositionUpdater= new DefaultPositionUpdater(fPositionCategory);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitionerExtension2#getManagingPositionCategories()
+     * @since 3.0
+     */
+    public String[] getManagingPositionCategories() {
+        return [ fPositionCategory ];
+    }
+
+    /*
+     * @see IDocumentPartitioner#connect
+     */
+    public void connect(IDocument document) {
+        Assert.isNotNull(cast(Object)document);
+        Assert.isTrue(!document.containsPositionCategory(fPositionCategory));
+
+        fDocument= document;
+        fDocument.addPositionCategory(fPositionCategory);
+
+        initialize();
+    }
+
+    /**
+     * Performs the initial partitioning of the partitioner's document.
+     */
+    protected void initialize() {
+
+        fScanner.setRange(fDocument, 0, fDocument.getLength());
+
+        try {
+            IToken token= fScanner.nextToken();
+            while (!token.isEOF()) {
+
+                String contentType= getTokenContentType(token);
+
+                if (isSupportedContentType(contentType)) {
+                    TypedPosition p= new TypedPosition(fScanner.getTokenOffset(), fScanner.getTokenLength(), contentType);
+                    fDocument.addPosition(fPositionCategory, p);
+                }
+
+                token= fScanner.nextToken();
+            }
+        } catch (BadLocationException x) {
+            // cannot happen as offsets come from scanner
+        } catch (BadPositionCategoryException x) {
+            // cannot happen if document has been connected before
+        }
+    }
+
+    /*
+     * @see IDocumentPartitioner#disconnect
+     */
+    public void disconnect() {
+
+        Assert.isTrue(fDocument.containsPositionCategory(fPositionCategory));
+
+        try {
+            fDocument.removePositionCategory(fPositionCategory);
+        } catch (BadPositionCategoryException x) {
+            // can not happen because of Assert
+        }
+    }
+
+    /*
+     * @see IDocumentPartitioner#documentAboutToBeChanged
+     */
+    public void documentAboutToBeChanged(DocumentEvent e) {
+
+        Assert.isTrue(e.getDocument() is fDocument);
+
+        fPreviousDocumentLength= e.getDocument().getLength();
+        fStartOffset= -1;
+        fEndOffset= -1;
+        fDeleteOffset= -1;
+    }
+
+    /*
+     * @see IDocumentPartitioner#documentChanged
+     */
+    public bool documentChanged(DocumentEvent e) {
+        IRegion region= documentChanged2(e);
+        return (region !is null);
+    }
+
+    /**
+     * Helper method for tracking the minimal region containing all partition changes.
+     * If <code>offset</code> is smaller than the remembered offset, <code>offset</code>
+     * will from now on be remembered. If <code>offset  + length</code> is greater than
+     * the remembered end offset, it will be remembered from now on.
+     *
+     * @param offset the offset
+     * @param length the length
+     */
+    private void rememberRegion(int offset, int length) {
+        // remember start offset
+        if (fStartOffset is -1)
+            fStartOffset= offset;
+        else if (offset < fStartOffset)
+            fStartOffset= offset;
+
+        // remember end offset
+        int endOffset= offset + length;
+        if (fEndOffset is -1)
+            fEndOffset= endOffset;
+        else if (endOffset > fEndOffset)
+            fEndOffset= endOffset;
+    }
+
+    /**
+     * Remembers the given offset as the deletion offset.
+     *
+     * @param offset the offset
+     */
+    private void rememberDeletedOffset(int offset) {
+        fDeleteOffset= offset;
+    }
+
+    /**
+     * Creates the minimal region containing all partition changes using the
+     * remembered offset, end offset, and deletion offset.
+     * @return the minimal region containing all the partition changes
+     */
+    private IRegion createRegion() {
+        if (fDeleteOffset is -1) {
+            if (fStartOffset is -1 || fEndOffset is -1)
+                return null;
+            return new Region(fStartOffset, fEndOffset - fStartOffset);
+        } else if (fStartOffset is -1 || fEndOffset is -1) {
+            return new Region(fDeleteOffset, 0);
+        } else {
+            int offset= Math.min(fDeleteOffset, fStartOffset);
+            int endOffset= Math.max(fDeleteOffset, fEndOffset);
+            return new Region(offset, endOffset - offset);
+        }
+    }
+
+    /*
+     * @see IDocumentPartitionerExtension#documentChanged2(DocumentEvent)
+     * @since 2.0
+     */
+    public IRegion documentChanged2(DocumentEvent e) {
+
+        try {
+
+            IDocument d= e.getDocument();
+            Position[] category= d.getPositions(fPositionCategory);
+            int first= 0;
+            int reparseStart= 0;
+            int originalSize= category.length;
+
+            if (originalSize > 0) {
+
+                /*
+                 * determine character position at which the scanner starts:
+                 * first position behind the last non-default partition the actual position is not involved with
+                 */
+
+                first= d.computeIndexInCategory(fPositionCategory, e.getOffset());
+
+                Position p= null;
+                do {
+                    --first;
+                    if (first < 0)
+                        break;
+
+                    p= category[first];
+
+                } while (p.overlapsWith(e.getOffset(), e.getLength()) ||
+                            (e.getOffset() is fPreviousDocumentLength &&
+                             (p.getOffset() + p.getLength() is fPreviousDocumentLength)));
+
+                fPositionUpdater.update(e);
+                for (int i= 0; i < category.length; i++) {
+                    p= category[i];
+                    if (p.isDeleted) {
+                        rememberDeletedOffset(e.getOffset());
+                        break;
+                    }
+                }
+                category= d.getPositions(fPositionCategory);
+
+                if (first >= 0) {
+                    p= category[first];
+                    reparseStart= p.getOffset() + p.getLength();
+                }
+
+                ++first;
+            }
+
+            fScanner.setRange(d, reparseStart, d.getLength() - reparseStart);
+
+            int lastScannedPosition= reparseStart;
+            IToken token= fScanner.nextToken();
+
+            while (!token.isEOF()) {
+
+
+                String contentType= getTokenContentType(token);
+
+                if (!isSupportedContentType(contentType)) {
+                    token= fScanner.nextToken();
+                    continue;
+                }
+
+                int start= fScanner.getTokenOffset();
+                int length= fScanner.getTokenLength();
+
+                lastScannedPosition= start + length - 1;
+
+                // remove all affected positions
+                while (first < category.length) {
+                    TypedPosition p= cast(TypedPosition) category[first];
+                    if (lastScannedPosition >= p.offset + p.length ||
+                            (p.overlapsWith(start, length) &&
+                                (!d.containsPosition(fPositionCategory, start, length) ||
+                                 !contentType.equals(p.getType())))) {
+
+                        rememberRegion(p.offset, p.length);
+                        d.removePosition(fPositionCategory, p);
+                        ++ first;
+
+                    } else
+                        break;
+                }
+
+                // if position already exists we are done
+                if (d.containsPosition(fPositionCategory, start, length))
+                    return createRegion();
+
+                // insert the new type position
+                try {
+                    d.addPosition(fPositionCategory, new TypedPosition(start, length, contentType));
+                    rememberRegion(start, length);
+                } catch (BadPositionCategoryException x) {
+                } catch (BadLocationException x) {
+                }
+
+                token= fScanner.nextToken();
+            }
+
+
+            // remove all positions behind lastScannedPosition since there aren't any further types
+            if (lastScannedPosition !is reparseStart) {
+                // if this condition is not met, nothing has been scanned because of a delete
+                ++ lastScannedPosition;
+            }
+            first= d.computeIndexInCategory(fPositionCategory, lastScannedPosition);
+
+            TypedPosition p;
+            while (first < category.length) {
+                p= cast(TypedPosition) category[first++];
+                d.removePosition(fPositionCategory, p);
+                rememberRegion(p.offset, p.length);
+            }
+
+        } catch (BadPositionCategoryException x) {
+            // should never happen on connected documents
+        } catch (BadLocationException x) {
+        }
+
+        return createRegion();
+    }
+
+
+    /**
+     * Returns the position in the partitoner's position category which is
+     * close to the given offset. This is, the position has either an offset which
+     * is the same as the given offset or an offset which is smaller than the given
+     * offset. This method profits from the knowledge that a partitioning is
+     * a ordered set of disjoint position.
+     *
+     * @param offset the offset for which to search the closest position
+     * @return the closest position in the partitioner's category
+     */
+    protected TypedPosition findClosestPosition(int offset) {
+
+        try {
+
+            int index= fDocument.computeIndexInCategory(fPositionCategory, offset);
+            Position[] category= fDocument.getPositions(fPositionCategory);
+
+            if (category.length is 0)
+                return null;
+
+            if (index < category.length) {
+                if (offset is category[index].offset)
+                    return cast(TypedPosition) category[index];
+            }
+
+            if (index > 0)
+                index--;
+
+            return cast(TypedPosition) category[index];
+
+        } catch (BadPositionCategoryException x) {
+        } catch (BadLocationException x) {
+        }
+
+        return null;
+    }
+
+
+    /*
+     * @see IDocumentPartitioner#getContentType
+     */
+    public String getContentType(int offset) {
+
+        TypedPosition p= findClosestPosition(offset);
+        if (p !is null && p.includes(offset))
+            return p.getType();
+
+        return IDocument.DEFAULT_CONTENT_TYPE;
+    }
+
+    /*
+     * @see IDocumentPartitioner#getPartition
+     */
+    public ITypedRegion getPartition(int offset) {
+
+        try {
+
+            Position[] category = fDocument.getPositions(fPositionCategory);
+
+            if (category is null || category.length is 0)
+                return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE);
+
+            int index= fDocument.computeIndexInCategory(fPositionCategory, offset);
+
+            if (index < category.length) {
+
+                TypedPosition next= cast(TypedPosition) category[index];
+
+                if (offset is next.offset)
+                    return new TypedRegion(next.getOffset(), next.getLength(), next.getType());
+
+                if (index is 0)
+                    return new TypedRegion(0, next.offset, IDocument.DEFAULT_CONTENT_TYPE);
+
+                TypedPosition previous= cast(TypedPosition) category[index - 1];
+                if (previous.includes(offset))
+                    return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType());
+
+                int endOffset= previous.getOffset() + previous.getLength();
+                return new TypedRegion(endOffset, next.getOffset() - endOffset, IDocument.DEFAULT_CONTENT_TYPE);
+            }
+
+            TypedPosition previous= cast(TypedPosition) category[category.length - 1];
+            if (previous.includes(offset))
+                return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType());
+
+            int endOffset= previous.getOffset() + previous.getLength();
+            return new TypedRegion(endOffset, fDocument.getLength() - endOffset, IDocument.DEFAULT_CONTENT_TYPE);
+
+        } catch (BadPositionCategoryException x) {
+        } catch (BadLocationException x) {
+        }
+
+        return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE);
+    }
+
+    /*
+     * @see IDocumentPartitioner#computePartitioning
+     */
+    public ITypedRegion[] computePartitioning(int offset, int length) {
+        return computePartitioning(offset, length, false);
+    }
+
+    /*
+     * @see IDocumentPartitioner#getLegalContentTypes
+     */
+    public String[] getLegalContentTypes() {
+        return TextUtilities.copy(fLegalContentTypes);
+    }
+
+    /**
+     * Returns whether the given type is one of the legal content types.
+     *
+     * @param contentType the content type to check
+     * @return <code>true</code> if the content type is a legal content type
+     */
+    protected bool isSupportedContentType(String contentType) {
+        if (contentType !is null) {
+            for (int i= 0; i < fLegalContentTypes.length; i++) {
+                if (fLegalContentTypes[i].equals(contentType))
+                    return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns a content type encoded in the given token. If the token's
+     * data is not <code>null</code> and a string it is assumed that
+     * it is the encoded content type.
+     *
+     * @param token the token whose content type is to be determined
+     * @return the token's content type
+     */
+    protected String getTokenContentType(IToken token) {
+        Object data= token.getData();
+        if ( auto str = cast(ArrayWrapperString)data )
+            return str.array;
+        return null;
+    }
+
+    /* zero-length partition support */
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitionerExtension2#getContentType(int)
+     * @since 3.0
+     */
+    public String getContentType(int offset, bool preferOpenPartitions) {
+        return getPartition(offset, preferOpenPartitions).getType();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitionerExtension2#getPartition(int)
+     * @since 3.0
+     */
+    public ITypedRegion getPartition(int offset, bool preferOpenPartitions) {
+        ITypedRegion region= getPartition(offset);
+        if (preferOpenPartitions) {
+            if (region.getOffset() is offset && !region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE)) {
+                if (offset > 0) {
+                    region= getPartition(offset - 1);
+                    if (region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE))
+                        return region;
+                }
+                return new TypedRegion(offset, 0, IDocument.DEFAULT_CONTENT_TYPE);
+            }
+        }
+        return region;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitionerExtension2#computePartitioning(int, int)
+     * @since 3.0
+     */
+    public ITypedRegion[] computePartitioning(int offset, int length, bool includeZeroLengthPartitions) {
+        List list= new ArrayList();
+
+        try {
+
+            int endOffset= offset + length;
+
+            Position[] category= fDocument.getPositions(fPositionCategory);
+
+            TypedPosition previous= null, current= null;
+            int start, end, gapOffset;
+            Position gap= null;
+
+            for (int i= 0; i < category.length; i++) {
+
+                current= cast(TypedPosition) category[i];
+
+                gapOffset= (previous !is null) ? previous.getOffset() + previous.getLength() : 0;
+                gap= new Position(gapOffset, current.getOffset() - gapOffset);
+                if ((includeZeroLengthPartitions || gap.getLength() > 0) && gap.overlapsWith(offset, length)) {
+                    start= Math.max(offset, gapOffset);
+                    end= Math.min(endOffset, gap.getOffset() + gap.getLength());
+                    list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE));
+                }
+
+                if (current.overlapsWith(offset, length)) {
+                    start= Math.max(offset, current.getOffset());
+                    end= Math.min(endOffset, current.getOffset() + current.getLength());
+                    list.add(new TypedRegion(start, end - start, current.getType()));
+                }
+
+                previous= current;
+            }
+
+            if (previous !is null) {
+                gapOffset= previous.getOffset() + previous.getLength();
+                gap= new Position(gapOffset, fDocument.getLength() - gapOffset);
+                if ((includeZeroLengthPartitions || gap.getLength() > 0) && ((includeZeroLengthPartitions && offset + length is gapOffset && gap.length is 0) || gap.overlapsWith(offset, length))) {
+                    start= Math.max(offset, gapOffset);
+                    end= Math.min(endOffset, fDocument.getLength());
+                    list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE));
+                }
+            }
+
+            if (list.isEmpty())
+                list.add(new TypedRegion(offset, length, IDocument.DEFAULT_CONTENT_TYPE));
+
+        } catch (BadPositionCategoryException x) {
+        }
+
+        return arraycast!(ITypedRegion)(list.toArray());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/RuleBasedScanner.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,246 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.rules.RuleBasedScanner;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * A generic scanner which can be "programmed" with a sequence of rules.
+ * The scanner is used to get the next token by evaluating its rule in sequence until
+ * one is successful. If a rule returns a token which is undefined, the scanner will proceed to
+ * the next rule. Otherwise the token provided by the rule will be returned by
+ * the scanner. If no rule returned a defined token, this scanner returns a token
+ * which returns <code>true</code> when calling <code>isOther</code>, unless the end
+ * of the file is reached. In this case the token returns <code>true</code> when calling
+ * <code>isEOF</code>.
+ *
+ * @see IRule
+ */
+public class RuleBasedScanner : ICharacterScanner, ITokenScanner {
+
+    /** The list of rules of this scanner */
+    protected IRule[] fRules;
+    /** The token to be returned by default if no rule fires */
+    protected IToken fDefaultReturnToken;
+    /** The document to be scanned */
+    protected IDocument fDocument;
+    /** The cached legal line delimiters of the document */
+    protected char[][] fDelimiters;
+    /** The offset of the next character to be read */
+    protected int fOffset;
+    /** The end offset of the range to be scanned */
+    protected int fRangeEnd;
+    /** The offset of the last read token */
+    protected int fTokenOffset;
+    /** The cached column of the current scanner position */
+    protected int fColumn;
+    /** Internal setting for the un-initialized column cache. */
+    protected static final int UNDEFINED= -1;
+
+    /**
+     * Creates a new rule based scanner which does not have any rule.
+     */
+    public this() {
+    }
+
+    /**
+     * Configures the scanner with the given sequence of rules.
+     *
+     * @param rules the sequence of rules controlling this scanner
+     */
+    public void setRules(IRule[] rules) {
+        if (rules !is null) {
+            fRules= new IRule[rules.length];
+            SimpleType!(IRule).arraycopy(rules, 0, fRules, 0, rules.length);
+        } else
+            fRules= null;
+    }
+
+    /**
+     * Configures the scanner's default return token. This is the token
+     * which is returned when none of the rules fired and EOF has not been
+     * reached.
+     *
+     * @param defaultReturnToken the default return token
+     * @since 2.0
+     */
+    public void setDefaultReturnToken(IToken defaultReturnToken) {
+        Assert.isNotNull(defaultReturnToken.getData());
+        fDefaultReturnToken= defaultReturnToken;
+    }
+
+    /*
+     * @see ITokenScanner#setRange(IDocument, int, int)
+     */
+    public void setRange(IDocument document, int offset, int length) {
+        Assert.isLegal(document !is null);
+        final int documentLength= document.getLength();
+        checkRange(offset, length, documentLength);
+
+        fDocument= document;
+        fOffset= offset;
+        fColumn= UNDEFINED;
+        fRangeEnd= offset + length;
+
+        String[] delimiters= fDocument.getLegalLineDelimiters();
+        fDelimiters= new char[][](delimiters.length);
+        for (int i= 0; i < delimiters.length; i++)
+            fDelimiters[i]= delimiters[i].toCharArray();
+
+        if (fDefaultReturnToken is null)
+            fDefaultReturnToken= new Token(null);
+    }
+
+    /**
+     * Checks that the given range is valid.
+     * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=69292
+     *
+     * @param offset the offset of the document range to scan
+     * @param length the length of the document range to scan
+     * @param documentLength the document's length
+     * @since 3.3
+     */
+    private void checkRange(int offset, int length, int documentLength) {
+        Assert.isLegal(offset > -1);
+        Assert.isLegal(length > -1);
+        Assert.isLegal(offset + length <= documentLength);
+    }
+
+    /*
+     * @see ITokenScanner#getTokenOffset()
+     */
+    public int getTokenOffset() {
+        return fTokenOffset;
+    }
+
+    /*
+     * @see ITokenScanner#getTokenLength()
+     */
+    public int getTokenLength() {
+        if (fOffset < fRangeEnd)
+            return fOffset - getTokenOffset();
+        return fRangeEnd - getTokenOffset();
+    }
+
+
+    /*
+     * @see ICharacterScanner#getColumn()
+     */
+    public int getColumn() {
+        if (fColumn is UNDEFINED) {
+            try {
+                int line= fDocument.getLineOfOffset(fOffset);
+                int start= fDocument.getLineOffset(line);
+
+                fColumn= fOffset - start;
+
+            } catch (BadLocationException ex) {
+            }
+        }
+        return fColumn;
+    }
+
+    /*
+     * @see ICharacterScanner#getLegalLineDelimiters()
+     */
+    public char[][] getLegalLineDelimiters() {
+        return fDelimiters;
+    }
+
+    /*
+     * @see ITokenScanner#nextToken()
+     */
+    public IToken nextToken() {
+
+        fTokenOffset= fOffset;
+        fColumn= UNDEFINED;
+
+        if (fRules !is null) {
+            for (int i= 0; i < fRules.length; i++) {
+                IToken token= (fRules[i].evaluate(this));
+                if (!token.isUndefined())
+                    return token;
+            }
+        }
+
+        if (read() is EOF)
+            return Token.EOF;
+        return fDefaultReturnToken;
+    }
+
+    /*
+     * @see ICharacterScanner#read()
+     */
+    public int read() {
+
+        try {
+
+            if (fOffset < fRangeEnd) {
+                try {
+                    return fDocument.getChar(fOffset);
+                } catch (BadLocationException e) {
+                }
+            }
+
+            return EOF;
+
+        } finally {
+            ++ fOffset;
+            fColumn= UNDEFINED;
+        }
+    }
+
+    /*
+     * @see ICharacterScanner#unread()
+     */
+    public void unread() {
+        --fOffset;
+        fColumn= UNDEFINED;
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/SingleLineRule.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * 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
+ *     Christopher Lenz (cmlenz@gmx.de) - support for line continuation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.rules.SingleLineRule;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * A specific configuration of pattern rule whereby
+ * the pattern begins with a specific sequence and may
+ * end with a specific sequence, but will not span more
+ * than a single line.
+ */
+public class SingleLineRule : PatternRule {
+
+    /**
+     * Creates a rule for the given starting and ending sequence
+     * which, if detected, will return the specified token.
+     *
+     * @param startSequence the pattern's start sequence
+     * @param endSequence the pattern's end sequence
+     * @param token the token to be returned on success
+     */
+    public this(String startSequence, String endSequence, IToken token) {
+        this(startSequence, endSequence, token, cast(wchar) 0);
+    }
+
+    /**
+     * Creates a rule for the given starting and ending sequence
+     * which, if detected, will return the specified token.
+     * Any character which follows the given escape character
+     * will be ignored.
+     *
+     * @param startSequence the pattern's start sequence
+     * @param endSequence the pattern's end sequence
+     * @param token the token to be returned on success
+     * @param escapeCharacter the escape character
+     */
+    public this(String startSequence, String endSequence, IToken token, char escapeCharacter) {
+        this(startSequence, endSequence, token, escapeCharacter, false);
+    }
+
+    /**
+     * Creates a rule for the given starting and ending sequence
+     * which, if detected, will return the specified token. Alternatively, the
+     * line can also be ended with the end of the file.
+     * Any character which follows the given escape character
+     * will be ignored.
+     *
+     * @param startSequence the pattern's start sequence
+     * @param endSequence the pattern's end sequence
+     * @param token the token to be returned on success
+     * @param escapeCharacter the escape character
+     * @param breaksOnEOF indicates whether the end of the file successfully terminates this rule
+     * @since 2.1
+     */
+    public this(String startSequence, String endSequence, IToken token, char escapeCharacter, bool breaksOnEOF) {
+        super(startSequence, endSequence, token, escapeCharacter, true, breaksOnEOF);
+    }
+
+    /**
+     * Creates a rule for the given starting and ending sequence
+     * which, if detected, will return the specified token. Alternatively, the
+     * line can also be ended with the end of the file.
+     * Any character which follows the given escape character
+     * will be ignored. In addition, an escape character immediately before an
+     * end of line can be set to continue the line.
+     *
+     * @param startSequence the pattern's start sequence
+     * @param endSequence the pattern's end sequence
+     * @param token the token to be returned on success
+     * @param escapeCharacter the escape character
+     * @param breaksOnEOF indicates whether the end of the file successfully terminates this rule
+     * @param escapeContinuesLine indicates whether the specified escape character is used for line
+     *        continuation, so that an end of line immediately after the escape character does not
+     *        terminate the line, even if <code>breakOnEOL</code> is true
+     * @since 3.0
+     */
+    public this(String startSequence, String endSequence, IToken token, char escapeCharacter, bool breaksOnEOF, bool escapeContinuesLine) {
+        super(startSequence, endSequence, token, escapeCharacter, true, breaksOnEOF, escapeContinuesLine);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/Token.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.rules.Token;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * Standard implementation of <code>IToken</code>.
+ */
+public class Token : IToken {
+
+    /** Internal token type: Undefined */
+    private static const int T_UNDEFINED= 0;
+    /** Internal token type: EOF */
+    private static const int T_EOF= 1;
+    /** Internal token type: Whitespace */
+    private static const int T_WHITESPACE= 2;
+    /** Internal token type: Others */
+    private static const int T_OTHER=   3;
+
+
+    /**
+     * Standard token: Undefined.
+     */
+    public static IToken UNDEFINED_;
+    public static IToken UNDEFINED(){
+        if( UNDEFINED_ is null ){
+            synchronized( Token.classinfo ){
+                if( UNDEFINED_ is null ){
+                    UNDEFINED_ = new Token(T_UNDEFINED);
+                }
+            }
+        }
+        return UNDEFINED_;
+    }
+    /**
+     * Standard token: End Of File.
+     */
+    public static IToken EOF_;
+    public static IToken EOF(){
+        if( EOF_ is null ){
+            synchronized( Token.classinfo ){
+                if( EOF_ is null ){
+                    EOF_ = new Token(T_EOF);
+                }
+            }
+        }
+        return EOF_;
+    }
+    /**
+     * Standard token: Whitespace.
+     */
+    public static IToken WHITESPACE_;
+    public static IToken WHITESPACE(){
+        if( WHITESPACE_ is null ){
+            synchronized( Token.classinfo ){
+                if( WHITESPACE_ is null ){
+                    WHITESPACE_ = new Token(T_WHITESPACE);
+                }
+            }
+        }
+        return WHITESPACE_;
+    }
+
+    /**
+     * Standard token: Neither {@link #UNDEFINED}, {@link #WHITESPACE}, nor {@link #EOF}.
+     * @deprecated will be removed
+     */
+    public static IToken OTHER_;
+    public static IToken OTHER(){
+        if( OTHER_ is null ){
+            synchronized( Token.classinfo ){
+                if( OTHER_ is null ){
+                    OTHER_ = new Token(T_OTHER);
+                }
+            }
+        }
+        return OTHER_;
+    }
+
+    /** The type of this token */
+    private int fType;
+    /** The data associated with this token */
+    private Object fData;
+
+    /**
+     * Creates a new token according to the given specification which does not
+     * have any data attached to it.
+     *
+     * @param type the type of the token
+     * @since 2.0
+     */
+    private this(int type) {
+        fType= type;
+        fData= null;
+    }
+
+    /**
+     * Creates a new token which represents neither undefined, whitespace, nor EOF.
+     * The newly created token has the given data attached to it.
+     *
+     * @param data the data attached to the newly created token
+     */
+    public this(Object data) {
+        fType= T_OTHER;
+        fData= data;
+    }
+
+    /**
+     * Re-initializes the data of this token. The token may not represent
+     * undefined, whitespace, or EOF.
+     *
+     * @param data to be attached to the token
+     * @since 2.0
+     */
+    public void setData(Object data) {
+        Assert.isTrue(isOther());
+        fData= data;
+    }
+
+    /*
+     * @see IToken#getData()
+     */
+    public Object getData() {
+        return fData;
+    }
+
+    /*
+     * @see IToken#isOther()
+     */
+    public bool isOther() {
+        return (fType is T_OTHER);
+    }
+
+    /*
+     * @see IToken#isEOF()
+     */
+    public bool isEOF() {
+        return (fType is T_EOF);
+    }
+
+    /*
+     * @see IToken#isWhitespace()
+     */
+    public bool isWhitespace() {
+        return (fType is T_WHITESPACE);
+    }
+
+    /*
+     * @see IToken#isUndefined()
+     */
+    public bool isUndefined() {
+        return (fType is T_UNDEFINED);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/WhitespaceRule.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.rules.WhitespaceRule;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+import java.lang.all;
+
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * An implementation of <code>IRule</code> capable of detecting whitespace.
+ * A whitespace rule uses a whitespace detector in order to find out which
+ * characters are whitespace characters.
+ *
+ * @see IWhitespaceDetector
+ */
+public class WhitespaceRule : IRule {
+
+    /** The whitespace detector used by this rule */
+    protected IWhitespaceDetector fDetector;
+
+    /**
+     * Creates a rule which, with the help of an
+     * whitespace detector, will return a whitespace
+     * token when a whitespace is detected.
+     *
+     * @param detector the rule's whitespace detector, may not be <code>null</code>
+     */
+    public this(IWhitespaceDetector detector) {
+        Assert.isNotNull(cast(Object)detector);
+        fDetector= detector;
+    }
+
+    /*
+     * @see IRule#evaluate(ICharacterScanner)
+     */
+    public IToken evaluate(ICharacterScanner scanner) {
+        int c= scanner.read();
+        if (fDetector.isWhitespace(cast(char) c)) {
+            do {
+                c= scanner.read();
+            } while (fDetector.isWhitespace(cast(char) c));
+            scanner.unread();
+            return Token.WHITESPACE;
+        }
+
+        scanner.unread();
+        return Token.UNDEFINED;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/WordPatternRule.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.rules.WordPatternRule;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WordRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.IWordDetector; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+
+import java.lang.all;
+
+
+import org.eclipse.core.runtime.Assert;
+
+
+
+/**
+ * A specific single line rule which stipulates that the start
+ * and end sequence occur within a single word, as defined by a word detector.
+ *
+ * @see IWordDetector
+ */
+public class WordPatternRule : SingleLineRule {
+
+    /** The word detector used by this rule */
+    protected IWordDetector fDetector;
+    /** The internal buffer used for pattern detection */
+    private StringBuffer fBuffer;
+
+    /**
+     * Creates a rule for the given starting and ending word
+     * pattern which, if detected, will return the specified token.
+     * A word detector is used to identify words.
+     *
+     * @param detector the word detector to be used
+     * @param startSequence the start sequence of the word pattern
+     * @param endSequence the end sequence of the word pattern
+     * @param token the token to be returned on success
+     */
+    public this(IWordDetector detector, String startSequence, String endSequence, IToken token) {
+        this(detector, startSequence, endSequence, token, cast(wchar)0);
+    }
+
+    /**
+    /**
+     * Creates a rule for the given starting and ending word
+     * pattern which, if detected, will return the specified token.
+     * A word detector is used to identify words.
+     * Any character which follows the given escapeCharacter will be ignored.
+     *
+     * @param detector the word detector to be used
+     * @param startSequence the start sequence of the word pattern
+     * @param endSequence the end sequence of the word pattern
+     * @param token the token to be returned on success
+     * @param escapeCharacter the escape character
+     */
+    public this(IWordDetector detector, String startSequence, String endSequence, IToken token, char escapeCharacter) {
+        fBuffer= new StringBuffer();
+        super(startSequence, endSequence, token, escapeCharacter);
+        Assert.isNotNull(cast(Object)detector);
+        fDetector= detector;
+    }
+
+    /**
+     * Returns whether the end sequence was detected.
+     * The rule acquires the rest of the word, using the
+     * provided word detector, and tests to determine if
+     * it ends with the end sequence.
+     *
+     * @param scanner the scanner to be used
+     * @return <code>true</code> if the word ends on the given end sequence
+     */
+    protected bool endSequenceDetected(ICharacterScanner scanner) {
+        fBuffer.truncate(0);
+        int c= scanner.read();
+        while (fDetector.isWordPart(cast(dchar) c)) {
+            fBuffer.append(cast(char) c);
+            c= scanner.read();
+        }
+        scanner.unread();
+
+        if (fBuffer.length() >= fEndSequence.length) {
+            for (int i=fEndSequence.length - 1, j= fBuffer.length() - 1; i >= 0; i--, j--) {
+                if (fEndSequence[i] !is fBuffer.slice()[j]) {
+                    unreadBuffer(scanner);
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        unreadBuffer(scanner);
+        return false;
+    }
+
+    /**
+     * Returns the characters in the buffer to the scanner.
+     * Note that the rule must also return the characters
+     * read in as part of the start sequence expect the first one.
+     *
+     * @param scanner the scanner to be used
+     */
+    protected void unreadBuffer(ICharacterScanner scanner) {
+        fBuffer.select(0, 0 );
+        fBuffer.replace(fStartSequence);
+        for (int i= fBuffer.length() - 1; i > 0; i--)
+            scanner.unread();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/rules/WordRule.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.rules.WordRule;
+
+import org.eclipse.jface.text.rules.FastPartitioner; // packageimport
+import org.eclipse.jface.text.rules.ITokenScanner; // packageimport
+import org.eclipse.jface.text.rules.Token; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.EndOfLineRule; // packageimport
+import org.eclipse.jface.text.rules.WhitespaceRule; // packageimport
+import org.eclipse.jface.text.rules.WordPatternRule; // packageimport
+import org.eclipse.jface.text.rules.IPredicateRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultPartitioner; // packageimport
+import org.eclipse.jface.text.rules.NumberRule; // packageimport
+import org.eclipse.jface.text.rules.SingleLineRule; // packageimport
+import org.eclipse.jface.text.rules.PatternRule; // packageimport
+import org.eclipse.jface.text.rules.IWordDetector; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.ICharacterScanner; // packageimport
+import org.eclipse.jface.text.rules.IRule; // packageimport
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer; // packageimport
+import org.eclipse.jface.text.rules.IToken; // packageimport
+import org.eclipse.jface.text.rules.IPartitionTokenScanner; // packageimport
+import org.eclipse.jface.text.rules.MultiLineRule; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitioner; // packageimport
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; // packageimport
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; // packageimport
+import org.eclipse.jface.text.rules.IWhitespaceDetector; // packageimport
+
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+
+
+
+/**
+ * An implementation of <code>IRule</code> capable of detecting words
+ * Word rules also allow for the association of tokens with specific words.
+ * That is, not only can the rule be used to provide tokens for exact matches,
+ * but also for the generalized notion of a word in the context in which it is used.
+ * A word rules uses a word detector to determine what a word is.
+ *
+ * @see IWordDetector
+ */
+public class WordRule : IRule {
+
+    /** Internal setting for the un-initialized column constraint. */
+    protected static const int UNDEFINED= -1;
+
+    /** The word detector used by this rule. */
+    protected IWordDetector fDetector;
+    /** The default token to be returned on success and if nothing else has been specified. */
+    protected IToken fDefaultToken;
+    /** The column constraint. */
+    protected int fColumn= UNDEFINED;
+    /** The table of predefined words and token for this rule. */
+    protected Map fWords;
+    /** Buffer used for pattern detection. */
+    private StringBuffer fBuffer;
+    /**
+     * Tells whether this rule is case sensitive.
+     * @since 3.3
+     */
+    private bool fIgnoreCase= false;
+
+    /**
+     * Creates a rule which, with the help of an word detector, will return the token
+     * associated with the detected word. If no token has been associated, the scanner
+     * will be rolled back and an undefined token will be returned in order to allow
+     * any subsequent rules to analyze the characters.
+     *
+     * @param detector the word detector to be used by this rule, may not be <code>null</code>
+     * @see #addWord(String, IToken)
+     */
+    public this(IWordDetector detector) {
+        this(detector, Token.UNDEFINED, false);
+    }
+
+    /**
+     * Creates a rule which, with the help of a word detector, will return the token
+     * associated with the detected word. If no token has been associated, the
+     * specified default token will be returned.
+     *
+     * @param detector the word detector to be used by this rule, may not be <code>null</code>
+     * @param defaultToken the default token to be returned on success
+     *          if nothing else is specified, may not be <code>null</code>
+     * @see #addWord(String, IToken)
+     */
+    public this(IWordDetector detector, IToken defaultToken) {
+        this(detector, defaultToken, false);
+    }
+
+    /**
+     * Creates a rule which, with the help of a word detector, will return the token
+     * associated with the detected word. If no token has been associated, the
+     * specified default token will be returned.
+     *
+     * @param detector the word detector to be used by this rule, may not be <code>null</code>
+     * @param defaultToken the default token to be returned on success
+     *          if nothing else is specified, may not be <code>null</code>
+     * @param ignoreCase the case sensitivity associated with this rule
+     * @see #addWord(String, IToken)
+     * @since 3.3
+     */
+    public this(IWordDetector detector, IToken defaultToken, bool ignoreCase) {
+        fWords= new HashMap();
+        fBuffer= new StringBuffer();
+
+        Assert.isNotNull(cast(Object)detector);
+        Assert.isNotNull(cast(Object)defaultToken);
+
+        fDetector= detector;
+        fDefaultToken= defaultToken;
+        fIgnoreCase= ignoreCase;
+    }
+
+    /**
+     * Adds a word and the token to be returned if it is detected.
+     *
+     * @param word the word this rule will search for, may not be <code>null</code>
+     * @param token the token to be returned if the word has been found, may not be <code>null</code>
+     */
+    public void addWord(String word, IToken token) {
+        //Assert.isNotNull(word);
+        Assert.isNotNull(cast(Object)token);
+
+        fWords.put(word, cast(Object)token);
+    }
+
+    /**
+     * Sets a column constraint for this rule. If set, the rule's token
+     * will only be returned if the pattern is detected starting at the
+     * specified column. If the column is smaller then 0, the column
+     * constraint is considered removed.
+     *
+     * @param column the column in which the pattern starts
+     */
+    public void setColumnConstraint(int column) {
+        if (column < 0)
+            column= UNDEFINED;
+        fColumn= column;
+    }
+
+    /*
+     * @see IRule#evaluate(ICharacterScanner)
+     */
+    public IToken evaluate(ICharacterScanner scanner) {
+        int c= scanner.read();
+        if (c !is ICharacterScanner.EOF && fDetector.isWordStart(cast(dchar) c)) {
+            if (fColumn is UNDEFINED || (fColumn is scanner.getColumn() - 1)) {
+
+                fBuffer.truncate(0);
+                do {
+                    fBuffer.append(cast(char) c);
+                    c= scanner.read();
+                } while (c !is ICharacterScanner.EOF && fDetector.isWordPart(cast(dchar) c));
+                scanner.unread();
+
+                String buffer= fBuffer.toString();
+                IToken token= cast(IToken)fWords.get(buffer);
+
+                if(fIgnoreCase) {
+                    Iterator iter= fWords.keySet().iterator();
+                    while (iter.hasNext()) {
+                        String key= stringcast(iter.next());
+                        if(buffer.equalsIgnoreCase(key)) {
+                            token= cast(IToken)fWords.get(key);
+                            break;
+                        }
+                    }
+                } else
+                    token= cast(IToken)fWords.get(buffer);
+
+                if (token !is null)
+                    return token;
+
+                if (fDefaultToken.isUndefined())
+                    unreadBuffer(scanner);
+
+                return fDefaultToken;
+            }
+        }
+
+        scanner.unread();
+        return Token.UNDEFINED;
+    }
+
+    /**
+     * Returns the characters in the buffer to the scanner.
+     *
+     * @param scanner the scanner to be used
+     */
+    protected void unreadBuffer(ICharacterScanner scanner) {
+        for (int i= fBuffer.length() - 1; i >= 0; i--)
+            scanner.unread();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/AbstractRulerColumn.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,726 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.text.source.AbstractRulerColumn;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.IViewportListener;
+import org.eclipse.jface.text.JFaceTextUtil;
+import org.eclipse.jface.text.TextEvent;
+
+
+/**
+ * Abstract implementation of a {@link IVerticalRulerColumn} that
+ * uses a {@link Canvas} to draw the ruler contents and which
+ * handles scrolling and mouse selection.
+ *
+ * <h3>Painting</h3>
+ * Subclasses can hook into the paint loop at three levels:
+ * <ul>
+ * <li>Override <strong>{@link #paint(GC, ILineRange)}</strong> to control the entire painting of
+ * the ruler.</li>
+ * <li>Override <strong>{@link #paintLine(GC, int, int, int, int)}</strong> to control the
+ * painting of a line.</li>
+ * <li>Leave the painting to the default implementation, but override <strong>{@link #computeBackground(int)}</strong>,
+ * <strong>{@link #computeForeground(int)}</strong> and <strong>{@link #computeText(int)}</strong>
+ * to specify the ruler appearance for a line.</li>
+ * </ul>
+ *
+ * <h3>Invalidation</h3>
+ * Subclasses may call {@link #redraw()} to mark the entire ruler as needing to be redrawn.
+ * Alternatively, use {@link #redraw(ILineRange)} to only invalidate a certain line range, for
+ * example due to changes to the display model.
+ *
+ * <h3>Configuration</h3>
+ * Subclasses can set the following properties. Setting them may trigger redrawing.
+ * <ul>
+ * <li>The {@link #setFont(Font) font} used to draw text in {@link #paintLine(GC, int, int, int, int)}.</li>
+ * <li>The horizontal {@link #setTextInset(int) text inset} for text drawn.</li>
+ * <li>The {@link #setDefaultBackground(Color) default background color} of the ruler.</li>
+ * <li>The {@link #setWidth(int) width} of the ruler.</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+public abstract class AbstractRulerColumn : IVerticalRulerColumn, IVerticalRulerInfo, IVerticalRulerInfoExtension {
+    private static const int DEFAULT_WIDTH= 12;
+    private static const int DEFAULT_TEXT_INSET= 2;
+
+    /**
+     * Handles all the mouse interaction in this line number ruler column.
+     */
+    private final class MouseHandler : MouseListener, MouseMoveListener {
+
+        /*
+         * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseUp(MouseEvent event) {
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseDown(MouseEvent event) {
+            fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseDoubleClick(MouseEvent event) {
+            fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseMove(MouseEvent event) {
+            fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
+        }
+    }
+
+    /**
+     * Internal listener class that updates the ruler upon scrolling and text modifications.
+     */
+    private final class InternalListener : IViewportListener, ITextListener {
+
+        /*
+         * @see IViewportListener#viewportChanged(int)
+         */
+        public void viewportChanged(int topPixel) {
+            int delta= topPixel - fLastTopPixel;
+            if (scrollVertical(delta))
+                fCanvas.update(); // force update the invalidated regions
+        }
+
+        /*
+         * @see ITextListener#textChanged(TextEvent)
+         */
+        public void textChanged(TextEvent event) {
+            /*
+             * Redraw: - when the viewer is drawing, and any of the following - the widget was not
+             * full before the change - the widget is not full after the change - the document event
+             * was a visual modification (no document event attached) - for example when the
+             * projection changes.
+             */
+            if (!event.getViewerRedrawState())
+                return;
+
+            if (fWasShowingEntireContents || event.getDocumentEvent() is null || JFaceTextUtil.isShowingEntireContents(fStyledText))
+                redraw();
+        }
+    }
+
+    /* Listeners */
+
+    /** The viewport listener. */
+    private const InternalListener fInternalListener;
+    /** The mouse handler. */
+    private const MouseHandler fMouseHandler;
+
+    /*
+     * Implementation and context of this ruler - created and set in createControl(), disposed of in
+     * columnRemoved().
+     */
+
+    /** The parent ruler, possibly <code>null</code>. */
+    private CompositeRuler fParentRuler;
+    /** The canvas, the only widget used to draw this ruler, possibly <code>null</code>. */
+    private Canvas fCanvas;
+    /** The text viewer, possibly <code>null</code>. */
+    private ITextViewer fTextViewer;
+    /** The text viewer's widget, possibly <code>null</code>. */
+    private StyledText fStyledText;
+
+    /* State when the canvas was last painted. */
+
+    /** The text widget's top pixel when the ruler was last painted. */
+    private int fLastTopPixel= -1;
+    /** Whether the text widget was showing the entire contents when the ruler was last painted. */
+    private bool fWasShowingEntireContents= false;
+
+    /* Configuration */
+
+    /** The width of this ruler. */
+    private int fWidth= DEFAULT_WIDTH;
+    /** The text inset. */
+    private int fTextInset= DEFAULT_TEXT_INSET;
+    /** The default background color, <code>null</code> to use the text viewer's background color. */
+    private Color fBackground;
+    /** The font, <code>null</code> to use the default font. */
+    private Font fFont;
+    /** The annotation model, possibly <code>null</code>. */
+    private IAnnotationModel fModel;
+    /** The annotation hover, possibly <code>null</code>. */
+    private IAnnotationHover fHover;
+
+    /**
+     * Creates a new ruler.
+     */
+    protected this() {
+        fMouseHandler= new MouseHandler();
+        fInternalListener= new InternalListener();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerColumn#createControl(org.eclipse.jface.text.source.CompositeRuler,
+     *      org.eclipse.swt.widgets.Composite)
+     */
+    public Control createControl(CompositeRuler parentRuler, Composite parentControl) {
+        Assert.isLegal(parentControl !is null);
+        Assert.isLegal(parentRuler !is null);
+        Assert.isLegal(fParentRuler is null); // only call when not yet initialized!
+
+        fParentRuler= parentRuler;
+
+        fTextViewer= getParentRuler().getTextViewer();
+        fTextViewer.addViewportListener(fInternalListener);
+        fTextViewer.addTextListener(fInternalListener);
+
+        fStyledText= fTextViewer.getTextWidget();
+
+        fCanvas= new Canvas(parentControl, getCanvasStyle());
+
+        fCanvas.setBackground(getDefaultBackground());
+        fCanvas.setFont(getFont());
+
+        fCanvas.addPaintListener(new class()  PaintListener {
+            public void paintControl(PaintEvent event) {
+                this.outer.paintControl(event);
+            }
+        });
+
+        fCanvas.addMouseListener(fMouseHandler);
+        fCanvas.addMouseMoveListener(fMouseHandler);
+
+        return fCanvas;
+    }
+
+    /**
+     * Returns the SWT style bits used when creating the ruler canvas.
+     * <p>
+     * The default implementation returns <code>SWT.NO_BACKGROUND</code>.</p>
+     * <p>
+     * Clients may reimplement this method to create a canvas with their
+     * desired style bits.</p>
+     *
+     * @return the SWT style bits, or <code>SWT.NONE</code> if none
+     */
+    protected int getCanvasStyle() {
+        return SWT.NO_BACKGROUND;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerColumn#getControl()
+     */
+    public final Control getControl() {
+        return fCanvas;
+    }
+
+    /**
+     * The new width in pixels. The <code>DEFAULT_WIDTH</code> constant
+     * specifies the default width.
+     *
+     * @param width the new width
+     */
+    protected final void setWidth(int width) {
+        Assert.isLegal(width >= 0);
+        if (fWidth !is width) {
+            fWidth= width;
+            CompositeRuler composite= getParentRuler();
+            if (composite !is null)
+                composite.relayout();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerColumn#getWidth()
+     */
+    public final int getWidth() {
+        return fWidth;
+    }
+
+    /**
+     * Returns the parent ruler, <code>null</code> before
+     * {@link #createControl(CompositeRuler, Composite)} has been called.
+     *
+     * @return the parent ruler or <code>null</code>
+     */
+    protected final CompositeRuler getParentRuler() {
+        return fParentRuler;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param font the font or <code>null</code> to use the default font
+     */
+    public final void setFont(Font font) {
+        if (fFont !is font) {
+            fFont= font;
+            redraw();
+        }
+    }
+
+    /**
+     * Returns the current font. If a font has not been explicitly set, the widget's font is
+     * returned.
+     *
+     * @return the font used to render text on the ruler.
+     */
+    protected final Font getFont() {
+        if (fFont !is null)
+            return fFont;
+        if (fStyledText !is null && !fStyledText.isDisposed())
+            return fStyledText.getFont();
+        return JFaceResources.getTextFont();
+    }
+
+    /**
+     * Sets the text inset (padding) used to draw text in {@link #paintLine(GC, int, int, int, int)}.
+     *
+     * @param textInset the new text inset
+     */
+    protected final void setTextInset(int textInset) {
+        if (textInset !is fTextInset) {
+            fTextInset= textInset;
+            redraw();
+        }
+    }
+
+    /**
+     * Returns the text inset for text drawn by {@link #paintLine(GC, int, int, int, int)}. The
+     * <code>DEFAULT_TEXT_INSET</code> constant specifies the default inset in pixels.
+     *
+     * @return the text inset for text
+     */
+    protected final int getTextInset() {
+        return fTextInset;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerColumn#setModel(org.eclipse.jface.text.source.IAnnotationModel)
+     */
+    public void setModel(IAnnotationModel model) {
+        if (fModel !is model) {
+            fModel= model;
+            redraw();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#getModel()
+     */
+    public final IAnnotationModel getModel() {
+        return fModel;
+    }
+
+    /**
+     * Sets the default background color for this column. The default background is used as default
+     * implementation of {@link #computeBackground(int)} and also to paint the area of the ruler
+     * that does not correspond to any lines (when the viewport is not entirely filled with lines).
+     *
+     * @param background the default background color, <code>null</code> to use the text widget's
+     *        background
+     */
+    protected final void setDefaultBackground(Color background) {
+        if (fBackground !is background) {
+            fBackground= background;
+            if (fCanvas !is null && !fCanvas.isDisposed())
+                fCanvas.setBackground(getDefaultBackground());
+            redraw();
+        }
+    }
+
+    /**
+     * Returns the background color. May return <code>null</code> if the system is shutting down.
+     *
+     * @return the background color
+     */
+    protected final Color getDefaultBackground() {
+        if (fBackground !is null)
+            return fBackground;
+        if (fStyledText !is null && !fStyledText.isDisposed())
+            return fStyledText.getBackground();
+        Display display;
+        if (fCanvas !is null && !fCanvas.isDisposed())
+            display= fCanvas.getDisplay();
+        else
+            display= Display.getCurrent();
+        if (display !is null)
+            return display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
+        return null;
+    }
+
+    /**
+     * Sets the annotation hover.
+     *
+     * @param hover the annotation hover, <code>null</code> for no hover
+     */
+    protected final void setHover(IAnnotationHover hover) {
+        if (fHover !is hover)
+            fHover= hover;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#getHover()
+     */
+    public IAnnotationHover getHover() {
+        return fHover;
+    }
+
+    /**
+     * Disposes this ruler column.
+     * <p>
+     * Subclasses may extend this method.</p>
+     * <p>
+     * Clients who created this column are responsible to call this method
+     * once the column is no longer used.</p>
+     */
+    public void dispose() {
+        if (fTextViewer !is null) {
+            fTextViewer.removeViewportListener(fInternalListener);
+            fTextViewer.removeTextListener(fInternalListener);
+            fTextViewer= null;
+        }
+
+        if (fStyledText !is null)
+            fStyledText= null;
+
+        if (fCanvas !is null) {
+            fCanvas.dispose();
+            fCanvas= null;
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerColumn#redraw()
+     */
+    public final void redraw() {
+        if (fCanvas !is null && !fCanvas.isDisposed())
+            fCanvas.redraw();
+    }
+
+    /**
+     * Marks the region covered by <code>lines</code> as needing to be redrawn.
+     *
+     * @param lines the lines to be redrawn in document coordinates
+     */
+    protected final void redraw(ILineRange lines) {
+        if (fCanvas is null || fCanvas.isDisposed())
+            return;
+        int firstModelLine= lines.getStartLine();
+        int lastModelLine= firstModelLine + lines.getNumberOfLines();
+        int firstWidgetLine= JFaceTextUtil.modelLineToWidgetLine(fTextViewer, firstModelLine);
+        int lastWidgetLine= JFaceTextUtil.modelLineToWidgetLine(fTextViewer, lastModelLine);
+
+        int from= Math.max(0, fStyledText.getLinePixel(firstWidgetLine));
+        // getLinePixel will return the last pixel of the last line if line is lineCount
+        int to= Math.min(fCanvas.getSize().y, fStyledText.getLinePixel(lastWidgetLine + 1));
+        fCanvas.redraw(0, from, fWidth, to - from, false);
+    }
+
+    /**
+     * Paints the ruler column.
+     *
+     * @param event the paint event
+     */
+    private void paintControl(PaintEvent event) {
+        if (fTextViewer is null)
+            return;
+        fWasShowingEntireContents= JFaceTextUtil.isShowingEntireContents(fStyledText);
+        fLastTopPixel= fStyledText.getTopPixel();
+
+        ILineRange lines= computeDirtyWidgetLines(event);
+        GC gc= event.gc;
+        paint(gc, lines);
+
+        if ((fCanvas.getStyle() & SWT.NO_BACKGROUND) !is 0) {
+            // fill empty area below any lines
+            int firstEmpty= Math.max(event.y, fStyledText.getLinePixel(fStyledText.getLineCount()));
+            int lastEmpty= event.y + event.height;
+            if (lastEmpty > firstEmpty) {
+                gc.setBackground(getDefaultBackground());
+                gc.fillRectangle(0, firstEmpty, getWidth(), lastEmpty - firstEmpty);
+            }
+        }
+    }
+
+    /**
+     * Computes the widget lines that need repainting given the clipping region of a paint event.
+     *
+     * @param event the paint event
+     * @return the lines in widget coordinates that need repainting
+     */
+    private ILineRange computeDirtyWidgetLines(PaintEvent event) {
+        int firstLine= fStyledText.getLineIndex(event.y);
+        int lastLine= fStyledText.getLineIndex(event.y + event.height - 1);
+        return new LineRange(firstLine, lastLine - firstLine + 1);
+    }
+
+    /**
+     * Paints the ruler. Note that <code>lines</code> reference widget line indices, and that
+     * <code>lines</code> may not cover the entire viewport, but only the lines that need to be
+     * painted. The lines may not be entirely visible.
+     * <p>
+     * Subclasses may replace or extend. The default implementation calls
+     * {@link #paintLine(GC, int, int, int, int)} for every visible line.
+     * </p>
+     *
+     * @param gc the graphics context to paint on
+     * @param lines the lines to paint in widget coordinates
+     */
+    protected void paint(GC gc, ILineRange lines) {
+        final int firstLine= lines.getStartLine();
+        final int lastLine= firstLine + lines.getNumberOfLines();
+        for (int line= firstLine; line < lastLine; line++) {
+            int modelLine= JFaceTextUtil.widgetLine2ModelLine(fTextViewer, line);
+            if (modelLine is -1)
+                continue;
+            int linePixel= fStyledText.getLinePixel(line);
+            int lineHeight= fStyledText.getLineHeight(fStyledText.getOffsetAtLine(line));
+            paintLine(gc, modelLine, line, linePixel, lineHeight);
+        }
+    }
+
+    /**
+     * Paints the ruler representation of a single line.
+     * <p>
+     * Subclasses may replace or extend. The default implementation draws the text obtained by
+     * {@link #computeText(int)} in the {@link #computeForeground(int) foreground color} and fills
+     * the entire width using the {@link #computeBackground(int) background color}. The text is
+     * drawn {@link #getTextInset()} pixels to the right of the left border.
+     * </p>
+     *
+     * @param gc the graphics context to paint on
+     * @param modelLine the model line (based on document coordinates)
+     * @param widgetLine the line in the text widget corresponding to <code>modelLine</code>
+     * @param linePixel the first y-pixel of the widget line
+     * @param lineHeight the line height in pixels
+     */
+    protected void paintLine(GC gc, int modelLine, int widgetLine, int linePixel, int lineHeight) {
+        gc.setBackground(computeBackground(modelLine));
+        gc.fillRectangle(0, linePixel, getWidth(), lineHeight);
+        String text= computeText(modelLine);
+        if (text !is null) {
+            gc.setForeground(computeForeground(modelLine));
+            gc.drawString(text, getTextInset(), linePixel, true);
+        }
+    }
+
+    /**
+     * Returns the text to be drawn for a certain line by {@link #paintLine(GC, int, int, int, int)},
+     * <code>null</code> for no text. The default implementation returns <code>null</code>.
+     * <p>
+     * Subclasses may replace or extend.
+     * </p>
+     *
+     * @param line the document line number
+     * @return the text to be drawn for the given line, <code>null</code> for no text
+     */
+    protected String computeText(int line) {
+        return null;
+    }
+
+    /**
+     * Returns the background color drawn for a certain line by
+     * {@link #paintLine(GC, int, int, int, int)}. The default implementation returns
+     * {@link #getDefaultBackground()}.
+     * <p>
+     * Subclasses may replace or extend.
+     * </p>
+     *
+     * @param line the document line number
+     * @return the background color for drawn for the given line
+     */
+    protected Color computeBackground(int line) {
+        return getDefaultBackground();
+    }
+
+    /**
+     * Returns the foreground color drawn for a certain line by
+     * {@link #paintLine(GC, int, int, int, int)}. The default implementation returns a
+     * {@link SWT#COLOR_DARK_GRAY} color.
+     * <p>
+     * Subclasses may replace or extend.
+     * </p>
+     *
+     * @param line the document line number
+     * @return the foreground color for drawn for the given line
+     */
+    protected Color computeForeground(int line) {
+        return fStyledText.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
+     */
+    public final int getLineOfLastMouseButtonActivity() {
+        return getParentRuler().getLineOfLastMouseButtonActivity();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfo#toDocumentLineNumber(int)
+     */
+    public final int toDocumentLineNumber(int y_coordinate) {
+        return getParentRuler().toDocumentLineNumber(y_coordinate);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#addVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener)
+     */
+    public void addVerticalRulerListener(IVerticalRulerListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#removeVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener)
+     */
+    public void removeVerticalRulerListener(IVerticalRulerListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Scrolls the canvas vertically (adapted from
+     * {@linkplain StyledText StyledText.scrollVertical()}).
+     *
+     * @param pixels the number of pixels to scroll (negative to scroll upwards)
+     * @return <code>true</code> if the widget was scrolled, <code>false</code> if the widget
+     *         was not scrolled
+     */
+    private bool scrollVertical(int pixels) {
+        if (pixels is 0 || fCanvas is null || fCanvas.isDisposed())
+            return false;
+
+        final int width= getWidth();
+        final int clientAreaHeight= fStyledText.getClientArea().height;
+        final int topMargin= 0;
+        final int leftMargin= 0;
+        final int bottomMargin= 0;
+
+        if (pixels > 0) {
+            // downwards scrolling - content moves upwards
+            int sourceY= topMargin + pixels;
+            int scrollHeight= clientAreaHeight - sourceY - bottomMargin;
+            if (scrollHeight > 0)
+                // scroll recycled area
+                fCanvas.scroll(leftMargin, topMargin, leftMargin, sourceY, width, scrollHeight, true);
+            if (sourceY > scrollHeight) {
+                // redraw in-between area
+                int redrawY= Math.max(0, topMargin + scrollHeight);
+                int redrawHeight= Math.min(clientAreaHeight, pixels - scrollHeight);
+                fCanvas.redraw(leftMargin, redrawY, width, redrawHeight, true);
+            }
+        } else {
+            // upwards scrolling - content moves downwards
+            int destinationY= topMargin - pixels;
+            int scrollHeight= clientAreaHeight - destinationY - bottomMargin;
+            if (scrollHeight > 0)
+                // scroll recycled area
+                fCanvas.scroll(leftMargin, destinationY, leftMargin, topMargin, width, scrollHeight, true);
+            if (destinationY > scrollHeight) {
+                // redraw in-between area
+                int redrawY= Math.max(0, topMargin + scrollHeight);
+                int redrawHeight= Math.min(clientAreaHeight, -pixels - scrollHeight);
+                fCanvas.redraw(leftMargin, redrawY, width, redrawHeight, true);
+            }
+        }
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationBarHoverManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,859 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.AnnotationBarHoverManager;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackAdapter;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.events.ShellListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.internal.text.InformationControlReplacer;
+import org.eclipse.jface.internal.text.InternalAccessor;
+import org.eclipse.jface.text.AbstractHoverInformationControlManager;
+import org.eclipse.jface.text.AbstractInformationControlManager;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.JFaceTextUtil;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.ITextViewerExtension8;
+
+
+/**
+ * This manager controls the layout, content, and visibility of an information
+ * control in reaction to mouse hover events issued by the vertical ruler of a
+ * source viewer.
+ * @since 2.0
+ */
+public class AnnotationBarHoverManager : AbstractHoverInformationControlManager {
+
+    /**
+     * The information control closer for the hover information. Closes the information control as soon as the mouse pointer leaves the subject area, a mouse button is pressed, the user presses a key, or the subject control is resized or moved.
+     *
+     * @since 3.0
+     * @deprecated As of 3.4, no longer used as closer from super class is used
+     */
+    protected class Closer : MouseTrackAdapter , IInformationControlCloser, MouseListener, MouseMoveListener, ControlListener, KeyListener, DisposeListener, ShellListener, Listener {
+
+        /** The closer's subject control */
+        private Control fSubjectControl;
+        /** The subject area */
+        private Rectangle fSubjectArea;
+        /** Indicates whether this closer is active */
+        private bool fIsActive= false;
+        /** The information control. */
+        private IInformationControl fInformationControlToClose;
+        /**
+         * <code>true</code> if a wheel handler is installed.
+         * @since 3.2
+         */
+        private bool fHasWheelFilter= false;
+        /**
+         * The cached display.
+         * @since 3.2
+         */
+        private Display fDisplay;
+
+
+        /**
+         * Creates a new information control closer.
+         */
+        public this() {
+        }
+
+        /*
+         * @see IInformationControlCloser#setSubjectControl(Control)
+         */
+        public void setSubjectControl(Control control) {
+            fSubjectControl= control;
+        }
+
+        /*
+         * @see IInformationControlCloser#setHoverControl(IHoverControl)
+         */
+        public void setInformationControl(IInformationControl control) {
+            fInformationControlToClose= control;
+        }
+
+        /*
+         * @see IInformationControlCloser#start(Rectangle)
+         */
+        public void start(Rectangle subjectArea) {
+
+            if (fIsActive) return;
+            fIsActive= true;
+
+            fSubjectArea= subjectArea;
+
+            fInformationControlToClose.addDisposeListener(this);
+            if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
+                fSubjectControl.addMouseListener(this);
+                fSubjectControl.addMouseMoveListener(this);
+                fSubjectControl.addMouseTrackListener(this);
+                fSubjectControl.getShell().addShellListener(this);
+                fSubjectControl.addControlListener(this);
+                fSubjectControl.addKeyListener(this);
+
+                fDisplay= fSubjectControl.getDisplay();
+                if (!fDisplay.isDisposed() && fHideOnMouseWheel) {
+                    fHasWheelFilter= true;
+                    fDisplay.addFilter(SWT.MouseWheel, this);
+                }
+            }
+        }
+
+        /*
+         * @see IInformationControlCloser#stop()
+         */
+        public void stop() {
+
+            if (!fIsActive)
+                return;
+            fIsActive= false;
+
+            if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
+                fSubjectControl.removeMouseListener(this);
+                fSubjectControl.removeMouseMoveListener(this);
+                fSubjectControl.removeMouseTrackListener(this);
+                fSubjectControl.getShell().removeShellListener(this);
+                fSubjectControl.removeControlListener(this);
+                fSubjectControl.removeKeyListener(this);
+            }
+
+            if (fDisplay !is null && !fDisplay.isDisposed() && fHasWheelFilter)
+                fDisplay.removeFilter(SWT.MouseWheel, this);
+            fHasWheelFilter= false;
+
+            fDisplay= null;
+
+        }
+
+        /**
+         * Stops the information control and if <code>delayRestart</code> is set allows restart only after a certain delay.
+         *
+         * @param delayRestart <code>true</code> if restart should be delayed
+         * @deprecated As of 3.4, replaced by {@link #stop()}. Note that <code>delayRestart</code> was never honored.
+         */
+        protected void stop(bool delayRestart) {
+            stop();
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseMove(MouseEvent event) {
+            if (!fSubjectArea.contains(event.x, event.y))
+                hideInformationControl();
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseUp(MouseEvent event) {
+        }
+
+        /*
+         * @see MouseListener#mouseDown(MouseEvent)
+         */
+        public void mouseDown(MouseEvent event) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see MouseListener#mouseDoubleClick(MouseEvent)
+         */
+        public void mouseDoubleClick(MouseEvent event) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+         * @since 3.2
+         */
+        public void handleEvent(Event event) {
+            if (event.type is SWT.MouseWheel)
+                hideInformationControl();
+        }
+
+        /*
+         * @see MouseTrackAdapter#mouseExit(MouseEvent)
+         */
+        public void mouseExit(MouseEvent event) {
+            if (!fAllowMouseExit)
+                hideInformationControl();
+        }
+
+        /*
+         * @see ControlListener#controlResized(ControlEvent)
+         */
+        public void controlResized(ControlEvent event) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see ControlListener#controlMoved(ControlEvent)
+         */
+        public void controlMoved(ControlEvent event) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see KeyListener#keyReleased(KeyEvent)
+         */
+        public void keyReleased(KeyEvent event) {
+        }
+
+        /*
+         * @see KeyListener#keyPressed(KeyEvent)
+         */
+        public void keyPressed(KeyEvent event) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see org.eclipse.swt.events.ShellListener#shellActivated(org.eclipse.swt.events.ShellEvent)
+         * @since 3.1
+         */
+        public void shellActivated(ShellEvent e) {
+        }
+
+        /*
+         * @see org.eclipse.swt.events.ShellListener#shellClosed(org.eclipse.swt.events.ShellEvent)
+         * @since 3.1
+         */
+        public void shellClosed(ShellEvent e) {
+        }
+
+        /*
+         * @see org.eclipse.swt.events.ShellListener#shellDeactivated(org.eclipse.swt.events.ShellEvent)
+         * @since 3.1
+         */
+        public void shellDeactivated(ShellEvent e) {
+            hideInformationControl();
+        }
+
+        /*
+         * @see org.eclipse.swt.events.ShellListener#shellDeiconified(org.eclipse.swt.events.ShellEvent)
+         * @since 3.1
+         */
+        public void shellDeiconified(ShellEvent e) {
+        }
+
+        /*
+         * @see org.eclipse.swt.events.ShellListener#shellIconified(org.eclipse.swt.events.ShellEvent)
+         * @since 3.1
+         */
+        public void shellIconified(ShellEvent e) {
+        }
+
+        /*
+         * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
+         */
+        public void widgetDisposed(DisposeEvent e) {
+            hideInformationControl();
+        }
+    }
+
+    /** The source viewer the manager is connected to */
+    private ISourceViewer fSourceViewer;
+    /** The vertical ruler the manager is registered with */
+    private IVerticalRulerInfo fVerticalRulerInfo;
+    /** The annotation hover the manager uses to retrieve the information to display. Can be <code>null</code>. */
+    private IAnnotationHover fAnnotationHover;
+    /**
+     * Indicates whether the mouse cursor is allowed to leave the subject area without closing the hover.
+     * @since 3.0
+     */
+    protected bool fAllowMouseExit= false;
+    /**
+     * Whether we should hide the over on mouse wheel action.
+     *
+     * @since 3.2
+     */
+    private bool fHideOnMouseWheel= true;
+
+    /**
+     * The current annotation hover.
+     * @since 3.2
+     */
+    private IAnnotationHover fCurrentHover;
+
+    /**
+     * Creates an annotation hover manager with the given parameters. In addition,
+     * the hovers anchor is RIGHT and the margin is 5 points to the right.
+     *
+     * @param sourceViewer the source viewer this manager connects to
+     * @param ruler the vertical ruler this manager connects to
+     * @param annotationHover the annotation hover providing the information to be displayed
+     * @param creator the information control creator
+     * @deprecated As of 2.1, replaced by {@link AnnotationBarHoverManager#AnnotationBarHoverManager(IVerticalRulerInfo, ISourceViewer, IAnnotationHover, IInformationControlCreator)}
+     */
+    public this(ISourceViewer sourceViewer, IVerticalRuler ruler, IAnnotationHover annotationHover, IInformationControlCreator creator) {
+        this(ruler, sourceViewer, annotationHover, creator);
+    }
+
+    /**
+     * Creates an annotation hover manager with the given parameters. In addition,
+     * the hovers anchor is RIGHT and the margin is 5 points to the right.
+     *
+     * @param rulerInfo the vertical ruler this manager connects to
+     * @param sourceViewer the source viewer this manager connects to
+     * @param annotationHover the annotation hover providing the information to be displayed or <code>null</code> if none
+     * @param creator the information control creator
+     * @since 2.1
+     */
+    public this(IVerticalRulerInfo rulerInfo, ISourceViewer sourceViewer, IAnnotationHover annotationHover, IInformationControlCreator creator) {
+        super(creator);
+
+        Assert.isNotNull(cast(Object)sourceViewer);
+
+        fSourceViewer= sourceViewer;
+        fVerticalRulerInfo= rulerInfo;
+        fAnnotationHover= annotationHover;
+
+        setAnchor(ANCHOR_RIGHT);
+        setMargins(5, 0);
+        // use closer from super class
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#computeInformation()
+     */
+    protected void computeInformation() {
+        fAllowMouseExit= false;
+        MouseEvent event= getHoverEvent();
+        IAnnotationHover hover= getHover(event);
+        if (hover is null) {
+            setInformation(cast(Object)null, null);
+            return;
+        }
+
+        int line= getHoverLine(event);
+
+        if ( cast(IAnnotationHoverExtension)hover ) {
+            IAnnotationHoverExtension extension= cast(IAnnotationHoverExtension) hover;
+            ILineRange range= extension.getHoverLineRange(fSourceViewer, line);
+            setCustomInformationControlCreator(extension.getHoverControlCreator());
+            range= adaptLineRange(range, line);
+            if (range !is null)
+                setInformation(extension.getHoverInfo(fSourceViewer, range, computeNumberOfVisibleLines()), computeArea(range));
+            else
+                setInformation(cast(Object)null, null);
+
+        } else {
+            setCustomInformationControlCreator(null);
+            setInformation(hover.getHoverInfo(fSourceViewer, line), computeArea(line));
+        }
+
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#showInformationControl(org.eclipse.swt.graphics.Rectangle)
+     * @since 3.2
+     */
+    protected void showInformationControl(Rectangle subjectArea) {
+        super.showInformationControl(subjectArea);
+        fCurrentHover= getHover(getHoverEvent());
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#hideInformationControl()
+     * @since 3.2
+     */
+    protected void hideInformationControl() {
+        fCurrentHover= null;
+        super.hideInformationControl();
+    }
+
+    /**
+     * Adapts a given line range so that the result is a line range that does
+     * not overlap with any collapsed region and fits into the view port of the
+     * attached viewer.
+     *
+     * @param lineRange the original line range
+     * @param line the anchor line
+     * @return the adapted line range
+     * @since 3.0
+     */
+    private ILineRange adaptLineRange(ILineRange lineRange, int line) {
+        if (lineRange !is null) {
+            lineRange= adaptLineRangeToFolding(lineRange, line);
+            if (lineRange !is null)
+                return adaptLineRangeToViewport(lineRange);
+        }
+        return null;
+    }
+
+    /**
+     * Adapts a given line range so that the result is a line range that does
+     * not overlap with any collapsed region of the attached viewer.
+     *
+     * @param lineRange the original line range
+     * @param line the anchor line
+     * @return the adapted line range
+     * @since 3.0
+     */
+    private ILineRange adaptLineRangeToFolding(ILineRange lineRange, int line) {
+
+        if ( cast(ITextViewerExtension5)fSourceViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) fSourceViewer;
+
+            try {
+                IRegion region= convertToRegion(lineRange);
+                IRegion[] coverage= extension.getCoveredModelRanges(region);
+                if (coverage !is null && coverage.length > 0) {
+                    IRegion container= findRegionContainingLine(coverage, line);
+                    if (container !is null)
+                        return convertToLineRange(container);
+                }
+
+            } catch (BadLocationException x) {
+            }
+
+            return null;
+        }
+
+        return lineRange;
+    }
+
+    /**
+     * Adapts a given line range so that the result is a line range that fits
+     * into the view port of the attached viewer.
+     *
+     * @param lineRange the original line range
+     * @return the adapted line range
+     * @since 3.0
+     */
+    private ILineRange adaptLineRangeToViewport(ILineRange lineRange) {
+
+        try {
+            StyledText text= fSourceViewer.getTextWidget();
+
+            int topLine= text.getTopIndex();
+            int rangeTopLine= getWidgetLineNumber(lineRange.getStartLine());
+            int topDelta= Math.max(topLine - rangeTopLine, 0);
+
+            Rectangle size= text.getClientArea();
+            Rectangle trim= text.computeTrim(0, 0, 0, 0);
+            int height= size.height - trim.height;
+
+            int lines= JFaceTextUtil.getLineIndex(text, height) - text.getTopIndex();
+
+            int bottomLine= topLine + lines;
+
+            int rangeBottomLine= getWidgetLineNumber(lineRange.getStartLine() + lineRange.getNumberOfLines() - 1);
+            int bottomDelta= Math.max(rangeBottomLine - bottomLine, 0);
+
+            return new LineRange(lineRange.getStartLine() + topDelta, lineRange.getNumberOfLines() - bottomDelta - topDelta);
+
+        } catch (BadLocationException ex) {
+        }
+
+        return null;
+    }
+
+    /**
+     * Converts a line range into a character range.
+     *
+     * @param lineRange the line range
+     * @return the corresponding character range
+     * @throws BadLocationException in case the given line range is invalid
+     */
+    private IRegion convertToRegion(ILineRange lineRange)  {
+        IDocument document= fSourceViewer.getDocument();
+        int startOffset= document.getLineOffset(lineRange.getStartLine());
+        int endLine= lineRange.getStartLine() + Math.max(0, lineRange.getNumberOfLines() - 1);
+        IRegion lineInfo= document.getLineInformation(endLine);
+        int endOffset= lineInfo.getOffset() + lineInfo.getLength();
+        return new Region(startOffset, endOffset - startOffset);
+    }
+
+    /**
+     * Returns the region out of the given set that contains the given line or
+     * <code>null</code>.
+     *
+     * @param regions the set of regions
+     * @param line the line
+     * @return the region of the set that contains the line
+     * @throws BadLocationException in case line is invalid
+     */
+    private IRegion findRegionContainingLine(IRegion[] regions, int line)  {
+        IDocument document= fSourceViewer.getDocument();
+        IRegion lineInfo= document.getLineInformation(line);
+        for (int i= 0; i < regions.length; i++) {
+            if (TextUtilities.overlaps(regions[i], lineInfo))
+                return regions[i];
+        }
+        return null;
+    }
+
+    /**
+     * Converts a given character region into a line range.
+     *
+     * @param region the character region
+     * @return the corresponding line range
+     * @throws BadLocationException in case the given region in invalid
+     */
+    private ILineRange convertToLineRange(IRegion region)  {
+        IDocument document= fSourceViewer.getDocument();
+        int startLine= document.getLineOfOffset(region.getOffset());
+        int endLine= document.getLineOfOffset(region.getOffset() + region.getLength());
+        return new LineRange(startLine, endLine - startLine + 1);
+    }
+
+    /**
+     * Returns the visible area of the vertical ruler covered by the given line
+     * range.
+     *
+     * @param lineRange the line range
+     * @return the visible area
+     */
+    private Rectangle computeArea(ILineRange lineRange) {
+        try {
+            StyledText text= fSourceViewer.getTextWidget();
+            final int startLine= getWidgetLineNumber(lineRange.getStartLine());
+            int y= JFaceTextUtil.computeLineHeight(text, 0, startLine, startLine) - text.getTopPixel();
+            int height= JFaceTextUtil.computeLineHeight(text, startLine, startLine + lineRange.getNumberOfLines(), lineRange.getNumberOfLines());
+            Point size= fVerticalRulerInfo.getControl().getSize();
+            return new Rectangle(0, y, size.x, height);
+        } catch (BadLocationException x) {
+        }
+        return null;
+    }
+
+    /**
+     * Returns the number of the currently visible lines.
+     *
+     * @return the number of the currently visible lines
+     * @deprecated to avoid deprecation warning
+     */
+    private int computeNumberOfVisibleLines() {
+        // Hack to reduce amount of copied code.
+        return LineNumberRulerColumn.getVisibleLinesInViewport(fSourceViewer.getTextWidget());
+    }
+
+    /**
+     * Determines the hover to be used to display information based on the source of the
+     * mouse hover event. If <code>fVerticalRulerInfo</code> is not a composite ruler, the
+     * standard hover is returned.
+     *
+     * @param event the source of the mouse hover event
+     * @return the hover depending on <code>source</code>, or <code>fAnnotationHover</code> if none can be found.
+     * @since 3.0
+     */
+    private IAnnotationHover getHover(MouseEvent event) {
+        if (event is null || event.getSource() is null)
+            return fAnnotationHover;
+
+        if ( cast(CompositeRuler)fVerticalRulerInfo ) {
+            CompositeRuler comp= cast(CompositeRuler) fVerticalRulerInfo;
+            for (Iterator it= comp.getDecoratorIterator(); it.hasNext();) {
+                Object o= it.next();
+                if ( cast(IVerticalRulerInfoExtension)o  && cast(IVerticalRulerInfo)o ) {
+                    if ((cast(IVerticalRulerInfo) o).getControl() is event.getSource()) {
+                        IAnnotationHover hover= (cast(IVerticalRulerInfoExtension) o).getHover();
+                        if (hover !is null)
+                            return hover;
+                    }
+                }
+            }
+        }
+        return fAnnotationHover;
+    }
+
+    /**
+     * Returns the line of interest deduced from the mouse hover event.
+     *
+     * @param event a mouse hover event that triggered hovering
+     * @return the document model line number on which the hover event occurred or <code>-1</code> if there is no event
+     * @since 3.0
+     */
+    private int getHoverLine(MouseEvent event) {
+        return event is null ? -1 : fVerticalRulerInfo.toDocumentLineNumber(event.y);
+    }
+
+    /**
+     * Returns for the widget line number for the given document line number.
+     *
+     * @param line the absolute line number
+     * @return the line number relative to the viewer's visible region
+     * @throws BadLocationException if <code>line</code> is not valid in the viewer's document
+     */
+    private int getWidgetLineNumber(int line)  {
+        if ( cast(ITextViewerExtension5)fSourceViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) fSourceViewer;
+            return extension.modelLine2WidgetLine(line);
+        }
+
+        IRegion region= fSourceViewer.getVisibleRegion();
+        int firstLine= fSourceViewer.getDocument().getLineOfOffset(region.getOffset());
+        return line - firstLine;
+    }
+
+    /**
+     * Determines graphical area covered by the given line.
+     *
+     * @param line the number of the line in the viewer whose graphical extend in the vertical ruler must be computed
+     * @return the graphical extend of the given line
+     */
+    private Rectangle computeArea(int line) {
+        try {
+            StyledText text= fSourceViewer.getTextWidget();
+            int widgetLine= getWidgetLineNumber(line);
+            int y= JFaceTextUtil.computeLineHeight(text, 0, widgetLine, widgetLine) - text.getTopPixel();
+            Point size= fVerticalRulerInfo.getControl().getSize();
+            return new Rectangle(0, y, size.x, text.getLineHeight(text.getOffsetAtLine(widgetLine)));
+        } catch (IllegalArgumentException ex) {
+        } catch (BadLocationException ex) {
+        }
+        return null;
+    }
+
+    /**
+     * Returns the annotation hover for this hover manager.
+     *
+     * @return the annotation hover for this hover manager or <code>null</code> if none
+     * @since 2.1
+     */
+    protected IAnnotationHover getAnnotationHover() {
+        return fAnnotationHover;
+    }
+
+    /**
+     * Returns the source viewer for this hover manager.
+     *
+     * @return the source viewer for this hover manager
+     * @since 2.1
+     */
+    protected ISourceViewer getSourceViewer() {
+        return fSourceViewer;
+    }
+
+    /**
+     * Returns the vertical ruler info for this hover manager
+     *
+     * @return the vertical ruler info for this hover manager
+     * @since 2.1
+     */
+    protected IVerticalRulerInfo getVerticalRulerInfo() {
+        return fVerticalRulerInfo;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#computeSizeConstraints(org.eclipse.swt.widgets.Control, org.eclipse.swt.graphics.Rectangle, org.eclipse.jface.text.IInformationControl)
+     * @since 3.0
+     */
+    protected Point computeSizeConstraints(Control subjectControl, Rectangle subjectArea, IInformationControl informationControl) {
+
+        Point constraints= super.computeSizeConstraints(subjectControl, subjectArea, informationControl);
+
+        // make as big as text area, if possible
+        StyledText styledText= fSourceViewer.getTextWidget();
+        if (styledText !is null) {
+            Rectangle r= styledText.getClientArea();
+            if (r !is null) {
+                constraints.x= r.width;
+                constraints.y= r.height;
+            }
+        }
+
+        return constraints;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractInformationControlManager#computeLocation(org.eclipse.swt.graphics.Rectangle, org.eclipse.swt.graphics.Point, org.eclipse.jface.text.AbstractInformationControlManager.Anchor)
+     * @since 3.0
+     */
+    protected Point computeLocation(Rectangle subjectArea, Point controlSize, Anchor anchor) {
+        MouseEvent event= getHoverEvent();
+        IAnnotationHover hover= getHover(event);
+
+        bool allowMouseExit= false;
+        if ( cast(IAnnotationHoverExtension)hover ) {
+            IAnnotationHoverExtension extension= cast(IAnnotationHoverExtension) hover;
+            allowMouseExit= extension.canHandleMouseCursor();
+        }
+        bool hideOnMouseWheel= true;
+        if ( cast(IAnnotationHoverExtension2)hover ) {
+            IAnnotationHoverExtension2 extension= cast(IAnnotationHoverExtension2) hover;
+            hideOnMouseWheel= !extension.canHandleMouseWheel();
+        }
+        fHideOnMouseWheel= hideOnMouseWheel;
+
+        if (allowMouseExit) {
+            fAllowMouseExit= true;
+
+            Control subjectControl= getSubjectControl();
+            // return a location that just overlaps the annotation on the bar
+            if (anchor is AbstractInformationControlManager.ANCHOR_RIGHT)
+                return subjectControl.toDisplay(subjectArea.x - 4, subjectArea.y - 2);
+            else if (anchor is AbstractInformationControlManager.ANCHOR_LEFT)
+                return subjectControl.toDisplay(subjectArea.x + subjectArea.width - controlSize.x + 4, subjectArea.y - 2);
+        }
+
+        fAllowMouseExit= false;
+        return super.computeLocation(subjectArea, controlSize, anchor);
+    }
+
+    /**
+     * Returns the currently shown annotation hover or <code>null</code> if none
+     * hover is shown.
+     *
+     * @return the currently shown annotation hover or <code>null</code>
+     * @since 3.2
+     */
+    public IAnnotationHover getCurrentAnnotationHover() {
+        return fCurrentHover;
+    }
+
+    /**
+     * Returns an adapter that gives access to internal methods.
+     * <p>
+     * <strong>Note:</strong> This method is not intended to be referenced or overridden by clients.
+     * </p>
+     *
+     * @return the replaceable information control accessor
+     * @since 3.4
+     * @noreference This method is not intended to be referenced by clients.
+     * @nooverride This method is not intended to be re-implemented or extended by clients.
+     */
+    public InternalAccessor getInternalAccessor() {
+        return new class()  InternalAccessor {
+            public IInformationControl getCurrentInformationControl() {
+                return this.outer.superGetInternalAccessor().getCurrentInformationControl();
+            }
+
+            public void setInformationControlReplacer(InformationControlReplacer replacer) {
+                this.outer.superGetInternalAccessor().setInformationControlReplacer(replacer);
+            }
+
+            public InformationControlReplacer getInformationControlReplacer() {
+                return this.outer.superGetInternalAccessor().getInformationControlReplacer();
+            }
+
+            public bool canReplace(IInformationControl control) {
+                return this.outer.superGetInternalAccessor().canReplace(control);
+            }
+
+            public bool isReplaceInProgress() {
+                return this.outer.superGetInternalAccessor().isReplaceInProgress();
+            }
+
+            public void replaceInformationControl(bool takeFocus) {
+                this.outer.superGetInternalAccessor().replaceInformationControl(takeFocus);
+            }
+
+            public void cropToClosestMonitor(Rectangle bounds) {
+                this.outer.superGetInternalAccessor().cropToClosestMonitor(bounds);
+            }
+
+            public void setHoverEnrichMode(ITextViewerExtension8_EnrichMode mode) {
+                this.outer.superGetInternalAccessor().setHoverEnrichMode(mode);
+            }
+
+            public bool getAllowMouseExit() {
+                return fAllowMouseExit;
+            }
+        };
+    }
+    private InternalAccessor superGetInternalAccessor() {
+        return super.getInternalAccessor();
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationColumn.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.source.AnnotationColumn;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+import java.lang.all;
+
+
+/**
+ * @deprecated use {@link org.eclipse.jface.text.source.AnnotationRulerColumn} instead.
+ * @since 2.0
+ */
+public final class AnnotationColumn : AnnotationRulerColumn {
+
+    /**
+     * Creates a new <code>AnnotationColumn</code> of the given width.
+     *
+     * @param width the width of this column
+     * @deprecated
+     */
+    public this(int width) {
+        super(width);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationPainter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1803 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.AnnotationPainter;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+import tango.text.convert.Format;
+import tango.io.Stdout;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.TextStyle;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IPaintPositionManager;
+import org.eclipse.jface.text.IPainter;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextInputListener;
+import org.eclipse.jface.text.ITextPresentationListener;
+import org.eclipse.jface.text.ITextViewerExtension2;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.JFaceTextUtil;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextPresentation;
+
+
+    /**
+     * A drawing strategy draws the decoration for an annotation onto the text widget.
+     *
+     * @since 3.0
+     */
+    public interface IDrawingStrategy {
+        /**
+         * Draws a decoration for an annotation onto the specified GC at the given text range. There
+         * are two different invocation modes of the <code>draw</code> method:
+         * <ul>
+         * <li><strong>drawing mode:</strong> the passed GC is the graphics context of a paint
+         * event occurring on the text widget. The strategy should draw the decoration onto the
+         * graphics context, such that the decoration appears at the given range in the text
+         * widget.</li>
+         * <li><strong>clearing mode:</strong> the passed GC is <code>null</code>. In this case
+         * the strategy must invalidate enough of the text widget's client area to cover any
+         * decoration drawn in drawing mode. This can usually be accomplished by calling
+         * {@linkplain StyledText#redrawRange(int, int, bool) textWidget.redrawRange(offset, length, true)}.</li>
+         * </ul>
+         *
+         * @param annotation the annotation to be drawn
+         * @param gc the graphics context, <code>null</code> when in clearing mode
+         * @param textWidget the text widget to draw on
+         * @param offset the offset of the line
+         * @param length the length of the line
+         * @param color the color of the line
+         */
+        void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color);
+    }
+
+    alias IDrawingStrategy AnnotationPainter_IDrawingStrategy;
+
+/**
+ * Paints decorations for annotations provided by an annotation model and/or
+ * highlights them in the associated source viewer.
+ * <p>
+ * The annotation painter can be configured with drawing strategies. A drawing
+ * strategy defines the visual presentation of a particular type of annotation
+ * decoration.</p>
+ * <p>
+ * Clients usually instantiate and configure objects of this class.</p>
+ *
+ * @since 2.1
+ */
+public class AnnotationPainter : IPainter, PaintListener, IAnnotationModelListener, IAnnotationModelListenerExtension, ITextPresentationListener {
+
+    /**
+     * Squiggles drawing strategy.
+     *
+     * @since 3.0
+     * @deprecated As of 3.4, replaced by {@link AnnotationPainter.UnderlineStrategy}
+     */
+    public static class SquigglesStrategy : IDrawingStrategy {
+
+        /*
+         * @see org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy#draw(org.eclipse.jface.text.source.Annotation, org.eclipse.swt.graphics.GC, org.eclipse.swt.custom.StyledText, int, int, org.eclipse.swt.graphics.Color)
+         * @since 3.0
+         */
+        public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) {
+            if (gc !is null) {
+
+                if (length < 1)
+                    return;
+
+                Point left= textWidget.getLocationAtOffset(offset);
+                Point right= textWidget.getLocationAtOffset(offset + length);
+                Rectangle rect= textWidget.getTextBounds(offset, offset + length - 1);
+                left.x= rect.x;
+                right.x= rect.x + rect.width;
+
+                int[] polyline= computePolyline(left, right, textWidget.getBaseline(offset), textWidget.getLineHeight(offset));
+
+                gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
+                gc.setLineStyle(SWT.LINE_SOLID);
+                gc.setForeground(color);
+                gc.drawPolyline(polyline);
+
+            } else {
+                textWidget.redrawRange(offset, length, true);
+            }
+        }
+
+        /**
+         * Computes an array of alternating x and y values which are the corners of the squiggly line of the
+         * given height between the given end points.
+         *
+         * @param left the left end point
+         * @param right the right end point
+         * @param baseline the font's baseline
+         * @param lineHeight the height of the line
+         * @return the array of alternating x and y values which are the corners of the squiggly line
+         */
+        private int[] computePolyline(Point left, Point right, int baseline, int lineHeight) {
+
+            final int WIDTH= 4; // must be even
+            final int HEIGHT= 2; // can be any number
+
+            int peaks= (right.x - left.x) / WIDTH;
+            if (peaks is 0 && right.x - left.x > 2)
+                peaks= 1;
+
+            int leftX= left.x;
+
+            // compute (number of point) * 2
+            int length_= ((2 * peaks) + 1) * 2;
+            if (length_ < 0)
+                return new int[0];
+
+            int[] coordinates= new int[length_];
+
+            // cache peeks' y-coordinates
+            int top= left.y + Math.min(baseline + 1, lineHeight - HEIGHT - 1);
+            int bottom= top + HEIGHT;
+
+            // populate array with peek coordinates
+            for (int i= 0; i < peaks; i++) {
+                int index= 4 * i;
+                coordinates[index]= leftX + (WIDTH * i);
+                coordinates[index+1]= bottom;
+                coordinates[index+2]= coordinates[index] + WIDTH/2;
+                coordinates[index+3]= top;
+            }
+
+            // the last down flank is missing
+            coordinates[length_-2]= Math.min(Math.max(0, right.x - 1), left.x + (WIDTH * peaks));
+            coordinates[length_-1]= bottom;
+
+            return coordinates;
+        }
+    }
+
+    /**
+     * Drawing strategy that does nothing.
+     *
+     * @since 3.0
+     */
+    public static final class NullStrategy : IDrawingStrategy {
+
+        /*
+         * @see org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy#draw(org.eclipse.jface.text.source.Annotation, org.eclipse.swt.graphics.GC, org.eclipse.swt.custom.StyledText, int, int, org.eclipse.swt.graphics.Color)
+         * @since 3.0
+         */
+        public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) {
+            // do nothing
+        }
+    }
+
+
+    /**
+     * A text style painting strategy draws the decoration for an annotation
+     * onto the text widget by applying a {@link TextStyle} on a given
+     * {@link StyleRange}.
+     *
+     * @since 3.4
+     */
+    public interface ITextStyleStrategy {
+
+        /**
+         * Applies a text style on the given <code>StyleRange</code>.
+         *
+         * @param styleRange the style range on which to apply the text style
+         * @param annotationColor the color of the annotation
+         */
+        void applyTextStyle(StyleRange styleRange, Color annotationColor);
+    }
+
+
+    /**
+     * @since 3.4
+     */
+    public static final class HighlightingStrategy : ITextStyleStrategy {
+        public void applyTextStyle(StyleRange styleRange, Color annotationColor) {
+            styleRange.background= annotationColor;
+        }
+    }
+
+
+    /**
+     * Underline text style strategy.
+     *
+     * @since 3.4
+     */
+    public static final class UnderlineStrategy : ITextStyleStrategy {
+
+        int fUnderlineStyle;
+
+        public this(int style) {
+            Assert.isLegal(style is SWT.UNDERLINE_SINGLE || style is SWT.UNDERLINE_DOUBLE || style is SWT.UNDERLINE_ERROR || style is SWT.UNDERLINE_SQUIGGLE);
+            fUnderlineStyle= style;
+        }
+
+        public void applyTextStyle(StyleRange styleRange, Color annotationColor) {
+            styleRange.underline= true;
+            styleRange.underlineStyle= fUnderlineStyle;
+            styleRange.underlineColor= annotationColor;
+        }
+    }
+
+
+    /**
+     * Box text style strategy.
+     *
+     * @since 3.4
+     */
+    public static final class BoxStrategy : ITextStyleStrategy {
+
+        int fBorderStyle;
+
+        public this(int style) {
+            Assert.isLegal(style is SWT.BORDER_DASH || style is SWT.BORDER_DASH || style is SWT.BORDER_SOLID);
+            fBorderStyle= style;
+        }
+
+        public void applyTextStyle(StyleRange styleRange, Color annotationColor) {
+            styleRange.borderStyle= fBorderStyle;
+            styleRange.borderColor= annotationColor;
+        }
+    }
+
+
+    /**
+     * Implementation of <code>IRegion</code> that can be reused
+     * by setting the offset and the length.
+     */
+    private static class ReusableRegion : Position , IRegion {
+        public override int getOffset(){
+            return super.getOffset();
+        }
+        public override int getLength(){
+            return super.getLength();
+        }
+    }
+
+    /**
+     * Tells whether this class is in debug mode.
+     * @since 3.0
+     */
+    private static bool DEBUG_;
+    private static bool DEBUG_init = false;
+    private static bool DEBUG(){
+        if( !DEBUG_init ){
+            DEBUG_init = true;
+            DEBUG_ = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.jface.text/debug/AnnotationPainter"));  //$NON-NLS-1$//$NON-NLS-2$
+        }
+        return DEBUG_;
+    }
+
+    /**
+     * The squiggly painter strategy.
+     * @since 3.0
+     */
+    private static IDrawingStrategy SQUIGGLES_STRATEGY_;
+    private static IDrawingStrategy SQUIGGLES_STRATEGY(){
+        if( SQUIGGLES_STRATEGY_ is null ){
+            synchronized( AnnotationPainter.classinfo ){
+                if( SQUIGGLES_STRATEGY_ is null ){
+                    SQUIGGLES_STRATEGY_ = new SquigglesStrategy();
+                }
+            }
+        }
+        return SQUIGGLES_STRATEGY_;
+    }
+
+
+    /**
+     * This strategy is used to mark the <code>null</code> value in the chache
+     * maps.
+     *
+     * @since 3.4
+     */
+    private static IDrawingStrategy NULL_STRATEGY_;
+    private static IDrawingStrategy NULL_STRATEGY(){
+        if( NULL_STRATEGY_ is null ){
+            synchronized( AnnotationPainter.classinfo ){
+                if( NULL_STRATEGY_ is null ){
+                    NULL_STRATEGY_= new NullStrategy();
+                }
+            }
+        }
+        return NULL_STRATEGY_;
+    }
+    /**
+     * The squiggles painter id.
+     * @since 3.0
+     */
+    private static Object SQUIGGLES_;
+    private static Object SQUIGGLES(){
+        if( SQUIGGLES_ is null ){
+            synchronized( AnnotationPainter.classinfo ){
+                if( SQUIGGLES_ is null ){
+                    SQUIGGLES_= new Object();
+                }
+            }
+        }
+        return SQUIGGLES_;
+    }
+    /**
+     * The squiggly painter strategy.
+     *
+     * @since 3.4
+     */
+    private static ITextStyleStrategy HIGHLIGHTING_STRATEGY_;
+    private static ITextStyleStrategy HIGHLIGHTING_STRATEGY(){
+        if( HIGHLIGHTING_STRATEGY_ is null ){
+            synchronized( AnnotationPainter.classinfo ){
+                if( HIGHLIGHTING_STRATEGY_ is null ){
+                    HIGHLIGHTING_STRATEGY_= new HighlightingStrategy();
+                }
+            }
+        }
+        return HIGHLIGHTING_STRATEGY_;
+    }
+
+    /**
+     * The highlighting text style strategy id.
+     *
+     * @since 3.4
+     */
+    private static Object HIGHLIGHTING_;
+    private static Object HIGHLIGHTING(){
+        if( HIGHLIGHTING_ is null ){
+            synchronized( AnnotationPainter.classinfo ){
+                if( HIGHLIGHTING_ is null ){
+                    HIGHLIGHTING_= new Object();
+                }
+            }
+        }
+        return HIGHLIGHTING_;
+    }
+
+    /**
+     * The presentation information (decoration) for an annotation.  Each such
+     * object represents one decoration drawn on the text area, such as squiggly lines
+     * and underlines.
+     */
+    private static class Decoration {
+        /** The position of this decoration */
+        private Position fPosition;
+        /** The color of this decoration */
+        private Color fColor;
+        /**
+         * The annotation's layer
+         * @since 3.0
+         */
+        private int fLayer;
+        /**
+         * The painting strategy for this decoration.
+         * @since 3.0
+         */
+        private Object fPaintingStrategy;
+    }
+
+
+    /** Indicates whether this painter is active */
+    private bool fIsActive= false;
+    /** Indicates whether this painter is managing decorations */
+    private bool fIsPainting= false;
+    /** Indicates whether this painter is setting its annotation model */
+    private /+volatile+/ bool  fIsSettingModel= false;
+    /** The associated source viewer */
+    private ISourceViewer fSourceViewer;
+    /** The cached widget of the source viewer */
+    private StyledText fTextWidget;
+    /** The annotation model providing the annotations to be drawn */
+    private IAnnotationModel fModel;
+    /** The annotation access */
+    private IAnnotationAccess fAnnotationAccess;
+    /**
+     * The map with decorations
+     * @since 3.0
+     */
+    private Map fDecorationsMap; // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=50767
+    /**
+     * The map with of highlighted decorations.
+     * @since 3.0
+     */
+    private Map fHighlightedDecorationsMap;
+    /**
+     * Mutex for highlighted decorations map.
+     * @since 3.0
+     */
+    private Object fDecorationMapLock;
+    /**
+     * Mutex for for decorations map.
+     * @since 3.0
+     */
+    private Object fHighlightedDecorationsMapLock;
+    /**
+     * Maps an annotation type to its registered color.
+     *
+     * @see #setAnnotationTypeColor(Object, Color)
+     */
+    private Map fAnnotationType2Color;
+
+    /**
+     * Cache that maps the annotation type to its color.
+     * @since 3.4
+     */
+    private Map fCachedAnnotationType2Color;
+    /**
+     * The range in which the current highlight annotations can be found.
+     * @since 3.0
+     */
+    private Position fCurrentHighlightAnnotationRange= null;
+    /**
+     * The range in which all added, removed and changed highlight
+     * annotations can be found since the last world change.
+     * @since 3.0
+     */
+    private Position fTotalHighlightAnnotationRange= null;
+    /**
+     * The range in which the currently drawn annotations can be found.
+     * @since 3.3
+     */
+    private Position fCurrentDrawRange= null;
+    /**
+     * The range in which all added, removed and changed drawn
+     * annotations can be found since the last world change.
+     * @since 3.3
+     */
+    private Position fTotalDrawRange= null;
+    /**
+     * The text input listener.
+     * @since 3.0
+     */
+    private ITextInputListener fTextInputListener;
+    /**
+     * Flag which tells that a new document input is currently being set.
+     * @since 3.0
+     */
+    private bool fInputDocumentAboutToBeChanged;
+    /**
+     * Maps annotation types to painting strategy identifiers.
+     *
+     * @see #addAnnotationType(Object, Object)
+     * @since 3.0
+     */
+    private Map fAnnotationType2PaintingStrategyId;
+    /**
+     * Maps annotation types to painting strategy identifiers.
+     * @since 3.4
+     */
+    private Map fCachedAnnotationType2PaintingStrategy;
+
+    /**
+     * Maps painting strategy identifiers to painting strategies.
+     *
+     * @since 3.0
+     */
+    private Map fPaintingStrategyId2PaintingStrategy;
+
+    /**
+     * Reuse this region for performance reasons.
+     * @since 3.3
+     */
+    private ReusableRegion fReusableRegion;
+
+    /**
+     * Creates a new annotation painter for the given source viewer and with the
+     * given annotation access. The painter is not initialized, i.e. no
+     * annotation types are configured to be painted.
+     *
+     * @param sourceViewer the source viewer for this painter
+     * @param access the annotation access for this painter
+     */
+    public this(ISourceViewer sourceViewer, IAnnotationAccess access) {
+        fDecorationsMap= new HashMap(); // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=50767
+        fHighlightedDecorationsMap= new HashMap(); // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=50767
+        fDecorationMapLock= new Object();
+        fHighlightedDecorationsMapLock= new Object();
+        fAnnotationType2Color= new HashMap();
+        fCachedAnnotationType2Color= new HashMap();
+        fReusableRegion= new ReusableRegion();
+        fAnnotationType2PaintingStrategyId= new HashMap();
+        fCachedAnnotationType2PaintingStrategy= new HashMap();
+        fPaintingStrategyId2PaintingStrategy= new HashMap();
+
+        fSourceViewer= sourceViewer;
+        fAnnotationAccess= access;
+        fTextWidget= sourceViewer.getTextWidget();
+
+        // default drawing strategies: squiggles were the only decoration style before version 3.0
+        fPaintingStrategyId2PaintingStrategy.put(SQUIGGLES, cast(Object)SQUIGGLES_STRATEGY);
+        fPaintingStrategyId2PaintingStrategy.put(HIGHLIGHTING, cast(Object)HIGHLIGHTING_STRATEGY);
+    }
+
+    /**
+     * Returns whether this painter has to draw any squiggles.
+     *
+     * @return <code>true</code> if there are squiggles to be drawn, <code>false</code> otherwise
+     */
+    private bool hasDecorations() {
+        synchronized (fDecorationMapLock) {
+            return !fDecorationsMap.isEmpty();
+        }
+    }
+
+    /**
+     * Enables painting. This painter registers a paint listener with the
+     * source viewer's widget.
+     */
+    private void enablePainting() {
+        if (!fIsPainting && hasDecorations()) {
+            fIsPainting= true;
+            fTextWidget.addPaintListener(this);
+            handleDrawRequest(null);
+        }
+    }
+
+    /**
+     * Disables painting, if is has previously been enabled. Removes
+     * any paint listeners registered with the source viewer's widget.
+     *
+     * @param redraw <code>true</code> if the widget should be redrawn after disabling
+     */
+    private void disablePainting(bool redraw) {
+        if (fIsPainting) {
+            fIsPainting= false;
+            fTextWidget.removePaintListener(this);
+            if (redraw && hasDecorations())
+                handleDrawRequest(null);
+        }
+    }
+
+    /**
+     * Sets the annotation model for this painter. Registers this painter
+     * as listener of the give model, if the model is not <code>null</code>.
+     *
+     * @param model the annotation model
+     */
+    private void setModel(IAnnotationModel model) {
+        if (fModel !is model) {
+            if (fModel !is null)
+                fModel.removeAnnotationModelListener(this);
+            fModel= model;
+            if (fModel !is null) {
+                try {
+                    fIsSettingModel= true;
+                    fModel.addAnnotationModelListener(this);
+                } finally {
+                    fIsSettingModel= false;
+                }
+            }
+        }
+    }
+
+    /**
+     * Updates the set of decorations based on the current state of
+     * the painter's annotation model.
+     *
+     * @param event the annotation model event
+     */
+    private void catchupWithModel(AnnotationModelEvent event) {
+
+        synchronized (fDecorationMapLock) {
+            if (fDecorationsMap is null)
+                return;
+        }
+
+        IRegion clippingRegion= computeClippingRegion(null, true);
+        IDocument document= fSourceViewer.getDocument();
+
+        int highlightAnnotationRangeStart= Integer.MAX_VALUE;
+        int highlightAnnotationRangeEnd= -1;
+
+        int drawRangeStart= Integer.MAX_VALUE;
+        int drawRangeEnd= -1;
+
+        if (fModel !is null) {
+
+            Map decorationsMap;
+            Map highlightedDecorationsMap;
+
+            // Clone decoration maps
+            synchronized (fDecorationMapLock) {
+                decorationsMap= new HashMap(fDecorationsMap);
+            }
+            synchronized (fHighlightedDecorationsMapLock) {
+                highlightedDecorationsMap= new HashMap(fHighlightedDecorationsMap);
+            }
+
+            bool isWorldChange= false;
+
+            Iterator e;
+            if (event is null || event.isWorldChange()) {
+                isWorldChange= true;
+
+                if (DEBUG && event is null)
+                    System.out_.println("AP: INTERNAL CHANGE"); //$NON-NLS-1$
+
+                Iterator iter= decorationsMap.entrySet().iterator();
+                while (iter.hasNext()) {
+                    Map.Entry entry= cast(Map.Entry)iter.next();
+                    Annotation annotation= cast(Annotation)entry.getKey();
+                    Decoration decoration= cast(Decoration)entry.getValue();
+                    drawDecoration(decoration, null, annotation, clippingRegion, document);
+                }
+
+                decorationsMap.clear();
+
+                highlightedDecorationsMap.clear();
+
+                e= fModel.getAnnotationIterator();
+
+
+            } else {
+
+                // Remove annotations
+                Annotation[] removedAnnotations= event.getRemovedAnnotations();
+                for (int i=0, length= removedAnnotations.length; i < length; i++) {
+                    Annotation annotation= removedAnnotations[i];
+                    Decoration decoration= cast(Decoration)highlightedDecorationsMap.remove(annotation);
+                    if (decoration !is null) {
+                        Position position= decoration.fPosition;
+                        if (position !is null) {
+                            highlightAnnotationRangeStart= Math.min(highlightAnnotationRangeStart, position.offset);
+                            highlightAnnotationRangeEnd= Math.max(highlightAnnotationRangeEnd, position.offset + position.length);
+                        }
+                    }
+                    decoration= cast(Decoration)decorationsMap.remove(annotation);
+                    if (decoration !is null) {
+                        drawDecoration(decoration, null, annotation, clippingRegion, document);
+                        Position position= decoration.fPosition;
+                        if (position !is null) {
+                            drawRangeStart= Math.min(drawRangeStart, position.offset);
+                            drawRangeEnd= Math.max(drawRangeEnd, position.offset + position.length);
+                        }
+                    }
+
+                }
+
+                // Update existing annotations
+                Annotation[] changedAnnotations= event.getChangedAnnotations();
+                for (int i=0, length= changedAnnotations.length; i < length; i++) {
+                    Annotation annotation= changedAnnotations[i];
+
+                    bool isHighlighting= false;
+
+                    Decoration decoration= cast(Decoration)highlightedDecorationsMap.get(annotation);
+
+                    if (decoration !is null) {
+                        isHighlighting= true;
+                        // The call below updates the decoration - no need to create new decoration
+                        decoration= getDecoration(annotation, decoration);
+                        if (decoration is null)
+                            highlightedDecorationsMap.remove(annotation);
+                    } else {
+                        decoration= getDecoration(annotation, decoration);
+                        if (decoration !is null && cast(ITextStyleStrategy)decoration.fPaintingStrategy ) {
+                            highlightedDecorationsMap.put(annotation, decoration);
+                            isHighlighting= true;
+                        }
+                    }
+
+                    bool usesDrawingStrategy= !isHighlighting && decoration !is null;
+
+                    Position position= null;
+                    if (decoration is null)
+                        position= fModel.getPosition(annotation);
+                    else
+                        position= decoration.fPosition;
+
+                    if (position !is null && !position.isDeleted()) {
+                        if (isHighlighting) {
+                            highlightAnnotationRangeStart= Math.min(highlightAnnotationRangeStart, position.offset);
+                            highlightAnnotationRangeEnd= Math.max(highlightAnnotationRangeEnd, position.offset + position.length);
+                        }
+                        if (usesDrawingStrategy) {
+                            drawRangeStart= Math.min(drawRangeStart, position.offset);
+                            drawRangeEnd= Math.max(drawRangeEnd, position.offset + position.length);
+                        }
+                    } else {
+                        highlightedDecorationsMap.remove(annotation);
+                    }
+
+                    if (usesDrawingStrategy) {
+                        Decoration oldDecoration= cast(Decoration)decorationsMap.get(annotation);
+                        if (oldDecoration !is null) {
+                            drawDecoration(oldDecoration, null, annotation, clippingRegion, document);
+
+                        if (decoration !is null)
+                            decorationsMap.put(annotation, decoration);
+                        else if (oldDecoration !is null)
+                            decorationsMap.remove(annotation);
+                        }
+                    }
+                }
+
+                e= Arrays.asList(event.getAddedAnnotations()).iterator();
+            }
+
+            // Add new annotations
+            while (e.hasNext()) {
+                Annotation annotation= cast(Annotation) e.next();
+                Decoration pp= getDecoration(annotation, null);
+                if (pp !is null) {
+                    if (cast(IDrawingStrategy)pp.fPaintingStrategy ) {
+                        decorationsMap.put(annotation, pp);
+                        drawRangeStart= Math.min(drawRangeStart, pp.fPosition.offset);
+                        drawRangeEnd= Math.max(drawRangeEnd, pp.fPosition.offset + pp.fPosition.length);
+                    } else if (cast(ITextStyleStrategy)pp.fPaintingStrategy ) {
+                        highlightedDecorationsMap.put(annotation, pp);
+                        highlightAnnotationRangeStart= Math.min(highlightAnnotationRangeStart, pp.fPosition.offset);
+                        highlightAnnotationRangeEnd= Math.max(highlightAnnotationRangeEnd, pp.fPosition.offset + pp.fPosition.length);
+                    }
+
+                }
+            }
+
+            synchronized (fDecorationMapLock) {
+                fDecorationsMap= decorationsMap;
+                updateDrawRanges(drawRangeStart, drawRangeEnd, isWorldChange);
+            }
+
+            synchronized (fHighlightedDecorationsMapLock) {
+                fHighlightedDecorationsMap= highlightedDecorationsMap;
+                updateHighlightRanges(highlightAnnotationRangeStart, highlightAnnotationRangeEnd, isWorldChange);
+            }
+        } else {
+            // annotation model is null -> clear all
+            synchronized (fDecorationMapLock) {
+                fDecorationsMap.clear();
+            }
+            synchronized (fHighlightedDecorationsMapLock) {
+                fHighlightedDecorationsMap.clear();
+            }
+        }
+    }
+
+    /**
+     * Updates the remembered highlight ranges.
+     *
+     * @param highlightAnnotationRangeStart the start of the range
+     * @param highlightAnnotationRangeEnd   the end of the range
+     * @param isWorldChange                 tells whether the range belongs to a annotation model event reporting a world change
+     * @since 3.0
+     */
+    private void updateHighlightRanges(int highlightAnnotationRangeStart, int highlightAnnotationRangeEnd, bool isWorldChange) {
+        if (highlightAnnotationRangeStart !is Integer.MAX_VALUE) {
+
+            int maxRangeStart= highlightAnnotationRangeStart;
+            int maxRangeEnd= highlightAnnotationRangeEnd;
+
+            if (fTotalHighlightAnnotationRange !is null) {
+                maxRangeStart= Math.min(maxRangeStart, fTotalHighlightAnnotationRange.offset);
+                maxRangeEnd= Math.max(maxRangeEnd, fTotalHighlightAnnotationRange.offset + fTotalHighlightAnnotationRange.length);
+            }
+
+            if (fTotalHighlightAnnotationRange is null)
+                fTotalHighlightAnnotationRange= new Position(0);
+            if (fCurrentHighlightAnnotationRange is null)
+                fCurrentHighlightAnnotationRange= new Position(0);
+
+            if (isWorldChange) {
+                fTotalHighlightAnnotationRange.offset= highlightAnnotationRangeStart;
+                fTotalHighlightAnnotationRange.length= highlightAnnotationRangeEnd - highlightAnnotationRangeStart;
+                fCurrentHighlightAnnotationRange.offset= maxRangeStart;
+                fCurrentHighlightAnnotationRange.length= maxRangeEnd - maxRangeStart;
+            } else {
+                fTotalHighlightAnnotationRange.offset= maxRangeStart;
+                fTotalHighlightAnnotationRange.length= maxRangeEnd - maxRangeStart;
+                fCurrentHighlightAnnotationRange.offset=highlightAnnotationRangeStart;
+                fCurrentHighlightAnnotationRange.length= highlightAnnotationRangeEnd - highlightAnnotationRangeStart;
+            }
+        } else {
+            if (isWorldChange) {
+                fCurrentHighlightAnnotationRange= fTotalHighlightAnnotationRange;
+                fTotalHighlightAnnotationRange= null;
+            } else {
+                fCurrentHighlightAnnotationRange= null;
+            }
+        }
+
+        adaptToDocumentLength(fCurrentHighlightAnnotationRange);
+        adaptToDocumentLength(fTotalHighlightAnnotationRange);
+    }
+
+    /**
+     * Updates the remembered decoration ranges.
+     *
+     * @param drawRangeStart    the start of the range
+     * @param drawRangeEnd      the end of the range
+     * @param isWorldChange     tells whether the range belongs to a annotation model event reporting a world change
+     * @since 3.3
+     */
+    private void updateDrawRanges(int drawRangeStart, int drawRangeEnd, bool isWorldChange) {
+        if (drawRangeStart !is Integer.MAX_VALUE) {
+
+            int maxRangeStart= drawRangeStart;
+            int maxRangeEnd= drawRangeEnd;
+
+            if (fTotalDrawRange !is null) {
+                maxRangeStart= Math.min(maxRangeStart, fTotalDrawRange.offset);
+                maxRangeEnd= Math.max(maxRangeEnd, fTotalDrawRange.offset + fTotalDrawRange.length);
+            }
+
+            if (fTotalDrawRange is null)
+                fTotalDrawRange= new Position(0);
+            if (fCurrentDrawRange is null)
+                fCurrentDrawRange= new Position(0);
+
+            if (isWorldChange) {
+                fTotalDrawRange.offset= drawRangeStart;
+                fTotalDrawRange.length= drawRangeEnd - drawRangeStart;
+                fCurrentDrawRange.offset= maxRangeStart;
+                fCurrentDrawRange.length= maxRangeEnd - maxRangeStart;
+            } else {
+                fTotalDrawRange.offset= maxRangeStart;
+                fTotalDrawRange.length= maxRangeEnd - maxRangeStart;
+                fCurrentDrawRange.offset=drawRangeStart;
+                fCurrentDrawRange.length= drawRangeEnd - drawRangeStart;
+            }
+        } else {
+            if (isWorldChange) {
+                fCurrentDrawRange= fTotalDrawRange;
+                fTotalDrawRange= null;
+            } else {
+                fCurrentDrawRange= null;
+            }
+        }
+
+        adaptToDocumentLength(fCurrentDrawRange);
+        adaptToDocumentLength(fTotalDrawRange);
+    }
+
+    /**
+     * Adapts the given position to the document length.
+     *
+     * @param position the position to adapt
+     * @since 3.0
+     */
+    private void adaptToDocumentLength(Position position) {
+        if (position is null)
+            return;
+
+        int length= fSourceViewer.getDocument().getLength();
+        position.offset= Math.min(position.offset, length);
+        position.length= Math.min(position.length, length - position.offset);
+    }
+
+    /**
+     * Returns a decoration for the given annotation if this
+     * annotation is valid and shown by this painter.
+     *
+     * @param annotation            the annotation
+     * @param decoration            the decoration to be adapted and returned or <code>null</code> if a new one must be created
+     * @return the decoration or <code>null</code> if there's no valid one
+     * @since 3.0
+     */
+    private Decoration getDecoration(Annotation annotation, Decoration decoration) {
+
+        if (annotation.isMarkedDeleted())
+            return null;
+
+        String type= annotation.getType();
+
+        Object paintingStrategy= getPaintingStrategy(type);
+        if (paintingStrategy is null || cast(NullStrategy)paintingStrategy )
+            return null;
+
+        Color color= getColor(stringcast(type));
+        if (color is null)
+            return null;
+
+        Position position= fModel.getPosition(annotation);
+        if (position is null || position.isDeleted())
+            return null;
+
+        if (decoration is null)
+            decoration= new Decoration();
+
+        decoration.fPosition= position;
+        decoration.fColor= color;
+        if ( cast(IAnnotationAccessExtension)fAnnotationAccess ) {
+            IAnnotationAccessExtension extension= cast(IAnnotationAccessExtension) fAnnotationAccess;
+            decoration.fLayer= extension.getLayer(annotation);
+        } else {
+            decoration.fLayer= IAnnotationAccessExtension.DEFAULT_LAYER;
+        }
+
+        decoration.fPaintingStrategy= paintingStrategy;
+
+        return decoration;
+    }
+
+    /**
+     * Returns the painting strategy for the given annotation.
+     *
+     * @param type the annotation type
+     * @return the annotation painter
+     * @since 3.0
+     */
+    private Object getPaintingStrategy(String type) {
+        Object strategy= fCachedAnnotationType2PaintingStrategy.get(type);
+        if (strategy !is null)
+            return strategy;
+
+        strategy= fPaintingStrategyId2PaintingStrategy.get(fAnnotationType2PaintingStrategyId.get(type));
+        if (strategy !is null) {
+            fCachedAnnotationType2PaintingStrategy.put(type, strategy);
+            return strategy;
+        }
+
+        if ( cast(IAnnotationAccessExtension)fAnnotationAccess ) {
+            IAnnotationAccessExtension ext = cast(IAnnotationAccessExtension) fAnnotationAccess;
+            Object[] sts = ext.getSupertypes(stringcast(type));
+            for (int i= 0; i < sts.length; i++) {
+                strategy= fPaintingStrategyId2PaintingStrategy.get(fAnnotationType2PaintingStrategyId.get(sts[i]));
+                if (strategy !is null) {
+                    fCachedAnnotationType2PaintingStrategy.put(type, strategy);
+                    return strategy;
+                }
+            }
+        }
+
+        fCachedAnnotationType2PaintingStrategy.put(type, cast(Object)NULL_STRATEGY);
+        return null;
+
+    }
+
+    /**
+     * Returns the color for the given annotation type
+     *
+     * @param annotationType the annotation type
+     * @return the color
+     * @since 3.0
+     */
+    private Color getColor(Object annotationType) {
+        Color color= cast(Color)fCachedAnnotationType2Color.get(annotationType);
+        if (color !is null)
+            return color;
+
+        color= cast(Color)fAnnotationType2Color.get(annotationType);
+        if (color !is null) {
+            fCachedAnnotationType2Color.put(annotationType, color);
+            return color;
+        }
+
+        if ( cast(IAnnotationAccessExtension)fAnnotationAccess ) {
+            IAnnotationAccessExtension extension= cast(IAnnotationAccessExtension) fAnnotationAccess;
+            Object[] superTypes= extension.getSupertypes(annotationType);
+            if (superTypes !is null) {
+                for (int i= 0; i < superTypes.length; i++) {
+                    color= cast(Color)fAnnotationType2Color.get(superTypes[i]);
+                    if (color !is null) {
+                        fCachedAnnotationType2Color.put(annotationType, color);
+                        return color;
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Recomputes the squiggles to be drawn and redraws them.
+     *
+     * @param event the annotation model event
+     * @since 3.0
+     */
+    private void updatePainting(AnnotationModelEvent event) {
+        disablePainting(event is null);
+
+        catchupWithModel(event);
+
+        if (!fInputDocumentAboutToBeChanged)
+            invalidateTextPresentation();
+
+        enablePainting();
+    }
+
+    private void invalidateTextPresentation() {
+        IRegion r= null;
+        synchronized (fHighlightedDecorationsMapLock) {
+            if (fCurrentHighlightAnnotationRange !is null)
+                r= new Region(fCurrentHighlightAnnotationRange.getOffset(), fCurrentHighlightAnnotationRange.getLength());
+        }
+        if (r is null)
+            return;
+
+        if ( cast(ITextViewerExtension2)fSourceViewer ) {
+            if (DEBUG)
+                System.out_.println(Format("AP: invalidating offset: {}, length= {}", r.getOffset(), r.getLength())); //$NON-NLS-1$ //$NON-NLS-2$
+
+            (cast(ITextViewerExtension2)fSourceViewer).invalidateTextPresentation(r.getOffset(), r.getLength());
+
+        } else {
+            fSourceViewer.invalidateTextPresentation();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextPresentationListener#applyTextPresentation(org.eclipse.jface.text.TextPresentation)
+     * @since 3.0
+     */
+    public void applyTextPresentation(TextPresentation tp) {
+        Set decorations;
+
+        synchronized (fHighlightedDecorationsMapLock) {
+            if (fHighlightedDecorationsMap is null || fHighlightedDecorationsMap.isEmpty())
+                return;
+
+            decorations= new HashSet(fHighlightedDecorationsMap.entrySet());
+        }
+
+        IRegion region= tp.getExtent();
+
+        if (DEBUG)
+            System.out_.println(Format("AP: applying text presentation offset: {}, length= {}", region.getOffset(), region.getLength())); //$NON-NLS-1$ //$NON-NLS-2$
+
+        for (int layer= 0, maxLayer= 1; layer < maxLayer; layer++) {
+
+            for (Iterator iter= decorations.iterator(); iter.hasNext();) {
+                Map.Entry entry= cast(Map.Entry)iter.next();
+
+                Annotation a= cast(Annotation)entry.getKey();
+                if (a.isMarkedDeleted())
+                    continue;
+
+                Decoration pp = cast(Decoration)entry.getValue();
+
+                maxLayer= Math.max(maxLayer, pp.fLayer + 1); // dynamically update layer maximum
+                if (pp.fLayer !is layer) // wrong layer: skip annotation
+                    continue;
+
+                Position p= pp.fPosition;
+                if ( cast(ITextViewerExtension5)fSourceViewer ) {
+                    ITextViewerExtension5 extension3= cast(ITextViewerExtension5) fSourceViewer;
+                    if (null is extension3.modelRange2WidgetRange(new Region(p.getOffset(), p.getLength())))
+                        continue;
+                } else if (!fSourceViewer.overlapsWithVisibleRegion(p.offset, p.length)) {
+                    continue;
+                }
+
+                int regionEnd= region.getOffset() + region.getLength();
+                int pEnd= p.getOffset() + p.getLength();
+                if (pEnd >= region.getOffset() && regionEnd > p.getOffset()) {
+                    int start= Math.max(p.getOffset(), region.getOffset());
+                    int end= Math.min(regionEnd, pEnd);
+                    int length= Math.max(end - start, 0);
+                    StyleRange styleRange= new StyleRange(start, length, null, null);
+                    (cast(ITextStyleStrategy)pp.fPaintingStrategy).applyTextStyle(styleRange, pp.fColor);
+                    tp.mergeStyleRange(styleRange);
+                }
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelListener#modelChanged(org.eclipse.jface.text.source.IAnnotationModel)
+     */
+    public synchronized void modelChanged(IAnnotationModel model) {
+        if (DEBUG)
+            System.err.println("AP: OLD API of AnnotationModelListener called"); //$NON-NLS-1$
+
+        modelChanged(new AnnotationModelEvent(model));
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelListenerExtension#modelChanged(org.eclipse.jface.text.source.AnnotationModelEvent)
+     */
+    public void modelChanged(AnnotationModelEvent event) {
+        Display textWidgetDisplay;
+        try {
+            StyledText textWidget= fTextWidget;
+            if (textWidget is null || textWidget.isDisposed())
+                return;
+            textWidgetDisplay= textWidget.getDisplay();
+        } catch (SWTException ex) {
+            if (ex.code is SWT.ERROR_WIDGET_DISPOSED)
+                return;
+            throw ex;
+        }
+
+        if (fIsSettingModel) {
+            // inside the UI thread -> no need for posting
+            if (textWidgetDisplay is Display.getCurrent())
+                updatePainting(event);
+            else {
+                /*
+                 * we can throw away the changes since
+                 * further update painting will happen
+                 */
+                return;
+            }
+        } else {
+            if (DEBUG && event !is null && event.isWorldChange()) {
+                System.out_.println("AP: WORLD CHANGED, stack trace follows:"); //$NON-NLS-1$
+                ExceptionPrintStackTrace( new Exception(""), Stdout );
+            }
+
+            // XXX: posting here is a problem for annotations that are being
+            // removed and the positions of which are not updated to document
+            // changes any more. If the document gets modified between
+            // now and running the posted runnable, the position information
+            // is not accurate any longer.
+            textWidgetDisplay.asyncExec( dgRunnable( (AnnotationModelEvent event_){
+                if (fTextWidget !is null && !fTextWidget.isDisposed())
+                    updatePainting(event_);
+            }, event ));
+        }
+    }
+
+    /**
+     * Sets the color in which the squiggly for the given annotation type should be drawn.
+     *
+     * @param annotationType the annotation type
+     * @param color the color
+     */
+    public void setAnnotationTypeColor(Object annotationType, Color color) {
+        if (color !is null)
+            fAnnotationType2Color.put(annotationType, color);
+        else
+            fAnnotationType2Color.remove(annotationType);
+        fCachedAnnotationType2Color.clear();
+    }
+
+    /**
+     * Adds the given annotation type to the list of annotation types whose
+     * annotations should be painted by this painter using squiggly drawing. If the annotation  type
+     * is already in this list, this method is without effect.
+     *
+     * @param annotationType the annotation type
+     */
+    public void addAnnotationType(Object annotationType) {
+        addAnnotationType(annotationType, SQUIGGLES);
+    }
+
+    /**
+     * Adds the given annotation type to the list of annotation types whose
+     * annotations should be painted by this painter using the given drawing strategy.
+     * If the annotation type is already in this list, the old drawing strategy gets replaced.
+     *
+     * @param annotationType the annotation type
+     * @param drawingStrategyID the id of the drawing strategy that should be used for this annotation type
+     * @since 3.0
+     */
+    public void addAnnotationType(Object annotationType, Object drawingStrategyID) {
+        fAnnotationType2PaintingStrategyId.put(annotationType, drawingStrategyID);
+        fCachedAnnotationType2PaintingStrategy.clear();
+
+        if (fTextInputListener is null) {
+            fTextInputListener= new class()  ITextInputListener {
+
+                /*
+                 * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
+                 */
+                public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+                    fInputDocumentAboutToBeChanged= true;
+                }
+
+                /*
+                 * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
+                 */
+                public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
+                    fInputDocumentAboutToBeChanged= false;
+                }
+            };
+            fSourceViewer.addTextInputListener(fTextInputListener);
+        }
+
+    }
+
+    /**
+     * Registers a new drawing strategy under the given ID. If there is already a
+     * strategy registered under <code>id</code>, the old strategy gets replaced.
+     * <p>The given id can be referenced when adding annotation types, see
+     * {@link #addAnnotationType(Object, Object)}.</p>
+     *
+     * @param id the identifier under which the strategy can be referenced, not <code>null</code>
+     * @param strategy the new strategy
+     * @since 3.0
+     */
+    public void addDrawingStrategy(Object id, IDrawingStrategy strategy) {
+        // don't permit null as null is used to signal that an annotation type is not
+        // registered with a specific strategy, and that its annotation hierarchy should be searched
+        if (id is null)
+            throw new IllegalArgumentException(null);
+        fPaintingStrategyId2PaintingStrategy.put(id, cast(Object)strategy);
+        fCachedAnnotationType2PaintingStrategy.clear();
+    }
+
+    /**
+     * Registers a new drawing strategy under the given ID. If there is already
+     * a strategy registered under <code>id</code>, the old strategy gets
+     * replaced.
+     * <p>
+     * The given id can be referenced when adding annotation types, see
+     * {@link #addAnnotationType(Object, Object)}.
+     * </p>
+     *
+     * @param id the identifier under which the strategy can be referenced, not <code>null</code>
+     * @param strategy the new strategy
+     * @since 3.4
+     */
+    public void addTextStyleStrategy(Object id, ITextStyleStrategy strategy) {
+        // don't permit null as null is used to signal that an annotation type is not
+        // registered with a specific strategy, and that its annotation hierarchy should be searched
+        if (id is null)
+            throw new IllegalArgumentException(null);
+        fPaintingStrategyId2PaintingStrategy.put(id, cast(Object)strategy);
+        fCachedAnnotationType2PaintingStrategy.clear();
+    }
+
+    /**
+     * Adds the given annotation type to the list of annotation types whose
+     * annotations should be highlighted this painter. If the annotation  type
+     * is already in this list, this method is without effect.
+     *
+     * @param annotationType the annotation type
+     * @since 3.0
+     */
+    public void addHighlightAnnotationType(Object annotationType) {
+        addAnnotationType(annotationType, HIGHLIGHTING);
+    }
+
+    /**
+     * Removes the given annotation type from the list of annotation types whose
+     * annotations are painted by this painter. If the annotation type is not
+     * in this list, this method is without effect.
+     *
+     * @param annotationType the annotation type
+     */
+    public void removeAnnotationType(Object annotationType) {
+        fCachedAnnotationType2PaintingStrategy.clear();
+        fAnnotationType2PaintingStrategyId.remove(annotationType);
+        if (fAnnotationType2PaintingStrategyId.isEmpty() && fTextInputListener !is null) {
+            fSourceViewer.removeTextInputListener(fTextInputListener);
+            fTextInputListener= null;
+            fInputDocumentAboutToBeChanged= false;
+        }
+    }
+
+    /**
+     * Removes the given annotation type from the list of annotation types whose
+     * annotations are highlighted by this painter. If the annotation type is not
+     * in this list, this method is without effect.
+     *
+     * @param annotationType the annotation type
+     * @since 3.0
+     */
+    public void removeHighlightAnnotationType(Object annotationType) {
+        removeAnnotationType(annotationType);
+    }
+
+    /**
+     * Clears the list of annotation types whose annotations are
+     * painted by this painter.
+     */
+    public void removeAllAnnotationTypes() {
+        fCachedAnnotationType2PaintingStrategy.clear();
+        fAnnotationType2PaintingStrategyId.clear();
+        if (fTextInputListener !is null) {
+            fSourceViewer.removeTextInputListener(fTextInputListener);
+            fTextInputListener= null;
+        }
+    }
+
+    /**
+     * Returns whether the list of annotation types whose annotations are painted
+     * by this painter contains at least on element.
+     *
+     * @return <code>true</code> if there is an annotation type whose annotations are painted
+     */
+    public bool isPaintingAnnotations() {
+        return !fAnnotationType2PaintingStrategyId.isEmpty();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPainter#dispose()
+     */
+    public void dispose() {
+
+        if (fAnnotationType2Color !is null) {
+            fAnnotationType2Color.clear();
+            fAnnotationType2Color= null;
+        }
+
+        if (fCachedAnnotationType2Color !is null) {
+            fCachedAnnotationType2Color.clear();
+            fCachedAnnotationType2Color= null;
+        }
+
+        if (fCachedAnnotationType2PaintingStrategy !is null) {
+            fCachedAnnotationType2PaintingStrategy.clear();
+            fCachedAnnotationType2PaintingStrategy= null;
+        }
+
+        if (fAnnotationType2PaintingStrategyId !is null) {
+            fAnnotationType2PaintingStrategyId.clear();
+            fAnnotationType2PaintingStrategyId= null;
+        }
+
+        fTextWidget= null;
+        fSourceViewer= null;
+        fAnnotationAccess= null;
+        fModel= null;
+        synchronized (fDecorationMapLock) {
+            fDecorationsMap= null;
+        }
+        synchronized (fHighlightedDecorationsMapLock) {
+            fHighlightedDecorationsMap= null;
+        }
+    }
+
+    /**
+     * Returns the document offset of the upper left corner of the source viewer's view port,
+     * possibly including partially visible lines.
+     *
+     * @return the document offset if the upper left corner of the view port
+     */
+    private int getInclusiveTopIndexStartOffset() {
+
+        if (fTextWidget !is null && !fTextWidget.isDisposed()) {
+            int top= JFaceTextUtil.getPartialTopIndex(fSourceViewer);
+            try {
+                IDocument document= fSourceViewer.getDocument();
+                return document.getLineOffset(top);
+            } catch (BadLocationException x) {
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Returns the first invisible document offset of the lower right corner of the source viewer's view port,
+     * possibly including partially visible lines.
+     *
+     * @return the first invisible document offset of the lower right corner of the view port
+     */
+    private int getExclusiveBottomIndexEndOffset() {
+
+        if (fTextWidget !is null && !fTextWidget.isDisposed()) {
+            int bottom= JFaceTextUtil.getPartialBottomIndex(fSourceViewer);
+            try {
+                IDocument document= fSourceViewer.getDocument();
+
+                if (bottom >= document.getNumberOfLines())
+                    bottom= document.getNumberOfLines() - 1;
+
+                return document.getLineOffset(bottom) + document.getLineLength(bottom);
+            } catch (BadLocationException x) {
+            }
+        }
+
+        return -1;
+    }
+
+    /*
+     * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
+     */
+    public void paintControl(PaintEvent event) {
+        if (fTextWidget !is null)
+            handleDrawRequest(event);
+    }
+
+    /**
+     * Handles the request to draw the annotations using the given graphical context.
+     *
+     * @param event the paint event or <code>null</code>
+     */
+    private void handleDrawRequest(PaintEvent event) {
+
+        if (fTextWidget is null) {
+            // is already disposed
+            return;
+        }
+
+        IRegion clippingRegion= computeClippingRegion(event, false);
+        if (clippingRegion is null)
+            return;
+
+        int vOffset= clippingRegion.getOffset();
+        int vLength= clippingRegion.getLength();
+
+        final GC gc= event !is null ? event.gc : null;
+
+        // Clone decorations
+        Collection decorations;
+        synchronized (fDecorationMapLock) {
+            decorations= new ArrayList(fDecorationsMap.size());
+            decorations.addAll(fDecorationsMap.entrySet());
+        }
+
+        /*
+         * Create a new list of annotations to be drawn, since removing from decorations is more
+         * expensive. One bucket per drawing layer. Use linked lists as addition is cheap here.
+         */
+        ArrayList toBeDrawn= new ArrayList(10);
+        for (Iterator e = decorations.iterator(); e.hasNext();) {
+            Map.Entry entry= cast(Map.Entry)e.next();
+
+            Annotation a= cast(Annotation)entry.getKey();
+            Decoration pp = cast(Decoration)entry.getValue();
+            // prune any annotation that is not drawable or does not need drawing
+            if (!(a.isMarkedDeleted() || skip(a) || !pp.fPosition.overlapsWith(vOffset, vLength))) {
+                // ensure sized appropriately
+                for (int i= toBeDrawn.size(); i <= pp.fLayer; i++)
+                    toBeDrawn.add(new LinkedList());
+                (cast(List) toBeDrawn.get(pp.fLayer)).add(cast(Object)entry);
+            }
+        }
+        IDocument document= fSourceViewer.getDocument();
+        for (Iterator it= toBeDrawn.iterator(); it.hasNext();) {
+            List layer= cast(List) it.next();
+            for (Iterator e = layer.iterator(); e.hasNext();) {
+                Map.Entry entry= cast(Map.Entry)e.next();
+                Annotation a= cast(Annotation)entry.getKey();
+                Decoration pp = cast(Decoration)entry.getValue();
+                drawDecoration(pp, gc, a, clippingRegion, document);
+            }
+        }
+    }
+
+    private void drawDecoration(Decoration pp, GC gc, Annotation annotation, IRegion clippingRegion, IDocument document) {
+        if (clippingRegion is null)
+            return;
+
+        if (!(cast(IDrawingStrategy)pp.fPaintingStrategy ))
+            return;
+
+        IDrawingStrategy drawingStrategy= cast(IDrawingStrategy)pp.fPaintingStrategy;
+
+        int clippingOffset= clippingRegion.getOffset();
+        int clippingLength= clippingRegion.getLength();
+
+        Position p= pp.fPosition;
+        try {
+
+            int startLine= document.getLineOfOffset(p.getOffset());
+            int lastInclusive= Math.max(p.getOffset(), p.getOffset() + p.getLength() - 1);
+            int endLine= document.getLineOfOffset(lastInclusive);
+
+            for (int i= startLine; i <= endLine; i++) {
+                int lineOffset= document.getLineOffset(i);
+                int paintStart= Math.max(lineOffset, p.getOffset());
+                String lineDelimiter= document.getLineDelimiter(i);
+                int delimiterLength= lineDelimiter !is null ? lineDelimiter.length() : 0;
+                int paintLength= Math.min(lineOffset + document.getLineLength(i) - delimiterLength, p.getOffset() + p.getLength()) - paintStart;
+                if (paintLength >= 0 && overlapsWith(paintStart, paintLength, clippingOffset, clippingLength)) {
+                    // otherwise inside a line delimiter
+                    IRegion widgetRange= getWidgetRange(paintStart, paintLength);
+                    if (widgetRange !is null) {
+                        drawingStrategy.draw(annotation, gc, fTextWidget, widgetRange.getOffset(), widgetRange.getLength(), pp.fColor);
+                    }
+                }
+            }
+
+        } catch (BadLocationException x) {
+        }
+    }
+
+    /**
+     * Computes the model (document) region that is covered by the paint event's clipping region. If
+     * <code>event</code> is <code>null</code>, the model range covered by the visible editor
+     * area (viewport) is returned.
+     *
+     * @param event the paint event or <code>null</code> to use the entire viewport
+     * @param isClearing tells whether the clipping is need for clearing an annotation
+     * @return the model region comprised by either the paint event's clipping region or the
+     *         viewport
+     * @since 3.2
+     */
+    private IRegion computeClippingRegion(PaintEvent event, bool isClearing) {
+        if (event is null) {
+
+            if (!isClearing && fCurrentDrawRange !is null)
+                return new Region(fCurrentDrawRange.offset, fCurrentDrawRange.length);
+
+            // trigger a repaint of the entire viewport
+            int vOffset= getInclusiveTopIndexStartOffset();
+            if (vOffset is -1)
+                return null;
+
+            // http://bugs.eclipse.org/bugs/show_bug.cgi?id=17147
+            int vLength= getExclusiveBottomIndexEndOffset() - vOffset;
+
+            return new Region(vOffset, vLength);
+        }
+
+        int widgetOffset;
+        try {
+            int widgetClippingStartOffset= fTextWidget.getOffsetAtLocation(new Point(0, event.y));
+            int firstWidgetLine= fTextWidget.getLineAtOffset(widgetClippingStartOffset);
+            widgetOffset= fTextWidget.getOffsetAtLine(firstWidgetLine);
+        } catch (IllegalArgumentException ex1) {
+            try {
+                int firstVisibleLine= JFaceTextUtil.getPartialTopIndex(fTextWidget);
+                widgetOffset= fTextWidget.getOffsetAtLine(firstVisibleLine);
+            } catch (IllegalArgumentException ex2) { // above try code might fail too
+                widgetOffset= 0;
+            }
+        }
+
+        int widgetEndOffset;
+        try {
+            int widgetClippingEndOffset= fTextWidget.getOffsetAtLocation(new Point(0, event.y + event.height));
+            int lastWidgetLine= fTextWidget.getLineAtOffset(widgetClippingEndOffset);
+            widgetEndOffset= fTextWidget.getOffsetAtLine(lastWidgetLine + 1);
+        } catch (IllegalArgumentException ex1) {
+            // happens if the editor is not "full", e.g. the last line of the document is visible in the editor
+            try {
+                int lastVisibleLine= JFaceTextUtil.getPartialBottomIndex(fTextWidget);
+                if (lastVisibleLine is fTextWidget.getLineCount() - 1)
+                    // last line
+                    widgetEndOffset= fTextWidget.getCharCount();
+                else
+                    widgetEndOffset= fTextWidget.getOffsetAtLine(lastVisibleLine + 1) - 1;
+            } catch (IllegalArgumentException ex2) { // above try code might fail too
+                widgetEndOffset= fTextWidget.getCharCount();
+            }
+        }
+
+        IRegion clippingRegion= getModelRange(widgetOffset, widgetEndOffset - widgetOffset);
+
+        return clippingRegion;
+    }
+
+    /**
+     * Should the given annotation be skipped when handling draw requests?
+     *
+     * @param annotation the annotation
+     * @return <code>true</code> iff the given annotation should be
+     *         skipped when handling draw requests
+     * @since 3.0
+     */
+    protected bool skip(Annotation annotation) {
+        return false;
+    }
+
+    /**
+     * Returns the widget region that corresponds to the
+     * given offset and length in the viewer's document.
+     *
+     * @param modelOffset the model offset
+     * @param modelLength the model length
+     * @return the corresponding widget region
+     */
+    private IRegion getWidgetRange(int modelOffset, int modelLength) {
+        fReusableRegion.setOffset(modelOffset);
+        fReusableRegion.setLength(modelLength);
+
+        if (fReusableRegion is null || fReusableRegion.getOffset() is Integer.MAX_VALUE)
+            return null;
+
+        if ( cast(ITextViewerExtension5)fSourceViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) fSourceViewer;
+            return extension.modelRange2WidgetRange(fReusableRegion);
+        }
+
+        IRegion region= fSourceViewer.getVisibleRegion();
+        int offset= region.getOffset();
+        int length= region.getLength();
+
+        if (overlapsWith(fReusableRegion, region)) {
+            int p1= Math.max(offset, fReusableRegion.getOffset());
+            int p2= Math.min(offset + length, fReusableRegion.getOffset() + fReusableRegion.getLength());
+            return new Region(p1 - offset, p2 - p1);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the model region that corresponds to the given region in the
+     * viewer's text widget.
+     *
+     * @param offset the offset in the viewer's widget
+     * @param length the length in the viewer's widget
+     * @return the corresponding document region
+     * @since 3.2
+     */
+    private IRegion getModelRange(int offset, int length) {
+        if (offset is Integer.MAX_VALUE)
+            return null;
+
+        if ( cast(ITextViewerExtension5)fSourceViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) fSourceViewer;
+            return extension.widgetRange2ModelRange(new Region(offset, length));
+        }
+
+        IRegion region= fSourceViewer.getVisibleRegion();
+        return new Region(region.getOffset() + offset, length);
+    }
+
+    /**
+     * Checks whether the intersection of the given text ranges
+     * is empty or not.
+     *
+     * @param range1 the first range to check
+     * @param range2 the second range to check
+     * @return <code>true</code> if intersection is not empty
+     */
+    private bool overlapsWith(IRegion range1, IRegion range2) {
+        return overlapsWith(range1.getOffset(), range1.getLength(), range2.getOffset(), range2.getLength());
+    }
+
+    /**
+     * Checks whether the intersection of the given text ranges
+     * is empty or not.
+     *
+     * @param offset1 offset of the first range
+     * @param length1 length of the first range
+     * @param offset2 offset of the second range
+     * @param length2 length of the second range
+     * @return <code>true</code> if intersection is not empty
+     */
+    private bool overlapsWith(int offset1, int length1, int offset2, int length2) {
+        int end= offset2 + length2;
+        int thisEnd= offset1 + length1;
+
+        if (length2 > 0) {
+            if (length1 > 0)
+                return offset1 < end && offset2 < thisEnd;
+            return  offset2 <= offset1 && offset1 < end;
+        }
+
+        if (length1 > 0)
+            return offset1 <= offset2 && offset2 < thisEnd;
+        return offset1 is offset2;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPainter#deactivate(bool)
+     */
+    public void deactivate(bool redraw) {
+        if (fIsActive) {
+            fIsActive= false;
+            disablePainting(redraw);
+            setModel(null);
+            catchupWithModel(null);
+        }
+    }
+
+    /**
+     * Returns whether the given reason causes a repaint.
+     *
+     * @param reason the reason
+     * @return <code>true</code> if repaint reason, <code>false</code> otherwise
+     * @since 3.0
+     */
+    protected bool isRepaintReason(int reason) {
+        return CONFIGURATION is reason || INTERNAL is reason;
+    }
+
+    /**
+     * Retrieves the annotation model from the given source viewer.
+     *
+     * @param sourceViewer the source viewer
+     * @return the source viewer's annotation model or <code>null</code> if none can be found
+     * @since 3.0
+     */
+    protected IAnnotationModel findAnnotationModel(ISourceViewer sourceViewer) {
+        if(sourceViewer !is null)
+            return sourceViewer.getAnnotationModel();
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPainter#paint(int)
+     */
+    public void paint(int reason) {
+        if (fSourceViewer.getDocument() is null) {
+            deactivate(false);
+            return;
+        }
+
+        if (!fIsActive) {
+            IAnnotationModel model= findAnnotationModel(fSourceViewer);
+            if (model !is null) {
+                fIsActive= true;
+                setModel(model);
+            }
+        } else if (isRepaintReason(reason))
+            updatePainting(null);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPainter#setPositionManager(org.eclipse.jface.text.IPaintPositionManager)
+     */
+    public void setPositionManager(IPaintPositionManager manager) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationRulerColumn.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1049 @@
+/*******************************************************************************
+ * 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
+ *     Nikolay Botev <bono8106@hotmail.com> - [projection] Editor loses keyboard focus when expanding folded region - https://bugs.eclipse.org/bugs/show_bug.cgi?id=184255
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.source.AnnotationRulerColumn;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+
+
+
+
+
+
+
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.IViewportListener;
+import org.eclipse.jface.text.JFaceTextUtil;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.TextEvent;
+
+
+/**
+ * A vertical ruler column showing graphical representations of annotations.
+ * Will become final.
+ * <p>
+ * Do not subclass.
+ * </p>
+ *
+ * @since 2.0
+ */
+public class AnnotationRulerColumn : IVerticalRulerColumn, IVerticalRulerInfo, IVerticalRulerInfoExtension {
+
+    /**
+     * Internal listener class.
+     */
+    class InternalListener : IViewportListener, IAnnotationModelListener, ITextListener {
+
+        /*
+         * @see IViewportListener#viewportChanged(int)
+         */
+        public void viewportChanged(int verticalPosition) {
+            if (verticalPosition !is fScrollPos)
+                redraw();
+        }
+
+        /*
+         * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
+         */
+        public void modelChanged(IAnnotationModel model) {
+            postRedraw();
+        }
+
+        /*
+         * @see ITextListener#textChanged(TextEvent)
+         */
+        public void textChanged(TextEvent e) {
+            if (e.getViewerRedrawState())
+                postRedraw();
+        }
+    }
+
+    /**
+     * Implementation of <code>IRegion</code> that can be reused
+     * by setting the offset and the length.
+     */
+    private static class ReusableRegion : Position , IRegion {
+        public override int getLength(){
+            return super.getLength();
+        }
+        public override int getOffset(){
+            return super.getOffset();
+        }
+    }
+
+    /**
+     * Pair of an annotation and their associated position. Used inside the paint method
+     * for sorting annotations based on the offset of their position.
+     * @since 3.0
+     */
+    private static class Tuple {
+        Annotation annotation;
+        Position position;
+
+        this(Annotation annotation, Position position) {
+            this.annotation= annotation;
+            this.position= position;
+        }
+    }
+
+    /**
+     * Comparator for <code>Tuple</code>s.
+     * @since 3.0
+     */
+    private static class TupleComparator : Comparator {
+        /*
+         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+         */
+        public int compare(Object o1, Object o2) {
+            Position p1= (cast(Tuple) o1).position;
+            Position p2= (cast(Tuple) o2).position;
+            return p1.getOffset() - p2.getOffset();
+        }
+    }
+
+    /** This column's parent ruler */
+    private CompositeRuler fParentRuler;
+    /** The cached text viewer */
+    private ITextViewer fCachedTextViewer;
+    /** The cached text widget */
+    private StyledText fCachedTextWidget;
+    /** The ruler's canvas */
+    private Canvas fCanvas;
+    /** The vertical ruler's model */
+    private IAnnotationModel fModel;
+    /** Cache for the actual scroll position in pixels */
+    private int fScrollPos;
+    /** The buffer for double buffering */
+    private Image fBuffer;
+    /** The internal listener */
+    private InternalListener fInternalListener;
+    /** The width of this vertical ruler */
+    private int fWidth;
+    /** Switch for enabling/disabling the setModel method. */
+    private bool fAllowSetModel= true;
+    /**
+     * The list of annotation types to be shown in this ruler.
+     * @since 3.0
+     */
+    private Set fConfiguredAnnotationTypes;
+    /**
+     * The list of allowed annotation types to be shown in this ruler.
+     * An allowed annotation type maps to <code>true</code>, a disallowed
+     * to <code>false</code>.
+     * @since 3.0
+     */
+    private Map fAllowedAnnotationTypes;
+    /**
+     * The annotation access extension.
+     * @since 3.0
+     */
+    private IAnnotationAccessExtension fAnnotationAccessExtension;
+    /**
+     * The hover for this column.
+     * @since 3.0
+     */
+    private IAnnotationHover fHover;
+    /**
+     * The cached annotations.
+     * @since 3.0
+     */
+    private List fCachedAnnotations;
+    /**
+     * The comparator for sorting annotations according to the offset of their position.
+     * @since 3.0
+     */
+    private Comparator fTupleComparator;
+    /**
+     * The hit detection cursor.
+     * @since 3.0
+     */
+    private Cursor fHitDetectionCursor;
+    /**
+     * The last cursor.
+     * @since 3.0
+     */
+    private Cursor fLastCursor;
+    /**
+     * This ruler's mouse listener.
+     * @since 3.0
+     */
+    private MouseListener fMouseListener;
+
+    private void instanceInit(){
+        fInternalListener= new InternalListener();
+        fConfiguredAnnotationTypes= new HashSet();
+        fAllowedAnnotationTypes= new HashMap();
+        fCachedAnnotations= new ArrayList();
+        fTupleComparator= new TupleComparator();
+    }
+    /**
+     * Constructs this column with the given arguments.
+     *
+     * @param model the annotation model to get the annotations from
+     * @param width the width of the vertical ruler
+     * @param annotationAccess the annotation access
+     * @since 3.0
+     */
+    public this(IAnnotationModel model, int width, IAnnotationAccess annotationAccess) {
+        this(width, annotationAccess);
+        fAllowSetModel= false;
+        fModel= model;
+        fModel.addAnnotationModelListener(fInternalListener);
+    }
+
+    /**
+     * Constructs this column with the given arguments.
+     *
+     * @param width the width of the vertical ruler
+     * @param annotationAccess the annotation access
+     * @since 3.0
+     */
+    public this(int width, IAnnotationAccess annotationAccess) {
+        instanceInit();
+        fWidth= width;
+        if ( cast(IAnnotationAccessExtension)annotationAccess )
+            fAnnotationAccessExtension= cast(IAnnotationAccessExtension) annotationAccess;
+    }
+
+    /**
+     * Constructs this column with the given arguments.
+     *
+     * @param model the annotation model to get the annotations from
+     * @param width the width of the vertical ruler
+     */
+    public this(IAnnotationModel model, int width) {
+        instanceInit();
+        fWidth= width;
+        fAllowSetModel= false;
+        fModel= model;
+        fModel.addAnnotationModelListener(fInternalListener);
+    }
+
+    /**
+     * Constructs this column with the given width.
+     *
+     * @param width the width of the vertical ruler
+     */
+    public this(int width) {
+        instanceInit();
+        fWidth= width;
+    }
+
+    /*
+     * @see IVerticalRulerColumn#getControl()
+     */
+    public Control getControl() {
+        return fCanvas;
+    }
+
+    /*
+     * @see IVerticalRulerColumn#getWidth()
+     */
+    public int getWidth() {
+        return fWidth;
+    }
+
+    /*
+     * @see IVerticalRulerColumn#createControl(CompositeRuler, Composite)
+     */
+    public Control createControl(CompositeRuler parentRuler, Composite parentControl) {
+
+        fParentRuler= parentRuler;
+        fCachedTextViewer= parentRuler.getTextViewer();
+        fCachedTextWidget= fCachedTextViewer.getTextWidget();
+
+        fHitDetectionCursor= new Cursor(parentControl.getDisplay(), SWT.CURSOR_HAND);
+
+        fCanvas= createCanvas(parentControl);
+
+        fCanvas.addPaintListener(new class()  PaintListener {
+            public void paintControl(PaintEvent event) {
+                if (fCachedTextViewer !is null)
+                    doubleBufferPaint(event.gc);
+            }
+        });
+
+        fCanvas.addDisposeListener(new class()  DisposeListener {
+            public void widgetDisposed(DisposeEvent e) {
+                handleDispose();
+                fCachedTextViewer= null;
+                fCachedTextWidget= null;
+            }
+        });
+
+        fMouseListener= new class()  MouseListener {
+            public void mouseUp(MouseEvent event) {
+                int lineNumber;
+                if (isPropagatingMouseListener()) {
+                    fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
+                    lineNumber= fParentRuler.getLineOfLastMouseButtonActivity();
+                } else
+                    lineNumber= fParentRuler.toDocumentLineNumber(event.y);
+
+                if (1 is event.button)
+                    mouseClicked(lineNumber);
+            }
+
+            public void mouseDown(MouseEvent event) {
+                if (isPropagatingMouseListener())
+                    fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
+            }
+
+            public void mouseDoubleClick(MouseEvent event) {
+                int lineNumber;
+                if (isPropagatingMouseListener()) {
+                    fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
+                    lineNumber= fParentRuler.getLineOfLastMouseButtonActivity();
+                } else
+                    lineNumber= fParentRuler.toDocumentLineNumber(event.y);
+
+                if (1 is event.button)
+                    mouseDoubleClicked(lineNumber);
+            }
+        };
+        fCanvas.addMouseListener(fMouseListener);
+
+        fCanvas.addMouseMoveListener(new class()  MouseMoveListener {
+            /*
+             * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
+             * @since 3.0
+             */
+            public void mouseMove(MouseEvent e) {
+                handleMouseMove(e);
+            }
+        });
+
+        if (fCachedTextViewer !is null) {
+            fCachedTextViewer.addViewportListener(fInternalListener);
+            fCachedTextViewer.addTextListener(fInternalListener);
+        }
+
+        return fCanvas;
+    }
+
+    /**
+     * Creates a canvas with the given parent.
+     *
+     * @param parent the parent
+     * @return the created canvas
+     */
+    private Canvas createCanvas(Composite parent) {
+        return new class(parent, SWT.NO_BACKGROUND | SWT.NO_FOCUS)  Canvas {
+            this( Composite p, int s ){
+                super(p,s);
+            }
+            /*
+             * @see org.eclipse.swt.widgets.Control#addMouseListener(org.eclipse.swt.events.MouseListener)
+             * @since 3.0
+             */
+            public void addMouseListener(MouseListener listener) {
+                if (isPropagatingMouseListener() || listener is fMouseListener)
+                    super.addMouseListener(listener);
+            }
+        };
+    }
+
+    /**
+     * Tells whether this ruler column propagates mouse listener
+     * events to its parent.
+     *
+     * @return <code>true</code> if propagating to parent
+     * @since 3.0
+     */
+    protected bool isPropagatingMouseListener() {
+        return true;
+    }
+
+    /**
+     * Hook method for a mouse double click event on the given ruler line.
+     *
+     * @param rulerLine the ruler line
+     */
+    protected void mouseDoubleClicked(int rulerLine) {
+    }
+
+    /**
+     * Hook method for a mouse click event on the given ruler line.
+     *
+     * @param rulerLine the ruler line
+     * @since 3.0
+     */
+    protected void mouseClicked(int rulerLine) {
+    }
+
+    /**
+     * Handles mouse moves.
+     *
+     * @param event the mouse move event
+     */
+    private void handleMouseMove(MouseEvent event) {
+        fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
+        if (fCachedTextViewer !is null) {
+            int line= toDocumentLineNumber(event.y);
+            Cursor cursor= (hasAnnotation(line) ? fHitDetectionCursor : null);
+            if (cursor !is fLastCursor) {
+                fCanvas.setCursor(cursor);
+                fLastCursor= cursor;
+            }
+        }
+    }
+
+    /**
+     * Tells whether the given line contains an annotation.
+     *
+     * @param lineNumber the line number
+     * @return <code>true</code> if the given line contains an annotation
+     */
+    protected bool hasAnnotation(int lineNumber) {
+
+        IAnnotationModel model= fModel;
+        if ( cast(IAnnotationModelExtension)fModel )
+            model= (cast(IAnnotationModelExtension)fModel).getAnnotationModel(SourceViewer.MODEL_ANNOTATION_MODEL);
+
+        if (model is null)
+            return false;
+
+        IRegion line;
+        try {
+            IDocument d= fCachedTextViewer.getDocument();
+            if (d is null)
+                return false;
+
+            line= d.getLineInformation(lineNumber);
+        }  catch (BadLocationException ex) {
+            return false;
+        }
+
+        int lineStart= line.getOffset();
+        int lineLength= line.getLength();
+
+        Iterator e;
+        if ( cast(IAnnotationModelExtension2)fModel )
+            e= (cast(IAnnotationModelExtension2)fModel).getAnnotationIterator(lineStart, lineLength + 1, true, true);
+        else
+            e= model.getAnnotationIterator();
+
+        while (e.hasNext()) {
+            Annotation a= cast(Annotation) e.next();
+
+            if (a.isMarkedDeleted())
+                continue;
+
+            if (skip(a))
+                continue;
+
+            Position p= model.getPosition(a);
+            if (p is null || p.isDeleted())
+                continue;
+
+            if (p.overlapsWith(lineStart, lineLength) || p.length is 0 && p.offset is lineStart + lineLength)
+                return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Disposes the ruler's resources.
+     */
+    private void handleDispose() {
+
+        if (fCachedTextViewer !is null) {
+            fCachedTextViewer.removeViewportListener(fInternalListener);
+            fCachedTextViewer.removeTextListener(fInternalListener);
+        }
+
+        if (fModel !is null)
+            fModel.removeAnnotationModelListener(fInternalListener);
+
+        if (fBuffer !is null) {
+            fBuffer.dispose();
+            fBuffer= null;
+        }
+
+        if (fHitDetectionCursor !is null) {
+            fHitDetectionCursor.dispose();
+            fHitDetectionCursor= null;
+        }
+
+        fConfiguredAnnotationTypes.clear();
+        fAllowedAnnotationTypes.clear();
+        fAnnotationAccessExtension= null;
+    }
+
+    /**
+     * Double buffer drawing.
+     *
+     * @param dest the GC to draw into
+     */
+    private void doubleBufferPaint(GC dest) {
+
+        Point size= fCanvas.getSize();
+
+        if (size.x <= 0 || size.y <= 0)
+            return;
+
+        if (fBuffer !is null) {
+            Rectangle r= fBuffer.getBounds();
+            if (r.width !is size.x || r.height !is size.y) {
+                fBuffer.dispose();
+                fBuffer= null;
+            }
+        }
+        if (fBuffer is null)
+            fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y);
+
+        GC gc= new GC(fBuffer);
+        gc.setFont(fCachedTextWidget.getFont());
+        try {
+            gc.setBackground(fCanvas.getBackground());
+            gc.fillRectangle(0, 0, size.x, size.y);
+
+            if ( cast(ITextViewerExtension5)fCachedTextViewer )
+                doPaint1(gc);
+            else
+                doPaint(gc);
+        } finally {
+            gc.dispose();
+        }
+
+        dest.drawImage(fBuffer, 0, 0);
+    }
+
+    /**
+     * Returns the document offset of the upper left corner of the source viewer's
+     * view port, possibly including partially visible lines.
+     *
+     * @return document offset of the upper left corner including partially visible lines
+     */
+    protected int getInclusiveTopIndexStartOffset() {
+        if (fCachedTextWidget is null || fCachedTextWidget.isDisposed())
+            return -1;
+
+        IDocument document= fCachedTextViewer.getDocument();
+        if (document is null)
+            return -1;
+
+        int top= JFaceTextUtil.getPartialTopIndex(fCachedTextViewer);
+        try {
+            return document.getLineOffset(top);
+        } catch (BadLocationException x) {
+            return -1;
+        }
+    }
+
+    /**
+     * Returns the first invisible document offset of the lower right corner of the source viewer's view port,
+     * possibly including partially visible lines.
+     *
+     * @return the first invisible document offset of the lower right corner of the view port
+     */
+    private int getExclusiveBottomIndexEndOffset() {
+        if (fCachedTextWidget is null || fCachedTextWidget.isDisposed())
+            return -1;
+
+        IDocument document= fCachedTextViewer.getDocument();
+        if (document is null)
+            return -1;
+
+        int bottom= JFaceTextUtil.getPartialBottomIndex(fCachedTextViewer);
+        try {
+            if (bottom >= document.getNumberOfLines())
+                bottom= document.getNumberOfLines() - 1;
+            return document.getLineOffset(bottom) + document.getLineLength(bottom);
+        } catch (BadLocationException x) {
+            return -1;
+        }
+    }
+
+    /**
+     * Draws the vertical ruler w/o drawing the Canvas background.
+     *
+     * @param gc the GC to draw into
+     */
+    protected void doPaint(GC gc) {
+
+        if (fModel is null || fCachedTextViewer is null)
+            return;
+
+        int topLeft= getInclusiveTopIndexStartOffset();
+        // http://dev.eclipse.org/bugs/show_bug.cgi?id=14938
+        // http://dev.eclipse.org/bugs/show_bug.cgi?id=22487
+        // we want the exclusive offset (right after the last character)
+        int bottomRight= getExclusiveBottomIndexEndOffset();
+        int viewPort= bottomRight - topLeft;
+
+        fScrollPos= fCachedTextWidget.getTopPixel();
+        Point dimension= fCanvas.getSize();
+
+        IDocument doc= fCachedTextViewer.getDocument();
+        if (doc is null)
+            return;
+
+        int topLine= -1, bottomLine= -1;
+        try {
+            IRegion region= fCachedTextViewer.getVisibleRegion();
+            topLine= doc.getLineOfOffset(region.getOffset());
+            bottomLine= doc.getLineOfOffset(region.getOffset() + region.getLength());
+        } catch (BadLocationException x) {
+            return;
+        }
+
+        // draw Annotations
+        Rectangle r= new Rectangle(0, 0, 0, 0);
+        int maxLayer= 1;    // loop at least once through layers.
+
+        for (int layer= 0; layer < maxLayer; layer++) {
+            Iterator iter;
+            if ( cast(IAnnotationModelExtension2)fModel )
+                iter= (cast(IAnnotationModelExtension2)fModel).getAnnotationIterator(topLeft, viewPort + 1, true, true);
+            else
+                iter= fModel.getAnnotationIterator();
+
+            while (iter.hasNext()) {
+                Annotation annotation= cast(Annotation) iter.next();
+
+                int lay= IAnnotationAccessExtension.DEFAULT_LAYER;
+                if (fAnnotationAccessExtension !is null)
+                    lay= fAnnotationAccessExtension.getLayer(annotation);
+                maxLayer= Math.max(maxLayer, lay+1);    // dynamically update layer maximum
+                if (lay !is layer)   // wrong layer: skip annotation
+                    continue;
+
+                if (skip(annotation))
+                    continue;
+
+                Position position= fModel.getPosition(annotation);
+                if (position is null)
+                    continue;
+
+                // https://bugs.eclipse.org/bugs/show_bug.cgi?id=20284
+                // Position.overlapsWith returns false if the position just starts at the end
+                // of the specified range. If the position has zero length, we want to include it anyhow
+                int viewPortSize= position.getLength() is 0 ? viewPort + 1 : viewPort;
+                if (!position.overlapsWith(topLeft, viewPortSize))
+                    continue;
+
+                try {
+
+                    int offset= position.getOffset();
+                    int length= position.getLength();
+
+                    int startLine= doc.getLineOfOffset(offset);
+                    if (startLine < topLine)
+                        startLine= topLine;
+
+                    int endLine= startLine;
+                    if (length > 0)
+                        endLine= doc.getLineOfOffset(offset + length - 1);
+                    if (endLine > bottomLine)
+                        endLine= bottomLine;
+
+                    startLine -= topLine;
+                    endLine -= topLine;
+
+                    r.x= 0;
+                    r.y= JFaceTextUtil.computeLineHeight(fCachedTextWidget, 0, startLine, startLine)  - fScrollPos;
+
+                    r.width= dimension.x;
+                    int lines= endLine - startLine;
+
+                    r.height= JFaceTextUtil.computeLineHeight(fCachedTextWidget, startLine, endLine + 1, lines + 1);
+
+                    if (r.y < dimension.y && fAnnotationAccessExtension !is null)  // annotation within visible area
+                        fAnnotationAccessExtension.paint(annotation, gc, fCanvas, r);
+
+                } catch (BadLocationException x) {
+                }
+            }
+        }
+    }
+
+    /**
+     * Draws the vertical ruler w/o drawing the Canvas background. Implementation based
+     * on <code>ITextViewerExtension5</code>. Will replace <code>doPaint(GC)</code>.
+     *
+     * @param gc the GC to draw into
+     */
+    protected void doPaint1(GC gc) {
+
+        if (fModel is null || fCachedTextViewer is null)
+            return;
+
+        ITextViewerExtension5 extension= cast(ITextViewerExtension5) fCachedTextViewer;
+
+        fScrollPos= fCachedTextWidget.getTopPixel();
+        Point dimension= fCanvas.getSize();
+
+        int vOffset= getInclusiveTopIndexStartOffset();
+        int vLength= getExclusiveBottomIndexEndOffset() - vOffset;
+
+        // draw Annotations
+        Rectangle r= new Rectangle(0, 0, 0, 0);
+        ReusableRegion range= new ReusableRegion();
+
+        int minLayer= Integer.MAX_VALUE, maxLayer= Integer.MIN_VALUE;
+        fCachedAnnotations.clear();
+        Iterator iter;
+        if ( cast(IAnnotationModelExtension2)fModel )
+            iter= (cast(IAnnotationModelExtension2)fModel).getAnnotationIterator(vOffset, vLength + 1, true, true);
+        else
+            iter= fModel.getAnnotationIterator();
+
+        while (iter.hasNext()) {
+            Annotation annotation= cast(Annotation) iter.next();
+
+            if (skip(annotation))
+                continue;
+
+            Position position= fModel.getPosition(annotation);
+            if (position is null)
+                continue;
+
+            // https://bugs.eclipse.org/bugs/show_bug.cgi?id=217710
+            int extendedVLength= position.getLength() is 0 ? vLength + 1 : vLength;
+            if (!position.overlapsWith(vOffset, extendedVLength))
+                continue;
+
+            int lay= IAnnotationAccessExtension.DEFAULT_LAYER;
+            if (fAnnotationAccessExtension !is null)
+                lay= fAnnotationAccessExtension.getLayer(annotation);
+
+            minLayer= Math.min(minLayer, lay);
+            maxLayer= Math.max(maxLayer, lay);
+            fCachedAnnotations.add(new Tuple(annotation, position));
+        }
+        Collections.sort(fCachedAnnotations, fTupleComparator);
+
+        for (int layer= minLayer; layer <= maxLayer; layer++) {
+            for (int i= 0, n= fCachedAnnotations.size(); i < n; i++) {
+                Tuple tuple= cast(Tuple) fCachedAnnotations.get(i);
+                Annotation annotation= tuple.annotation;
+                Position position= tuple.position;
+
+                int lay= IAnnotationAccessExtension.DEFAULT_LAYER;
+                if (fAnnotationAccessExtension !is null)
+                    lay= fAnnotationAccessExtension.getLayer(annotation);
+                if (lay !is layer)   // wrong layer: skip annotation
+                    continue;
+
+                range.setOffset(position.getOffset());
+                range.setLength(position.getLength());
+                IRegion widgetRegion= extension.modelRange2WidgetRange(range);
+                if (widgetRegion is null)
+                    continue;
+
+                int startLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset());
+                if (startLine is -1)
+                    continue;
+
+                int endLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset() + Math.max(widgetRegion.getLength() -1, 0));
+                if (endLine is -1)
+                    continue;
+
+                r.x= 0;
+                r.y= JFaceTextUtil.computeLineHeight(fCachedTextWidget, 0, startLine, startLine)  - fScrollPos;
+
+                r.width= dimension.x;
+                int lines= endLine - startLine;
+                r.height= JFaceTextUtil.computeLineHeight(fCachedTextWidget, startLine, endLine + 1, lines + 1);
+
+                if (r.y < dimension.y && fAnnotationAccessExtension !is null)  // annotation within visible area
+                    fAnnotationAccessExtension.paint(annotation, gc, fCanvas, r);
+            }
+        }
+
+        fCachedAnnotations.clear();
+    }
+
+
+    /**
+     * Post a redraw request for this column into the UI thread.
+     */
+    private void postRedraw() {
+        if (fCanvas !is null && !fCanvas.isDisposed()) {
+            Display d= fCanvas.getDisplay();
+            if (d !is null) {
+                d.asyncExec(new class()  Runnable {
+                    public void run() {
+                        redraw();
+                    }
+                });
+            }
+        }
+    }
+
+    /*
+     * @see IVerticalRulerColumn#redraw()
+     */
+    public void redraw() {
+        if (fCanvas !is null && !fCanvas.isDisposed()) {
+            GC gc= new GC(fCanvas);
+            doubleBufferPaint(gc);
+            gc.dispose();
+        }
+    }
+
+    /*
+     * @see IVerticalRulerColumn#setModel
+     */
+    public void setModel(IAnnotationModel model) {
+        if (fAllowSetModel && model !is fModel) {
+
+            if (fModel !is null)
+                fModel.removeAnnotationModelListener(fInternalListener);
+
+            fModel= model;
+
+            if (fModel !is null)
+                fModel.addAnnotationModelListener(fInternalListener);
+
+            postRedraw();
+        }
+    }
+
+    /*
+     * @see IVerticalRulerColumn#setFont(Font)
+     */
+    public void setFont(Font font) {
+    }
+
+    /**
+     * Returns the cached text viewer.
+     *
+     * @return the cached text viewer
+     */
+    protected ITextViewer getCachedTextViewer() {
+        return fCachedTextViewer;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#getModel()
+     */
+    public IAnnotationModel getModel() {
+        return fModel;
+    }
+
+    /**
+     * Adds the given annotation type to this annotation ruler column. Starting
+     * with this call, annotations of the given type are shown in this annotation
+     * ruler column.
+     *
+     * @param annotationType the annotation type
+     * @since 3.0
+     */
+    public void addAnnotationType(Object annotationType) {
+        fConfiguredAnnotationTypes.add(annotationType);
+        fAllowedAnnotationTypes.clear();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
+     * @since 3.0
+     */
+    public int getLineOfLastMouseButtonActivity() {
+        return fParentRuler.getLineOfLastMouseButtonActivity();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfo#toDocumentLineNumber(int)
+     * @since 3.0
+     */
+    public int toDocumentLineNumber(int y_coordinate) {
+        return fParentRuler.toDocumentLineNumber(y_coordinate);
+    }
+
+    /**
+     * Removes the given annotation type from this annotation ruler column.
+     * Annotations of the given type are no longer shown in this annotation
+     * ruler column.
+     *
+     * @param annotationType the annotation type
+     * @since 3.0
+     */
+    public void removeAnnotationType(Object annotationType) {
+        fConfiguredAnnotationTypes.remove(annotationType);
+        fAllowedAnnotationTypes.clear();
+    }
+
+    /**
+     * Returns whether the given annotation should be skipped by the drawing
+     * routine.
+     *
+     * @param annotation the annotation
+     * @return <code>true</code> if annotation of the given type should be
+     *         skipped, <code>false</code> otherwise
+     * @since 3.0
+     */
+    private bool skip(Annotation annotation) {
+        Object annotationType= stringcast(annotation.getType());
+        Boolean allowed= cast(Boolean) fAllowedAnnotationTypes.get(annotationType);
+        if (allowed !is null)
+            return !allowed.booleanValue();
+
+        bool skip= skip(annotationType);
+        fAllowedAnnotationTypes.put(annotationType, !skip ? Boolean.TRUE : Boolean.FALSE);
+        return skip;
+    }
+
+    /**
+     * Computes whether the annotation of the given type should be skipped or
+     * not.
+     *
+     * @param annotationType the annotation type
+     * @return <code>true</code> if annotation should be skipped, <code>false</code>
+     *         otherwise
+     * @since 3.0
+     */
+    private bool skip(Object annotationType) {
+        if (fAnnotationAccessExtension !is null) {
+            Iterator e= fConfiguredAnnotationTypes.iterator();
+            while (e.hasNext()) {
+                if (fAnnotationAccessExtension.isSubtype(annotationType, e.next()))
+                    return false;
+            }
+            return true;
+        }
+        return !fConfiguredAnnotationTypes.contains(annotationType);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#getHover()
+     * @since 3.0
+     */
+    public IAnnotationHover getHover() {
+        return fHover;
+    }
+
+    /**
+     * @param hover The hover to set.
+     * @since 3.0
+     */
+    public void setHover(IAnnotationHover hover) {
+        fHover= hover;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#addVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener)
+     * @since 3.0
+     */
+    public void addVerticalRulerListener(IVerticalRulerListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#removeVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener)
+     * @since 3.0
+     */
+    public void removeVerticalRulerListener(IVerticalRulerListener listener) {
+        throw new UnsupportedOperationException();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ChangeRulerColumn.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,632 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.source.ChangeRulerColumn;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.internal.text.revisions.RevisionPainter;
+import org.eclipse.jface.internal.text.source.DiffPainter;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.IViewportListener;
+import org.eclipse.jface.text.JFaceTextUtil;
+import org.eclipse.jface.text.TextEvent;
+import org.eclipse.jface.text.revisions.IRevisionRulerColumn;
+import org.eclipse.jface.text.revisions.RevisionInformation;
+import org.eclipse.jface.viewers.ISelectionProvider;
+
+/**
+ * A vertical ruler column displaying line numbers and serving as a UI for quick diff.
+ * Clients instantiate and configure object of this class.
+ *
+ * @since 3.0
+ */
+public final class ChangeRulerColumn : IVerticalRulerColumn, IVerticalRulerInfo, IVerticalRulerInfoExtension, IChangeRulerColumn, IRevisionRulerColumn {
+    /**
+     * Handles all the mouse interaction in this line number ruler column.
+     */
+    private class MouseHandler : MouseListener, MouseMoveListener {
+
+        /*
+         * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseUp(MouseEvent event) {
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseDown(MouseEvent event) {
+            fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseDoubleClick(MouseEvent event) {
+            fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseMove(MouseEvent event) {
+            fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
+        }
+    }
+
+    /**
+     * Internal listener class.
+     */
+    private class InternalListener : IViewportListener, ITextListener {
+
+        /*
+         * @see IViewportListener#viewportChanged(int)
+         */
+        public void viewportChanged(int verticalPosition) {
+            if (verticalPosition !is fScrollPos)
+                redraw();
+        }
+
+        /*
+         * @see ITextListener#textChanged(TextEvent)
+         */
+        public void textChanged(TextEvent event) {
+
+            if (!event.getViewerRedrawState())
+                return;
+
+            if (fSensitiveToTextChanges || event.getDocumentEvent() is null)
+                postRedraw();
+
+        }
+    }
+
+    /**
+     * The view(port) listener.
+     */
+    private /+const+/ InternalListener fInternalListener;
+    /**
+     * The mouse handler.
+     * @since 3.2
+     */
+    private /+const+/ MouseHandler fMouseHandler;
+    /**
+     * The revision painter.
+     * @since 3.2
+     */
+    private const RevisionPainter fRevisionPainter;
+    /**
+     * The diff info painter.
+     * @since 3.2
+     */
+    private const DiffPainter fDiffPainter;
+
+    /** This column's parent ruler */
+    private CompositeRuler fParentRuler;
+    /** Cached text viewer */
+    private ITextViewer fCachedTextViewer;
+    /** Cached text widget */
+    private StyledText fCachedTextWidget;
+    /** The columns canvas */
+    private Canvas fCanvas;
+    /** The background color */
+    private Color fBackground;
+    /** The ruler's annotation model. */
+    private IAnnotationModel fAnnotationModel;
+    /** The width of the change ruler column. */
+    private const int fWidth= 5;
+
+    /** Cache for the actual scroll position in pixels */
+    private int fScrollPos;
+    /** The buffer for double buffering */
+    private Image fBuffer;
+    /** Indicates whether this column reacts on text change events */
+    private bool fSensitiveToTextChanges= false;
+
+    private void instanceInit(){
+        fInternalListener= new InternalListener();
+        fMouseHandler= new MouseHandler();
+    }
+    /**
+     * Creates a new ruler column.
+     *
+     * @deprecated since 3.2 use {@link #ChangeRulerColumn(ISharedTextColors)} instead
+     */
+    public this() {
+        instanceInit();
+        fRevisionPainter= null;
+        fDiffPainter= new DiffPainter(this, null);
+    }
+
+    /**
+     * Creates a new revision ruler column.
+     *
+     * @param sharedColors the colors to look up RGBs
+     * @since 3.2
+     */
+    public this(ISharedTextColors sharedColors) {
+        instanceInit();
+        Assert.isNotNull(cast(Object)sharedColors);
+        fRevisionPainter= new RevisionPainter(this, sharedColors);
+        fDiffPainter= new DiffPainter(this, null); // no shading
+    }
+
+    /**
+     * Returns the System background color for list widgets.
+     *
+     * @return the System background color for list widgets
+     */
+    private Color getBackground() {
+        if (fBackground is null)
+            return fCachedTextWidget.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
+        return fBackground;
+    }
+
+    /*
+     * @see IVerticalRulerColumn#createControl(CompositeRuler, Composite)
+     */
+    public Control createControl(CompositeRuler parentRuler, Composite parentControl) {
+
+        fParentRuler= parentRuler;
+        fCachedTextViewer= parentRuler.getTextViewer();
+        fCachedTextWidget= fCachedTextViewer.getTextWidget();
+
+        fCanvas= new Canvas(parentControl, SWT.NONE);
+        fCanvas.setBackground(getBackground());
+
+        fCanvas.addPaintListener(new class()  PaintListener {
+            public void paintControl(PaintEvent event) {
+                if (fCachedTextViewer !is null)
+                    doubleBufferPaint(event.gc);
+            }
+        });
+
+        fCanvas.addDisposeListener(new class()  DisposeListener {
+            public void widgetDisposed(DisposeEvent e) {
+                handleDispose();
+                fCachedTextViewer= null;
+                fCachedTextWidget= null;
+            }
+        });
+
+        fCanvas.addMouseListener(fMouseHandler);
+        fCanvas.addMouseMoveListener(fMouseHandler);
+
+        if (fCachedTextViewer !is null) {
+
+            fCachedTextViewer.addViewportListener(fInternalListener);
+            fCachedTextViewer.addTextListener(fInternalListener);
+        }
+
+        fRevisionPainter.setParentRuler(parentRuler);
+        fDiffPainter.setParentRuler(parentRuler);
+
+        return fCanvas;
+    }
+
+    /**
+     * Disposes the column's resources.
+     */
+    protected void handleDispose() {
+
+        if (fCachedTextViewer !is null) {
+            fCachedTextViewer.removeViewportListener(fInternalListener);
+            fCachedTextViewer.removeTextListener(fInternalListener);
+        }
+
+        if (fBuffer !is null) {
+            fBuffer.dispose();
+            fBuffer= null;
+        }
+    }
+
+    /**
+     * Double buffer drawing.
+     *
+     * @param dest the GC to draw into
+     */
+    private void doubleBufferPaint(GC dest) {
+
+        Point size= fCanvas.getSize();
+
+        if (size.x <= 0 || size.y <= 0)
+            return;
+
+        if (fBuffer !is null) {
+            Rectangle r= fBuffer.getBounds();
+            if (r.width !is size.x || r.height !is size.y) {
+                fBuffer.dispose();
+                fBuffer= null;
+            }
+        }
+        if (fBuffer is null)
+            fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y);
+
+        GC gc= new GC(fBuffer);
+        gc.setFont(fCanvas.getFont());
+
+        try {
+            gc.setBackground(getBackground());
+            gc.fillRectangle(0, 0, size.x, size.y);
+
+            doPaint(gc);
+        } finally {
+            gc.dispose();
+        }
+
+        dest.drawImage(fBuffer, 0, 0);
+    }
+
+    /**
+     * Returns the view port height in lines.
+     *
+     * @return the view port height in lines
+     * @deprecated as of 3.2 the number of lines in the viewport cannot be computed because
+     *             StyledText supports variable line heights
+     */
+    protected int getVisibleLinesInViewport() {
+        // Hack to reduce amount of copied code.
+        return LineNumberRulerColumn.getVisibleLinesInViewport(fCachedTextWidget);
+    }
+
+    /**
+     * Returns <code>true</code> if the viewport displays the entire viewer contents, i.e. the
+     * viewer is not vertically scrollable.
+     *
+     * @return <code>true</code> if the viewport displays the entire contents, <code>false</code> otherwise
+     * @since 3.2
+     */
+    protected final bool isViewerCompletelyShown() {
+        return JFaceTextUtil.isShowingEntireContents(fCachedTextWidget);
+    }
+
+    /**
+     * Draws the ruler column.
+     *
+     * @param gc the GC to draw into
+     */
+    private void doPaint(GC gc) {
+        ILineRange visibleModelLines= computeVisibleModelLines();
+        if (visibleModelLines is null)
+            return;
+
+        fSensitiveToTextChanges= isViewerCompletelyShown();
+
+        fScrollPos= fCachedTextWidget.getTopPixel();
+
+        fRevisionPainter.paint(gc, visibleModelLines);
+        if (!fRevisionPainter.hasInformation()) // don't paint quick diff colors if revisions are painted
+            fDiffPainter.paint(gc, visibleModelLines);
+    }
+
+    /*
+     * @see IVerticalRulerColumn#redraw()
+     */
+    public void redraw() {
+
+        if (fCachedTextViewer !is null && fCanvas !is null && !fCanvas.isDisposed()) {
+            GC gc= new GC(fCanvas);
+            doubleBufferPaint(gc);
+            gc.dispose();
+        }
+    }
+
+    /*
+     * @see IVerticalRulerColumn#setFont(Font)
+     */
+    public void setFont(Font font) {
+    }
+
+    /**
+     * Returns the parent (composite) ruler of this ruler column.
+     *
+     * @return the parent ruler
+     * @since 3.0
+     */
+    private CompositeRuler getParentRuler() {
+        return fParentRuler;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
+     */
+    public int getLineOfLastMouseButtonActivity() {
+        return getParentRuler().getLineOfLastMouseButtonActivity();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfo#toDocumentLineNumber(int)
+     */
+    public int toDocumentLineNumber(int y_coordinate) {
+        return getParentRuler().toDocumentLineNumber(y_coordinate);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#getHover()
+     */
+    public IAnnotationHover getHover() {
+        int activeLine= getParentRuler().getLineOfLastMouseButtonActivity();
+        if (fRevisionPainter.hasHover(activeLine))
+            return fRevisionPainter.getHover();
+        if (fDiffPainter.hasHover(activeLine))
+            return fDiffPainter.getHover();
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IChangeRulerColumn#setHover(org.eclipse.jface.text.source.IAnnotationHover)
+     */
+    public void setHover(IAnnotationHover hover) {
+        fRevisionPainter.setHover(hover);
+        fDiffPainter.setHover(hover);
+    }
+
+    /*
+     * @see IVerticalRulerColumn#setModel(IAnnotationModel)
+     */
+    public void setModel(IAnnotationModel model) {
+        setAnnotationModel(model);
+        fRevisionPainter.setModel(model);
+        fDiffPainter.setModel(model);
+    }
+
+    private void setAnnotationModel(IAnnotationModel model) {
+        if (fAnnotationModel !is model)
+            fAnnotationModel= model;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IChangeRulerColumn#setBackground(org.eclipse.swt.graphics.Color)
+     */
+    public void setBackground(Color background) {
+        fBackground= background;
+        if (fCanvas !is null && !fCanvas.isDisposed())
+            fCanvas.setBackground(getBackground());
+        fRevisionPainter.setBackground(background);
+        fDiffPainter.setBackground(background);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IChangeRulerColumn#setAddedColor(org.eclipse.swt.graphics.Color)
+     */
+    public void setAddedColor(Color addedColor) {
+        fDiffPainter.setAddedColor(addedColor);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IChangeRulerColumn#setChangedColor(org.eclipse.swt.graphics.Color)
+     */
+    public void setChangedColor(Color changedColor) {
+        fDiffPainter.setChangedColor(changedColor);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IChangeRulerColumn#setDeletedColor(org.eclipse.swt.graphics.Color)
+     */
+    public void setDeletedColor(Color deletedColor) {
+        fDiffPainter.setDeletedColor(deletedColor);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#getModel()
+     */
+    public IAnnotationModel getModel() {
+        return fAnnotationModel;
+    }
+
+    /*
+     * @see IVerticalRulerColumn#getControl()
+     */
+    public Control getControl() {
+        return fCanvas;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfo#getWidth()
+     */
+    public int getWidth() {
+        return fWidth;
+    }
+
+    /**
+     * Triggers a redraw in the display thread.
+     */
+    protected final void postRedraw() {
+        if (fCanvas !is null && !fCanvas.isDisposed()) {
+            Display d= fCanvas.getDisplay();
+            if (d !is null) {
+                d.asyncExec(new class()  Runnable {
+                    public void run() {
+                        redraw();
+                    }
+                });
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#addVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener)
+     */
+    public void addVerticalRulerListener(IVerticalRulerListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#removeVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener)
+     */
+    public void removeVerticalRulerListener(IVerticalRulerListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Computes the document based line range visible in the text widget.
+     *
+     * @return the document based line range visible in the text widget
+     * @since 3.2
+     */
+    private final ILineRange computeVisibleModelLines() {
+        IDocument doc= fCachedTextViewer.getDocument();
+        if (doc is null)
+            return null;
+
+        int topLine;
+        IRegion coverage;
+
+        if ( cast(ITextViewerExtension5)fCachedTextViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) fCachedTextViewer;
+
+            // ITextViewer.getTopIndex returns the fully visible line, but we want the partially
+            // visible one
+            int widgetTopLine= JFaceTextUtil.getPartialTopIndex(fCachedTextWidget);
+            topLine= extension.widgetLine2ModelLine(widgetTopLine);
+
+            coverage= extension.getModelCoverage();
+
+        } else {
+            topLine= JFaceTextUtil.getPartialTopIndex(fCachedTextViewer);
+            coverage= fCachedTextViewer.getVisibleRegion();
+        }
+
+        int bottomLine= fCachedTextViewer.getBottomIndex();
+        if (bottomLine !is -1)
+            ++ bottomLine;
+
+        // clip by coverage window
+        try {
+            int firstLine= doc.getLineOfOffset(coverage.getOffset());
+            if (firstLine > topLine)
+                topLine= firstLine;
+
+            int lastLine= doc.getLineOfOffset(coverage.getOffset() + coverage.getLength());
+            if (lastLine < bottomLine || bottomLine is -1)
+                bottomLine= lastLine;
+        } catch (BadLocationException x) {
+            ExceptionPrintStackTrace(x);
+            return null;
+        }
+
+        ILineRange visibleModelLines= new LineRange(topLine, bottomLine - topLine + 1);
+        return visibleModelLines;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.revisions.IRevisionRulerColumn#setRevisionInformation(org.eclipse.jface.text.revisions.RevisionInformation)
+     */
+    public void setRevisionInformation(RevisionInformation info) {
+        fRevisionPainter.setRevisionInformation(info);
+        fRevisionPainter.setBackground(getBackground());
+    }
+
+    /**
+     * Returns the revision selection provider.
+     *
+     * @return the revision selection provider
+     * @since 3.2
+     */
+    public ISelectionProvider getRevisionSelectionProvider() {
+        return fRevisionPainter.getRevisionSelectionProvider();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/CompositeRuler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,947 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.CompositeRuler;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.EventListener;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.HelpListener;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension;
+import org.eclipse.jface.text.ITextViewerExtension5;
+
+
+/**
+ * Standard implementation of
+ * {@link org.eclipse.jface.text.source.IVerticalRuler}.
+ * <p>
+ * This ruler does not have a a visual representation of its own. The
+ * presentation comes from the configurable list of vertical ruler columns. Such
+ * columns must implement the
+ * {@link org.eclipse.jface.text.source.IVerticalRulerColumn}. interface.</p>
+ * <p>
+ * Clients may instantiate and configure this class.</p>
+ *
+ * @see org.eclipse.jface.text.source.IVerticalRulerColumn
+ * @see org.eclipse.jface.text.ITextViewer
+ * @since 2.0
+ */
+public class CompositeRuler : IVerticalRuler, IVerticalRulerExtension, IVerticalRulerInfoExtension {
+
+
+    /**
+     * Layout of the composite vertical ruler. Arranges the list of columns.
+     */
+    class RulerLayout : Layout {
+
+        /**
+         * Creates the new ruler layout.
+         */
+        protected this() {
+        }
+
+        /*
+         * @see Layout#computeSize(Composite, int, int, bool)
+         */
+        protected Point computeSize(Composite composite, int wHint, int hHint, bool flushCache) {
+            Control[] children= composite.getChildren();
+            Point size= new Point(0, 0);
+            for (int i= 0; i < children.length; i++) {
+                Point s= children[i].computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache);
+                size.x += s.x;
+                size.y= Math.max(size.y, s.y);
+            }
+            size.x += (Math.max(0, children.length -1) * fGap);
+            return size;
+        }
+
+        /*
+         * @see Layout#layout(Composite, bool)
+         */
+        protected void layout(Composite composite, bool flushCache) {
+            Rectangle clArea= composite.getClientArea();
+            int rulerHeight= clArea.height;
+
+            int x= 0;
+            Iterator e= fDecorators.iterator();
+            while (e.hasNext()) {
+                IVerticalRulerColumn column= cast(IVerticalRulerColumn) e.next();
+                int columnWidth= column.getWidth();
+                column.getControl().setBounds(x, 0, columnWidth, rulerHeight);
+                x += (columnWidth + fGap);
+            }
+        }
+    }
+
+    /**
+     * A canvas that adds listeners to all its children. Used by the implementation of the
+     * vertical ruler to propagate listener additions and removals to the ruler's columns.
+     */
+    static class CompositeRulerCanvas : Canvas {
+
+        /**
+         * Keeps the information for which event type a listener object has been added.
+         */
+        static class ListenerInfo {
+            ClassInfo fClass;
+            EventListener fListener;
+        }
+
+        /** The list of listeners added to this canvas. */
+        private List fCachedListeners;
+        /**
+         * Internal listener for opening the context menu.
+         * @since 3.0
+         */
+        private Listener fMenuDetectListener;
+
+        /**
+         * Creates a new composite ruler canvas.
+         *
+         * @param parent the parent composite
+         * @param style the SWT styles
+         */
+        public this(Composite parent, int style) {
+            fCachedListeners= new ArrayList();
+
+            super(parent, style);
+            fMenuDetectListener= new class()  Listener {
+                public void handleEvent(Event event) {
+                    if (event.type is SWT.MenuDetect) {
+                        Menu menu= getMenu();
+                        if (menu !is null) {
+                            menu.setLocation(event.x, event.y);
+                            menu.setVisible(true);
+                        }
+                    }
+                }
+            };
+            super.addDisposeListener(new class()  DisposeListener {
+                public void widgetDisposed(DisposeEvent e) {
+                    if (fCachedListeners !is null) {
+                        fCachedListeners.clear();
+                        fCachedListeners= null;
+                    }
+                }
+            });
+        }
+
+        /**
+         * Adds the given listener object as listener of the given type (<code>clazz</code>) to
+         * the given control.
+         *
+         * @param clazz the listener type
+         * @param control the control to add the listener to
+         * @param listener the listener to be added
+         */
+        private void addListener(ClassInfo clazz, Control control, EventListener listener) {
+            if (ControlListener.classinfo.opEquals(clazz)) {
+                control. addControlListener(cast(ControlListener) listener);
+                return;
+            }
+            if (FocusListener.classinfo.opEquals(clazz)) {
+                control. addFocusListener(cast(FocusListener) listener);
+                return;
+            }
+            if (HelpListener.classinfo.opEquals(clazz)) {
+                control. addHelpListener(cast(HelpListener) listener);
+                return;
+            }
+            if (KeyListener.classinfo.opEquals(clazz)) {
+                control. addKeyListener(cast(KeyListener) listener);
+                return;
+            }
+            if (MouseListener.classinfo.opEquals(clazz)) {
+                control. addMouseListener(cast(MouseListener) listener);
+                return;
+            }
+            if (MouseMoveListener.classinfo.opEquals(clazz)) {
+                control. addMouseMoveListener(cast(MouseMoveListener) listener);
+                return;
+            }
+            if (MouseTrackListener.classinfo.opEquals(clazz)) {
+                control. addMouseTrackListener(cast(MouseTrackListener) listener);
+                return;
+            }
+            if (PaintListener.classinfo.opEquals(clazz)) {
+                control. addPaintListener(cast(PaintListener) listener);
+                return;
+            }
+            if (TraverseListener.classinfo.opEquals(clazz)) {
+                control. addTraverseListener(cast(TraverseListener) listener);
+                return;
+            }
+            if (DisposeListener.classinfo.opEquals(clazz)) {
+                control. addDisposeListener(cast(DisposeListener) listener);
+                return;
+            }
+        }
+
+        /**
+         * Removes the given listener object as listener of the given type (<code>clazz</code>) from
+         * the given control.
+         *
+         * @param clazz the listener type
+         * @param control the control to remove the listener from
+         * @param listener the listener to be removed
+         */
+        private void removeListener(ClassInfo clazz, Control control, EventListener listener) {
+            if (ControlListener.classinfo.opEquals(clazz)) {
+                control. removeControlListener(cast(ControlListener) listener);
+                return;
+            }
+            if (FocusListener.classinfo.opEquals(clazz)) {
+                control. removeFocusListener(cast(FocusListener) listener);
+                return;
+            }
+            if (HelpListener.classinfo.opEquals(clazz)) {
+                control. removeHelpListener(cast(HelpListener) listener);
+                return;
+            }
+            if (KeyListener.classinfo.opEquals(clazz)) {
+                control. removeKeyListener(cast(KeyListener) listener);
+                return;
+            }
+            if (MouseListener.classinfo.opEquals(clazz)) {
+                control. removeMouseListener(cast(MouseListener) listener);
+                return;
+            }
+            if (MouseMoveListener.classinfo.opEquals(clazz)) {
+                control. removeMouseMoveListener(cast(MouseMoveListener) listener);
+                return;
+            }
+            if (MouseTrackListener.classinfo.opEquals(clazz)) {
+                control. removeMouseTrackListener(cast(MouseTrackListener) listener);
+                return;
+            }
+            if (PaintListener.classinfo.opEquals(clazz)) {
+                control. removePaintListener(cast(PaintListener) listener);
+                return;
+            }
+            if (TraverseListener.classinfo.opEquals(clazz)) {
+                control. removeTraverseListener(cast(TraverseListener) listener);
+                return;
+            }
+            if (DisposeListener.classinfo.opEquals(clazz)) {
+                control. removeDisposeListener(cast(DisposeListener) listener);
+                return;
+            }
+        }
+
+        /**
+         * Adds the given listener object to the internal book keeping under
+         * the given listener type (<code>clazz</code>).
+         *
+         * @param clazz the listener type
+         * @param listener the listener object
+         */
+        private void addListener(ClassInfo clazz, EventListener listener) {
+            Control[] children= getChildren();
+            for (int i= 0; i < children.length; i++) {
+                if (children[i] !is null && !children[i].isDisposed())
+                    addListener(clazz, children[i], listener);
+            }
+
+            ListenerInfo info= new ListenerInfo();
+            info.fClass= clazz;
+            info.fListener= listener;
+            fCachedListeners.add(info);
+        }
+
+        /**
+         * Removes the given listener object from the internal book keeping under
+         * the given listener type (<code>clazz</code>).
+         *
+         * @param clazz the listener type
+         * @param listener the listener object
+         */
+        private void removeListener(ClassInfo clazz, EventListener listener) {
+            int length= fCachedListeners.size();
+            for (int i= 0; i < length; i++) {
+                ListenerInfo info= cast(ListenerInfo) fCachedListeners.get(i);
+                if (listener is info.fListener && clazz.opEquals(info.fClass)) {
+                    fCachedListeners.remove(i);
+                    break;
+                }
+            }
+
+            Control[] children= getChildren();
+            for (int i= 0; i < children.length; i++) {
+                if (children[i] !is null && !children[i].isDisposed())
+                    removeListener(clazz, children[i], listener);
+            }
+        }
+
+        /**
+         * Tells this canvas that a child has been added.
+         *
+         * @param child the child
+         */
+        public void childAdded(Control child) {
+            if (child !is null && !child.isDisposed()) {
+                int length= fCachedListeners.size();
+                for (int i= 0; i < length; i++) {
+                    ListenerInfo info= cast(ListenerInfo) fCachedListeners.get(i);
+                    addListener(info.fClass, child, info.fListener);
+                }
+                child.addListener(SWT.MenuDetect, fMenuDetectListener);
+            }
+        }
+
+        /**
+         * Tells this canvas that a child has been removed.
+         *
+         * @param child the child
+         */
+        public void childRemoved(Control child) {
+            if (child !is null && !child.isDisposed()) {
+                int length= fCachedListeners.size();
+                for (int i= 0; i < length; i++) {
+                    ListenerInfo info= cast(ListenerInfo) fCachedListeners.get(i);
+                    removeListener(info.fClass, child, info.fListener);
+                }
+                child.removeListener(SWT.MenuDetect, fMenuDetectListener);
+            }
+        }
+
+        /*
+         * @see Control#removeControlListener(ControlListener)
+         */
+        public void removeControlListener(ControlListener listener) {
+            removeListener(ControlListener.classinfo, listener);
+            super.removeControlListener(listener);
+        }
+
+        /*
+         * @see Control#removeFocusListener(FocusListener)
+         */
+        public void removeFocusListener(FocusListener listener) {
+            removeListener(FocusListener.classinfo, listener);
+            super.removeFocusListener(listener);
+        }
+
+        /*
+         * @see Control#removeHelpListener(HelpListener)
+         */
+        public void removeHelpListener(HelpListener listener) {
+            removeListener(HelpListener.classinfo, listener);
+            super.removeHelpListener(listener);
+        }
+
+        /*
+         * @see Control#removeKeyListener(KeyListener)
+         */
+        public void removeKeyListener(KeyListener listener) {
+            removeListener(KeyListener.classinfo, listener);
+            super.removeKeyListener(listener);
+        }
+
+        /*
+         * @see Control#removeMouseListener(MouseListener)
+         */
+        public void removeMouseListener(MouseListener listener) {
+            removeListener(MouseListener.classinfo, listener);
+            super.removeMouseListener(listener);
+        }
+
+        /*
+         * @see Control#removeMouseMoveListener(MouseMoveListener)
+         */
+        public void removeMouseMoveListener(MouseMoveListener listener) {
+            removeListener(MouseMoveListener.classinfo, listener);
+            super.removeMouseMoveListener(listener);
+        }
+
+        /*
+         * @see Control#removeMouseTrackListener(MouseTrackListener)
+         */
+        public void removeMouseTrackListener(MouseTrackListener listener) {
+            removeListener(MouseTrackListener.classinfo, listener);
+            super.removeMouseTrackListener(listener);
+        }
+
+        /*
+         * @see Control#removePaintListener(PaintListener)
+         */
+        public void removePaintListener(PaintListener listener) {
+            removeListener(PaintListener.classinfo, listener);
+            super.removePaintListener(listener);
+        }
+
+        /*
+         * @see Control#removeTraverseListener(TraverseListener)
+         */
+        public void removeTraverseListener(TraverseListener listener) {
+            removeListener(TraverseListener.classinfo, listener);
+            super.removeTraverseListener(listener);
+        }
+
+        /*
+         * @see Widget#removeDisposeListener(DisposeListener)
+         */
+        public void removeDisposeListener(DisposeListener listener) {
+            removeListener(DisposeListener.classinfo, listener);
+            super.removeDisposeListener(listener);
+        }
+
+        /*
+         * @seeControl#addControlListener(ControlListener)
+         */
+        public void addControlListener(ControlListener listener) {
+            super.addControlListener(listener);
+            addListener(ControlListener.classinfo, listener);
+        }
+
+        /*
+         * @see Control#addFocusListener(FocusListener)
+         */
+        public void addFocusListener(FocusListener listener) {
+            super.addFocusListener(listener);
+            addListener(FocusListener.classinfo, listener);
+        }
+
+        /*
+         * @see Control#addHelpListener(HelpListener)
+         */
+        public void addHelpListener(HelpListener listener) {
+            super.addHelpListener(listener);
+            addListener(HelpListener.classinfo, listener);
+        }
+
+        /*
+         * @see Control#addKeyListener(KeyListener)
+         */
+        public void addKeyListener(KeyListener listener) {
+            super.addKeyListener(listener);
+            addListener(KeyListener.classinfo, listener);
+        }
+
+        /*
+         * @see Control#addMouseListener(MouseListener)
+         */
+        public void addMouseListener(MouseListener listener) {
+            super.addMouseListener(listener);
+            addListener(MouseListener.classinfo, listener);
+        }
+
+        /*
+         * @see Control#addMouseMoveListener(MouseMoveListener)
+         */
+        public void addMouseMoveListener(MouseMoveListener listener) {
+            super.addMouseMoveListener(listener);
+            addListener(MouseMoveListener.classinfo, listener);
+        }
+
+        /*
+         * @see Control#addMouseTrackListener(MouseTrackListener)
+         */
+        public void addMouseTrackListener(MouseTrackListener listener) {
+            super.addMouseTrackListener(listener);
+            addListener(MouseTrackListener.classinfo, listener);
+        }
+
+        /*
+         * @seeControl#addPaintListener(PaintListener)
+         */
+        public void addPaintListener(PaintListener listener) {
+            super.addPaintListener(listener);
+            addListener(PaintListener.classinfo, listener);
+        }
+
+        /*
+         * @see Control#addTraverseListener(TraverseListener)
+         */
+        public void addTraverseListener(TraverseListener listener) {
+            super.addTraverseListener(listener);
+            addListener(TraverseListener.classinfo, listener);
+        }
+
+        /*
+         * @see Widget#addDisposeListener(DisposeListener)
+         */
+        public void addDisposeListener(DisposeListener listener) {
+            super.addDisposeListener(listener);
+            addListener(DisposeListener.classinfo, listener);
+        }
+    }
+
+    /** The ruler's viewer */
+    private ITextViewer fTextViewer;
+    /** The ruler's canvas to which to add the ruler columns */
+    private CompositeRulerCanvas fComposite;
+    /** The ruler's annotation model */
+    private IAnnotationModel fModel;
+    /** The list of columns */
+    private List fDecorators;
+    /** The cached location of the last mouse button activity */
+    private Point fLocation;
+    /** The cached line of the list mouse button activity */
+    private int fLastMouseButtonActivityLine= -1;
+    /** The gap between the individual columns of this composite ruler */
+    private int fGap;
+    /**
+     * The set of annotation listeners.
+     * @since 3.0
+     */
+    private Set fAnnotationListeners;
+
+
+    /**
+     * Constructs a new composite vertical ruler.
+     */
+    public this() {
+        this(0);
+    }
+
+    /**
+     * Constructs a new composite ruler with the given gap between its columns.
+     *
+     * @param gap
+     */
+    public this(int gap) {
+        fDecorators= new ArrayList(2);
+        fLocation= new Point(-1, -1);
+        fAnnotationListeners= new HashSet();
+
+        fGap= gap;
+    }
+
+    /**
+     * Inserts the given column at the specified slot to this composite ruler.
+     * Columns are counted from left to right.
+     *
+     * @param index the index
+     * @param rulerColumn the decorator to be inserted
+     */
+    public void addDecorator(int index, IVerticalRulerColumn rulerColumn) {
+        rulerColumn.setModel(getModel());
+
+        if (index > fDecorators.size())
+            fDecorators.add(cast(Object)rulerColumn);
+        else
+            fDecorators.add(index, cast(Object)rulerColumn);
+
+        if (fComposite !is null && !fComposite.isDisposed()) {
+            rulerColumn.createControl(this, fComposite);
+            fComposite.childAdded(rulerColumn.getControl());
+            layoutTextViewer();
+        }
+    }
+
+    /**
+     * Removes the decorator in the specified slot from this composite ruler.
+     *
+     * @param index the index
+     */
+    public void removeDecorator(int index) {
+        IVerticalRulerColumn rulerColumn= cast(IVerticalRulerColumn) fDecorators.get(index);
+        removeDecorator(rulerColumn);
+    }
+
+    /**
+     * Removes the given decorator from the composite ruler.
+     *
+     * @param rulerColumn the ruler column to be removed
+     * @since 3.0
+     */
+    public void removeDecorator(IVerticalRulerColumn rulerColumn) {
+        fDecorators.remove(cast(Object)rulerColumn);
+        if (rulerColumn !is null) {
+            Control cc= rulerColumn.getControl();
+            if (cc !is null && !cc.isDisposed()) {
+                fComposite.childRemoved(cc);
+                cc.dispose();
+            }
+        }
+        layoutTextViewer();
+    }
+
+    /**
+     * Layouts the text viewer. This also causes this ruler to get
+     * be layouted.
+     */
+    private void layoutTextViewer() {
+
+        Control parent= fTextViewer.getTextWidget();
+
+        if ( cast(ITextViewerExtension)fTextViewer ) {
+            ITextViewerExtension extension= cast(ITextViewerExtension) fTextViewer;
+            parent= extension.getControl();
+        }
+
+        if ( cast(Composite)parent  && !parent.isDisposed())
+            (cast(Composite) parent).layout(true);
+    }
+
+    /*
+     * @see IVerticalRuler#getControl()
+     */
+    public Control getControl() {
+        return fComposite;
+    }
+
+    /*
+     * @see IVerticalRuler#createControl(Composite, ITextViewer)
+     */
+    public Control createControl(Composite parent, ITextViewer textViewer) {
+
+        fTextViewer= textViewer;
+
+        fComposite= new CompositeRulerCanvas(parent, SWT.NONE);
+        fComposite.setLayout(new RulerLayout());
+
+        Iterator iter= fDecorators.iterator();
+        while (iter.hasNext()) {
+            IVerticalRulerColumn column= cast(IVerticalRulerColumn) iter.next();
+            column.createControl(this, fComposite);
+            fComposite.childAdded(column.getControl());
+        }
+
+        return fComposite;
+    }
+
+    /*
+     * @see IVerticalRuler#setModel(IAnnotationModel)
+     */
+    public void setModel(IAnnotationModel model) {
+
+        fModel= model;
+
+        Iterator e= fDecorators.iterator();
+        while (e.hasNext()) {
+            IVerticalRulerColumn column= cast(IVerticalRulerColumn) e.next();
+            column.setModel(model);
+        }
+    }
+
+    /*
+     * @see IVerticalRuler#getModel()
+     */
+    public IAnnotationModel getModel() {
+        return fModel;
+    }
+
+    /*
+     * @see IVerticalRuler#update()
+     */
+    public void update() {
+        if (fComposite !is null && !fComposite.isDisposed()) {
+            Display d= fComposite.getDisplay();
+            if (d !is null) {
+                d.asyncExec(new class()  Runnable {
+                    public void run() {
+                        immediateUpdate();
+                    }
+                });
+            }
+        }
+    }
+
+    /**
+     * Immediately redraws the entire ruler (without asynchronous posting).
+     *
+     * @since 3.2
+     */
+    public void immediateUpdate() {
+        Iterator e= fDecorators.iterator();
+        while (e.hasNext()) {
+            IVerticalRulerColumn column= cast(IVerticalRulerColumn) e.next();
+            column.redraw();
+        }
+    }
+
+    /*
+     * @see IVerticalRulerExtension#setFont(Font)
+     */
+    public void setFont(Font font) {
+        Iterator e= fDecorators.iterator();
+        while (e.hasNext()) {
+            IVerticalRulerColumn column= cast(IVerticalRulerColumn) e.next();
+            column.setFont(font);
+        }
+    }
+
+    /*
+     * @see IVerticalRulerInfo#getWidth()
+     */
+    public int getWidth() {
+        int width= 0;
+        Iterator e= fDecorators.iterator();
+        while (e.hasNext()) {
+            IVerticalRulerColumn column= cast(IVerticalRulerColumn) e.next();
+            width += (column.getWidth() + fGap);
+        }
+        return Math.max(0, width - fGap);
+    }
+
+    /*
+     * @see IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
+     */
+    public int getLineOfLastMouseButtonActivity() {
+        if (fLastMouseButtonActivityLine is -1)
+            fLastMouseButtonActivityLine= toDocumentLineNumber(fLocation.y);
+        else if (fTextViewer.getDocument() is null || fLastMouseButtonActivityLine >= fTextViewer.getDocument().getNumberOfLines())
+            fLastMouseButtonActivityLine= -1;
+        return fLastMouseButtonActivityLine;
+    }
+
+    /*
+     * @see IVerticalRulerInfo#toDocumentLineNumber(int)
+     */
+    public int toDocumentLineNumber(int y_coordinate) {
+        if (fTextViewer is null || y_coordinate is -1)
+            return -1;
+
+        StyledText text= fTextViewer.getTextWidget();
+        int line= text.getLineIndex(y_coordinate);
+
+        if (line is text.getLineCount() - 1) {
+            // check whether y_coordinate exceeds last line
+            if (y_coordinate > text.getLinePixel(line + 1))
+                return -1;
+        }
+
+        return widgetLine2ModelLine(fTextViewer, line);
+    }
+
+    /**
+     * Returns the line in the given viewer's document that correspond to the given
+     * line of the viewer's widget.
+     *
+     * @param viewer the viewer
+     * @param widgetLine the widget line
+     * @return the corresponding line the viewer's document
+     * @since 2.1
+     */
+    protected final static int widgetLine2ModelLine(ITextViewer viewer, int widgetLine) {
+
+        if ( cast(ITextViewerExtension5)viewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) viewer;
+            return extension.widgetLine2ModelLine(widgetLine);
+        }
+
+        try {
+            IRegion r= viewer.getVisibleRegion();
+            IDocument d= viewer.getDocument();
+            return widgetLine += d.getLineOfOffset(r.getOffset());
+        } catch (BadLocationException x) {
+        }
+        return widgetLine;
+    }
+
+    /**
+     * Returns this ruler's text viewer.
+     *
+     * @return this ruler's text viewer
+     */
+    public ITextViewer getTextViewer() {
+        return fTextViewer;
+    }
+
+    /*
+     * @see IVerticalRulerExtension#setLocationOfLastMouseButtonActivity(int, int)
+     */
+    public void setLocationOfLastMouseButtonActivity(int x, int y) {
+        fLocation.x= x;
+        fLocation.y= y;
+        fLastMouseButtonActivityLine= -1;
+    }
+
+    /**
+     * Returns an iterator over the <code>IVerticalRulerColumns</code> that make up this
+     * composite column.
+     *
+     * @return an iterator over the contained columns.
+     * @since 3.0
+     */
+    public Iterator getDecoratorIterator() {
+        Assert.isNotNull(cast(Object)fDecorators, "fDecorators must be initialized"); //$NON-NLS-1$
+        return fDecorators.iterator();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#getHover()
+     * @since 3.0
+     */
+    public IAnnotationHover getHover() {
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#addVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener)
+     * @since 3.0
+     */
+    public void addVerticalRulerListener(IVerticalRulerListener listener) {
+        fAnnotationListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#removeVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener)
+     * @since 3.0
+     */
+    public void removeVerticalRulerListener(IVerticalRulerListener listener) {
+        fAnnotationListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Fires the annotation selected event to all registered vertical ruler
+     * listeners.
+     * TODO use robust iterators
+     *
+     * @param event the event to fire
+     * @since 3.0
+     */
+    public void fireAnnotationSelected(VerticalRulerEvent event) {
+        // forward to listeners
+        for (Iterator it= fAnnotationListeners.iterator(); it.hasNext();) {
+            IVerticalRulerListener listener= cast(IVerticalRulerListener) it.next();
+            listener.annotationSelected(event);
+        }
+    }
+
+    /**
+     * Fires the annotation default selected event to all registered vertical
+     * ruler listeners.
+     * TODO use robust iterators
+     *
+     * @param event the event to fire
+     * @since 3.0
+     */
+    public void fireAnnotationDefaultSelected(VerticalRulerEvent event) {
+        // forward to listeners
+        for (Iterator it= fAnnotationListeners.iterator(); it.hasNext();) {
+            IVerticalRulerListener listener= cast(IVerticalRulerListener) it.next();
+            listener.annotationDefaultSelected(event);
+        }
+    }
+
+    /**
+     * Informs all registered vertical ruler listeners that the content menu on a selected annotation\
+     * is about to be shown.
+     * TODO use robust iterators
+     *
+     * @param event the event to fire
+     * @param menu the menu that is about to be shown
+     * @since 3.0
+     */
+    public void fireAnnotationContextMenuAboutToShow(VerticalRulerEvent event, Menu menu) {
+        // forward to listeners
+        for (Iterator it= fAnnotationListeners.iterator(); it.hasNext();) {
+            IVerticalRulerListener listener= cast(IVerticalRulerListener) it.next();
+            listener.annotationContextMenuAboutToShow(event, menu);
+        }
+    }
+
+    /**
+     * Relayouts the receiver.
+     *
+     * @since 3.3
+     */
+    public void relayout() {
+        layoutTextViewer();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ContentAssistantFacade.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.ContentAssistantFacade;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+
+
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.contentassist.ICompletionListener;
+import org.eclipse.jface.text.contentassist.IContentAssistant;
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension2;
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4;
+
+
+/**
+ * Facade to allow minimal access to the given content assistant.
+ * <p>
+ * The offered API access can grow over time.
+ * </p>
+ *
+ * @since 3.4
+ */
+public final class ContentAssistantFacade {
+
+    private IContentAssistant fContentAssistant;
+
+    /**
+     * Creates a new facade.
+     *
+     * @param contentAssistant the content assistant which implements {@link IContentAssistantExtension2} and {@link IContentAssistantExtension4}
+     */
+    public this(IContentAssistant contentAssistant) {
+        Assert.isLegal(cast(IContentAssistantExtension4)contentAssistant && cast(IContentAssistantExtension4)contentAssistant );
+        fContentAssistant= contentAssistant;
+    }
+
+    /**
+     * Returns the handler for the given command identifier.
+     * <p>
+     * The same handler instance will be returned when called a more than once
+     * with the same command identifier.
+     * </p>
+     *
+     * @param commandId the command identifier
+     * @return the handler for the given command identifier
+     * @throws IllegalArgumentException if the command is not supported by this
+     *             content assistant
+     * @throws IllegalStateException if called when the content assistant is
+     *             uninstalled
+     */
+    public IHandler getHandler(String commandId) {
+        if (fContentAssistant is null)
+            throw new IllegalStateException();
+        return (cast(IContentAssistantExtension4)fContentAssistant).getHandler(commandId);
+    }
+
+    /**
+     * Adds a completion listener that will be informed before proposals are
+     * computed.
+     *
+     * @param listener the listener
+     * @throws IllegalStateException if called when the content assistant is
+     *             uninstalled
+     */
+    public void addCompletionListener(ICompletionListener listener) {
+        if (fContentAssistant is null)
+            throw new IllegalStateException();
+        (cast(IContentAssistantExtension2)fContentAssistant).addCompletionListener(listener);
+    }
+
+    /**
+     * Removes a completion listener.
+     *
+     * @param listener the listener to remove
+     * @throws IllegalStateException if called when the content assistant is
+     *             uninstalled
+     */
+    public void removeCompletionListener(ICompletionListener listener) {
+        (cast(IContentAssistantExtension2)fContentAssistant).removeCompletionListener(listener);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/DefaultAnnotationHover.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,307 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.text.source.DefaultAnnotationHover;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+
+
+
+
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.source.projection.AnnotationBag;
+
+/**
+ * Standard implementation of {@link org.eclipse.jface.text.source.IAnnotationHover}.
+ *
+ * @since 3.2
+ */
+public class DefaultAnnotationHover : IAnnotationHover {
+
+
+    /**
+     * Tells whether the line number should be shown when no annotation is found
+     * under the cursor.
+     *
+     * @since 3.4
+     */
+    private bool fShowLineNumber;
+
+    /**
+     * Creates a new default annotation hover.
+     *
+     * @since 3.4
+     */
+    public this() {
+        this(false);
+    }
+
+    /**
+     * Creates a new default annotation hover.
+     *
+     * @param showLineNumber <code>true</code> if the line number should be shown when no annotation is found
+     * @since 3.4
+     */
+    public this(bool showLineNumber) {
+        fShowLineNumber= showLineNumber;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationHover#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer, int)
+     */
+    public String getHoverInfo(ISourceViewer sourceViewer, int lineNumber) {
+        List javaAnnotations= getAnnotationsForLine(sourceViewer, lineNumber);
+        if (javaAnnotations !is null) {
+
+            if (javaAnnotations.size() is 1) {
+
+                // optimization
+                Annotation annotation= cast(Annotation) javaAnnotations.get(0);
+                String message= annotation.getText();
+                if (message !is null && message.trim().length() > 0)
+                    return formatSingleMessage(message);
+
+            } else {
+
+                List messages= new ArrayList();
+
+                Iterator e= javaAnnotations.iterator();
+                while (e.hasNext()) {
+                    Annotation annotation= cast(Annotation) e.next();
+                    String message= annotation.getText();
+                    if (message !is null && message.trim().length() > 0)
+                        messages.add(message.trim());
+                }
+
+                if (messages.size() is 1)
+                    return formatSingleMessage(stringcast(messages.get(0)));
+
+                if (messages.size() > 1)
+                    return formatMultipleMessages(messages);
+            }
+        }
+
+        if (fShowLineNumber && lineNumber > -1)
+            return JFaceTextMessages.getFormattedString("DefaultAnnotationHover.lineNumber", stringcast(Integer.toString(lineNumber + 1)) ); //$NON-NLS-1$
+
+        return null;
+    }
+
+    /**
+     * Tells whether the annotation should be included in
+     * the computation.
+     *
+     * @param annotation the annotation to test
+     * @return <code>true</code> if the annotation is included in the computation
+     */
+    protected bool isIncluded(Annotation annotation) {
+        return true;
+    }
+
+    /**
+     * Hook method to format the given single message.
+     * <p>
+     * Subclasses can change this to create a different
+     * format like HTML.
+     * </p>
+     *
+     * @param message the message to format
+     * @return the formatted message
+     */
+    protected String formatSingleMessage(String message) {
+        return message;
+    }
+
+    /**
+     * Hook method to formats the given messages.
+     * <p>
+     * Subclasses can change this to create a different
+     * format like HTML.
+     * </p>
+     *
+     * @param messages the messages to format
+     * @return the formatted message
+     */
+    protected String formatMultipleMessages(List messages) {
+        StringBuffer buffer= new StringBuffer();
+        buffer.append(JFaceTextMessages.getString("DefaultAnnotationHover.multipleMarkers")); //$NON-NLS-1$
+
+        Iterator e= messages.iterator();
+        while (e.hasNext()) {
+            buffer.append('\n');
+            String listItemText= stringcast( e.next());
+            buffer.append(JFaceTextMessages.getFormattedString("DefaultAnnotationHover.listItem", stringcast(listItemText ))); //$NON-NLS-1$
+        }
+        return buffer.toString();
+    }
+
+    private bool isRulerLine(Position position, IDocument document, int line) {
+        if (position.getOffset() > -1 && position.getLength() > -1) {
+            try {
+                return line is document.getLineOfOffset(position.getOffset());
+            } catch (BadLocationException x) {
+            }
+        }
+        return false;
+    }
+
+    private IAnnotationModel getAnnotationModel(ISourceViewer viewer) {
+        if ( cast(ISourceViewerExtension2)viewer ) {
+            ISourceViewerExtension2 extension= cast(ISourceViewerExtension2) viewer;
+            return extension.getVisualAnnotationModel();
+        }
+        return viewer.getAnnotationModel();
+    }
+
+    private bool isDuplicateAnnotation(Map messagesAtPosition, Position position, String message) {
+        if (messagesAtPosition.containsKey(position)) {
+            Object value= messagesAtPosition.get(position);
+            if (message==/++/stringcast(value))
+                return true;
+
+            if ( cast(List)value ) {
+                List messages= cast(List)value;
+                if  (messages.contains(message))
+                    return true;
+
+                messages.add(message);
+            } else {
+                ArrayList messages= new ArrayList();
+                messages.add(value);
+                messages.add(message);
+                messagesAtPosition.put(position, messages);
+            }
+        } else
+            messagesAtPosition.put(position, message);
+        return false;
+    }
+
+    private bool includeAnnotation(Annotation annotation, Position position, HashMap messagesAtPosition) {
+        if (!isIncluded(annotation))
+            return false;
+
+        String text= annotation.getText();
+        return (text !is null && !isDuplicateAnnotation(messagesAtPosition, position, text));
+    }
+
+    private List getAnnotationsForLine(ISourceViewer viewer, int line) {
+        IAnnotationModel model= getAnnotationModel(viewer);
+        if (model is null)
+            return null;
+
+        IDocument document= viewer.getDocument();
+        List javaAnnotations= new ArrayList();
+        HashMap messagesAtPosition= new HashMap();
+        Iterator iterator= model.getAnnotationIterator();
+
+        while (iterator.hasNext()) {
+            Annotation annotation= cast(Annotation) iterator.next();
+
+            Position position= model.getPosition(annotation);
+            if (position is null)
+                continue;
+
+            if (!isRulerLine(position, document, line))
+                continue;
+
+            if ( cast(AnnotationBag)annotation ) {
+                AnnotationBag bag= cast(AnnotationBag) annotation;
+                Iterator e= bag.iterator();
+                while (e.hasNext()) {
+                    annotation= cast(Annotation) e.next();
+                    position= model.getPosition(annotation);
+                    if (position !is null && includeAnnotation(annotation, position, messagesAtPosition))
+                        javaAnnotations.add(annotation);
+                }
+                continue;
+            }
+
+            if (includeAnnotation(annotation, position, messagesAtPosition))
+                javaAnnotations.add(annotation);
+        }
+
+        return javaAnnotations;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/DefaultCharacterPairMatcher.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,424 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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:
+ *     Christian Plesner Hansen (plesner@quenta.org) - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.source.DefaultCharacterPairMatcher;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+import java.util.HashSet;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension3;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
+
+/**
+ * A character pair matcher that matches a specified set of character
+ * pairs against each other.  Only characters that occur in the same
+ * partitioning are matched.
+ *
+ * @since 3.3
+ */
+public class DefaultCharacterPairMatcher : ICharacterPairMatcher {
+
+    private int fAnchor= -1;
+    private const CharPairs fPairs;
+    private const String fPartitioning;
+
+    /**
+     * Creates a new character pair matcher that matches the specified
+     * characters within the specified partitioning.  The specified
+     * list of characters must have the form
+     * <blockquote>{ <i>start</i>, <i>end</i>, <i>start</i>, <i>end</i>, ..., <i>start</i>, <i>end</i> }</blockquote>
+     * For instance:
+     * <pre>
+     * char[] chars = new char[] {'(', ')', '{', '}', '[', ']'};
+     * new SimpleCharacterPairMatcher(chars, ...);
+     * </pre>
+     * 
+     * @param chars a list of characters
+     * @param partitioning the partitioning to match within
+     */
+    public this(char[] chars, String partitioning) {
+        Assert.isLegal(chars.length % 2 is 0);
+        Assert.isNotNull(partitioning);
+        fPairs= new CharPairs(chars);
+        fPartitioning= partitioning;
+    }
+    
+    /**
+     * Creates a new character pair matcher that matches characters
+     * within the default partitioning.  The specified list of
+     * characters must have the form
+     * <blockquote>{ <i>start</i>, <i>end</i>, <i>start</i>, <i>end</i>, ..., <i>start</i>, <i>end</i> }</blockquote>
+     * For instance:
+     * <pre>
+     * char[] chars = new char[] {'(', ')', '{', '}', '[', ']'};
+     * new SimpleCharacterPairMatcher(chars);
+     * </pre>
+     * 
+     * @param chars a list of characters
+     */
+    public this(char[] chars) {
+        this(chars, IDocumentExtension3.DEFAULT_PARTITIONING);
+    }
+    
+    /* @see ICharacterPairMatcher#match(IDocument, int) */
+    public IRegion match(IDocument doc, int offset) {
+        if (doc is null || offset < 0 || offset > doc.getLength()) return null;
+        try {
+            return performMatch(doc, offset);
+        } catch (BadLocationException ble) {
+            return null;
+        }
+    }
+        
+    /*
+     * Performs the actual work of matching for #match(IDocument, int).
+     */
+    private IRegion performMatch(IDocument doc, int caretOffset)  {
+        final int charOffset= caretOffset - 1;
+        final char prevChar= doc.getChar(Math.max(charOffset, 0));
+        if (!fPairs.contains(prevChar)) return null;
+        final bool isForward= fPairs.isStartCharacter(prevChar);
+        fAnchor= isForward ? ICharacterPairMatcher.LEFT : ICharacterPairMatcher.RIGHT;
+        final int searchStartPosition= isForward ? caretOffset : caretOffset - 2;
+        final int adjustedOffset= isForward ? charOffset : caretOffset;
+        final String partition= TextUtilities.getContentType(doc, fPartitioning, charOffset, false);
+        final DocumentPartitionAccessor partDoc= new DocumentPartitionAccessor(doc, fPartitioning, partition);
+        int endOffset= findMatchingPeer(partDoc, prevChar, fPairs.getMatching(prevChar),
+                isForward,  isForward ? doc.getLength() : -1,
+                searchStartPosition);
+        if (endOffset is -1) return null;
+        final int adjustedEndOffset= isForward ? endOffset + 1: endOffset;
+        if (adjustedEndOffset is adjustedOffset) return null;
+        return new Region(Math.min(adjustedOffset, adjustedEndOffset),
+                Math.abs(adjustedEndOffset - adjustedOffset));
+    }
+
+    /**
+     * Searches <code>doc</code> for the specified end character, <code>end</code>.
+     * 
+     * @param doc the document to search
+     * @param start the opening matching character
+     * @param end the end character to search for
+     * @param searchForward search forwards or backwards?
+     * @param boundary a boundary at which the search should stop
+     * @param startPos the start offset
+     * @return the index of the end character if it was found, otherwise -1
+     * @throws BadLocationException
+     */
+    private int findMatchingPeer(DocumentPartitionAccessor doc, char start, char end, bool searchForward, int boundary, int startPos)  {
+        int pos= startPos;
+        while (pos !is boundary) {
+            final char c= doc.getChar(pos);
+            if (doc.isMatch(pos, end)) {
+                return pos;
+            } else if (c is start && doc.inPartition(pos)) {
+                pos= findMatchingPeer(doc, start, end, searchForward, boundary,
+                        doc.getNextPosition(pos, searchForward));
+                if (pos is -1) return -1;
+            }
+            pos= doc.getNextPosition(pos, searchForward);
+        }
+        return -1;
+    }
+
+    /* @see ICharacterPairMatcher#getAnchor() */
+    public int getAnchor() {
+        return fAnchor;
+    }
+    
+    /* @see ICharacterPairMatcher#dispose() */
+    public void dispose() { }
+
+    /* @see ICharacterPairMatcher#clear() */
+    public void clear() {
+        fAnchor= -1;
+    }
+
+    /**
+     * Utility class that wraps a document and gives access to
+     * partitioning information.  A document is tied to a particular
+     * partition and, when considering whether or not a position is a
+     * valid match, only considers position within its partition.
+     */
+    private static class DocumentPartitionAccessor {
+        
+        private const IDocument fDocument;
+        private const String fPartitioning, fPartition;
+        private ITypedRegion fCachedPartition;
+        
+        /**
+         * Creates a new partitioned document for the specified document.
+         * 
+         * @param doc the document to wrap
+         * @param partitioning the partitioning used
+         * @param partition the partition managed by this document
+         */
+        public this(IDocument doc, String partitioning,
+                String partition) {
+            fDocument= doc;
+            fPartitioning= partitioning;
+            fPartition= partition;
+        }
+    
+        /**
+         * Returns the character at the specified position in this document.
+         * 
+         * @param pos an offset within this document
+         * @return the character at the offset
+         * @throws BadLocationException
+         */
+        public char getChar(int pos)  {
+            return fDocument.getChar(pos);
+        }
+        
+        /**
+         * Returns true if the character at the specified position is a
+         * valid match for the specified end character.  To be a valid
+         * match, it must be in the appropriate partition and equal to the
+         * end character.
+         * 
+         * @param pos an offset within this document
+         * @param end the end character to match against
+         * @return true exactly if the position represents a valid match
+         * @throws BadLocationException
+         */
+        public bool isMatch(int pos, char end)  {
+            return getChar(pos) is end && inPartition(pos);
+        }
+        
+        /**
+         * Returns true if the specified offset is within the partition
+         * managed by this document.
+         * 
+         * @param pos an offset within this document
+         * @return true if the offset is within this document's partition
+         */
+        public bool inPartition(int pos) {
+            final ITypedRegion partition= getPartition(pos);
+            return partition !is null && partition.getType().equals(fPartition);
+        }
+        
+        /**
+         * Returns the next position to query in the search.  The position
+         * is not guaranteed to be in this document's partition.
+         * 
+         * @param pos an offset within the document
+         * @param searchForward the direction of the search
+         * @return the next position to query
+         */
+        public int getNextPosition(int pos, bool searchForward) {
+            final ITypedRegion partition= getPartition(pos);
+            if (partition is null) return simpleIncrement(pos, searchForward);
+            if (fPartition.equals(partition.getType()))
+                return simpleIncrement(pos, searchForward);
+            if (searchForward) {
+                int end= partition.getOffset() + partition.getLength();
+                if (pos < end)
+                    return end;
+            } else {
+                int offset= partition.getOffset();
+                if (pos > offset)
+                    return offset - 1;
+            }
+            return simpleIncrement(pos, searchForward);
+        }
+    
+        private int simpleIncrement(int pos, bool searchForward) {
+            return pos + (searchForward ? 1 : -1);
+        }
+        
+        /**
+         * Returns partition information about the region containing the
+         * specified position.
+         * 
+         * @param pos a position within this document.
+         * @return positioning information about the region containing the
+         *   position
+         */
+        private ITypedRegion getPartition(int pos) {
+            if (fCachedPartition is null || !contains(fCachedPartition, pos)) {
+                Assert.isTrue(pos >= 0 && pos <= fDocument.getLength());
+                try {
+                    fCachedPartition= TextUtilities.getPartition(fDocument, fPartitioning, pos, false);
+                } catch (BadLocationException e) {
+                    fCachedPartition= null;
+                }
+            }
+            return fCachedPartition;
+        }
+        
+        private static bool contains(IRegion region, int pos) {
+            int offset= region.getOffset();
+            return offset <= pos && pos < offset + region.getLength();
+        }
+        
+    }
+
+    /**
+     * Utility class that encapsulates access to matching character pairs.
+     */
+    private static class CharPairs {
+
+        private const char[] fPairs;
+
+        public this(char[] pairs) {
+            fPairs= pairs;
+        }
+
+        /**
+         * Returns true if the specified character pair occurs in one
+         * of the character pairs.
+         * 
+         * @param c a character
+         * @return true exactly if the character occurs in one of the pairs
+         */
+        public bool contains(char c) {
+            return getAllCharacters().contains(new Character(c));
+        }
+
+        private Set/*<Character>*/ fCharsCache= null;
+        /**
+         * @return A set containing all characters occurring in character pairs.
+         */
+        private Set/*<Character>*/ getAllCharacters() {
+            if (fCharsCache is null) {
+                Set/*<Character>*/ set= new HashSet/*<Character>*/();
+                for (int i= 0; i < fPairs.length; i++)
+                    set.add(new Character(fPairs[i]));
+                fCharsCache= set;
+            }
+            return fCharsCache;
+        }
+
+        /**
+         * Returns true if the specified character opens a character pair
+         * when scanning in the specified direction.
+         *
+         * @param c a character
+         * @param searchForward the direction of the search
+         * @return whether or not the character opens a character pair
+         */
+        public bool isOpeningCharacter(char c, bool searchForward) {
+            for (int i= 0; i < fPairs.length; i += 2) {
+                if (searchForward && getStartChar(i) is c) return true;
+                else if (!searchForward && getEndChar(i) is c) return true;
+            }
+            return false;
+        }
+
+        /**
+         * Returns true of the specified character is a start character.
+         * 
+         * @param c a character
+         * @return true exactly if the character is a start character
+         */
+        public bool isStartCharacter(char c) {
+            return this.isOpeningCharacter(c, true);
+        }
+    
+        /**
+         * Returns the matching character for the specified character.
+         * 
+         * @param c a character occurring in a character pair
+         * @return the matching character
+         */
+        public char getMatching(char c) {
+            for (int i= 0; i < fPairs.length; i += 2) {
+                if (getStartChar(i) is c) return getEndChar(i);
+                else if (getEndChar(i) is c) return getStartChar(i);
+            }
+            Assert.isTrue(false);
+            return '\0';
+        }
+    
+        private char getStartChar(int i) {
+            return fPairs[i];
+        }
+    
+        private char getEndChar(int i) {
+            return fPairs[i + 1];
+        }
+    
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationAccess.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.source.IAnnotationAccess;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * An annotation access provides access to information that is not available via
+ * the API of {@link org.eclipse.jface.text.source.Annotation}. With version
+ * 3.0 all this information is now available from the annotation itself.
+ * <p>
+ *
+ * In order to provide backward compatibility for clients of
+ * <code>IAnnotationAccess</code>, extension interfaces are used as a means
+ * of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.source.IAnnotationAccessExtension} since
+ *     version 3.0 replacing all methods in that interface</li>
+ * <li>{@link IAnnotationAccessExtension2} since
+ *     version 3.2 allowing to set a quick assist assistant to an annotation access.</li>
+ * </ul></p>
+ * <p>
+ * Clients usually implement this interface and its extension interfaces.</p>
+ *
+ * @see org.eclipse.jface.text.source.IAnnotationAccessExtension
+ * @see org.eclipse.jface.text.source.Annotation
+ * @since 2.1
+ */
+public interface IAnnotationAccess {
+
+    /**
+     * Returns the type of the given annotation.
+     *
+     * @param annotation the annotation
+     * @return the type of the given annotation or <code>null</code> if it has none.
+     * @deprecated use <code>Annotation.getType()</code>
+     */
+    Object getType(Annotation annotation);
+
+    /**
+     * Returns whether the given annotation spans multiple lines.
+     *
+     * @param annotation the annotation
+     * @return <code>true</code> if the annotation spans multiple lines,
+     *  <code>false</code> otherwise
+     *
+     * @deprecated assumed to always return <code>true</code>
+     */
+    bool isMultiLine(Annotation annotation);
+
+    /**
+     * Returns whether the given annotation is temporary rather than persistent.
+     *
+     * @param annotation the annotation
+     * @return <code>true</code> if the annotation is temporary,
+     *  <code>false</code> otherwise
+     * @deprecated use <code>Annotation.isPersistent()</code>
+     */
+    bool isTemporary(Annotation annotation);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationAccessExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.source.IAnnotationAccessExtension;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.source.IAnnotationAccess}.<p>
+ * This interface replaces the methods of <code>IAnnotationAccess</code>.<p>
+ * This interface provides
+ * <ul>
+ * <li> a label for the annotation type of a given annotation</li>
+ * <li> the paint layer of a given annotation</li>
+ * <li> means to paint a given annotation</li>
+ * <li> information about the type hierarchy of the annotation type of a given annotation</li>
+ * <ul>.
+ *
+ * @see org.eclipse.jface.text.source.IAnnotationAccess
+ * @since 3.0
+ */
+public interface IAnnotationAccessExtension {
+
+    /**
+     * The default annotation layer.
+     */
+    static const int DEFAULT_LAYER= IAnnotationPresentation.DEFAULT_LAYER;
+
+    /**
+     * Returns the label for the given annotation's type.
+     *
+     * @param annotation the annotation
+     * @return the label the given annotation's type or <code>null</code> if no such label exists
+     */
+    String getTypeLabel(Annotation annotation);
+
+    /**
+     * Returns the layer for given annotation. Annotations are considered
+     * being located at layers and are considered being painted starting with
+     * layer 0 upwards. Thus an annotation at layer 5 will be drawn on top of
+     * all co-located annotations at the layers 4 - 0.
+     *
+     * @param annotation the annotation
+     * @return the layer of the given annotation
+     */
+    int getLayer(Annotation annotation);
+
+    /**
+     * Draws a graphical representation of the given annotation within the given bounds.
+     * <p>
+     * <em>Note that this method is not used when drawing annotations on the editor's
+     * text widget. This is handled trough a {@link org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy}.</em>
+     * </p>
+     * @param annotation the given annotation
+     * @param gc the drawing GC
+     * @param canvas the canvas to draw on
+     * @param bounds the bounds inside the canvas to draw on
+     */
+    void paint(Annotation annotation, GC gc, Canvas canvas, Rectangle bounds);
+
+    /**
+     * Returns <code>true</code> if painting <code>annotation</code> will produce something
+     * meaningful, <code>false</code> if not. E.g. if no image is available.
+     * <p>
+     * <em>Note that this method is not used when drawing annotations on the editor's
+     * text widget. This is handled trough a {@link org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy}.</em>
+     * </p>
+     * @param annotation the annotation to check whether it can be painted
+     * @return <code>true</code> if painting <code>annotation</code> will succeed
+     */
+    bool isPaintable(Annotation annotation);
+
+    /**
+     * Returns <code>true</code> if the given annotation is of the given type
+     * or <code>false</code> otherwise.
+     *
+     * @param annotationType the annotation type
+     * @param potentialSupertype the potential super annotation type
+     * @return <code>true</code> if annotation type is a sub-type of the potential annotation super type
+     */
+    bool isSubtype(Object annotationType, Object potentialSupertype);
+
+    /**
+     * Returns the list of super types for the given annotation type. This does not include the type
+     * itself. The index in the array of super types indicates the length of the path in the hierarchy
+     * graph to the given annotation type.
+     *
+     * @param annotationType the annotation type to check
+     * @return the super types for the given annotation type
+     */
+    Object[] getSupertypes(Object annotationType);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationAccessExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.source.IAnnotationAccessExtension2;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.source.IAnnotationAccess}.<p>
+ * This interface allows clients to set a quick assist assistant.
+ *
+ * @see org.eclipse.jface.text.source.IAnnotationAccess
+ * @since 3.2
+ */
+public interface IAnnotationAccessExtension2 {
+    
+    /**
+     * Provides this annotation access with a quick assist assistant that
+     * is used to decide whether the quick fix image should be shown.
+     * 
+     * @param assistant the quick assist assistant
+     */
+    void setQuickAssistAssistant(IQuickAssistAssistant assistant);
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationHover.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.IAnnotationHover;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+
+
+/**
+ * Provides the information to be displayed in a hover popup window which
+ * appears over the presentation area of annotations.
+ * <p>
+ * In order to provide backward compatibility for clients of
+ * <code>IAnnotationHover</code>, extension interfaces are used as a means of
+ * evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.source.IAnnotationHoverExtension} since
+ *     version 3.0 allowing a text hover to provide a creator for the hover control.
+ *     This allows for sophisticated hovers in a way that information computed by
+ *     the hover can be displayed in the best possible form.</li>
+ * <li>{@link org.eclipse.jface.text.source.IAnnotationHoverExtension2} since
+ *     version 3.2 allowing a text hover to specify whether it handles mouse-wheel 
+ *     events itself.</li>
+ * </ul></p>
+ * <p>
+ * Clients may implement this interface.</p>
+ *
+ * @see org.eclipse.jface.text.source.IAnnotationHoverExtension
+ * @see org.eclipse.jface.text.source.IAnnotationHoverExtension2
+ */
+public interface IAnnotationHover {
+
+    /**
+     * Returns the text which should be presented in the a
+     * hover popup window. This information is requested based on
+     * the specified line number.
+     *
+     * @param sourceViewer the source viewer this hover is registered with
+     * @param lineNumber the line number for which information is requested
+     * @return the requested information or <code>null</code> if no such information exists
+     */
+    String getHoverInfo(ISourceViewer sourceViewer, int lineNumber);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationHoverExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.IAnnotationHoverExtension;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.jface.text.IInformationControlCreator;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.source.IAnnotationHover} for
+ * <ul>
+ * <li>providing its own information control creator</li>
+ * <li>providing the range of lines for which the hover for a given line is valid</li>
+ * <li>providing whether the information control can interact with the mouse cursor</li>
+ * </ul>
+ *
+ * @see org.eclipse.jface.text.IInformationControlCreator
+ * @see org.eclipse.jface.text.source.IAnnotationHover
+ * @since 3.0
+ */
+public interface IAnnotationHoverExtension {
+
+    /**
+     * Returns the hover control creator of this annotation hover.
+     *
+     * @return the hover control creator
+     */
+    IInformationControlCreator getHoverControlCreator();
+
+    /**
+     * Returns whether the provided information control can interact with the mouse cursor. I.e. the
+     * hover must implement custom information control management.
+     *
+     * @return <code>true</code> if the mouse cursor can be handled
+     */
+    bool canHandleMouseCursor();
+
+    /**
+     * Returns the object which should be presented in the a
+     * hover popup window. The information is requested based on
+     * the specified line range.
+     *
+     * @param sourceViewer the source viewer this hover is registered with
+     * @param lineRange the line range for which information is requested
+     * @param visibleNumberOfLines the number of visible lines
+     * @return the requested information or <code>null</code> if no such information exists
+     */
+    Object getHoverInfo(ISourceViewer sourceViewer, ILineRange lineRange, int visibleNumberOfLines);
+
+    /**
+     * Returns the range of lines that include the given line number for which
+     * the same hover information is valid.
+     *
+     * @param viewer the viewer which the hover is queried for
+     * @param lineNumber the line number of the line for which a hover is displayed for
+     * @return the computed line range or <code>null</code> for no range
+     */
+    ILineRange getHoverLineRange(ISourceViewer viewer, int lineNumber);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationHoverExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.source.IAnnotationHoverExtension2;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.source.IAnnotationHover} for
+ * <ul>
+ * <li>providing whether the information control can interact with the mouse wheel</li>
+ * </ul>
+ *
+ * @see org.eclipse.jface.text.source.IAnnotationHover
+ * @since 3.2
+ */
+public interface IAnnotationHoverExtension2 {
+    /**
+     * Returns whether the provided information control can interact with the mouse wheel. I.e. the
+     * hover will not be closed when the mouse wheel is moved.
+     *
+     * @return <code>true</code> if the mouse wheel is handled by the hover
+     */
+    bool canHandleMouseWheel();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationPresentation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.source.IAnnotationPresentation;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+
+/**
+ * Interface for annotations that can take care of their own visible representation.
+ *
+ * @since 3.0
+ */
+public interface IAnnotationPresentation {
+
+    /**
+     * The default annotation layer.
+     */
+    static const int DEFAULT_LAYER= 0;
+
+
+    /**
+     * Returns the annotations drawing layer.
+     *
+     * @return the annotations drawing layer
+     */
+    int getLayer();
+
+    /**
+     * Implement this method to draw a graphical representation
+     * of this annotation within the given bounds.
+     * <p>
+     * <em>Note that this method is not used when drawing annotations on the editor's
+     * text widget. This is handled trough a {@link org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy}.</em>
+     * </p>
+     * @param gc the drawing GC
+     * @param canvas the canvas to draw on
+     * @param bounds the bounds inside the canvas to draw on
+     */
+    void paint(GC gc, Canvas canvas, Rectangle bounds);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/IChangeRulerColumn.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.IChangeRulerColumn;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.swt.graphics.Color;
+
+/**
+ * An <code>IChangeRulerColumn</code> can display quick diff information.
+ *
+ * @since 3.0
+ */
+public interface IChangeRulerColumn : IVerticalRulerColumn, IVerticalRulerInfoExtension {
+
+    /** The ID under which the quick diff model is registered with a document's annotation model. */
+    public static const String QUICK_DIFF_MODEL_ID= "diff"; //$NON-NLS-1$
+
+    /**
+     * Sets the hover of this ruler column.
+     *
+     * @param hover the hover that will produce hover information text for this ruler column
+     */
+    public abstract void setHover(IAnnotationHover hover);
+
+    /**
+     * Sets the background color for normal lines. The color has to be disposed of by the caller when
+     * the receiver is no longer used.
+     *
+     * @param backgroundColor the new color to be used as standard line background
+     */
+    public abstract void setBackground(Color backgroundColor);
+
+    /**
+     * Sets the background color for added lines. The color has to be disposed of by the caller when
+     * the receiver is no longer used.
+     *
+     * @param addedColor the new color to be used for the added lines background
+     */
+    public abstract void setAddedColor(Color addedColor);
+
+    /**
+     * Sets the background color for changed lines. The color has to be disposed of by the caller when
+     * the receiver is no longer used.
+     *
+     * @param changedColor the new color to be used for the changed lines background
+     */
+    public abstract void setChangedColor(Color changedColor);
+
+    /**
+     * Sets the color for the deleted lines indicator. The color has to be disposed of by the caller when
+     * the receiver is no longer used.
+     *
+     * @param deletedColor the new color to be used for the deleted lines indicator.
+     */
+    public abstract void setDeletedColor(Color deletedColor);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ICharacterPairMatcher.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.ICharacterPairMatcher;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+
+/**
+ * A character pair matcher finds to a character at a certain document offset
+ * the matching peer character. It is the matchers responsibility to define the
+ * concepts of "matching" and "peer". The matching process starts at a given
+ * offset. Starting of this offset, the matcher chooses a character close to
+ * this offset. The anchor defines whether the chosen character is left or right
+ * of the initial offset. The matcher then searches for the matching peer
+ * character of the chosen character and if it finds one, delivers the minimal
+ * region of the document that contains both characters.
+ *
+ * @since 2.1
+ */
+public interface ICharacterPairMatcher {
+
+    /**
+     * Indicates the anchor value "right".
+     */
+    static const int RIGHT= 0;
+    /**
+     * Indicates the anchor value "left".
+     */
+    static const int LEFT= 1;
+
+
+    /**
+     * Disposes this pair matcher.
+     */
+    void dispose();
+
+    /**
+     * Clears this pair matcher. I.e. the matcher throws away all state it might
+     * remember and prepares itself for a new call of the <code>match</code>
+     * method.
+     */
+    void clear();
+
+    /**
+     * Starting at the given offset, the matcher chooses a character close to this offset.
+     * The matcher then searches for the matching peer character of the chosen character
+     * and if it finds one, returns the minimal region of the document that contains both characters.
+     * It returns <code>null</code> if there is no peer character.
+     *
+     * @param iDocument the document to work on
+     * @param i the start offset
+     * @return the minimal region containing the peer characters
+     */
+    IRegion match(IDocument iDocument, int i);
+
+    /**
+     * Returns the anchor for the region of the matching peer characters. The anchor
+     * says whether the character that has been chosen to search for its peer character
+     * has been left or right of the initial offset.
+     *
+     * @return <code>RIGHT</code> or <code>LEFT</code>
+     */
+    int getAnchor();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ILineDiffInfo.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.ILineDiffInfo;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Describes the change state of one line, which consists of the state of the line itself, which
+ * can be <code>UNCHANGED</code>, <code>CHANGED</code> or <code>ADDED</code>, and the number of
+ * deleted lines before and after this line.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.0
+ */
+public interface ILineDiffInfo {
+
+    /** Denotes an unchanged line. */
+    static const int UNCHANGED= 0;
+
+    /** Denotes an added line. */
+    static const int ADDED= 1;
+
+    /** Denotes a changed line. */
+    static const int CHANGED= 2;
+
+    /**
+     * Returns the number of deleted lines after this line.
+     *
+     * @return the number of lines after this line.
+     */
+    int getRemovedLinesBelow();
+
+    /**
+     * Returns the number of deleted lines before this line.
+     *
+     * @return the number of lines before this line.
+     */
+    int getRemovedLinesAbove();
+
+    /**
+     * Returns the type of this line, one out of <code>UNCHANGED</code>, <code>CHANGED</code> or
+     * <code>ADDED</code>.
+     *
+     * @return the type of this line.
+     */
+    int getChangeType();
+
+    /**
+     * Returns whether this line has any changes (to itself, or any deletions before or after it).
+     *
+     * @return <code>true</code>, if the line's state (as returned by <code>getType</code>) is
+     * either <code>CHANGED</code> or <code>ADDED</code> or either of <code>getRemovedLinesBelow</code>
+     * and <code>getRemovedLinesAbove</code> would return a number &gt; 0
+     */
+    bool hasChanges();
+
+    /**
+     * Returns the original text of this changed region
+     *
+     * @return the original text of this changed region, including any deleted lines. The returned
+     * value and its elements may not be <code>null/code>, it may however be of zero length
+     */
+    String[] getOriginalText();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ILineDiffer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.source.ILineDiffer;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Iterator;
+
+import org.eclipse.jface.text.BadLocationException;
+
+
+/**
+ * Protocol that allows direct access to line information. Usually, implementations will also
+ * implement <code>IAnnotationModel</code>, which only allows <code>Iterator</code> based access
+ * to annotations.
+ * <p>
+ * <code>ILineDiffer</code> also allows to revert any lines to their original
+ * contents as defined by the quick diff reference used by the receiver.
+ * </p>
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ * <p>
+ * In order to provide backward compatibility for clients of <code>ILineDiffer</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interface
+ * exists:
+ * <ul>
+ * <li> {@link ILineDifferExtension} (since version 3.1): introducing the concept
+ *      suspending and resuming an <code>ILineDiffer</code>.</li>
+ * <li> {@link ILineDifferExtension2} (since version 3.3): allowing to query the suspension state
+ * of an <code>ILineDiffer</code>.</li>
+ * </ul>
+ * </p>
+ *
+ * @since 3.0
+ */
+public interface ILineDiffer {
+
+    /**
+     * Determines the line state for line <code>line</code> in the targeted document.
+     *
+     * @param line the line to get diff information for
+     * @return the line information object for <code>line</code> or <code>null</code> if none
+     */
+    ILineDiffInfo getLineInfo(int line);
+
+    /**
+     * Reverts a single changed line to its original state, not touching any lines that
+     * are deleted at its borders.
+     *
+     * @param line the line number of the line to be restored.
+     * @throws BadLocationException if <code>line</code> is out of bounds.
+     */
+    void revertLine(int line) ;
+
+    /**
+     * Reverts a block of modified / added lines to their original state, including any deleted
+     * lines inside the block or at its borders. A block is considered to be a range of modified
+     * (e.g. changed, or added) lines.
+     *
+     * @param line any line in the block to be reverted.
+     * @throws BadLocationException if <code>line</code> is out of bounds.
+     */
+    void revertBlock(int line) ;
+
+    /**
+     * Reverts a range of lines to their original state, including any deleted
+     * lines inside the block or at its borders.
+     *
+     * @param line any line in the block to be reverted.
+     * @param nLines the number of lines to be reverted, must be &gt; 0.
+     * @throws BadLocationException if <code>line</code> is out of bounds.
+     */
+    void revertSelection(int line, int nLines) ;
+
+    /**
+     * Restores the deleted lines after <code>line</code>.
+     *
+     * @param line the deleted lines following this line number are restored.
+     * @return the number of restored lines.
+     * @throws BadLocationException if <code>line</code> is out of bounds.
+     */
+    int restoreAfterLine(int line) ;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ILineDifferExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.source.ILineDifferExtension;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Extension interface for {@link ILineDiffer}.
+ * <p>
+ * Introduces the concept of suspending a differ. A <code>ILineDiffer</code> may
+ * be suspended into a dormant state, and resumed to normal operation.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface ILineDifferExtension {
+
+    /**
+     * Suspends the receiver. All differences are cleared.
+     */
+    void suspend();
+
+    /**
+     * Resumes the receiver. Must only be called after suspend.
+     */
+    void resume();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ILineDifferExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.source.ILineDifferExtension2;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Extension interface for {@link ILineDiffer}.
+ * <p>
+ * Allows to query the suspension state.
+ * </p>
+ *
+ * @since 3.3
+ */
+public interface ILineDifferExtension2 {
+    /**
+     * Returns <code>true</code> if the receiver is suspended, <code>false</code> otherwise.
+     * 
+     * @return <code>true</code> if the receiver is suspended, <code>false</code> otherwise
+     */
+    bool isSuspended();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ILineRange.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.ILineRange;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Describes a range of lines.
+ * <p>
+ * Note that the number of lines is 1-based, e.g. <code>getStartLine() + getNumberOfLines()</code>
+ * computes the first line <em>after</em> the range, and a range with
+ * <code>getNumberOfLines() is 0</code> is empty.
+ * </p>
+ * 
+ * @since 3.0
+ */
+public interface ILineRange {
+
+    /**
+     * Returns the start line of this line range or <code>-1</code>.
+     *
+     * @return the start line of this line range or <code>-1</code> if this line range is invalid.
+     */
+    int getStartLine();
+
+    /**
+     * Returns the number of lines of this line range or <code>-1</code>.
+     *
+     * @return the number of lines in this line range or <code>-1</code> if this line range is invalid.
+     */
+    int getNumberOfLines();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/IOverviewRuler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.IOverviewRuler;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * This interface defines a visual component which may serve
+ * text viewers as an overview annotation presentation area.  This means,
+ * presentation of annotations is independent from the actual view port of
+ * the text viewer. The annotations of the viewer's whole document are
+ * visible in the overview ruler.
+ * <p>
+ * This interfaces embodies three contracts:
+ * <ul>
+ * <li> The overview ruler retrieves the annotations it presents from an annotation model.
+ * <li> The ruler is a visual component which must be integrated in a hierarchy of SWT controls.
+ * <li> The ruler provides interested clients with mapping and
+ *      interaction information. This covers the mapping between
+ *      coordinates of the ruler's control and line numbers based
+ *      on the connected text viewer's document (<code>IVerticalRulerInfo</code>).
+ * </ul></p>
+ * <p>
+ * Clients may implement this interface or use the default implementation provided
+ * by <code>OverviewlRuler</code>.</p>
+ *
+ * @see org.eclipse.jface.text.ITextViewer
+ * @since 2.1
+ */
+public interface IOverviewRuler : IVerticalRuler {
+
+    /**
+     * Returns whether there is an annotation an the given vertical coordinate. This
+     * method takes the compression factor of the overview ruler into account.
+     *
+     * @param y the y-coordinate
+     * @return <code>true</code> if there is an annotation, <code>false</code> otherwise
+     */
+    bool hasAnnotation(int y);
+
+    /**
+     * Returns the height of the visual presentation of an annotation in this
+     * overview ruler. Assumes that all annotations are represented using the
+     * same height.
+     *
+     * @return int the visual height of an annotation
+     */
+    int getAnnotationHeight();
+
+    /**
+     * Sets the color for the given annotation type in this overview ruler.
+     *
+     * @param annotationType the annotation type
+     * @param color the color
+     */
+    void setAnnotationTypeColor(Object annotationType, Color color);
+
+    /**
+     * Sets the drawing layer for the given annotation type in this overview ruler.
+     *
+     * @param annotationType the annotation type
+     * @param layer the drawing layer
+     */
+    void setAnnotationTypeLayer(Object annotationType, int layer);
+
+    /**
+     * Adds the given annotation type to this overview ruler. Starting with this
+     * call, annotations of the given type are shown in the overview ruler.
+     *
+     * @param annotationType the annotation type
+     */
+    void addAnnotationType(Object annotationType);
+
+    /**
+     * Removes the given annotation type from this overview ruler. Annotations
+     * of the given type are no longer shown in the overview ruler.
+     *
+     * @param annotationType the annotation type
+     */
+    void removeAnnotationType(Object annotationType);
+
+    /**
+     * Adds the given annotation type to the header of this ruler. Starting with
+     * this call, the presence of annotations is tracked and the header is drawn
+     * in the configured color.
+     *
+     * @param annotationType the annotation type to be tracked
+     */
+    void addHeaderAnnotationType(Object annotationType);
+
+    /**
+     * Removes the given annotation type from the header of this ruler. The
+     * presence of annotations of the given type is no longer tracked and the
+     * header is drawn in the default color, depending on the other configured
+     * configured annotation types.
+     *
+     * @param annotationType the annotation type to be removed
+     */
+    void removeHeaderAnnotationType(Object annotationType);
+
+    /**
+     * Returns this rulers header control. This is the little area between the
+     * top of the text widget and the top of this overview ruler.
+     *
+     * @return the header control of this overview ruler.
+     */
+    Control getHeaderControl();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ISharedTextColors.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.source.ISharedTextColors;
+
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.RGB;
+
+
+/**
+ * Manages SWT color objects. Until the <code>dispose</code> method is called,
+ * the same color object is returned for equal <code>RGB</code> values.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 2.1
+ */
+public interface ISharedTextColors {
+
+    /**
+     * Returns the color object for the value represented by the given
+     * <code>RGB</code> object.
+     *
+     * @param rgb the RBG color specification
+     * @return the color object for the given RGB value
+     */
+    Color getColor(RGB rgb);
+
+    /**
+     * Tells this object to dispose all its managed colors.
+     */
+    void dispose();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ISourceViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.ISourceViewer;
+
+import org.eclipse.jface.text.source.SourceViewerConfiguration;
+import org.eclipse.jface.text.source.IAnnotationHover;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.Annotation;
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextOperationTarget;
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * In addition to the text viewer functionality a source viewer supports:
+ * <ul>
+ * <li>visual annotations based on an annotation model
+ * <li>visual range indication
+ * <li>management of text viewer add-ons
+ * <li>explicit configuration
+ * </ul>
+ * It is assumed that range indication and visual annotations are shown inside
+ * the same presentation area. There are no assumptions about whether this area
+ * is different from the viewer's text widget.
+ * <p>
+ * As the visibility of visual annotations can dynamically be changed, it is
+ * assumed that the annotation presentation area can dynamically be hidden if it
+ * is different from the text widget.
+ * <p>
+ * In order to provide backward compatibility for clients of
+ * <code>ISourceViewer</code>, extension interfaces are used as a means of
+ * evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.source.ISourceViewerExtension} since version 2.1
+ * introducing the concept of an annotation overview.</li>
+ * <li>{@link org.eclipse.jface.text.source.ISourceViewerExtension2} since version 3.0
+ * allowing source viewers to roll back a previously performed configuration and
+ * allows access to the viewer's visual annotation model.</li>
+ * <li>{@link org.eclipse.jface.text.source.ISourceViewerExtension3} since version 3.2
+ * introducing the concept of a quick assist assistant and providing access
+ * to the quick assist invocation context as well as the current annotation hover.</li>
+ * <li>{@link org.eclipse.jface.text.source.ISourceViewerExtension4} since version 3.4
+ * introducing API to access a minimal set of content assistant APIs.</li>
+ * </ul></p>
+ * <p>
+ * Clients may implement this interface and its extension interfaces or use the
+ * default implementation provided by
+ * {@link org.eclipse.jface.text.source.SourceViewer}.</p>
+ *
+ * @see org.eclipse.jface.text.source.ISourceViewerExtension
+ * @see org.eclipse.jface.text.source.ISourceViewerExtension2
+ * @see org.eclipse.jface.text.source.ISourceViewerExtension3
+ * @see org.eclipse.jface.text.source.ISourceViewerExtension4
+ */
+public interface ISourceViewer : ITextViewer {
+
+    /**
+     * Text operation code for requesting content assist to show completion
+     * proposals for the current insert position.
+     */
+    static const int CONTENTASSIST_PROPOSALS= ITextOperationTarget.STRIP_PREFIX + 1;
+
+    /**
+     * Text operation code for requesting content assist to show
+     * the content information for the current insert position.
+     */
+    static const int CONTENTASSIST_CONTEXT_INFORMATION=  ITextOperationTarget.STRIP_PREFIX + 2;
+
+    /**
+     * Text operation code for formatting the selected text or complete document
+     * of this viewer if the selection is empty.
+     */
+    static const int FORMAT= ITextOperationTarget.STRIP_PREFIX + 3;
+
+    /**
+     * Text operation code for requesting information at the current insertion position.
+     * @since 2.0
+     */
+    static const int INFORMATION = ITextOperationTarget.STRIP_PREFIX + 4;
+
+    /*
+     * XXX: Cannot continue numbering due to operation codes used in ProjectionViewer
+     */
+
+    /**
+     * Text operation code for requesting quick assist. This will normally
+     * show quick assist and quick fix proposals for the current position.
+     * @since 3.2
+     */
+    static const int QUICK_ASSIST= ITextOperationTarget.STRIP_PREFIX + 10;
+
+    /**
+     * Configures the source viewer using the given configuration. Prior to 3.0 this
+     * method can only be called once. Since 3.0 this method can be called again
+     * after a call to {@link ISourceViewerExtension2#unconfigure()}.
+     *
+     * @param configuration the source viewer configuration to be used
+     */
+    void configure(SourceViewerConfiguration configuration);
+
+    /**
+     * Sets the annotation hover of this source viewer. The annotation hover
+     * provides the information to be displayed in a hover popup window
+     * if requested over the annotation presentation area. The annotation
+     * hover is assumed to be line oriented.
+     *
+     * @param annotationHover the hover to be used, <code>null</code> is a valid argument
+     */
+    void setAnnotationHover(IAnnotationHover annotationHover);
+
+    /**
+     * Sets the given document as this viewer's text model and the
+     * given annotation model as the model for this viewer's visual
+     * annotations. The presentation is accordingly updated. An appropriate
+     * <code>TextEvent</code> is issued. This text event does not carry
+     * a related document event.
+     *
+     * @param document the viewer's new input document
+     * @param annotationModel the model for the viewer's visual annotations
+     *
+     * @see ITextViewer#setDocument(IDocument)
+     */
+    void setDocument(IDocument document, IAnnotationModel annotationModel);
+
+    /**
+     * Sets the given document as this viewer's text model and the
+     * given annotation model as the model for this viewer's visual
+     * annotations. The presentation is accordingly updated whereby
+     * only the specified region is exposed. An appropriate
+     * <code>TextEvent</code> is issued. The text event does not carry a
+     * related document event. This method is a convenience method for
+     * <code>setDocument(document, annotationModel);setVisibleRegion(offset, length)</code>.
+     *
+     * @param document the new input document
+     * @param annotationModel the model of the viewer's visual annotations
+     * @param modelRangeOffset the offset of the model range
+     * @param modelRangeLength the length of the model range
+     *
+     * @see ITextViewer#setDocument(IDocument, int, int)
+     */
+    void setDocument(IDocument document, IAnnotationModel annotationModel, int modelRangeOffset, int modelRangeLength);
+
+    /**
+     * Returns this viewer's annotation model. Use
+     * {@link ISourceViewerExtension2#getVisualAnnotationModel()}in order to
+     * get access to the viewer's visual annotation model.
+     *
+     * @return this viewer's annotation model
+     */
+    IAnnotationModel getAnnotationModel();
+
+    /**
+     * Sets the annotation used by this viewer as range indicator. The
+     * range covered by this annotation is referred to as range indication.
+     *
+     * @param rangeIndicator the annotation to be used as this viewer's range indicator
+     */
+    void setRangeIndicator(Annotation rangeIndicator);
+
+    /**
+     * Sets the viewers's range indication to the specified range. Its is indicated
+     * whether the cursor should also be moved to the beginning of the specified range.
+     *
+     * @param offset the offset of the range
+     * @param length the length of the range
+     * @param moveCursor indicates whether the cursor should be moved to the given offset
+     */
+    void setRangeIndication(int offset, int length, bool moveCursor);
+
+    /**
+     * Returns the viewer's range indication.
+     *
+     * @return the viewer's range indication.
+     */
+    IRegion getRangeIndication();
+
+    /**
+     * Removes the viewer's range indication. There is no visible range indication
+     * after this method completed.
+     */
+    void removeRangeIndication();
+
+    /**
+     * Controls the visibility of annotations and in the case of separate
+     * presentation areas of text and annotations, the visibility of the
+     * annotation's presentation area.<p>
+     * By default, annotations and their presentation area are visible.
+     *
+     * @param show indicates the visibility of annotations
+     */
+    void showAnnotations(bool show);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ISourceViewerExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.ISourceViewerExtension;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+import java.lang.all;
+
+/**
+ * Extension interface {@link org.eclipse.jface.text.source.ISourceViewer}.
+ * <p>
+ * Extends the source viewer with the concept of an annotation overview. The
+ * annotation overview differs from the annotation presentation in that it is
+ * independent from the viewer's view port, i.e. the annotations of the whole
+ * document are visible. There are no assumptions about the area in which the
+ * annotation overview is shown.
+ * <p>
+ * As the visibility of annotation overview can dynamically be changed, it is
+ * assumed that the presentation area can dynamically be hidden if it is
+ * different from the text widget.
+ *
+ * @see org.eclipse.jface.text.source.ISourceViewer
+ * @since 2.1
+ */
+public interface ISourceViewerExtension {
+
+    /**
+     * Shows/hides an overview representation of the annotations of the whole document of this viewer.
+     *
+     * @param show <code>true</code> if annotation overview should be visible, <code>false</code> otherwise
+     */
+    void showAnnotationsOverview(bool show);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ISourceViewerExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.ISourceViewerExtension2;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+import java.lang.all;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.source.ISourceViewer}.<p>
+ * Allows the source viewer to roll back a previous configuration process and allows
+ * clients access to the viewer's visual annotation model.
+ *
+ * @since 3.0
+ */
+public interface ISourceViewerExtension2 {
+
+    /**
+     * Rolls back the configuration process of this source viewer. The source
+     * viewer can be configured again after a call to this method. Unlike
+     * {@link ISourceViewer#configure(SourceViewerConfiguration)} this method
+     * can be called more than once without interleaving calls to
+     * {@link ISourceViewer#configure(SourceViewerConfiguration)}.
+     */
+    void unconfigure();
+
+    /**
+     * Returns the visual annotation model of this viewer.
+     *
+     * @return the visual annotation model of this viewer
+     */
+    IAnnotationModel getVisualAnnotationModel();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ISourceViewerExtension3.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.source.ISourceViewerExtension3;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+import java.lang.all;
+
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.source.ISourceViewer}.<p>
+ * It introduces the concept of a quick assist assistant and provides access
+ * to the quick assist invocation context. It also gives access to any currently 
+ * showing annotation hover.</p>
+ *
+ * @see IQuickAssistAssistant
+ * @see IQuickAssistInvocationContext
+ * @since 3.2
+ */
+public interface ISourceViewerExtension3 {
+
+    /**
+     * Returns this viewers quick assist assistant.
+     *
+     * @return the quick assist assistant or <code>null</code> if none is configured
+     * @since 3.2
+     */
+    public IQuickAssistAssistant getQuickAssistAssistant();
+
+    /**
+     * Returns this viewer's quick assist invocation context.
+     *
+     * @return the quick assist invocation context or <code>null</code> if none is available
+     */
+    IQuickAssistInvocationContext getQuickAssistInvocationContext();
+
+    /**
+     * Returns the currently displayed annotation hover if any, <code>null</code> otherwise.
+     *
+     * @return the currently displayed annotation hover or <code>null</code>
+     */
+    IAnnotationHover getCurrentAnnotationHover();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ISourceViewerExtension4.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.ISourceViewerExtension4;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.contentassist.IContentAssistant;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.source.ISourceViewer}.
+ * <p>
+ * It introduces API to access a minimal set of content assistant APIs.</li>
+ * </p>
+ * 
+ * @see IContentAssistant
+ * @since 3.4
+ */
+public interface ISourceViewerExtension4 {
+
+    /**
+     * Returns a facade for this viewer's content assistant.
+     * 
+     * @return a content assistant facade or <code>null</code> if none is
+     *         configured
+     */
+    public ContentAssistantFacade getContentAssistantFacade();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRuler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.source.IVerticalRuler;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * This interface defines a visual component which may serve text viewers as an
+ * annotation presentation area. Implementers of this interface have to define
+ * the presentation modus. This can either depend on the connected viewer's view
+ * port or not. If the modus is view port dependent the ruler only shows those
+ * annotations that are attached to document regions that are visible in the
+ * view port. If independent, the presented annotations can also be attached to
+ * invisible document regions.
+ *
+ * This interfaces comprises three contracts:
+ * <ul>
+ * <li>The vertical ruler retrieves the annotations it presents from an
+ *     annotation model.
+ * <li>The ruler is a visual component which must be integrated in a hierarchy
+ *     of SWT controls.
+ * <li>The ruler provides interested clients with mapping and interaction
+ *     information. This covers the mapping between coordinates of the ruler's
+ *     control and line numbers based on the connected text viewer's document (see
+ *     {@link org.eclipse.jface.text.source.IVerticalRulerInfo}).
+ * </ul>
+ * <p>
+ * In order to provide backward compatibility for clients of
+ * <code>IVerticalRuler</code>, extension interfaces are used as a means of
+ * evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.source.IVerticalRulerExtension} since
+ *     version 2.0 introducing setters for font and mouse button activity location.</li>
+ * </ul></p>
+ * <p>
+ * Clients may implement this interface or use the default implementation
+ * provided by {@link org.eclipse.jface.text.source.CompositeRuler} and
+ * {@link org.eclipse.jface.text.source.VerticalRuler}.</p>
+ *
+ * @see org.eclipse.jface.text.source.IVerticalRulerExtension
+ * @see org.eclipse.jface.text.ITextViewer
+ */
+public interface IVerticalRuler : IVerticalRulerInfo {
+
+    /**
+     * Associates an annotation model with this ruler.
+     * A value <code>null</code> is acceptable and clears the ruler.
+     *
+     * @param model the new annotation model, may be <code>null</code>
+     */
+    void setModel(IAnnotationModel model);
+
+    /**
+     * Returns the current annotation model of this ruler or <code>null</code>
+     * if the ruler has no model.
+     *
+     * @return this ruler's annotation model or <code>null</code> if there is no model
+     */
+    IAnnotationModel getModel();
+
+    /**
+     * Forces the vertical ruler to synchronize itself with its
+     * annotation model and its viewer's view port.
+     */
+    void update();
+
+    /**
+     * Creates the ruler's SWT control.
+     *
+     * @param parent the parent control of the ruler's control
+     * @param textViewer the text viewer to which this ruler belongs
+     * @return the ruler's SWT control
+     */
+    Control createControl(Composite parent, ITextViewer textViewer);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRulerColumn.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.IVerticalRulerColumn;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+
+/**
+ * A vertical ruler column is an element that can be added to a composite
+ * vertical ruler ({@link org.eclipse.jface.text.source.CompositeRuler}). A
+ * composite vertical ruler is a vertical ruler with  dynamically changing
+ * appearance and behavior depending on its actual arrangement of ruler columns.
+ * A vertical ruler column supports a subset of the contract of a vertical
+ * ruler.
+ *
+ * @see org.eclipse.jface.text.source.CompositeRuler
+ * @since 2.0
+ */
+public interface IVerticalRulerColumn {
+
+    /**
+     * Associates an annotation model with this ruler column.
+     * A value <code>null</code> is acceptable and clears the ruler.
+     *
+     * @param model the new annotation model, may be <code>null</code>
+     */
+    void setModel(IAnnotationModel model);
+
+    /**
+     * Redraws this column.
+     */
+    void redraw();
+
+    /**
+     * Creates the column's SWT control.
+     *
+     * @param parentRuler the parent ruler of this column
+     * @param parentControl the control of the parent ruler
+     * @return the column's SWT control
+     */
+    Control createControl(CompositeRuler parentRuler, Composite parentControl);
+
+    /**
+     * Returns the column's SWT control.
+     *
+     * @return the column's SWT control
+     */
+    Control getControl();
+
+    /**
+     * Returns the width of this column's control.
+     *
+     * @return the width of this column's control
+     */
+    int getWidth();
+
+    /**
+     * Sets the font of this ruler column.
+     *
+     * @param font the new font of the ruler column
+     */
+    void setFont(Font font);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRulerExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.IVerticalRulerExtension;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.swt.graphics.Font;
+
+
+/**
+ * Extension interface for {@link IVerticalRuler}.
+ * <p>
+ * Allows to set the font of the vertical ruler and to set the location of the
+ * last mouse button activity.
+ *
+ * @since 2.0
+ */
+public interface IVerticalRulerExtension {
+
+    /**
+     * Sets the font of this vertical ruler.
+     *
+     * @param font the new font of the vertical ruler
+     */
+    void setFont(Font font);
+
+    /**
+     * Sets the location of the last mouse button activity. This method is used for
+     * example by external mouse listeners.
+     *
+     * @param x the x-coordinate
+     * @param y the y-coordinate
+     */
+    void setLocationOfLastMouseButtonActivity(int x, int y);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRulerInfo.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.IVerticalRulerInfo;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+import java.lang.all;
+
+
+import org.eclipse.swt.widgets.Control;
+
+
+/**
+ * A vertical ruler is a visual component which may serve text viewers as an
+ * annotation presentation area. The vertical ruler info provides interested
+ * clients with the mapping and interaction aspect of the vertical ruler. This
+ * covers the mapping between coordinates of the ruler's control and line
+ * numbers based on the connected text viewer's document.
+ *
+ * In order to provide backward compatibility for clients of
+ * <code>IVerticalRulerInfo</code>, extension interfaces are used as a means
+ * of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.source.IVerticalRulerInfoExtension} since
+ * version 3.0 allowing custom annotation hovers and specific annotation models.
+ * </li>
+ * </ul>
+ *
+ * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension
+ * @since 2.0
+ */
+public interface IVerticalRulerInfo {
+
+    /**
+     * Returns the ruler's SWT control.
+     *
+     * @return the ruler's SWT control
+     */
+    Control getControl();
+
+    /**
+     * Returns the line number of the last mouse button activity.
+     * Based on the input document of the connected text viewer.
+     *
+     * @return the line number of the last mouse button activity or <code>-1</code> if 
+     *          the last mouse activity does not correspond to a valid document line
+     */
+    int getLineOfLastMouseButtonActivity();
+
+    /**
+     * Translates a y-coordinate of the ruler's SWT control into
+     * the according line number of the document of the connected text viewer.
+     *
+     * @param y_coordinate a y-coordinate of the ruler's SWT control
+     * @return the line number of that coordinate or <code>-1</code> if that
+     *          coordinate does not correspond to a valid document line
+     */
+    int toDocumentLineNumber(int y_coordinate);
+
+    /**
+     * Returns the width of this ruler's control.
+     *
+     * @return the width of this ruler's control
+     */
+    int getWidth();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRulerInfoExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.IVerticalRulerInfoExtension;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Extension interface for
+ * {@link org.eclipse.jface.text.source.IVerticalRulerInfo}.
+ * <p>
+ * Introduces the ability to define a custom hover to be used when hovering over
+ * the vertical ruler described by this info instance, and to specify the
+ * annotation model used by it.
+ * <p>
+ * It also allows client to register as listeners on the represented vertical
+ * ruler and sends out notifications similar to selection events such as that a
+ * particular annotation presented in the vertical ruler has been selected.
+ *
+ * @see org.eclipse.jface.text.source.IVerticalRuler
+ * @see org.eclipse.jface.text.source.IAnnotationModel
+ * @since 3.0
+ */
+public interface IVerticalRulerInfoExtension {
+    /**
+     * Returns the hover for this vertical ruler (column).
+     *
+     * @return the hover for this column
+     */
+    IAnnotationHover getHover();
+
+    /**
+     * Returns the model currently used by the receiver.
+     *
+     * @return the model of the receiver, or <code>null</code> if no model is
+     *         installed.
+     */
+    IAnnotationModel getModel();
+
+    /**
+     * Registers a vertical ruler listener to be informed if an annotation gets
+     * selected on the vertical ruler.
+     *
+     * @param listener the listener to be informed
+     */
+    void addVerticalRulerListener(IVerticalRulerListener listener);
+
+    /**
+     * Removes a previously registered listener. If <code>listener</code> is not registered
+     * with the receiver, calling this method has no effect.
+     *
+     * @param listener the listener to be removed
+     */
+    void removeVerticalRulerListener(IVerticalRulerListener listener);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRulerListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.source.IVerticalRulerListener;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.swt.widgets.Menu;
+
+
+/**
+ * Interface for listening to annotation related events happening on a vertical ruler.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.0
+ */
+public interface IVerticalRulerListener {
+
+    /**
+     * Called when an annotation is selected in the vertical ruler.
+     *
+     * @param event the annotation event that occurred
+     */
+    void annotationSelected(VerticalRulerEvent event);
+
+    /**
+     * Called when a default selection occurs on an
+     * annotation in the vertical ruler.
+     *
+     * @param event the annotation event that occurred
+     */
+    void annotationDefaultSelected(VerticalRulerEvent event);
+
+    /**
+     * Called when the context menu is opened on an annotation in the
+     * vertical ruler.
+     *
+     * @param event the annotation event that occurred
+     * @param menu the menu that is about to be shown
+     */
+    void annotationContextMenuAboutToShow(VerticalRulerEvent event, Menu menu);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/ImageUtilities.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.ImageUtilities;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+
+/**
+ * Provides methods for drawing images onto a canvas.
+ * <p>
+ * This class is neither intended to be instantiated nor subclassed.
+ * </p>
+ *
+ * @since 3.0
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ImageUtilities {
+
+    /**
+     * Draws an image aligned inside the given rectangle on the given canvas.
+     *
+     * @param image the image to be drawn
+     * @param gc the drawing GC
+     * @param canvas the canvas on which to draw
+     * @param r the clipping rectangle
+     * @param halign the horizontal alignment of the image to be drawn
+     * @param valign the vertical alignment of the image to be drawn
+     */
+    public static void drawImage(Image image, GC gc, Canvas canvas, Rectangle r, int halign, int valign) {
+        if (image !is null) {
+
+            Rectangle bounds= image.getBounds();
+
+            int x= 0;
+            switch(halign) {
+            case SWT.LEFT:
+                break;
+            case SWT.CENTER:
+                x= (r.width - bounds.width) / 2;
+                break;
+            case SWT.RIGHT:
+                x= r.width - bounds.width;
+                break;
+            default:
+            }
+
+            int y= 0;
+            switch (valign) {
+            case SWT.TOP: {
+                FontMetrics fontMetrics= gc.getFontMetrics();
+                y= (fontMetrics.getHeight() - bounds.height)/2;
+                break;
+            }
+            case SWT.CENTER:
+                y= (r.height - bounds.height) / 2;
+                break;
+            case SWT.BOTTOM: {
+                FontMetrics fontMetrics= gc.getFontMetrics();
+                y= r.height - (fontMetrics.getHeight() + bounds.height)/2;
+                break;
+            }
+            default:
+            }
+
+            gc.drawImage(image, r.x+x, r.y+y);
+        }
+    }
+
+    /**
+     * Draws an image aligned inside the given rectangle on the given canvas.
+     *
+     * @param image the image to be drawn
+     * @param gc the drawing GC
+     * @param canvas the canvas on which to draw
+     * @param r the clipping rectangle
+     * @param align the alignment of the image to be drawn
+     */
+    public static void drawImage(Image image, GC gc, Canvas canvas, Rectangle r, int align_) {
+        drawImage(image, gc, canvas, r, align_, SWT.CENTER);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/JFaceTextMessages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.source.JFaceTextMessages;
+
+import java.lang.all;
+
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+import java.text.MessageFormat;
+
+/**
+ * Accessor for the <code>JFaceTextMessages.properties</code> file in
+ * package <code>org.eclipse.jface.text</code>.
+ * @since 2.0
+ */
+class JFaceTextMessages {
+
+    /** The resource bundle name. */
+//     private static const String RESOURCE_BUNDLE= "org.eclipse.jface.text.JFaceTextMessages";//$NON-NLS-1$
+
+    /** The resource bundle. */
+    private static ResourceBundle fgResourceBundle_;//= ResourceBundle.getBundle(RESOURCE_BUNDLE);
+
+    private static ResourceBundle fgResourceBundle(){
+        if( fgResourceBundle_ is null ){
+            synchronized(JFaceTextMessages.classinfo ){
+                if( fgResourceBundle_ is null ){
+                    fgResourceBundle_ = ResourceBundle.getBundle(
+            getImportData!("org.eclipse.jface.text.JFaceTextMessages.properties"));
+                }
+            }
+        }
+        return fgResourceBundle_;
+    }
+
+    /**
+     * Prohibits the creation of accessor objects.
+     */
+    private this() {
+    }
+
+    /**
+     * Returns the string found in the resource bundle under the given key or a place holder string.
+     *
+     * @param key the look up key
+     * @return the value found under the given key
+     */
+    public static String getString(String key) {
+        try {
+            return fgResourceBundle.getString(key);
+        } catch (MissingResourceException e) {
+            return "!" ~ key ~ "!";//$NON-NLS-2$ //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Gets a string from the resource bundle and formats it with the argument
+     *
+     * @param key   the string used to get the bundle value, must not be null
+     * @param args arguments used when formatting the string
+     * @return the formatted string
+     * @since 3.0
+     */
+    public static String getFormattedString(String key, Object[] args...) {
+        String format= null;
+        try {
+            format= fgResourceBundle.getString(key);
+        } catch (MissingResourceException e) {
+            return "!" ~ key ~ "!";//$NON-NLS-2$ //$NON-NLS-1$
+        }
+        return MessageFormat.format(format, args);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/LineChangeHover.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,399 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.LineChangeHover;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Iterator;
+
+
+
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.text.DefaultInformationControl;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.information.IInformationProviderExtension2;
+
+
+/**
+ * A hover for line oriented diffs. It determines the text to show as hover for a certain line in the
+ * document.
+ *
+ * @since 3.0
+ */
+public class LineChangeHover : IAnnotationHover, IAnnotationHoverExtension, IInformationProviderExtension2 {
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationHover#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer, int)
+     */
+    public String getHoverInfo(ISourceViewer sourceViewer, int lineNumber) {
+        return null;
+    }
+
+    /**
+     * Formats the source w/ syntax coloring etc. This implementation replaces tabs with spaces.
+     * May be overridden by subclasses.
+     *
+     * @param content the hover content
+     * @return <code>content</code> reformatted
+     */
+    protected String formatSource(String content) {
+        if (content !is null) {
+            StringBuffer sb= new StringBuffer(content);
+            final String tabReplacement= getTabReplacement();
+            for (int pos= 0; pos < sb.length(); pos++) {
+                if (sb.slice()[pos] is '\t'){
+                    sb.select( pos, pos + 1);
+                    sb.replace(tabReplacement);
+                }
+            }
+            return sb.toString();
+        }
+        return content;
+    }
+
+    /**
+     * Returns a replacement for the tab character. The default implementation
+     * returns a tabulator character, but subclasses may override to specify a
+     * number of spaces.
+     *
+     * @return a whitespace String that will be substituted for the tabulator
+     *         character
+     */
+    protected String getTabReplacement() {
+        return "\t"; //$NON-NLS-1$
+    }
+
+    /**
+     * Computes the content of the hover for the document contained in <code>viewer</code> on
+     * line <code>line</code>.
+     *
+     * @param viewer the connected viewer
+     * @param first the first line in <code>viewer</code>'s document to consider
+     * @param last the last line in <code>viewer</code>'s document to consider
+     * @param maxLines the max number of lines
+     * @return The hover content corresponding to the parameters
+     * @see #getHoverInfo(ISourceViewer, int)
+     * @see #getHoverInfo(ISourceViewer, ILineRange, int)
+     */
+    private String computeContent(ISourceViewer viewer, int first, int last, int maxLines) {
+        ILineDiffer differ= getDiffer(viewer);
+        if (differ is null)
+            return null;
+
+        final List lines= new LinkedList();
+        for (int l= first; l <= last; l++) {
+            ILineDiffInfo info= differ.getLineInfo(l);
+            if (info !is null)
+                lines.add(cast(Object)info);
+        }
+
+        return decorateText(lines, maxLines);
+    }
+
+    /**
+     * Takes a list of <code>ILineDiffInfo</code>s and computes a hover of at most <code>maxLines</code>.
+     * Added lines are prefixed with a <code>'+'</code>, changed lines with <code>'>'</code> and
+     * deleted lines with <code>'-'</code>.
+     * <p>Deleted and added lines can even each other out, so that a number of deleted lines get
+     * displayed where - in the current document - the added lines are.
+     *
+     * @param diffInfos a <code>List</code> of <code>ILineDiffInfo</code>
+     * @param maxLines the maximum number of lines. Note that adding up all annotations might give
+     * more than that due to deleted lines.
+     * @return a <code>String</code> suitable for hover display
+     */
+    protected String decorateText(List diffInfos, int maxLines) {
+        /* maxLines controls the size of the hover (not more than what fits into the display are of
+         * the viewer).
+         * added controls how many lines are added - added lines are
+         */
+        String text= ""; //$NON-NLS-1$
+        int added= 0;
+        for (Iterator it= diffInfos.iterator(); it.hasNext();) {
+            ILineDiffInfo info= cast(ILineDiffInfo)it.next();
+            String[] original= info.getOriginalText();
+            int type= info.getChangeType();
+            int i= 0;
+            if (type is ILineDiffInfo.ADDED)
+                added++;
+            else if (type is ILineDiffInfo.CHANGED) {
+                text ~= "> " ~ (original.length > 0 ? original[i++] : ""); //$NON-NLS-1$ //$NON-NLS-2$
+                maxLines--;
+            } else if (type is ILineDiffInfo.UNCHANGED) {
+                maxLines++;
+            }
+            if (maxLines is 0)
+                return trimTrailing(text);
+            for (; i < original.length; i++) {
+                text ~= "- " ~ original[i]; //$NON-NLS-1$
+                added--;
+                if (--maxLines is 0)
+                    return trimTrailing(text);
+            }
+        }
+        text= text.trim();
+        if (text.length() is 0 && added-- > 0 && maxLines-- > 0)
+            text ~= "+ "; //$NON-NLS-1$
+        while (added-- > 0 && maxLines-- > 0)
+            text ~= "\n+ "; //$NON-NLS-1$
+        return text;
+    }
+
+    /**
+     * Trims trailing spaces
+     *
+     * @param text a <code>String</code>
+     * @return a copy of <code>text</code> with trailing spaces removed
+     */
+    private String trimTrailing(String text) {
+        int pos= text.length() - 1;
+        while (pos >= 0 && Character.isWhitespace(text.charAt(pos))) {
+            pos--;
+        }
+        return text.substring(0, pos + 1);
+    }
+
+    /**
+     * Extracts the line differ - if any - from the viewer's document's annotation model.
+     * @param viewer the viewer
+     * @return a line differ for the document displayed in viewer, or <code>null</code>.
+     */
+    private ILineDiffer getDiffer(ISourceViewer viewer) {
+        IAnnotationModel model= viewer.getAnnotationModel();
+
+        if (model is null)
+            return null;
+
+        if ( cast(IAnnotationModelExtension)model ) {
+            IAnnotationModel diffModel= (cast(IAnnotationModelExtension)model).getAnnotationModel(stringcast(IChangeRulerColumn.QUICK_DIFF_MODEL_ID));
+            if (diffModel !is null)
+                model= diffModel;
+        }
+        if ( cast(ILineDiffer)model ) {
+            if (cast(ILineDifferExtension2)model && (cast(ILineDifferExtension2)model).isSuspended())
+                return null;
+            return cast(ILineDiffer)model;
+        }
+        return null;
+    }
+
+    /**
+     * Computes the block of lines which form a contiguous block of changes covering <code>line</code>.
+     *
+     * @param viewer the source viewer showing
+     * @param line the line which a hover is displayed for
+     * @param min the first line in <code>viewer</code>'s document to consider
+     * @param max the last line in <code>viewer</code>'s document to consider
+     * @return the selection in the document displayed in <code>viewer</code> containing <code>line</code>
+     * that is covered by the hover information returned by the receiver.
+     */
+    protected Point computeLineRange(ISourceViewer viewer, int line, int min, int max) {
+        /* Algorithm:
+         * All lines that have changes to themselves (added, changed) are taken that form a
+         * contiguous block of lines that includes <code>line</code>.
+         *
+         * If <code>line</code> is itself unchanged, if there is a deleted line either above or
+         * below, or both, the lines +/- 1 from <code>line</code> are included in the search as well,
+         * without applying this last rule to them, though. (I.e., if <code>line</code> is unchanged,
+         * but has a deleted line above, this one is taken in. If the line above has changes, the block
+         * is extended from there. If the line has no changes itself, the search stops).
+         *
+         * The block never extends the visible line range of the viewer.
+         */
+
+        ILineDiffer differ= getDiffer(viewer);
+        if (differ is null)
+            return new Point(-1, -1);
+
+        // backward search
+
+        int l= line;
+        ILineDiffInfo info= differ.getLineInfo(l);
+        // search backwards until a line has no changes to itself
+        while (l >= min && info !is null && (info.getChangeType() is ILineDiffInfo.CHANGED || info.getChangeType() is ILineDiffInfo.ADDED)) {
+            info= differ.getLineInfo(--l);
+        }
+
+        int first= Math.min(l + 1, line);
+
+        // forward search
+
+        l= line;
+        info= differ.getLineInfo(l);
+        // search forward until a line has no changes to itself
+        while (l <= max && info !is null && (info.getChangeType() is ILineDiffInfo.CHANGED || info.getChangeType() is ILineDiffInfo.ADDED)) {
+            info= differ.getLineInfo(++l);
+        }
+
+        int last= Math.max(l - 1, line);
+
+        return new Point(first, last);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer, org.eclipse.jface.text.source.ILineRange, int)
+     */
+    public Object getHoverInfo(ISourceViewer sourceViewer, ILineRange lineRange, int visibleLines) {
+        int first= adaptFirstLine(sourceViewer, lineRange.getStartLine());
+        int last= adaptLastLine(sourceViewer, lineRange.getStartLine() + lineRange.getNumberOfLines() - 1);
+        String content= computeContent(sourceViewer, first, last, visibleLines);
+        return stringcast(formatSource(content));
+    }
+
+    /**
+     * Adapts the start line to the implementation of <code>ILineDiffInfo</code>.
+     *
+     * @param viewer the source viewer
+     * @param startLine the line to adapt
+     * @return <code>startLine - 1</code> if that line exists and is an
+     *         unchanged line followed by deletions, <code>startLine</code>
+     *         otherwise
+     */
+    private int adaptFirstLine(ISourceViewer viewer, int startLine) {
+        ILineDiffer differ= getDiffer(viewer);
+        if (differ !is null && startLine > 0) {
+            int l= startLine - 1;
+            ILineDiffInfo info= differ.getLineInfo(l);
+            if (info !is null && info.getChangeType() is ILineDiffInfo.UNCHANGED && info.getRemovedLinesBelow() > 0)
+                return l;
+        }
+        return startLine;
+    }
+
+    /**
+     * Adapts the last line to the implementation of <code>ILineDiffInfo</code>.
+     *
+     * @param viewer the source viewer
+     * @param lastLine the line to adapt
+     * @return <code>lastLine - 1</code> if that line exists and is an
+     *         unchanged line followed by deletions, <code>startLine</code>
+     *         otherwise
+     */
+    private int adaptLastLine(ISourceViewer viewer, int lastLine) {
+        ILineDiffer differ= getDiffer(viewer);
+        if (differ !is null && lastLine > 0) {
+            ILineDiffInfo info= differ.getLineInfo(lastLine);
+            if (info !is null && info.getChangeType() is ILineDiffInfo.UNCHANGED)
+                return lastLine - 1;
+        }
+        return lastLine;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverLineRange(org.eclipse.jface.text.source.ISourceViewer, int)
+     */
+    public ILineRange getHoverLineRange(ISourceViewer viewer, int lineNumber) {
+        IDocument document= viewer.getDocument();
+        if (document !is null) {
+            Point range= computeLineRange(viewer, lineNumber, 0, Math.max(0, document.getNumberOfLines() - 1));
+            if (range.x !is -1 && range.y !is -1)
+                return new LineRange(range.x, range.y - range.x + 1);
+        }
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#canHandleMouseCursor()
+     */
+    public bool canHandleMouseCursor() {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverControlCreator()
+     */
+    public IInformationControlCreator getHoverControlCreator() {
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.information.IInformationProviderExtension2#getInformationPresenterControlCreator()
+     * @since 3.2
+     */
+    public IInformationControlCreator getInformationPresenterControlCreator() {
+        return new class()  IInformationControlCreator {
+            public IInformationControl createInformationControl(Shell parent) {
+                return new DefaultInformationControl(parent, cast(ToolBarManager)null, null);
+            }
+        };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/LineNumberChangeRulerColumn.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,457 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.LineNumberChangeRulerColumn;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.internal.text.revisions.RevisionPainter;
+import org.eclipse.jface.internal.text.source.DiffPainter;
+import org.eclipse.jface.text.revisions.IRevisionListener;
+import org.eclipse.jface.text.revisions.IRevisionRulerColumn;
+import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension;
+import org.eclipse.jface.text.revisions.RevisionInformation;
+import org.eclipse.jface.viewers.ISelectionProvider;
+
+/**
+ * A vertical ruler column displaying line numbers and serving as a UI for quick diff.
+ * Clients usually instantiate and configure object of this class.
+ *
+ * @since 3.0
+ */
+public final class LineNumberChangeRulerColumn : LineNumberRulerColumn , IVerticalRulerInfo, IVerticalRulerInfoExtension, IChangeRulerColumn, IRevisionRulerColumn, IRevisionRulerColumnExtension {
+
+    public Control getControl() {
+        return super.getControl();
+    }
+
+    /** The ruler's annotation model. */
+    private IAnnotationModel fAnnotationModel;
+    /** <code>true</code> if changes should be displayed using character indications instead of background colors. */
+    private bool fCharacterDisplay;
+    /**
+     * The revision painter strategy.
+     *
+     * @since 3.2
+     */
+    private const RevisionPainter fRevisionPainter;
+    /**
+     * The diff information painter strategy.
+     *
+     * @since 3.2
+     */
+    private const DiffPainter fDiffPainter;
+    /**
+     * Whether to show number or to behave like a change ruler column.
+     * @since 3.3
+     */
+    private bool fShowNumbers= true;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param sharedColors the shared colors provider to use
+     */
+    public this(ISharedTextColors sharedColors) {
+        Assert.isNotNull(cast(Object)sharedColors);
+        fRevisionPainter= new RevisionPainter(this, sharedColors);
+        fDiffPainter= new DiffPainter(this, sharedColors);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.LineNumberRulerColumn#createControl(org.eclipse.jface.text.source.CompositeRuler, org.eclipse.swt.widgets.Composite)
+     */
+    public Control createControl(CompositeRuler parentRuler, Composite parentControl) {
+        Control control= super.createControl(parentRuler, parentControl);
+        fRevisionPainter.setParentRuler(parentRuler);
+        fDiffPainter.setParentRuler(parentRuler);
+        return control;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
+     */
+    public int getLineOfLastMouseButtonActivity() {
+        return getParentRuler().getLineOfLastMouseButtonActivity();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfo#toDocumentLineNumber(int)
+     */
+    public int toDocumentLineNumber(int y_coordinate) {
+        return getParentRuler().toDocumentLineNumber(y_coordinate);
+    }
+
+    /*
+     * @see IVerticalRulerColumn#setModel(IAnnotationModel)
+     */
+    public void setModel(IAnnotationModel model) {
+        setAnnotationModel(model);
+        fRevisionPainter.setModel(model);
+        fDiffPainter.setModel(model);
+        updateNumberOfDigits();
+        computeIndentations();
+        layout(true);
+        postRedraw();
+    }
+
+    private void setAnnotationModel(IAnnotationModel model) {
+        if (fAnnotationModel !is model)
+            fAnnotationModel= model;
+    }
+
+
+    /**
+     * Sets the display mode of the ruler. If character mode is set to <code>true</code>, diff
+     * information will be displayed textually on the line number ruler.
+     *
+     * @param characterMode <code>true</code> if diff information is to be displayed textually.
+     */
+    public void setDisplayMode(bool characterMode) {
+        if (characterMode !is fCharacterDisplay) {
+            fCharacterDisplay= characterMode;
+            updateNumberOfDigits();
+            computeIndentations();
+            layout(true);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#getModel()
+     */
+    public IAnnotationModel getModel() {
+        return fAnnotationModel;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.LineNumberRulerColumn#createDisplayString(int)
+     */
+    protected String createDisplayString(int line) {
+        StringBuffer buffer= new StringBuffer();
+        if (fShowNumbers)
+            buffer.append(super.createDisplayString(line));
+        if (fCharacterDisplay && getModel() !is null)
+            buffer.append(fDiffPainter.getDisplayCharacter(line));
+        return buffer.toString();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.LineNumberRulerColumn#computeNumberOfDigits()
+     */
+    protected int computeNumberOfDigits() {
+        int digits;
+        if (fCharacterDisplay && getModel() !is null) {
+            if (fShowNumbers)
+                digits= super.computeNumberOfDigits() + 1;
+            else
+                digits= 1;
+        } else {
+            if (fShowNumbers)
+                digits= super.computeNumberOfDigits();
+            else
+                digits= 0;
+        }
+        if (fRevisionPainter.hasInformation())
+            digits+= fRevisionPainter.getRequiredWidth();
+        return digits;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#addVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener)
+     */
+    public void addVerticalRulerListener(IVerticalRulerListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#removeVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener)
+     */
+    public void removeVerticalRulerListener(IVerticalRulerListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.LineNumberRulerColumn#doPaint(org.eclipse.swt.graphics.GC)
+     */
+    void doPaint(GC gc, ILineRange visibleLines) {
+        Color foreground= gc.getForeground();
+        if (visibleLines !is null) {
+            if (fRevisionPainter.hasInformation())
+                fRevisionPainter.paint(gc, visibleLines);
+            else if (fDiffPainter.hasInformation()) // don't paint quick diff colors if revisions are painted
+                fDiffPainter.paint(gc, visibleLines);
+        }
+        gc.setForeground(foreground);
+        if (fShowNumbers || fCharacterDisplay)
+            super.doPaint(gc, visibleLines);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#getHover()
+     */
+    public IAnnotationHover getHover() {
+        int activeLine= getParentRuler().getLineOfLastMouseButtonActivity();
+        if (fRevisionPainter.hasHover(activeLine))
+            return fRevisionPainter.getHover();
+        if (fDiffPainter.hasHover(activeLine))
+            return fDiffPainter.getHover();
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IChangeRulerColumn#setHover(org.eclipse.jface.text.source.IAnnotationHover)
+     */
+    public void setHover(IAnnotationHover hover) {
+        fRevisionPainter.setHover(hover);
+        fDiffPainter.setHover(hover);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IChangeRulerColumn#setBackground(org.eclipse.swt.graphics.Color)
+     */
+    public void setBackground(Color background) {
+        super.setBackground(background);
+        fRevisionPainter.setBackground(background);
+        fDiffPainter.setBackground(background);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IChangeRulerColumn#setAddedColor(org.eclipse.swt.graphics.Color)
+     */
+    public void setAddedColor(Color addedColor) {
+        fDiffPainter.setAddedColor(addedColor);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IChangeRulerColumn#setChangedColor(org.eclipse.swt.graphics.Color)
+     */
+    public void setChangedColor(Color changedColor) {
+        fDiffPainter.setChangedColor(changedColor);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IChangeRulerColumn#setDeletedColor(org.eclipse.swt.graphics.Color)
+     */
+    public void setDeletedColor(Color deletedColor) {
+        fDiffPainter.setDeletedColor(deletedColor);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.revisions.IRevisionRulerColumn#setRevisionInformation(org.eclipse.jface.text.revisions.RevisionInformation)
+     */
+    public void setRevisionInformation(RevisionInformation info) {
+        fRevisionPainter.setRevisionInformation(info);
+        updateNumberOfDigits();
+        computeIndentations();
+        layout(true);
+        postRedraw();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension#getRevisionSelectionProvider()
+     * @since 3.2
+     */
+    public ISelectionProvider getRevisionSelectionProvider() {
+        return fRevisionPainter.getRevisionSelectionProvider();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension#setRenderingMode(org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension.RenderingMode)
+     * @since 3.3
+     */
+    public void setRevisionRenderingMode(RenderingMode renderingMode) {
+        fRevisionPainter.setRenderingMode(renderingMode);
+    }
+
+    /**
+     * Sets the line number display mode.
+     *
+     * @param showNumbers <code>true</code> to show numbers, <code>false</code> to only show
+     *        diff / revision info.
+     * @since 3.3
+     */
+    public void showLineNumbers(bool showNumbers) {
+        if (fShowNumbers !is showNumbers) {
+            fShowNumbers= showNumbers;
+            updateNumberOfDigits();
+            computeIndentations();
+            layout(true);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.LineNumberRulerColumn#getWidth()
+     * @since 3.3
+     */
+    public int getWidth() {
+        int width= super.getWidth();
+        return width > 0 ? width : 8; // minimal width to display quick diff / revisions if no textual info is shown
+    }
+
+    /**
+     * Returns <code>true</code> if the ruler is showing line numbers, <code>false</code>
+     * otherwise
+     *
+     * @return <code>true</code> if line numbers are shown, <code>false</code> otherwise
+     * @since 3.3
+     */
+    public bool isShowingLineNumbers() {
+        return fShowNumbers;
+    }
+
+    /**
+     * Returns <code>true</code> if the ruler is showing revision information, <code>false</code>
+     * otherwise
+     *
+     * @return <code>true</code> if revision information is shown, <code>false</code> otherwise
+     * @since 3.3
+     */
+    public bool isShowingRevisionInformation() {
+        return fRevisionPainter.hasInformation();
+    }
+
+    /**
+     * Returns <code>true</code> if the ruler is showing change information, <code>false</code>
+     * otherwise
+     *
+     * @return <code>true</code> if change information is shown, <code>false</code> otherwise
+     * @since 3.3
+     */
+    public bool isShowingChangeInformation() {
+        return fDiffPainter.hasInformation();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension#showRevisionAuthor(bool)
+     * @since 3.3
+     */
+    public void showRevisionAuthor(bool show) {
+        fRevisionPainter.showRevisionAuthor(show);
+        updateNumberOfDigits();
+        computeIndentations();
+        layout(true);
+        postRedraw();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension#showRevisionId(bool)
+     * @since 3.3
+     */
+    public void showRevisionId(bool show) {
+        fRevisionPainter.showRevisionId(show);
+        updateNumberOfDigits();
+        computeIndentations();
+        layout(true);
+        postRedraw();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension#addRevisionListener(org.eclipse.jface.text.revisions.IRevisionListener)
+     * @since 3.3
+     */
+    public void addRevisionListener(IRevisionListener listener) {
+        fRevisionPainter.addRevisionListener(listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension#removeRevisionListener(org.eclipse.jface.text.revisions.IRevisionListener)
+     * @since 3.3
+     */
+    public void removeRevisionListener(IRevisionListener listener) {
+        fRevisionPainter.removeRevisionListener(listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.LineNumberRulerColumn#handleDispose()
+     * @since 3.3
+     */
+    protected void handleDispose() {
+        fRevisionPainter.setParentRuler(null);
+        fRevisionPainter.setModel(null);
+        fDiffPainter.setParentRuler(null);
+        fDiffPainter.setModel(null);
+        super.handleDispose();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/LineNumberRulerColumn.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,976 @@
+/*******************************************************************************
+ * 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
+ *     Nikolay Botev <bono8106@hotmail.com> - [rulers] Shift clicking in line number column doesn't select range - https://bugs.eclipse.org/bugs/show_bug.cgi?id=32166
+ *     Nikolay Botev <bono8106@hotmail.com> - [rulers] Clicking in line number ruler should not trigger annotation ruler - https://bugs.eclipse.org/bugs/show_bug.cgi?id=40889
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.source.LineNumberRulerColumn;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.Set;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.TypedListener;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.IViewportListener;
+import org.eclipse.jface.text.JFaceTextUtil;
+import org.eclipse.jface.text.TextEvent;
+
+
+/**
+ * A vertical ruler column displaying line numbers.
+ * Clients usually instantiate and configure object of this class.
+ *
+ * @since 2.0
+ */
+public class LineNumberRulerColumn : IVerticalRulerColumn {
+
+    /**
+     * Internal listener class.
+     */
+    class InternalListener : IViewportListener, ITextListener {
+
+        /**
+         * @since 3.1
+         */
+        private bool fCachedRedrawState= true;
+
+        /*
+         * @see IViewportListener#viewportChanged(int)
+         */
+        public void viewportChanged(int verticalPosition) {
+            if (fCachedRedrawState && verticalPosition !is fScrollPos)
+                redraw();
+        }
+
+        /*
+         * @see ITextListener#textChanged(TextEvent)
+         */
+        public void textChanged(TextEvent event) {
+
+            fCachedRedrawState= event.getViewerRedrawState();
+            if (!fCachedRedrawState)
+                return;
+
+            if (updateNumberOfDigits()) {
+                computeIndentations();
+                layout(event.getViewerRedrawState());
+                return;
+            }
+
+            bool viewerCompletelyShown= isViewerCompletelyShown();
+            if (viewerCompletelyShown || fSensitiveToTextChanges || event.getDocumentEvent() is null)
+                postRedraw();
+            fSensitiveToTextChanges= viewerCompletelyShown;
+        }
+    }
+
+    /**
+     * Handles all the mouse interaction in this line number ruler column.
+     */
+    class MouseHandler : MouseListener, MouseMoveListener {
+
+        /** The cached view port size. */
+        private int fCachedViewportSize;
+        /** The area of the line at which line selection started. */
+        private int fStartLineOffset;
+        /** The number of the line at which line selection started. */
+        private int fStartLineNumber;
+        /** The auto scroll direction. */
+        private int fAutoScrollDirection;
+        /* @since 3.2 */
+        private bool fIsListeningForMove= false;
+
+        /*
+         * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseUp(MouseEvent event) {
+            // see bug 45700
+            if (event.button is 1) {
+                stopSelecting();
+                stopAutoScroll();
+            }
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseDown(MouseEvent event) {
+            fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
+            // see bug 45700
+            if (event.button is 1) {
+                startSelecting((event.stateMask & SWT.SHIFT) !is 0);
+            }
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseDoubleClick(MouseEvent event) {
+            fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
+            stopSelecting();
+            stopAutoScroll();
+        }
+
+        /*
+         * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
+         */
+        public void mouseMove(MouseEvent event) {
+            if (fIsListeningForMove && !autoScroll(event)) {
+                int newLine= fParentRuler.toDocumentLineNumber(event.y);
+                expandSelection(newLine);
+            }
+            fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
+        }
+
+        /**
+         * Called when line drag selection started. Adds mouse move and track
+         * listeners to this column's control.
+         *
+         * @param expandExistingSelection if <code>true</code> the existing selection will be expanded,
+         *          otherwise a new selection is started
+         */
+        private void startSelecting(bool expandExistingSelection) {
+            try {
+
+                // select line
+                IDocument document= fCachedTextViewer.getDocument();
+                int lineNumber= fParentRuler.getLineOfLastMouseButtonActivity();
+                if (expandExistingSelection && cast(ITextViewerExtension5)fCachedTextViewer
+                        && fCachedTextViewer.getTextWidget() !is null) {
+                    ITextViewerExtension5 extension5= (cast(ITextViewerExtension5)fCachedTextViewer);
+                    // Find model curosr position
+                    int widgetCaret= fCachedTextViewer.getTextWidget().getCaretOffset();
+                    int modelCaret= extension5.widgetOffset2ModelOffset(widgetCaret);
+                    // Find model selection range
+                    Point selection= fCachedTextViewer.getSelectedRange();
+                    // Start from tail of selection range (opposite of cursor position)
+                    int startOffset= modelCaret is selection.x ? selection.x + selection.y : selection.x;
+
+                    fStartLineNumber= document.getLineOfOffset(startOffset);
+                    fStartLineOffset= startOffset;
+
+                    expandSelection(lineNumber);
+                } else {
+                    fStartLineNumber= lineNumber;
+                    fStartLineOffset= document.getLineInformation(fStartLineNumber).getOffset();
+                    fCachedTextViewer.setSelectedRange(fStartLineOffset, 0);
+                }
+                fCachedViewportSize= getVisibleLinesInViewport();
+
+                // prepare for drag selection
+                fIsListeningForMove= true;
+
+            } catch (BadLocationException x) {
+            }
+        }
+
+        /**
+         * Called when line drag selection stopped. Removes all previously
+         * installed listeners from this column's control.
+         */
+        private void stopSelecting() {
+            // drag selection stopped
+            fIsListeningForMove= false;
+        }
+
+        /**
+         * Expands the line selection from the remembered start line to the
+         * given line.
+         *
+         * @param lineNumber the line to which to expand the selection
+         */
+        private void expandSelection(int lineNumber) {
+            try {
+
+                IDocument document= fCachedTextViewer.getDocument();
+                IRegion lineInfo= document.getLineInformation(lineNumber);
+
+                Display display= fCachedTextWidget.getDisplay();
+                Point absolutePosition= display.getCursorLocation();
+                Point relativePosition= fCachedTextWidget.toControl(absolutePosition);
+
+                int offset;
+
+                if (relativePosition.x < 0)
+                    offset= lineInfo.getOffset();
+                else {
+                    try {
+                        int widgetOffset= fCachedTextWidget.getOffsetAtLocation(relativePosition);
+                        Point p= fCachedTextWidget.getLocationAtOffset(widgetOffset);
+                        if (p.x > relativePosition.x)
+                            widgetOffset--;
+
+                        // Convert to model offset
+                        if ( cast(ITextViewerExtension5)fCachedTextViewer ) {
+                            ITextViewerExtension5 extension= cast(ITextViewerExtension5)fCachedTextViewer;
+                            offset= extension.widgetOffset2ModelOffset(widgetOffset);
+                        } else
+                            offset= widgetOffset + fCachedTextViewer.getVisibleRegion().getOffset();
+
+                    } catch (IllegalArgumentException ex) {
+                        int lineEndOffset= lineInfo.getOffset() + lineInfo.getLength();
+
+                        // Convert to widget offset
+                        int lineEndWidgetOffset;
+                        if ( cast(ITextViewerExtension5)fCachedTextViewer ) {
+                            ITextViewerExtension5 extension= cast(ITextViewerExtension5)fCachedTextViewer;
+                            lineEndWidgetOffset= extension.modelOffset2WidgetOffset(lineEndOffset);
+                        } else
+                            lineEndWidgetOffset= lineEndOffset - fCachedTextViewer.getVisibleRegion().getOffset();
+
+                        Point p= fCachedTextWidget.getLocationAtOffset(lineEndWidgetOffset);
+                        if (p.x < relativePosition.x)
+                            offset= lineEndOffset;
+                        else
+                            offset= lineInfo.getOffset();
+                    }
+                }
+
+                int start= Math.min(fStartLineOffset, offset);
+                int end= Math.max(fStartLineOffset, offset);
+
+                if (lineNumber < fStartLineNumber)
+                    fCachedTextViewer.setSelectedRange(end, start - end);
+                else
+                    fCachedTextViewer.setSelectedRange(start, end - start);
+
+            } catch (BadLocationException x) {
+            }
+        }
+
+        /**
+         * Called when auto scrolling stopped. Clears the auto scroll direction.
+         */
+        private void stopAutoScroll() {
+            fAutoScrollDirection= SWT.NULL;
+        }
+
+        /**
+         * Called on drag selection.
+         *
+         * @param event the mouse event caught by the mouse move listener
+         * @return <code>true</code> if scrolling happened, <code>false</code> otherwise
+         */
+        private bool autoScroll(MouseEvent event) {
+            Rectangle area= fCanvas.getClientArea();
+
+            if (event.y > area.height) {
+                autoScroll(SWT.DOWN);
+                return true;
+            }
+
+            if (event.y < 0) {
+                autoScroll(SWT.UP);
+                return true;
+            }
+
+            stopAutoScroll();
+            return false;
+        }
+
+        /**
+         * Scrolls the viewer into the given direction.
+         *
+         * @param direction the scroll direction
+         */
+        private void autoScroll(int direction) {
+
+            if (fAutoScrollDirection is direction)
+                return;
+
+            final int TIMER_INTERVAL= 5;
+            final Display display= fCanvas.getDisplay();
+            Runnable timer= null;
+            switch (direction) {
+                case SWT.UP:
+                    timer= new class()  Runnable {
+                        public void run() {
+                            if (fAutoScrollDirection is SWT.UP) {
+                                int top= getInclusiveTopIndex();
+                                if (top > 0) {
+                                    fCachedTextViewer.setTopIndex(top -1);
+                                    expandSelection(top -1);
+                                    display.timerExec(TIMER_INTERVAL, this);
+                                }
+                            }
+                        }
+                    };
+                    break;
+                case  SWT.DOWN:
+                    timer= new class()  Runnable {
+                        public void run() {
+                            if (fAutoScrollDirection is SWT.DOWN) {
+                                int top= getInclusiveTopIndex();
+                                fCachedTextViewer.setTopIndex(top +1);
+                                expandSelection(top +1 + fCachedViewportSize);
+                                display.timerExec(TIMER_INTERVAL, this);
+                            }
+                        }
+                    };
+                    break;
+                default:
+            }
+
+            if (timer !is null) {
+                fAutoScrollDirection= direction;
+                display.timerExec(TIMER_INTERVAL, timer);
+            }
+        }
+
+        /**
+         * Returns the viewer's first visible line, even if only partially visible.
+         *
+         * @return the viewer's first visible line
+         */
+        private int getInclusiveTopIndex() {
+            if (fCachedTextWidget !is null && !fCachedTextWidget.isDisposed()) {
+                return JFaceTextUtil.getPartialTopIndex(fCachedTextViewer);
+            }
+            return -1;
+        }
+    }
+
+    /** This column's parent ruler */
+    private CompositeRuler fParentRuler;
+    /** Cached text viewer */
+    private ITextViewer fCachedTextViewer;
+    /** Cached text widget */
+    private StyledText fCachedTextWidget;
+    /** The columns canvas */
+    private Canvas fCanvas;
+    /** Cache for the actual scroll position in pixels */
+    private int fScrollPos;
+    /** The drawable for double buffering */
+    private Image fBuffer;
+    /** The internal listener */
+    private InternalListener fInternalListener;
+    /** The font of this column */
+    private Font fFont;
+    /** The indentation cache */
+    private int[] fIndentation;
+    /** Indicates whether this column reacts on text change events */
+    private bool fSensitiveToTextChanges= false;
+    /** The foreground color */
+    private Color fForeground;
+    /** The background color */
+    private Color fBackground;
+    /** Cached number of displayed digits */
+    private int fCachedNumberOfDigits= -1;
+    /** Flag indicating whether a relayout is required */
+    private bool fRelayoutRequired= false;
+    /**
+     * Redraw runnable lock
+     * @since 3.0
+     */
+    private Object fRunnableLock;
+    /**
+     * Redraw runnable state
+     * @since 3.0
+     */
+    private bool fIsRunnablePosted= false;
+    /**
+     * Redraw runnable
+     * @since 3.0
+     */
+    private Runnable fRunnable;
+    private void fRunnable_init() {
+        fRunnable = new class() Runnable {
+            public void run() {
+                synchronized (fRunnableLock) {
+                    fIsRunnablePosted= false;
+                }
+                redraw();
+            }
+        };
+    }
+    /* @since 3.2 */
+    private MouseHandler fMouseHandler;
+
+
+    /**
+     * Constructs a new vertical ruler column.
+     */
+    public this() {
+        fInternalListener= new InternalListener();
+        fRunnableLock= new Object();
+        fRunnable_init();
+    }
+
+    /**
+     * Sets the foreground color of this column.
+     *
+     * @param foreground the foreground color
+     */
+    public void setForeground(Color foreground) {
+        fForeground= foreground;
+    }
+
+    /**
+     * Returns the foreground color being used to print the line numbers.
+     *
+     * @return the configured foreground color
+     * @since 3.0
+     */
+    protected Color getForeground() {
+        return fForeground;
+    }
+
+    /**
+     * Sets the background color of this column.
+     *
+     * @param background the background color
+     */
+    public void setBackground(Color background) {
+        fBackground= background;
+        if (fCanvas !is null && !fCanvas.isDisposed())
+            fCanvas.setBackground(getBackground(fCanvas.getDisplay()));
+    }
+
+    /**
+     * Returns the System background color for list widgets.
+     *
+     * @param display the display
+     * @return the System background color for list widgets
+     */
+    protected Color getBackground(Display display) {
+        if (fBackground is null)
+            return display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
+        return fBackground;
+    }
+
+    /*
+     * @see IVerticalRulerColumn#getControl()
+     */
+    public Control getControl() {
+        return fCanvas;
+    }
+
+    /*
+     * @see IVerticalRuleColumnr#getWidth
+     */
+    public int getWidth() {
+        return fIndentation[0];
+    }
+
+    /**
+     * Computes the number of digits to be displayed. Returns
+     * <code>true</code> if the number of digits changed compared
+     * to the previous call of this method. If the method is called
+     * for the first time, the return value is also <code>true</code>.
+     *
+     * @return whether the number of digits has been changed
+     * @since 3.0
+     */
+    protected bool updateNumberOfDigits() {
+        if (fCachedTextViewer is null)
+            return false;
+
+        int digits= computeNumberOfDigits();
+
+        if (fCachedNumberOfDigits !is digits) {
+            fCachedNumberOfDigits= digits;
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Does the real computation of the number of digits. Subclasses may override this method if
+     * they need extra space on the line number ruler.
+     *
+     * @return the number of digits to be displayed on the line number ruler.
+     */
+    protected int computeNumberOfDigits() {
+        IDocument document= fCachedTextViewer.getDocument();
+        int lines= document is null ? 0 : document.getNumberOfLines();
+
+        int digits= 2;
+        while (lines > Math.pow(cast(real)10.0, cast(uint)digits) -1) {
+            ++digits;
+        }
+        return digits;
+    }
+
+    /**
+     * Layouts the enclosing viewer to adapt the layout to changes of the
+     * size of the individual components.
+     *
+     * @param redraw <code>true</code> if this column can be redrawn
+     */
+    protected void layout(bool redraw) {
+        if (!redraw) {
+            fRelayoutRequired= true;
+            return;
+        }
+
+        fRelayoutRequired= false;
+        if ( cast(ITextViewerExtension)fCachedTextViewer ) {
+            ITextViewerExtension extension= cast(ITextViewerExtension) fCachedTextViewer;
+            Control control= extension.getControl();
+            if ( cast(Composite)control  && !control.isDisposed()) {
+                Composite composite= cast(Composite) control;
+                composite.layout(true);
+            }
+        }
+    }
+
+    /**
+     * Computes the indentations for the given font and stores them in
+     * <code>fIndentation</code>.
+     */
+    protected void computeIndentations() {
+        if (fCanvas is null || fCanvas.isDisposed())
+            return;
+
+        GC gc= new GC(fCanvas);
+        try {
+
+            gc.setFont(fCanvas.getFont());
+
+            fIndentation= new int[fCachedNumberOfDigits + 1];
+
+            char[] nines= new char[fCachedNumberOfDigits];
+            Arrays.fill(nines, '9');
+            String nineString= new_String(nines);
+            Point p= gc.stringExtent(nineString);
+            fIndentation[0]= p.x;
+
+            for (int i= 1; i <= fCachedNumberOfDigits; i++) {
+                p= gc.stringExtent(nineString.substring(0, i));
+                fIndentation[i]= fIndentation[0] - p.x;
+            }
+
+        } finally {
+            gc.dispose();
+        }
+    }
+
+    /*
+     * @see IVerticalRulerColumn#createControl(CompositeRuler, Composite)
+     */
+    public Control createControl(CompositeRuler parentRuler, Composite parentControl) {
+
+        fParentRuler= parentRuler;
+        fCachedTextViewer= parentRuler.getTextViewer();
+        fCachedTextWidget= fCachedTextViewer.getTextWidget();
+
+        fCanvas= new class(parentControl, SWT.NO_FOCUS )  Canvas {
+            this(Composite c, int s ){
+                super(c,s);
+            }
+            /*
+             * @see org.eclipse.swt.widgets.Control#addMouseListener(org.eclipse.swt.events.MouseListener)
+             * @since 3.4
+             */
+            public void addMouseListener(MouseListener listener) {
+                // see bug 40889, bug 230073 and AnnotationRulerColumn#isPropagatingMouseListener()
+                if (listener is fMouseHandler)
+                    super.addMouseListener(listener);
+                else {
+                    TypedListener typedListener= null;
+                    if (listener !is null)
+                        typedListener= new TypedListener(listener);
+                    addListener(SWT.MouseDoubleClick, typedListener);
+                }
+            }
+        };
+        fCanvas.setBackground(getBackground(fCanvas.getDisplay()));
+        fCanvas.setForeground(fForeground);
+
+        fCanvas.addPaintListener(new class()  PaintListener {
+            public void paintControl(PaintEvent event) {
+                if (fCachedTextViewer !is null)
+                    doubleBufferPaint(event.gc);
+            }
+        });
+
+        fCanvas.addDisposeListener(new class()  DisposeListener {
+            public void widgetDisposed(DisposeEvent e) {
+                handleDispose();
+                fCachedTextViewer= null;
+                fCachedTextWidget= null;
+            }
+        });
+
+        fMouseHandler= new MouseHandler();
+        fCanvas.addMouseListener(fMouseHandler);
+        fCanvas.addMouseMoveListener(fMouseHandler);
+
+        if (fCachedTextViewer !is null) {
+
+            fCachedTextViewer.addViewportListener(fInternalListener);
+            fCachedTextViewer.addTextListener(fInternalListener);
+
+            if (fFont is null) {
+                if (fCachedTextWidget !is null && !fCachedTextWidget.isDisposed())
+                    fFont= fCachedTextWidget.getFont();
+            }
+        }
+
+        if (fFont !is null)
+            fCanvas.setFont(fFont);
+
+        updateNumberOfDigits();
+        computeIndentations();
+        return fCanvas;
+    }
+
+    /**
+     * Disposes the column's resources.
+     */
+    protected void handleDispose() {
+
+        if (fCachedTextViewer !is null) {
+            fCachedTextViewer.removeViewportListener(fInternalListener);
+            fCachedTextViewer.removeTextListener(fInternalListener);
+        }
+
+        if (fBuffer !is null) {
+            fBuffer.dispose();
+            fBuffer= null;
+        }
+    }
+
+    /**
+     * Double buffer drawing.
+     *
+     * @param dest the GC to draw into
+     */
+    private void doubleBufferPaint(GC dest) {
+
+        Point size= fCanvas.getSize();
+
+        if (size.x <= 0 || size.y <= 0)
+            return;
+
+        if (fBuffer !is null) {
+            Rectangle r= fBuffer.getBounds();
+            if (r.width !is size.x || r.height !is size.y) {
+                fBuffer.dispose();
+                fBuffer= null;
+            }
+        }
+        if (fBuffer is null)
+            fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y);
+
+        GC gc= new GC(fBuffer);
+        gc.setFont(fCanvas.getFont());
+        if (fForeground !is null)
+            gc.setForeground(fForeground);
+
+        try {
+            gc.setBackground(getBackground(fCanvas.getDisplay()));
+            gc.fillRectangle(0, 0, size.x, size.y);
+
+            ILineRange visibleLines= JFaceTextUtil.getVisibleModelLines(fCachedTextViewer);
+            if (visibleLines is null)
+                return;
+            fScrollPos= fCachedTextWidget.getTopPixel();
+            doPaint(gc, visibleLines);
+        } finally {
+            gc.dispose();
+        }
+
+        dest.drawImage(fBuffer, 0, 0);
+    }
+
+    /**
+     * Returns the view port height in lines.
+     *
+     * @return the view port height in lines
+     * @deprecated as of 3.2 the number of lines in the viewport cannot be computed because
+     *             StyledText supports variable line heights
+     */
+    protected int getVisibleLinesInViewport() {
+        return getVisibleLinesInViewport(fCachedTextWidget);
+    }
+
+
+    /**
+     * Returns <code>true</code> if the viewport displays the entire viewer contents, i.e. the
+     * viewer is not vertically scrollable.
+     *
+     * @return <code>true</code> if the viewport displays the entire contents, <code>false</code> otherwise
+     * @since 3.2
+     */
+    protected final bool isViewerCompletelyShown() {
+        return JFaceTextUtil.isShowingEntireContents(fCachedTextWidget);
+    }
+
+    /**
+     * Draws the ruler column.
+     *
+     * @param gc the GC to draw into
+     * @param visibleLines the visible model lines
+     * @since 3.2
+     */
+    void doPaint(GC gc, ILineRange visibleLines) {
+        Display display= fCachedTextWidget.getDisplay();
+
+        // draw diff info
+        int y= -JFaceTextUtil.getHiddenTopLinePixels(fCachedTextWidget);
+
+        int lastLine= end(visibleLines);
+        for (int line= visibleLines.getStartLine(); line < lastLine; line++) {
+            int widgetLine= JFaceTextUtil.modelLineToWidgetLine(fCachedTextViewer, line);
+            if (widgetLine is -1)
+                continue;
+
+            int lineHeight= fCachedTextWidget.getLineHeight(fCachedTextWidget.getOffsetAtLine(widgetLine));
+            paintLine(line, y, lineHeight, gc, display);
+            y += lineHeight;
+        }
+    }
+
+    /* @since 3.2 */
+    private static int end(ILineRange range) {
+        return range.getStartLine() + range.getNumberOfLines();
+    }
+
+    /**
+     * Computes the string to be printed for <code>line</code>. The default implementation returns
+     * <code>Integer.toString(line + 1)</code>.
+     *
+     * @param line the line number for which the line number string is generated
+     * @return the string to be printed on the line number bar for <code>line</code>
+     * @since 3.0
+     */
+    protected String createDisplayString(int line) {
+        return Integer.toString(line + 1);
+    }
+
+    /**
+     * Returns the difference between the baseline of the widget and the
+     * baseline as specified by the font for <code>gc</code>. When drawing
+     * line numbers, the returned bias should be added to obtain text lined up
+     * on the correct base line of the text widget.
+     *
+     * @param gc the <code>GC</code> to get the font metrics from
+     * @param widgetLine the widget line
+     * @return the baseline bias to use when drawing text that is lined up with
+     *         <code>fCachedTextWidget</code>
+     * @since 3.2
+     */
+    private int getBaselineBias(GC gc, int widgetLine) {
+        /*
+         * https://bugs.eclipse.org/bugs/show_bug.cgi?id=62951
+         * widget line height may be more than the font height used for the
+         * line numbers, since font styles (bold, italics...) can have larger
+         * font metrics than the simple font used for the numbers.
+         */
+        int offset= fCachedTextWidget.getOffsetAtLine(widgetLine);
+        int widgetBaseline= fCachedTextWidget.getBaseline(offset);
+
+        FontMetrics fm= gc.getFontMetrics();
+        int fontBaseline= fm.getAscent() + fm.getLeading();
+        int baselineBias= widgetBaseline - fontBaseline;
+        return Math.max(0, baselineBias);
+    }
+
+    /**
+     * Paints the line. After this method is called the line numbers are painted on top
+     * of the result of this method.
+     *
+     * @param line the line of the document which the ruler is painted for
+     * @param y the y-coordinate of the box being painted for <code>line</code>, relative to <code>gc</code>
+     * @param lineheight the height of one line (and therefore of the box being painted)
+     * @param gc the drawing context the client may choose to draw on.
+     * @param display the display the drawing occurs on
+     * @since 3.0
+     */
+    protected void paintLine(int line, int y, int lineheight, GC gc, Display display) {
+        int widgetLine= JFaceTextUtil.modelLineToWidgetLine(fCachedTextViewer, line);
+
+        String s= createDisplayString(line);
+        int indentation= fIndentation[s.length];
+        int baselineBias= getBaselineBias(gc, widgetLine);
+        gc.drawString(s, indentation, y + baselineBias, true);
+    }
+
+    /**
+     * Triggers a redraw in the display thread.
+     *
+     * @since 3.0
+     */
+    protected final void postRedraw() {
+        if (fCanvas !is null && !fCanvas.isDisposed()) {
+            Display d= fCanvas.getDisplay();
+            if (d !is null) {
+                synchronized (fRunnableLock) {
+                    if (fIsRunnablePosted)
+                        return;
+                    fIsRunnablePosted= true;
+                }
+                d.asyncExec(fRunnable);
+            }
+        }
+    }
+
+    /*
+     * @see IVerticalRulerColumn#redraw()
+     */
+    public void redraw() {
+
+        if (fRelayoutRequired) {
+            layout(true);
+            return;
+        }
+
+        if (fCachedTextViewer !is null && fCanvas !is null && !fCanvas.isDisposed()) {
+            GC gc= new GC(fCanvas);
+            doubleBufferPaint(gc);
+            gc.dispose();
+        }
+    }
+
+    /*
+     * @see IVerticalRulerColumn#setModel(IAnnotationModel)
+     */
+    public void setModel(IAnnotationModel model) {
+    }
+
+    /*
+     * @see IVerticalRulerColumn#setFont(Font)
+     */
+    public void setFont(Font font) {
+        fFont= font;
+        if (fCanvas !is null && !fCanvas.isDisposed()) {
+            fCanvas.setFont(fFont);
+            updateNumberOfDigits();
+            computeIndentations();
+        }
+    }
+
+    /**
+     * Returns the parent (composite) ruler of this ruler column.
+     *
+     * @return the parent ruler
+     * @since 3.0
+     */
+    protected CompositeRuler getParentRuler() {
+        return fParentRuler;
+    }
+
+
+    /**
+     * Returns the number of lines in the view port.
+     *
+     * @param textWidget
+     * @return the number of lines visible in the view port <code>-1</code> if there's no client area
+     * @deprecated this method should not be used - it relies on the widget using a uniform line height
+     */
+    static int getVisibleLinesInViewport(StyledText textWidget) {
+        if (textWidget !is null) {
+            Rectangle clArea= textWidget.getClientArea();
+            if (!clArea.isEmpty()) {
+                int firstPixel= 0;
+                int lastPixel= clArea.height - 1; // XXX what about margins? don't take trims as they include scrollbars
+                int first= JFaceTextUtil.getLineIndex(textWidget, firstPixel);
+                int last= JFaceTextUtil.getLineIndex(textWidget, lastPixel);
+                return last - first;
+            }
+        }
+        return -1;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/LineRange.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.source.LineRange;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Default implementation of {@link ILineRange}.
+ *
+ * @since 3.0
+ */
+public final class LineRange : ILineRange {
+
+    private int fStartLine;
+    private int fNumberOfLines;
+
+    /**
+     * Creates a new line range with the given specification.
+     *
+     * @param startLine the start line
+     * @param numberOfLines the number of lines
+     */
+    public this(int startLine, int numberOfLines) {
+        fStartLine= startLine;
+        fNumberOfLines= numberOfLines;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.ILineRange#getStartLine()
+     */
+    public int getStartLine() {
+        return fStartLine;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.ILineRange#getNumberOfLines()
+     */
+    public int getNumberOfLines() {
+        return fNumberOfLines;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/MatchingCharacterPainter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,342 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.MatchingCharacterPainter;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IPaintPositionManager;
+import org.eclipse.jface.text.IPainter;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+
+/**
+ * Highlights the peer character matching the character near the caret position.
+ * This painter can be configured with an
+ * {@link org.eclipse.jface.text.source.ICharacterPairMatcher}.
+ * <p>
+ * Clients instantiate and configure object of this class.</p>
+ *
+ * @since 2.1
+ */
+public final class MatchingCharacterPainter : IPainter, PaintListener {
+
+    /** Indicates whether this painter is active */
+    private bool fIsActive= false;
+    /** The source viewer this painter is associated with */
+    private ISourceViewer fSourceViewer;
+    /** The viewer's widget */
+    private StyledText fTextWidget;
+    /** The color in which to highlight the peer character */
+    private Color fColor;
+    /** The paint position manager */
+    private IPaintPositionManager fPaintPositionManager;
+    /** The strategy for finding matching characters */
+    private ICharacterPairMatcher fMatcher;
+    /** The position tracking the matching characters */
+    private Position fPairPosition;
+    /** The anchor indicating whether the character is left or right of the caret */
+    private int fAnchor;
+
+
+    /**
+     * Creates a new MatchingCharacterPainter for the given source viewer using
+     * the given character pair matcher. The character matcher is not adopted by
+     * this painter. Thus,  it is not disposed. However, this painter requires
+     * exclusive access to the given pair matcher.
+     *
+     * @param sourceViewer
+     * @param matcher
+     */
+    public this(ISourceViewer sourceViewer, ICharacterPairMatcher matcher) {
+        fPairPosition= new Position(0, 0);
+        fSourceViewer= sourceViewer;
+        fMatcher= matcher;
+        fTextWidget= sourceViewer.getTextWidget();
+    }
+
+    /**
+     * Sets the color in which to highlight the match character.
+     *
+     * @param color the color
+     */
+    public void setColor(Color color) {
+        fColor= color;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPainter#dispose()
+     */
+    public void dispose() {
+        if (fMatcher !is null) {
+            fMatcher.clear();
+            fMatcher= null;
+        }
+
+        fColor= null;
+        fTextWidget= null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPainter#deactivate(bool)
+     */
+    public void deactivate(bool redraw) {
+        if (fIsActive) {
+            fIsActive= false;
+            fTextWidget.removePaintListener(this);
+            if (fPaintPositionManager !is null)
+                fPaintPositionManager.unmanagePosition(fPairPosition);
+            if (redraw)
+                handleDrawRequest(null);
+        }
+    }
+
+    /*
+     * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
+     */
+    public void paintControl(PaintEvent event) {
+        if (fTextWidget !is null)
+            handleDrawRequest(event.gc);
+    }
+
+    /**
+     * Handles a redraw request.
+     *
+     * @param gc the GC to draw into.
+     */
+    private void handleDrawRequest(GC gc) {
+
+        if (fPairPosition.isDeleted)
+            return;
+
+        int offset= fPairPosition.getOffset();
+        int length= fPairPosition.getLength();
+        if (length < 1)
+            return;
+
+        if ( cast(ITextViewerExtension5)fSourceViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) fSourceViewer;
+            IRegion widgetRange= extension.modelRange2WidgetRange(new Region(offset, length));
+            if (widgetRange is null)
+                return;
+
+            try {
+                // don't draw if the pair position is really hidden and widgetRange just
+                // marks the coverage around it.
+                IDocument doc= fSourceViewer.getDocument();
+                int startLine= doc.getLineOfOffset(offset);
+                int endLine= doc.getLineOfOffset(offset + length);
+                if (extension.modelLine2WidgetLine(startLine) is -1 || extension.modelLine2WidgetLine(endLine) is -1)
+                    return;
+            } catch (BadLocationException e) {
+                return;
+            }
+
+            offset= widgetRange.getOffset();
+            length= widgetRange.getLength();
+
+        } else {
+            IRegion region= fSourceViewer.getVisibleRegion();
+            if (region.getOffset() > offset || region.getOffset() + region.getLength() < offset + length)
+                return;
+            offset -= region.getOffset();
+        }
+
+        if (ICharacterPairMatcher.RIGHT is fAnchor)
+            draw(gc, offset, 1);
+        else
+            draw(gc, offset + length -1, 1);
+    }
+
+    /**
+     * Highlights the given widget region.
+     *
+     * @param gc the GC to draw into
+     * @param offset the offset of the widget region
+     * @param length the length of the widget region
+     */
+    private void draw(GC gc, int offset, int length) {
+        if (gc !is null) {
+
+            gc.setForeground(fColor);
+
+            Rectangle bounds;
+            if (length > 0)
+                bounds= fTextWidget.getTextBounds(offset, offset + length - 1);
+            else {
+                Point loc= fTextWidget.getLocationAtOffset(offset);
+                bounds= new Rectangle(loc.x, loc.y, 1, fTextWidget.getLineHeight(offset));
+            }
+
+            // draw box around line segment
+            gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1);
+
+            // draw box around character area
+//          int widgetBaseline= fTextWidget.getBaseline();
+//          FontMetrics fm= gc.getFontMetrics();
+//          int fontBaseline= fm.getAscent() + fm.getLeading();
+//          int fontBias= widgetBaseline - fontBaseline;
+
+//          gc.drawRectangle(left.x, left.y + fontBias, right.x - left.x - 1, fm.getHeight() - 1);
+
+        } else {
+            fTextWidget.redrawRange(offset, length, true);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPainter#paint(int)
+     */
+    public void paint(int reason) {
+
+        IDocument document= fSourceViewer.getDocument();
+        if (document is null) {
+            deactivate(false);
+            return;
+        }
+
+        Point selection= fSourceViewer.getSelectedRange();
+        if (selection.y > 0) {
+            deactivate(true);
+            return;
+        }
+
+        IRegion pair= fMatcher.match(document, selection.x);
+        if (pair is null) {
+            deactivate(true);
+            return;
+        }
+
+        if (fIsActive) {
+
+            if (IPainter.CONFIGURATION is reason) {
+
+                // redraw current highlighting
+                handleDrawRequest(null);
+
+            } else if (pair.getOffset() !is fPairPosition.getOffset() ||
+                    pair.getLength() !is fPairPosition.getLength() ||
+                    fMatcher.getAnchor() !is fAnchor) {
+
+                // otherwise only do something if position is different
+
+                // remove old highlighting
+                handleDrawRequest(null);
+                // update position
+                fPairPosition.isDeleted_= false;
+                fPairPosition.offset= pair.getOffset();
+                fPairPosition.length= pair.getLength();
+                fAnchor= fMatcher.getAnchor();
+                // apply new highlighting
+                handleDrawRequest(null);
+
+            }
+        } else {
+
+            fIsActive= true;
+
+            fPairPosition.isDeleted_= false;
+            fPairPosition.offset= pair.getOffset();
+            fPairPosition.length= pair.getLength();
+            fAnchor= fMatcher.getAnchor();
+
+            fTextWidget.addPaintListener(this);
+            fPaintPositionManager.managePosition(fPairPosition);
+            handleDrawRequest(null);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPainter#setPositionManager(org.eclipse.jface.text.IPaintPositionManager)
+     */
+    public void setPositionManager(IPaintPositionManager manager) {
+        fPaintPositionManager= manager;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/OverviewRuler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1448 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.OverviewRuler;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+
+
+
+
+
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackAdapter;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.JFaceTextUtil;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextEvent;
+import org.eclipse.jface.text.source.projection.AnnotationBag;
+
+
+/**
+ * Ruler presented next to a source viewer showing all annotations of the
+ * viewer's annotation model in a compact format. The ruler has the same height
+ * as the source viewer.
+ * <p>
+ * Clients usually instantiate and configure objects of this class.</p>
+ *
+ * @since 2.1
+ */
+public class OverviewRuler : IOverviewRuler {
+
+    /**
+     * Internal listener class.
+     */
+    class InternalListener : ITextListener, IAnnotationModelListener, IAnnotationModelListenerExtension {
+
+        /*
+         * @see ITextListener#textChanged
+         */
+        public void textChanged(TextEvent e) {
+            if (fTextViewer !is null && e.getDocumentEvent() is null && e.getViewerRedrawState()) {
+                // handle only changes of visible document
+                redraw();
+            }
+        }
+
+        /*
+         * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
+         */
+        public void modelChanged(IAnnotationModel model) {
+            update();
+        }
+
+        /*
+         * @see org.eclipse.jface.text.source.IAnnotationModelListenerExtension#modelChanged(org.eclipse.jface.text.source.AnnotationModelEvent)
+         * @since 3.3
+         */
+        public void modelChanged(AnnotationModelEvent event) {
+            if (!event.isValid())
+                return;
+
+            if (event.isWorldChange()) {
+                update();
+                return;
+            }
+
+            Annotation[] annotations= event.getAddedAnnotations();
+            int length= annotations.length;
+            for (int i= 0; i < length; i++) {
+                if (!skip(annotations[i].getType())) {
+                    update();
+                    return;
+                }
+            }
+
+            annotations= event.getRemovedAnnotations();
+            length= annotations.length;
+            for (int i= 0; i < length; i++) {
+                if (!skip(annotations[i].getType())) {
+                    update();
+                    return;
+                }
+            }
+
+            annotations= event.getChangedAnnotations();
+            length= annotations.length;
+            for (int i= 0; i < length; i++) {
+                if (!skip(annotations[i].getType())) {
+                    update();
+                    return;
+                }
+            }
+
+        }
+    }
+
+    /**
+     * Enumerates the annotations of a specified type and characteristics
+     * of the associated annotation model.
+     */
+    class FilterIterator : Iterator {
+
+        final static int TEMPORARY= 1 << 1;
+        final static int PERSISTENT= 1 << 2;
+        final static int IGNORE_BAGS= 1 << 3;
+
+        private Iterator fIterator;
+        private Object fType;
+        private Annotation fNext;
+        private int fStyle;
+
+        /**
+         * Creates a new filter iterator with the given specification.
+         *
+         * @param annotationType the annotation type
+         * @param style the style
+         */
+        public this(Object annotationType, int style) {
+            fType= annotationType;
+            fStyle= style;
+            if (fModel !is null) {
+                fIterator= fModel.getAnnotationIterator();
+                skip();
+            }
+        }
+
+        /**
+         * Creates a new filter iterator with the given specification.
+         *
+         * @param annotationType the annotation type
+         * @param style the style
+         * @param iterator the iterator
+         */
+        public this(Object annotationType, int style, Iterator iterator) {
+            fType= annotationType;
+            fStyle= style;
+            fIterator= iterator;
+            skip();
+        }
+
+        private void skip() {
+
+            bool temp= (fStyle & TEMPORARY) !is 0;
+            bool pers= (fStyle & PERSISTENT) !is 0;
+            bool ignr= (fStyle & IGNORE_BAGS) !is 0;
+
+            while (fIterator.hasNext()) {
+                Annotation next= cast(Annotation) fIterator.next();
+
+                if (next.isMarkedDeleted())
+                    continue;
+
+                if (ignr && ( cast(AnnotationBag)next ))
+                    continue;
+
+                fNext= next;
+                Object annotationType= stringcast(next.getType());
+                if (fType is null || fType.opEquals(annotationType) || !fConfiguredAnnotationTypes.contains(annotationType) && isSubtype(annotationType)) {
+                    if (temp && pers) return;
+                    if (pers && next.isPersistent()) return;
+                    if (temp && !next.isPersistent()) return;
+                }
+            }
+            fNext= null;
+        }
+
+        private bool isSubtype(Object annotationType) {
+            if ( cast(IAnnotationAccessExtension)fAnnotationAccess ) {
+                IAnnotationAccessExtension extension= cast(IAnnotationAccessExtension) fAnnotationAccess;
+                return extension.isSubtype(annotationType, fType);
+            }
+            return cast(bool) fType.opEquals(annotationType);
+        }
+
+        /*
+         * @see Iterator#hasNext()
+         */
+        public bool hasNext() {
+            return fNext !is null;
+        }
+        /*
+         * @see Iterator#next()
+         */
+        public Object next() {
+            try {
+                return fNext;
+            } finally {
+                if (fIterator !is null)
+                    skip();
+            }
+        }
+        /*
+         * @see Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    /**
+     * The painter of the overview ruler's header.
+     */
+    class HeaderPainter : PaintListener {
+
+        private Color fIndicatorColor;
+        private Color fSeparatorColor;
+
+        /**
+         * Creates a new header painter.
+         */
+        public this() {
+            fSeparatorColor= fHeader.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
+        }
+
+        /**
+         * Sets the header color.
+         *
+         * @param color the header color
+         */
+        public void setColor(Color color) {
+            fIndicatorColor= color;
+        }
+
+        private void drawBevelRect(GC gc, int x, int y, int w, int h, Color topLeft, Color bottomRight) {
+            gc.setForeground(topLeft is null ? fSeparatorColor : topLeft);
+            gc.drawLine(x, y, x + w -1, y);
+            gc.drawLine(x, y, x, y + h -1);
+
+            gc.setForeground(bottomRight is null ? fSeparatorColor : bottomRight);
+            gc.drawLine(x + w, y, x + w, y + h);
+            gc.drawLine(x, y + h, x + w, y + h);
+        }
+
+        public void paintControl(PaintEvent e) {
+            if (fIndicatorColor is null)
+                return;
+
+            Point s= fHeader.getSize();
+
+            e.gc.setBackground(fIndicatorColor);
+            Rectangle r= new Rectangle(INSET, (s.y - (2*ANNOTATION_HEIGHT)) / 2, s.x - (2*INSET), 2*ANNOTATION_HEIGHT);
+            e.gc.fillRectangle(r);
+            Display d= fHeader.getDisplay();
+            if (d !is null)
+//              drawBevelRect(e.gc, r.x, r.y, r.width -1, r.height -1, d.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW), d.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
+                drawBevelRect(e.gc, r.x, r.y, r.width -1, r.height -1, null, null);
+
+            e.gc.setForeground(fSeparatorColor);
+            e.gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
+            e.gc.drawLine(0, s.y -1, s.x -1, s.y -1);
+        }
+    }
+
+    private static const int INSET= 2;
+    private static const int ANNOTATION_HEIGHT= 4;
+    private static bool ANNOTATION_HEIGHT_SCALABLE= true;
+
+
+    /** The model of the overview ruler */
+    private IAnnotationModel fModel;
+    /** The view to which this ruler is connected */
+    private ITextViewer fTextViewer;
+    /** The ruler's canvas */
+    private Canvas fCanvas;
+    /** The ruler's header */
+    private Canvas fHeader;
+    /** The buffer for double buffering */
+    private Image fBuffer;
+    /** The internal listener */
+    private InternalListener fInternalListener;
+    /** The width of this vertical ruler */
+    private int fWidth;
+    /** The hit detection cursor */
+    private Cursor fHitDetectionCursor;
+    /** The last cursor */
+    private Cursor fLastCursor;
+    /** The line of the last mouse button activity */
+    private int fLastMouseButtonActivityLine= -1;
+    /** The actual annotation height */
+    private int fAnnotationHeight= -1;
+    /** The annotation access */
+    private IAnnotationAccess fAnnotationAccess;
+    /** The header painter */
+    private HeaderPainter fHeaderPainter;
+    /**
+     * The list of annotation types to be shown in this ruler.
+     * @since 3.0
+     */
+    private Set fConfiguredAnnotationTypes;
+    /**
+     * The list of annotation types to be shown in the header of this ruler.
+     * @since 3.0
+     */
+    private Set fConfiguredHeaderAnnotationTypes;
+    /** The mapping between annotation types and colors */
+    private Map fAnnotationTypes2Colors;
+    /** The color manager */
+    private ISharedTextColors fSharedTextColors;
+    /**
+     * All available annotation types sorted by layer.
+     *
+     * @since 3.0
+     */
+    private List fAnnotationsSortedByLayer;
+    /**
+     * All available layers sorted by layer.
+     * This list may contain duplicates.
+     * @since 3.0
+     */
+    private List fLayersSortedByLayer;
+    /**
+     * Map of allowed annotation types.
+     * An allowed annotation type maps to <code>true</code>, a disallowed
+     * to <code>false</code>.
+     * @since 3.0
+     */
+    private Map fAllowedAnnotationTypes;
+    /**
+     * Map of allowed header annotation types.
+     * An allowed annotation type maps to <code>true</code>, a disallowed
+     * to <code>false</code>.
+     * @since 3.0
+     */
+    private Map fAllowedHeaderAnnotationTypes;
+    /**
+     * The cached annotations.
+     * @since 3.0
+     */
+    private List fCachedAnnotations;
+
+    /**
+     * Redraw runnable lock
+     * @since 3.3
+     */
+    private Object fRunnableLock;
+    /**
+     * Redraw runnable state
+     * @since 3.3
+     */
+    private bool fIsRunnablePosted= false;
+    /**
+     * Redraw runnable
+     * @since 3.3
+     */
+    private Runnable fRunnable;
+    /**
+     * Tells whether temporary annotations are drawn with
+     * a separate color. This color will be computed by
+     * discoloring the original annotation color.
+     *
+     * @since 3.4
+     */
+    private bool fIsTemporaryAnnotationDiscolored;
+
+
+    /**
+     * Constructs a overview ruler of the given width using the given annotation access and the given
+     * color manager.
+     * <p><strong>Note:</strong> As of 3.4, temporary annotations are no longer discolored.
+     * Use {@link #OverviewRuler(IAnnotationAccess, int, ISharedTextColors, bool)} if you
+     * want to keep the old behavior.</p>
+     *
+     * @param annotationAccess the annotation access
+     * @param width the width of the vertical ruler
+     * @param sharedColors the color manager
+     */
+    public this(IAnnotationAccess annotationAccess, int width, ISharedTextColors sharedColors) {
+        this(annotationAccess, width, sharedColors, false);
+    }
+
+    /**
+     * Constructs a overview ruler of the given width using the given annotation
+     * access and the given color manager.
+     *
+     * @param annotationAccess the annotation access
+     * @param width the width of the vertical ruler
+     * @param sharedColors the color manager
+     * @param discolorTemporaryAnnotation <code>true</code> if temporary annotations should be discolored
+     * @since 3.4
+     */
+    public this(IAnnotationAccess annotationAccess, int width, ISharedTextColors sharedColors, bool discolorTemporaryAnnotation) {
+        // SWT instance init
+        fInternalListener= new InternalListener();
+        fConfiguredAnnotationTypes= new HashSet();
+        fConfiguredHeaderAnnotationTypes= new HashSet();
+        fAnnotationTypes2Colors= new HashMap();
+        fAnnotationsSortedByLayer= new ArrayList();
+        fLayersSortedByLayer= new ArrayList();
+        fAllowedAnnotationTypes= new HashMap();
+        fAllowedHeaderAnnotationTypes= new HashMap();
+        fCachedAnnotations= new ArrayList();
+        fRunnableLock= new Object();
+        fRunnable= dgRunnable( {
+            synchronized (fRunnableLock) {
+                fIsRunnablePosted= false;
+            }
+            redraw();
+            updateHeader();
+        });
+
+        fAnnotationAccess= annotationAccess;
+        fWidth= width;
+        fSharedTextColors= sharedColors;
+        fIsTemporaryAnnotationDiscolored= discolorTemporaryAnnotation;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfo#getControl()
+     */
+    public Control getControl() {
+        return fCanvas;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRulerInfo#getWidth()
+     */
+    public int getWidth() {
+        return fWidth;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRuler#setModel(org.eclipse.jface.text.source.IAnnotationModel)
+     */
+    public void setModel(IAnnotationModel model) {
+        if (model !is fModel || model !is null) {
+
+            if (fModel !is null)
+                fModel.removeAnnotationModelListener(fInternalListener);
+
+            fModel= model;
+
+            if (fModel !is null)
+                fModel.addAnnotationModelListener(fInternalListener);
+
+            update();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRuler#createControl(org.eclipse.swt.widgets.Composite, org.eclipse.jface.text.ITextViewer)
+     */
+    public Control createControl(Composite parent, ITextViewer textViewer) {
+
+        fTextViewer= textViewer;
+
+        fHitDetectionCursor= new Cursor(parent.getDisplay(), SWT.CURSOR_HAND);
+
+        fHeader= new Canvas(parent, SWT.NONE);
+
+        if ( cast(IAnnotationAccessExtension)fAnnotationAccess ) {
+            fHeader.addMouseTrackListener(new class()  MouseTrackAdapter {
+                /*
+                 * @see org.eclipse.swt.events.MouseTrackAdapter#mouseHover(org.eclipse.swt.events.MouseEvent)
+                 * @since 3.3
+                 */
+                public void mouseEnter(MouseEvent e) {
+                    updateHeaderToolTipText();
+                }
+            });
+        }
+
+        fCanvas= new Canvas(parent, SWT.NO_BACKGROUND);
+
+        fCanvas.addPaintListener(new class()  PaintListener {
+            public void paintControl(PaintEvent event) {
+                if (fTextViewer !is null)
+                    doubleBufferPaint(event.gc);
+            }
+        });
+
+        fCanvas.addDisposeListener(new class()  DisposeListener {
+            public void widgetDisposed(DisposeEvent event) {
+                handleDispose();
+                fTextViewer= null;
+            }
+        });
+
+        fCanvas.addMouseListener(new class()  MouseAdapter {
+            public void mouseDown(MouseEvent event) {
+                handleMouseDown(event);
+            }
+        });
+
+        fCanvas.addMouseMoveListener(new class()  MouseMoveListener {
+            public void mouseMove(MouseEvent event) {
+                handleMouseMove(event);
+            }
+        });
+
+        if (fTextViewer !is null)
+            fTextViewer.addTextListener(fInternalListener);
+
+        return fCanvas;
+    }
+
+    /**
+     * Disposes the ruler's resources.
+     */
+    private void handleDispose() {
+
+        if (fTextViewer !is null) {
+            fTextViewer.removeTextListener(fInternalListener);
+            fTextViewer= null;
+        }
+
+        if (fModel !is null)
+            fModel.removeAnnotationModelListener(fInternalListener);
+
+        if (fBuffer !is null) {
+            fBuffer.dispose();
+            fBuffer= null;
+        }
+
+        if (fHitDetectionCursor !is null) {
+            fHitDetectionCursor.dispose();
+            fHitDetectionCursor= null;
+        }
+
+        fConfiguredAnnotationTypes.clear();
+        fAllowedAnnotationTypes.clear();
+        fConfiguredHeaderAnnotationTypes.clear();
+        fAllowedHeaderAnnotationTypes.clear();
+        fAnnotationTypes2Colors.clear();
+        fAnnotationsSortedByLayer.clear();
+        fLayersSortedByLayer.clear();
+    }
+
+    /**
+     * Double buffer drawing.
+     *
+     * @param dest the GC to draw into
+     */
+    private void doubleBufferPaint(GC dest) {
+
+        Point size= fCanvas.getSize();
+
+        if (size.x <= 0 || size.y <= 0)
+            return;
+
+        if (fBuffer !is null) {
+            Rectangle r= fBuffer.getBounds();
+            if (r.width !is size.x || r.height !is size.y) {
+                fBuffer.dispose();
+                fBuffer= null;
+            }
+        }
+        if (fBuffer is null)
+            fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y);
+
+        GC gc= new GC(fBuffer);
+        try {
+            gc.setBackground(fCanvas.getBackground());
+            gc.fillRectangle(0, 0, size.x, size.y);
+
+            cacheAnnotations();
+
+            if ( cast(ITextViewerExtension5)fTextViewer )
+                doPaint1(gc);
+            else
+                doPaint(gc);
+
+        } finally {
+            gc.dispose();
+        }
+
+        dest.drawImage(fBuffer, 0, 0);
+    }
+
+    /**
+     * Draws this overview ruler.
+     *
+     * @param gc the GC to draw into
+     */
+    private void doPaint(GC gc) {
+
+        Rectangle r= new Rectangle(0, 0, 0, 0);
+        int yy, hh= ANNOTATION_HEIGHT;
+
+        IDocument document= fTextViewer.getDocument();
+        IRegion visible= fTextViewer.getVisibleRegion();
+
+        StyledText textWidget= fTextViewer.getTextWidget();
+        int maxLines= textWidget.getLineCount();
+
+        Point size= fCanvas.getSize();
+        int writable= JFaceTextUtil.computeLineHeight(textWidget, 0, maxLines, maxLines);
+
+        if (size.y > writable)
+            size.y= Math.max(writable - fHeader.getSize().y, 0);
+
+        for (Iterator iterator= fAnnotationsSortedByLayer.iterator(); iterator.hasNext();) {
+            Object annotationType= iterator.next();
+
+            if (skip(annotationType))
+                continue;
+
+            int[] style= [ FilterIterator.PERSISTENT, FilterIterator.TEMPORARY ];
+            for (int t=0; t < style.length; t++) {
+
+                Iterator e= new FilterIterator(annotationType, style[t], fCachedAnnotations.iterator());
+                Color fill= getFillColor(annotationType, style[t] is FilterIterator.TEMPORARY);
+                Color stroke= getStrokeColor(annotationType, style[t] is FilterIterator.TEMPORARY);
+
+                for (int i= 0; e.hasNext(); i++) {
+
+                    Annotation a= cast(Annotation) e.next();
+                    Position p= fModel.getPosition(a);
+
+                    if (p is null || !p.overlapsWith(visible.getOffset(), visible.getLength()))
+                        continue;
+
+                    int annotationOffset= Math.max(p.getOffset(), visible.getOffset());
+                    int annotationEnd= Math.min(p.getOffset() + p.getLength(), visible.getOffset() + visible.getLength());
+                    int annotationLength= annotationEnd - annotationOffset;
+
+                    try {
+                        if (ANNOTATION_HEIGHT_SCALABLE) {
+                            int numbersOfLines= document.getNumberOfLines(annotationOffset, annotationLength);
+                            // don't count empty trailing lines
+                            IRegion lastLine= document.getLineInformationOfOffset(annotationOffset + annotationLength);
+                            if (lastLine.getOffset() is annotationOffset + annotationLength) {
+                                numbersOfLines -= 2;
+                                hh= (numbersOfLines * size.y) / maxLines + ANNOTATION_HEIGHT;
+                                if (hh < ANNOTATION_HEIGHT)
+                                    hh= ANNOTATION_HEIGHT;
+                            } else
+                                hh= ANNOTATION_HEIGHT;
+                        }
+                        fAnnotationHeight= hh;
+
+                        int startLine= textWidget.getLineAtOffset(annotationOffset - visible.getOffset());
+                        yy= Math.min((startLine * size.y) / maxLines, size.y - hh);
+
+                        if (fill !is null) {
+                            gc.setBackground(fill);
+                            gc.fillRectangle(INSET, yy, size.x-(2*INSET), hh);
+                        }
+
+                        if (stroke !is null) {
+                            gc.setForeground(stroke);
+                            r.x= INSET;
+                            r.y= yy;
+                            r.width= size.x - (2 * INSET);
+                            r.height= hh;
+                            gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
+                            gc.drawRectangle(r);
+                        }
+                    } catch (BadLocationException x) {
+                    }
+                }
+            }
+        }
+    }
+
+    private void cacheAnnotations() {
+        fCachedAnnotations.clear();
+        if (fModel !is null) {
+            Iterator iter= fModel.getAnnotationIterator();
+            while (iter.hasNext()) {
+                Annotation annotation= cast(Annotation) iter.next();
+
+                if (annotation.isMarkedDeleted())
+                    continue;
+
+                if (skip(annotation.getType()))
+                    continue;
+
+                fCachedAnnotations.add(annotation);
+            }
+        }
+    }
+
+    /**
+     * Draws this overview ruler. Uses <code>ITextViewerExtension5</code> for
+     * its implementation. Will replace <code>doPaint(GC)</code>.
+     *
+     * @param gc the GC to draw into
+     */
+    private void doPaint1(GC gc) {
+
+        Rectangle r= new Rectangle(0, 0, 0, 0);
+        int yy, hh= ANNOTATION_HEIGHT;
+
+        ITextViewerExtension5 extension= cast(ITextViewerExtension5) fTextViewer;
+        IDocument document= fTextViewer.getDocument();
+        StyledText textWidget= fTextViewer.getTextWidget();
+
+        int maxLines= textWidget.getLineCount();
+        Point size= fCanvas.getSize();
+        int writable= JFaceTextUtil.computeLineHeight(textWidget, 0, maxLines, maxLines);
+        if (size.y > writable)
+            size.y= Math.max(writable - fHeader.getSize().y, 0);
+
+        for (Iterator iterator= fAnnotationsSortedByLayer.iterator(); iterator.hasNext();) {
+            Object annotationType= iterator.next();
+
+            if (skip(annotationType))
+                continue;
+
+            int[] style= [ FilterIterator.PERSISTENT, FilterIterator.TEMPORARY ];
+            for (int t=0; t < style.length; t++) {
+
+                Iterator e= new FilterIterator(annotationType, style[t], fCachedAnnotations.iterator());
+                Color fill= getFillColor(annotationType, style[t] is FilterIterator.TEMPORARY);
+                Color stroke= getStrokeColor(annotationType, style[t] is FilterIterator.TEMPORARY);
+
+                for (int i= 0; e.hasNext(); i++) {
+
+                    Annotation a= cast(Annotation) e.next();
+                    Position p= fModel.getPosition(a);
+
+                    if (p is null)
+                        continue;
+
+                    IRegion widgetRegion= extension.modelRange2WidgetRange(new Region(p.getOffset(), p.getLength()));
+                    if (widgetRegion is null)
+                        continue;
+
+                    try {
+                        if (ANNOTATION_HEIGHT_SCALABLE) {
+                            int numbersOfLines= document.getNumberOfLines(p.getOffset(), p.getLength());
+                            // don't count empty trailing lines
+                            IRegion lastLine= document.getLineInformationOfOffset(p.getOffset() + p.getLength());
+                            if (lastLine.getOffset() is p.getOffset() + p.getLength()) {
+                                numbersOfLines -= 2;
+                                hh= (numbersOfLines * size.y) / maxLines + ANNOTATION_HEIGHT;
+                                if (hh < ANNOTATION_HEIGHT)
+                                    hh= ANNOTATION_HEIGHT;
+                            } else
+                                hh= ANNOTATION_HEIGHT;
+                        }
+                        fAnnotationHeight= hh;
+
+                        int startLine= textWidget.getLineAtOffset(widgetRegion.getOffset());
+                        yy= Math.min((startLine * size.y) / maxLines, size.y - hh);
+
+                        if (fill !is null) {
+                            gc.setBackground(fill);
+                            gc.fillRectangle(INSET, yy, size.x-(2*INSET), hh);
+                        }
+
+                        if (stroke !is null) {
+                            gc.setForeground(stroke);
+                            r.x= INSET;
+                            r.y= yy;
+                            r.width= size.x - (2 * INSET);
+                            r.height= hh;
+                            gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
+                            gc.drawRectangle(r);
+                        }
+                    } catch (BadLocationException x) {
+                    }
+                }
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRuler#update()
+     */
+     public void update() {
+        if (fCanvas !is null && !fCanvas.isDisposed()) {
+            Display d= fCanvas.getDisplay();
+            if (d !is null) {
+                synchronized (fRunnableLock) {
+                    if (fIsRunnablePosted)
+                        return;
+                    fIsRunnablePosted= true;
+                }
+                d.asyncExec(fRunnable);
+            }
+        }
+    }
+
+    /**
+     * Redraws the overview ruler.
+     */
+    private void redraw() {
+        if (fTextViewer is null || fModel is null)
+            return;
+
+        if (fCanvas !is null && !fCanvas.isDisposed()) {
+            GC gc= new GC(fCanvas);
+            doubleBufferPaint(gc);
+            gc.dispose();
+        }
+    }
+
+    /**
+     * Translates a given y-coordinate of this ruler into the corresponding
+     * document lines. The number of lines depends on the concrete scaling
+     * given as the ration between the height of this ruler and the length
+     * of the document.
+     *
+     * @param y_coordinate the y-coordinate
+     * @return the corresponding document lines
+     */
+    private int[] toLineNumbers(int y_coordinate) {
+
+        StyledText textWidget=  fTextViewer.getTextWidget();
+        int maxLines= textWidget.getContent().getLineCount();
+
+        int rulerLength= fCanvas.getSize().y;
+        int writable= JFaceTextUtil.computeLineHeight(textWidget, 0, maxLines, maxLines);
+
+        if (rulerLength > writable)
+            rulerLength= Math.max(writable - fHeader.getSize().y, 0);
+
+        if (y_coordinate >= writable || y_coordinate >= rulerLength)
+            return [-1, -1];
+
+        int[] lines= new int[2];
+
+        int pixel0= Math.max(y_coordinate - 1, 0);
+        int pixel1= Math.min(rulerLength, y_coordinate + 1);
+        rulerLength= Math.max(rulerLength, 1);
+
+        lines[0]= (pixel0 * maxLines) / rulerLength;
+        lines[1]= (pixel1 * maxLines) / rulerLength;
+
+        if ( cast(ITextViewerExtension5)fTextViewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) fTextViewer;
+            lines[0]= extension.widgetLine2ModelLine(lines[0]);
+            lines[1]= extension.widgetLine2ModelLine(lines[1]);
+        } else {
+            try {
+                IRegion visible= fTextViewer.getVisibleRegion();
+                int lineNumber= fTextViewer.getDocument().getLineOfOffset(visible.getOffset());
+                lines[0] += lineNumber;
+                lines[1] += lineNumber;
+            } catch (BadLocationException x) {
+            }
+        }
+
+        return lines;
+    }
+
+    /**
+     * Returns the position of the first annotation found in the given line range.
+     *
+     * @param lineNumbers the line range
+     * @return the position of the first found annotation
+     */
+    private Position getAnnotationPosition(int[] lineNumbers) {
+        if (lineNumbers[0] is -1)
+            return null;
+
+        Position found= null;
+
+        try {
+            IDocument d= fTextViewer.getDocument();
+            IRegion line= d.getLineInformation(lineNumbers[0]);
+
+            int start= line.getOffset();
+
+            line= d.getLineInformation(lineNumbers[lineNumbers.length - 1]);
+            int end= line.getOffset() + line.getLength();
+
+            for (int i= fAnnotationsSortedByLayer.size() -1; i >= 0; i--) {
+
+                Object annotationType= fAnnotationsSortedByLayer.get(i);
+
+                Iterator e= new FilterIterator(annotationType, FilterIterator.PERSISTENT | FilterIterator.TEMPORARY);
+                while (e.hasNext() && found is null) {
+                    Annotation a= cast(Annotation) e.next();
+                    if (a.isMarkedDeleted())
+                        continue;
+
+                    if (skip(a.getType()))
+                        continue;
+
+                    Position p= fModel.getPosition(a);
+                    if (p is null)
+                        continue;
+
+                    int posOffset= p.getOffset();
+                    int posEnd= posOffset + p.getLength();
+                    IRegion region= d.getLineInformationOfOffset(posEnd);
+                    // trailing empty lines don't count
+                    if (posEnd > posOffset && region.getOffset() is posEnd) {
+                        posEnd--;
+                        region= d.getLineInformationOfOffset(posEnd);
+                    }
+
+                    if (posOffset <= end && posEnd >= start)
+                            found= p;
+                }
+            }
+        } catch (BadLocationException x) {
+        }
+
+        return found;
+    }
+
+    /**
+     * Returns the line which  corresponds best to one of
+     * the underlying annotations at the given y-coordinate.
+     *
+     * @param lineNumbers the line numbers
+     * @return the best matching line or <code>-1</code> if no such line can be found
+     */
+    private int findBestMatchingLineNumber(int[] lineNumbers) {
+        if (lineNumbers is null || lineNumbers.length < 1)
+            return -1;
+
+        try {
+            Position pos= getAnnotationPosition(lineNumbers);
+            if (pos is null)
+                return -1;
+            return fTextViewer.getDocument().getLineOfOffset(pos.getOffset());
+        } catch (BadLocationException ex) {
+            return -1;
+        }
+    }
+
+    /**
+     * Handles mouse clicks.
+     *
+     * @param event the mouse button down event
+     */
+    private void handleMouseDown(MouseEvent event) {
+        if (fTextViewer !is null) {
+            int[] lines= toLineNumbers(event.y);
+            Position p= getAnnotationPosition(lines);
+            if (p is null && event.button is 1) {
+                try {
+                    p= new Position(fTextViewer.getDocument().getLineInformation(lines[0]).getOffset(), 0);
+                } catch (BadLocationException e) {
+                    // do nothing
+                }
+            }
+            if (p !is null) {
+                fTextViewer.revealRange(p.getOffset(), p.getLength());
+                fTextViewer.setSelectedRange(p.getOffset(), p.getLength());
+            }
+            fTextViewer.getTextWidget().setFocus();
+        }
+        fLastMouseButtonActivityLine= toDocumentLineNumber(event.y);
+    }
+
+    /**
+     * Handles mouse moves.
+     *
+     * @param event the mouse move event
+     */
+    private void handleMouseMove(MouseEvent event) {
+        if (fTextViewer !is null) {
+            int[] lines= toLineNumbers(event.y);
+            Position p= getAnnotationPosition(lines);
+            Cursor cursor= (p !is null ? fHitDetectionCursor : null);
+            if (cursor !is fLastCursor) {
+                fCanvas.setCursor(cursor);
+                fLastCursor= cursor;
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IOverviewRuler#addAnnotationType(java.lang.Object)
+     */
+    public void addAnnotationType(Object annotationType) {
+        fConfiguredAnnotationTypes.add(annotationType);
+        fAllowedAnnotationTypes.clear();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IOverviewRuler#removeAnnotationType(java.lang.Object)
+     */
+    public void removeAnnotationType(Object annotationType) {
+        fConfiguredAnnotationTypes.remove(annotationType);
+        fAllowedAnnotationTypes.clear();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IOverviewRuler#setAnnotationTypeLayer(java.lang.Object, int)
+     */
+    public void setAnnotationTypeLayer(Object annotationType, int layer) {
+        int j= fAnnotationsSortedByLayer.indexOf(annotationType);
+        if (j !is -1) {
+            fAnnotationsSortedByLayer.remove(j);
+            fLayersSortedByLayer.remove(j);
+        }
+
+        if (layer >= 0) {
+            int i= 0;
+            int size= fLayersSortedByLayer.size();
+            while (i < size && layer >= (cast(Integer)fLayersSortedByLayer.get(i)).intValue())
+                i++;
+            Integer layerObj= new Integer(layer);
+            fLayersSortedByLayer.add(i, layerObj);
+            fAnnotationsSortedByLayer.add(i, annotationType);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IOverviewRuler#setAnnotationTypeColor(java.lang.Object, org.eclipse.swt.graphics.Color)
+     */
+    public void setAnnotationTypeColor(Object annotationType, Color color) {
+        if (color !is null)
+            fAnnotationTypes2Colors.put(annotationType, color);
+        else
+            fAnnotationTypes2Colors.remove(annotationType);
+    }
+
+    /**
+     * Returns whether the given annotation type should be skipped by the drawing routine.
+     *
+     * @param annotationType the annotation type
+     * @return <code>true</code> if annotation of the given type should be skipped
+     */
+    private bool skip(Object annotationType) {
+        return !contains(annotationType, fAllowedAnnotationTypes, fConfiguredAnnotationTypes);
+    }
+    private bool skip(String annotationType) {
+        return !contains(stringcast(annotationType), fAllowedAnnotationTypes, fConfiguredAnnotationTypes);
+    }
+
+    /**
+     * Returns whether the given annotation type should be skipped by the drawing routine of the header.
+     *
+     * @param annotationType the annotation type
+     * @return <code>true</code> if annotation of the given type should be skipped
+     * @since 3.0
+     */
+    private bool skipInHeader(Object annotationType) {
+        return !contains(annotationType, fAllowedHeaderAnnotationTypes, fConfiguredHeaderAnnotationTypes);
+    }
+
+    /**
+     * Returns whether the given annotation type is mapped to <code>true</code>
+     * in the given <code>allowed</code> map or covered by the <code>configured</code>
+     * set.
+     *
+     * @param annotationType the annotation type
+     * @param allowed the map with allowed annotation types mapped to booleans
+     * @param configured the set with configured annotation types
+     * @return <code>true</code> if annotation is contained, <code>false</code>
+     *         otherwise
+     * @since 3.0
+     */
+    private bool contains(Object annotationType, Map allowed, Set configured) {
+        Boolean cached= cast(Boolean) allowed.get(annotationType);
+        if (cached !is null)
+            return cached.booleanValue();
+
+        bool covered= isCovered(annotationType, configured);
+        allowed.put(annotationType, covered ? Boolean.TRUE : Boolean.FALSE);
+        return covered;
+    }
+
+    /**
+     * Computes whether the annotations of the given type are covered by the given <code>configured</code>
+     * set. This is the case if either the type of the annotation or any of its
+     * super types is contained in the <code>configured</code> set.
+     *
+     * @param annotationType the annotation type
+     * @param configured the set with configured annotation types
+     * @return <code>true</code> if annotation is covered, <code>false</code>
+     *         otherwise
+     * @since 3.0
+     */
+    private bool isCovered(Object annotationType, Set configured) {
+        if ( cast(IAnnotationAccessExtension)fAnnotationAccess ) {
+            IAnnotationAccessExtension extension= cast(IAnnotationAccessExtension) fAnnotationAccess;
+            Iterator e= configured.iterator();
+            while (e.hasNext()) {
+                if (extension.isSubtype(annotationType,e.next()))
+                    return true;
+            }
+            return false;
+        }
+        return configured.contains(annotationType);
+    }
+
+    /**
+     * Returns a specification of a color that lies between the given
+     * foreground and background color using the given scale factor.
+     *
+     * @param fg the foreground color
+     * @param bg the background color
+     * @param scale the scale factor
+     * @return the interpolated color
+     */
+    private static RGB interpolate(RGB fg, RGB bg, double scale) {
+        return new RGB(
+            cast(int) ((1.0-scale) * fg.red + scale * bg.red),
+            cast(int) ((1.0-scale) * fg.green + scale * bg.green),
+            cast(int) ((1.0-scale) * fg.blue + scale * bg.blue)
+        );
+    }
+
+    /**
+     * Returns the grey value in which the given color would be drawn in grey-scale.
+     *
+     * @param rgb the color
+     * @return the grey-scale value
+     */
+    private static double greyLevel(RGB rgb) {
+        if (rgb.red is rgb.green && rgb.green is rgb.blue)
+            return rgb.red;
+        return  (0.299 * rgb.red + 0.587 * rgb.green + 0.114 * rgb.blue + 0.5);
+    }
+
+    /**
+     * Returns whether the given color is dark or light depending on the colors grey-scale level.
+     *
+     * @param rgb the color
+     * @return <code>true</code> if the color is dark, <code>false</code> if it is light
+     */
+    private static bool isDark(RGB rgb) {
+        return greyLevel(rgb) > 128;
+    }
+
+    /**
+     * Returns a color based on the color configured for the given annotation type and the given scale factor.
+     *
+     * @param annotationType the annotation type
+     * @param scale the scale factor
+     * @return the computed color
+     */
+    private Color getColor(Object annotationType, double scale) {
+        Color base= findColor(annotationType);
+        if (base is null)
+            return null;
+
+        RGB baseRGB= base.getRGB();
+        RGB background= fCanvas.getBackground().getRGB();
+
+        bool darkBase= isDark(baseRGB);
+        bool darkBackground= isDark(background);
+        if (darkBase && darkBackground)
+            background= new RGB(255, 255, 255);
+        else if (!darkBase && !darkBackground)
+            background= new RGB(0, 0, 0);
+
+        return fSharedTextColors.getColor(interpolate(baseRGB, background, scale));
+    }
+
+    /**
+     * Returns the color for the given annotation type
+     *
+     * @param annotationType the annotation type
+     * @return the color
+     * @since 3.0
+     */
+    private Color findColor(Object annotationType) {
+        Color color= cast(Color) fAnnotationTypes2Colors.get(annotationType);
+        if (color !is null)
+            return color;
+
+        if ( cast(IAnnotationAccessExtension)fAnnotationAccess ) {
+            IAnnotationAccessExtension extension= cast(IAnnotationAccessExtension) fAnnotationAccess;
+            Object[] superTypes= extension.getSupertypes(annotationType);
+            if (superTypes !is null) {
+                for (int i= 0; i < superTypes.length; i++) {
+                    color= cast(Color) fAnnotationTypes2Colors.get(superTypes[i]);
+                    if (color !is null)
+                        return color;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the stroke color for the given annotation type and characteristics.
+     *
+     * @param annotationType the annotation type
+     * @param temporary <code>true</code> if for temporary annotations
+     * @return the stroke color
+     */
+    private Color getStrokeColor(Object annotationType, bool temporary) {
+        return getColor(annotationType, temporary && fIsTemporaryAnnotationDiscolored ? 0.5 : 0.2);
+    }
+
+    /**
+     * Returns the fill color for the given annotation type and characteristics.
+     *
+     * @param annotationType the annotation type
+     * @param temporary <code>true</code> if for temporary annotations
+     * @return the fill color
+     */
+    private Color getFillColor(Object annotationType, bool temporary) {
+        return getColor(annotationType, temporary && fIsTemporaryAnnotationDiscolored ? 0.9 : 0.75);
+    }
+
+    /*
+     * @see IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
+     */
+    public int getLineOfLastMouseButtonActivity() {
+        if (fLastMouseButtonActivityLine >= fTextViewer.getDocument().getNumberOfLines())
+            fLastMouseButtonActivityLine= -1;
+        return fLastMouseButtonActivityLine;
+    }
+
+    /*
+     * @see IVerticalRulerInfo#toDocumentLineNumber(int)
+     */
+    public int toDocumentLineNumber(int y_coordinate) {
+
+        if (fTextViewer is null || y_coordinate is -1)
+            return -1;
+
+        int[] lineNumbers= toLineNumbers(y_coordinate);
+        int bestLine= findBestMatchingLineNumber(lineNumbers);
+        if (bestLine is -1 && lineNumbers.length > 0)
+            return lineNumbers[0];
+        return  bestLine;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IVerticalRuler#getModel()
+     */
+    public IAnnotationModel getModel() {
+        return fModel;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IOverviewRuler#getAnnotationHeight()
+     */
+    public int getAnnotationHeight() {
+        return fAnnotationHeight;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IOverviewRuler#hasAnnotation(int)
+     */
+    public bool hasAnnotation(int y) {
+        return findBestMatchingLineNumber(toLineNumbers(y)) !is -1;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IOverviewRuler#getHeaderControl()
+     */
+    public Control getHeaderControl() {
+        return fHeader;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IOverviewRuler#addHeaderAnnotationType(java.lang.Object)
+     */
+    public void addHeaderAnnotationType(Object annotationType) {
+        fConfiguredHeaderAnnotationTypes.add(annotationType);
+        fAllowedHeaderAnnotationTypes.clear();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IOverviewRuler#removeHeaderAnnotationType(java.lang.Object)
+     */
+    public void removeHeaderAnnotationType(Object annotationType) {
+        fConfiguredHeaderAnnotationTypes.remove(annotationType);
+        fAllowedHeaderAnnotationTypes.clear();
+    }
+
+    /**
+     * Updates the header of this ruler.
+     */
+    private void updateHeader() {
+        if (fHeader is null || fHeader.isDisposed())
+            return;
+
+        fHeader.setToolTipText(null);
+
+        Object colorType= null;
+        outer: for (int i= fAnnotationsSortedByLayer.size() -1; i >= 0; i--) {
+            Object annotationType= fAnnotationsSortedByLayer.get(i);
+            if (skipInHeader(annotationType) || skip(annotationType))
+                continue;
+
+            Iterator e= new FilterIterator(annotationType, FilterIterator.PERSISTENT | FilterIterator.TEMPORARY | FilterIterator.IGNORE_BAGS, fCachedAnnotations.iterator());
+            while (e.hasNext()) {
+                if (e.next() !is null) {
+                    colorType= annotationType;
+                    break outer;
+                }
+            }
+        }
+
+        Color color= null;
+        if (colorType !is null)
+            color= findColor(colorType);
+
+        if (color is null) {
+            if (fHeaderPainter !is null)
+                fHeaderPainter.setColor(null);
+        }   else {
+            if (fHeaderPainter is null) {
+                fHeaderPainter= new HeaderPainter();
+                fHeader.addPaintListener(fHeaderPainter);
+            }
+            fHeaderPainter.setColor(color);
+        }
+
+        fHeader.redraw();
+
+    }
+
+    /**
+     * Updates the header tool tip text of this ruler.
+     */
+    private void updateHeaderToolTipText() {
+        if (fHeader is null || fHeader.isDisposed())
+            return;
+
+        if (fHeader.getToolTipText() !is null)
+            return;
+
+        String overview= ""; //$NON-NLS-1$
+
+        for (int i= fAnnotationsSortedByLayer.size() -1; i >= 0; i--) {
+
+            Object annotationType= fAnnotationsSortedByLayer.get(i);
+
+            if (skipInHeader(annotationType) || skip(annotationType))
+                continue;
+
+            int count= 0;
+            String annotationTypeLabel= null;
+
+            Iterator e= new FilterIterator(annotationType, FilterIterator.PERSISTENT | FilterIterator.TEMPORARY | FilterIterator.IGNORE_BAGS, fCachedAnnotations.iterator());
+            while (e.hasNext()) {
+                Annotation annotation= cast(Annotation)e.next();
+                if (annotation !is null) {
+                    if (annotationTypeLabel is null)
+                        annotationTypeLabel= (cast(IAnnotationAccessExtension)fAnnotationAccess).getTypeLabel(annotation);
+                    count++;
+                }
+            }
+
+            if (annotationTypeLabel !is null) {
+                if (overview.length() > 0)
+                    overview ~= "\n"; //$NON-NLS-1$
+                overview ~= JFaceTextMessages.getFormattedString("OverviewRulerHeader.toolTipTextEntry", stringcast(annotationTypeLabel), new Integer(count) ); //$NON-NLS-1$
+            }
+        }
+
+        if (overview.length() > 0)
+            fHeader.setToolTipText(overview);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/OverviewRulerHoverManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.OverviewRulerHoverManager;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.jface.text.IInformationControlCreator;
+
+/**
+ * This manager controls the layout, content, and visibility of an information
+ * control in reaction to mouse hover events issued by the overview ruler of a
+ * source viewer.
+ *
+ * @since 2.1
+ */
+class OverviewRulerHoverManager : AnnotationBarHoverManager {
+
+    /**
+     * Creates an overview hover manager with the given parameters. In addition,
+     * the hovers anchor is RIGHT and the margin is 5 points to the right.
+     *
+     * @param ruler the overview ruler this manager connects to
+     * @param sourceViewer the source viewer this manager connects to
+     * @param annotationHover the annotation hover providing the information to be displayed
+     * @param creator the information control creator
+     */
+    public this(IOverviewRuler ruler, ISourceViewer sourceViewer, IAnnotationHover annotationHover, IInformationControlCreator creator) {
+        super(ruler, sourceViewer, annotationHover, creator);
+        setAnchor(ANCHOR_LEFT);
+        StyledText textWidget= sourceViewer.getTextWidget();
+        if (textWidget !is null) {
+            ScrollBar verticalBar= textWidget.getVerticalBar();
+            if (verticalBar !is null)
+                setMargins(verticalBar.getSize().x, 5);
+        }
+    }
+
+    /*
+     * @see AbstractHoverInformationControlManager#computeInformation()
+     */
+    protected void computeInformation() {
+        Point location= getHoverEventLocation();
+        int line= getVerticalRulerInfo().toDocumentLineNumber(location.y);
+        IAnnotationHover hover= getAnnotationHover();
+        
+        IInformationControlCreator controlCreator= null;
+        if ( cast(IAnnotationHoverExtension)hover )
+            controlCreator= (cast(IAnnotationHoverExtension)hover).getHoverControlCreator();
+        setCustomInformationControlCreator(controlCreator);
+        
+        setInformation(hover.getHoverInfo(getSourceViewer(), line), computeArea(location.y));
+    }
+
+    /**
+     * Determines graphical area covered for which the hover is valid.
+     *
+     * @param y y-coordinate in the vertical ruler
+     * @return the graphical extend where the hover is valid
+     */
+    private Rectangle computeArea(int y) {
+        // This is OK (see constructor)
+        IOverviewRuler overviewRuler= cast(IOverviewRuler) getVerticalRulerInfo();
+
+        int hover_height= overviewRuler.getAnnotationHeight();
+        int hover_width= getVerticalRulerInfo().getControl().getSize().x;
+
+        // Calculate y-coordinate for hover
+        int hover_y= y;
+        bool hasAnnotation= true;
+        while (hasAnnotation && hover_y > y - hover_height) {
+            hover_y--;
+            hasAnnotation= overviewRuler.hasAnnotation(hover_y);
+        }
+        hover_y++;
+
+        return new Rectangle(0, hover_y, hover_width, hover_height);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1164 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.SourceViewer;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Stack;
+import java.util.Iterator;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.jface.internal.text.NonDeletingPositionUpdater;
+import org.eclipse.jface.internal.text.StickyHoverManager;
+import org.eclipse.jface.text.AbstractHoverInformationControlManager;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DocumentRewriteSession;
+import org.eclipse.jface.text.DocumentRewriteSessionType;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension4;
+import org.eclipse.jface.text.IPositionUpdater;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.IRewriteTarget;
+import org.eclipse.jface.text.ISlaveDocumentManager;
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension;
+import org.eclipse.jface.text.ITextViewerExtension2;
+import org.eclipse.jface.text.ITextViewerExtension8;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextViewer;
+import org.eclipse.jface.text.contentassist.IContentAssistant;
+import org.eclipse.jface.text.contentassist.IContentAssistantExtension4;
+import org.eclipse.jface.text.formatter.FormattingContext;
+import org.eclipse.jface.text.formatter.FormattingContextProperties;
+import org.eclipse.jface.text.formatter.IContentFormatter;
+import org.eclipse.jface.text.formatter.IContentFormatterExtension;
+import org.eclipse.jface.text.formatter.IFormattingContext;
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
+import org.eclipse.jface.text.information.IInformationPresenter;
+import org.eclipse.jface.text.presentation.IPresentationReconciler;
+import org.eclipse.jface.text.projection.ChildDocument;
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
+import org.eclipse.jface.text.reconciler.IReconciler;
+
+/**
+ * SWT based implementation of
+ * {@link org.eclipse.jface.text.source.ISourceViewer} and its extension
+ * interfaces. The same rules apply as for
+ * {@link org.eclipse.jface.text.TextViewer}. A source viewer uses an
+ * <code>IVerticalRuler</code> as its annotation presentation area. The
+ * vertical ruler is a small strip shown left of the viewer's text widget. A
+ * source viewer uses an <code>IOverviewRuler</code> as its presentation area
+ * for the annotation overview. The overview ruler is a small strip shown right
+ * of the viewer's text widget.
+ * <p>
+ * Clients are supposed to instantiate a source viewer and subsequently to
+ * communicate with it exclusively using the <code>ISourceViewer</code> and
+ * its extension interfaces.</p>
+ * <p>
+ * Clients may subclass this class but should expect some breakage by future releases.</p>
+ */
+public class SourceViewer : TextViewer , ISourceViewer, ISourceViewerExtension, ISourceViewerExtension2, ISourceViewerExtension3, ISourceViewerExtension4 {
+
+
+    /**
+     * Layout of a source viewer. Vertical ruler, text widget, and overview ruler are shown side by side.
+     */
+    protected class RulerLayout : Layout {
+
+        /** The gap between the text viewer and the vertical ruler. */
+        protected int fGap;
+
+        /**
+         * Creates a new ruler layout with the given gap between text viewer and vertical ruler.
+         *
+         * @param gap the gap between text viewer and vertical ruler
+         */
+        public this(int gap) {
+            fGap= gap;
+        }
+
+        /*
+         * @see Layout#computeSize(Composite, int, int, bool)
+         */
+        protected Point computeSize(Composite composite, int wHint, int hHint, bool flushCache) {
+            Control[] children= composite.getChildren();
+            Point s= children[children.length - 1].computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache);
+            if (fVerticalRuler !is null && fIsVerticalRulerVisible)
+                s.x += fVerticalRuler.getWidth() + fGap;
+            return s;
+        }
+
+        /*
+         * @see Layout#layout(Composite, bool)
+         */
+        protected void layout(Composite composite, bool flushCache) {
+            Rectangle clArea= composite.getClientArea();
+            Rectangle trim= getTextWidget().computeTrim(0, 0, 0, 0);
+            int topTrim= - trim.y;
+            int scrollbarHeight= trim.height - topTrim; // scrollbar is only under the client area
+
+            int x= clArea.x;
+            int width= clArea.width;
+
+            if (fOverviewRuler !is null && fIsOverviewRulerVisible) {
+                int overviewRulerWidth= fOverviewRuler.getWidth();
+                fOverviewRuler.getControl().setBounds(clArea.x + clArea.width - overviewRulerWidth - 1, clArea.y + scrollbarHeight, overviewRulerWidth, clArea.height - 3*scrollbarHeight);
+                fOverviewRuler.getHeaderControl().setBounds(clArea.x + clArea.width - overviewRulerWidth - 1, clArea.y, overviewRulerWidth, scrollbarHeight);
+
+                width -= overviewRulerWidth + fGap;
+            }
+
+            if (fVerticalRuler !is null && fIsVerticalRulerVisible) {
+                int verticalRulerWidth= fVerticalRuler.getWidth();
+                final Control verticalRulerControl= fVerticalRuler.getControl();
+                int oldWidth= verticalRulerControl.getBounds().width;
+                verticalRulerControl.setBounds(clArea.x, clArea.y + topTrim, verticalRulerWidth, clArea.height - scrollbarHeight - topTrim);
+                if (flushCache && getVisualAnnotationModel() !is null && oldWidth is verticalRulerWidth)
+                    verticalRulerControl.redraw();
+
+                x += verticalRulerWidth + fGap;
+                width -= verticalRulerWidth + fGap;
+            }
+
+            getTextWidget().setBounds(x, clArea.y, width, clArea.height);
+        }
+    }
+
+    /**
+     * The size of the gap between the vertical ruler and the text widget
+     * (value <code>2</code>).
+     * <p>
+     * Note: As of 3.2, the text editor framework is no longer using 2 as
+     * gap but 1, see {{@link #GAP_SIZE_1 }.
+     * </p>
+     */
+    protected final static int GAP_SIZE= 2;
+    /**
+     * The size of the gap between the vertical ruler and the text widget
+     * (value <code>1</code>).
+     * @since 3.2
+     */
+    protected final static int GAP_SIZE_1= 1;
+    /**
+     * Partial name of the position category to manage remembered selections.
+     * @since 3.0
+     */
+    protected final static String _SELECTION_POSITION_CATEGORY= "__selection_category"; //$NON-NLS-1$
+    /**
+     * Key of the model annotation model inside the visual annotation model.
+     * @since 3.0
+     */
+    private static Object MODEL_ANNOTATION_MODEL_;
+    protected static Object MODEL_ANNOTATION_MODEL(){
+        if( MODEL_ANNOTATION_MODEL_ is null ){
+            synchronized(SourceViewer.classinfo ){
+                if( MODEL_ANNOTATION_MODEL_ is null ){
+                    MODEL_ANNOTATION_MODEL_ = new Object();
+                }
+            }
+        }
+        return MODEL_ANNOTATION_MODEL_;
+    }
+
+    /** The viewer's content assistant */
+    protected IContentAssistant fContentAssistant;
+    /**
+     * The viewer's facade to its content assistant.
+     * @since 3.4
+     */
+    private ContentAssistantFacade fContentAssistantFacade;
+    /**
+     * Flag indicating whether the viewer's content assistant is installed.
+     * @since 2.0
+     */
+    protected bool fContentAssistantInstalled;
+    /**
+     * This viewer's quick assist assistant.
+     * @since 3.2
+     */
+    protected IQuickAssistAssistant fQuickAssistAssistant;
+    /**
+     * Flag indicating whether this viewer's quick assist assistant is installed.
+     * @since 3.2
+     */
+    protected bool fQuickAssistAssistantInstalled;
+    /** The viewer's content formatter */
+    protected IContentFormatter fContentFormatter;
+    /** The viewer's model reconciler */
+    protected IReconciler fReconciler;
+    /** The viewer's presentation reconciler */
+    protected IPresentationReconciler fPresentationReconciler;
+    /** The viewer's annotation hover */
+    protected IAnnotationHover fAnnotationHover;
+    /**
+     * Stack of saved selections in the underlying document
+     * @since 3.0
+     */
+    protected const Stack fSelections;
+    /**
+     * Position updater for saved selections
+     * @since 3.0
+     */
+    protected IPositionUpdater fSelectionUpdater= null;
+    /**
+     * Position category used by the selection updater
+     * @since 3.0
+     */
+    protected String fSelectionCategory;
+    /**
+     * The viewer's overview ruler annotation hover
+     * @since 3.0
+     */
+    protected IAnnotationHover fOverviewRulerAnnotationHover;
+    /**
+     * The viewer's information presenter
+     * @since 2.0
+     */
+    protected IInformationPresenter fInformationPresenter;
+
+    /** Visual vertical ruler */
+    private IVerticalRuler fVerticalRuler;
+    /** Visibility of vertical ruler */
+    private bool fIsVerticalRulerVisible;
+    /** The SWT widget used when supporting a vertical ruler */
+    private Composite fComposite;
+    /** The vertical ruler's annotation model */
+    private IAnnotationModel fVisualAnnotationModel;
+    /** The viewer's range indicator to be shown in the vertical ruler */
+    private Annotation fRangeIndicator;
+    /** The viewer's vertical ruler hovering controller */
+    private AnnotationBarHoverManager fVerticalRulerHoveringController;
+    /**
+     * The viewer's overview ruler hovering controller
+     * @since 2.1
+     */
+    private AbstractHoverInformationControlManager fOverviewRulerHoveringController;
+
+    /**
+     * The overview ruler.
+     * @since 2.1
+     */
+    private IOverviewRuler fOverviewRuler;
+    /**
+     * The visibility of the overview ruler
+     * @since 2.1
+     */
+    private bool fIsOverviewRulerVisible;
+
+
+    /**
+     * Constructs a new source viewer. The vertical ruler is initially visible.
+     * The viewer has not yet been initialized with a source viewer configuration.
+     *
+     * @param parent the parent of the viewer's control
+     * @param ruler the vertical ruler used by this source viewer
+     * @param styles the SWT style bits for the viewer's control,
+     *          <em>if <code>SWT.WRAP</code> is set then a custom document adapter needs to be provided, see {@link #createDocumentAdapter()}
+     */
+    public this(Composite parent, IVerticalRuler ruler, int styles) {
+        this(parent, ruler, null, false, styles);
+    }
+
+    /**
+     * Constructs a new source viewer. The vertical ruler is initially visible.
+     * The overview ruler visibility is controlled by the value of <code>showAnnotationsOverview</code>.
+     * The viewer has not yet been initialized with a source viewer configuration.
+     *
+     * @param parent the parent of the viewer's control
+     * @param verticalRuler the vertical ruler used by this source viewer
+     * @param overviewRuler the overview ruler
+     * @param showAnnotationsOverview <code>true</code> if the overview ruler should be visible, <code>false</code> otherwise
+     * @param styles the SWT style bits for the viewer's control,
+     *          <em>if <code>SWT.WRAP</code> is set then a custom document adapter needs to be provided, see {@link #createDocumentAdapter()}
+     * @since 2.1
+     */
+    public this(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, bool showAnnotationsOverview, int styles) {
+        fSelections= new Stack();
+        super();
+
+        fVerticalRuler= verticalRuler;
+        fIsVerticalRulerVisible= (verticalRuler !is null);
+        fOverviewRuler= overviewRuler;
+        fIsOverviewRulerVisible= (showAnnotationsOverview && overviewRuler !is null);
+
+        createControl(parent, styles);
+    }
+
+    /*
+     * @see TextViewer#createControl(Composite, int)
+     */
+    protected void createControl(Composite parent, int styles) {
+
+        if (fVerticalRuler !is null || fOverviewRuler !is null) {
+            styles= (styles & ~SWT.BORDER);
+            fComposite= new Canvas(parent, SWT.NONE);
+            fComposite.setLayout(createLayout());
+            parent= fComposite;
+        }
+
+        super.createControl(parent, styles);
+
+        if (fVerticalRuler !is null)
+            fVerticalRuler.createControl(fComposite, this);
+        if (fOverviewRuler !is null)
+            fOverviewRuler.createControl(fComposite, this);
+    }
+
+    /**
+     * Creates the layout used for this viewer.
+     * Subclasses may override this method.
+     *
+     * @return the layout used for this viewer
+     * @since 3.0
+     */
+    protected Layout createLayout() {
+        return new RulerLayout(GAP_SIZE_1);
+    }
+
+    /*
+     * @see TextViewer#getControl()
+     */
+    public Control getControl() {
+        if (fComposite !is null)
+            return fComposite;
+        return super.getControl();
+    }
+
+    /*
+     * @see ISourceViewer#setAnnotationHover(IAnnotationHover)
+     */
+    public void setAnnotationHover(IAnnotationHover annotationHover) {
+        fAnnotationHover= annotationHover;
+    }
+
+    /**
+     * Sets the overview ruler's annotation hover of this source viewer.
+     * The annotation hover provides the information to be displayed in a hover
+     * popup window if requested over the overview rulers area. The annotation
+     * hover is assumed to be line oriented.
+     *
+     * @param annotationHover the hover to be used, <code>null</code> is a valid argument
+     * @since 3.0
+     */
+    public void setOverviewRulerAnnotationHover(IAnnotationHover annotationHover) {
+        fOverviewRulerAnnotationHover= annotationHover;
+    }
+
+    /*
+     * @see ISourceViewer#configure(SourceViewerConfiguration)
+     */
+    public void configure(SourceViewerConfiguration configuration) {
+
+        if (getTextWidget() is null)
+            return;
+
+        setDocumentPartitioning(configuration.getConfiguredDocumentPartitioning(this));
+
+        // install content type independent plug-ins
+        fPresentationReconciler= configuration.getPresentationReconciler(this);
+        if (fPresentationReconciler !is null)
+            fPresentationReconciler.install(this);
+
+        fReconciler= configuration.getReconciler(this);
+        if (fReconciler !is null)
+            fReconciler.install(this);
+
+        fContentAssistant= configuration.getContentAssistant(this);
+        if (fContentAssistant !is null) {
+            fContentAssistant.install(this);
+            if ( cast(IContentAssistantExtension4)fContentAssistant  && cast(IContentAssistantExtension4)fContentAssistant )
+                fContentAssistantFacade= new ContentAssistantFacade(fContentAssistant);
+            fContentAssistantInstalled= true;
+        }
+
+        fQuickAssistAssistant= configuration.getQuickAssistAssistant(this);
+        if (fQuickAssistAssistant !is null) {
+            fQuickAssistAssistant.install(this);
+            fQuickAssistAssistantInstalled= true;
+        }
+
+        fContentFormatter= configuration.getContentFormatter(this);
+
+        fInformationPresenter= configuration.getInformationPresenter(this);
+        if (fInformationPresenter !is null)
+            fInformationPresenter.install(this);
+
+        setUndoManager(configuration.getUndoManager(this));
+
+        getTextWidget().setTabs(configuration.getTabWidth(this));
+
+        setAnnotationHover(configuration.getAnnotationHover(this));
+        setOverviewRulerAnnotationHover(configuration.getOverviewRulerAnnotationHover(this));
+
+        setHoverControlCreator(configuration.getInformationControlCreator(this));
+
+        setHyperlinkPresenter(configuration.getHyperlinkPresenter(this));
+        IHyperlinkDetector[] hyperlinkDetectors= configuration.getHyperlinkDetectors(this);
+        int eventStateMask= configuration.getHyperlinkStateMask(this);
+        setHyperlinkDetectors(hyperlinkDetectors, eventStateMask);
+
+        // install content type specific plug-ins
+        String[] types= configuration.getConfiguredContentTypes(this);
+        for (int i= 0; i < types.length; i++) {
+
+            String t= types[i];
+
+            setAutoEditStrategies(configuration.getAutoEditStrategies(this, t), t);
+            setTextDoubleClickStrategy(configuration.getDoubleClickStrategy(this, t), t);
+
+            int[] stateMasks= configuration.getConfiguredTextHoverStateMasks(this, t);
+            if (stateMasks !is null) {
+                for (int j= 0; j < stateMasks.length; j++)  {
+                    int stateMask= stateMasks[j];
+                    setTextHover(configuration.getTextHover(this, t, stateMask), t, stateMask);
+                }
+            } else {
+                setTextHover(configuration.getTextHover(this, t), t, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK);
+            }
+
+            String[] prefixes= configuration.getIndentPrefixes(this, t);
+            if (prefixes !is null && prefixes.length > 0)
+                setIndentPrefixes(prefixes, t);
+
+            prefixes= configuration.getDefaultPrefixes(this, t);
+            if (prefixes !is null && prefixes.length > 0)
+                setDefaultPrefixes(prefixes, t);
+        }
+
+        activatePlugins();
+    }
+
+    /**
+     * After this method has been executed the caller knows that any installed annotation hover has been installed.
+     */
+    protected void ensureAnnotationHoverManagerInstalled() {
+        if (fVerticalRuler !is null && (fAnnotationHover !is null || !isVerticalRulerOnlyShowingAnnotations()) && fVerticalRulerHoveringController is null && fHoverControlCreator !is null) {
+            fVerticalRulerHoveringController= new AnnotationBarHoverManager(fVerticalRuler, this, fAnnotationHover, fHoverControlCreator);
+            fVerticalRulerHoveringController.install(fVerticalRuler.getControl());
+            fVerticalRulerHoveringController.getInternalAccessor().setInformationControlReplacer(new StickyHoverManager(this));
+        }
+    }
+
+    /**
+     * After this method has been executed the caller knows that any installed overview hover has been installed.
+     */
+    protected void ensureOverviewHoverManagerInstalled() {
+        if (fOverviewRuler !is null &&  fOverviewRulerAnnotationHover !is null  && fOverviewRulerHoveringController is null && fHoverControlCreator !is null)  {
+            fOverviewRulerHoveringController= new OverviewRulerHoverManager(fOverviewRuler, this, fOverviewRulerAnnotationHover, fHoverControlCreator);
+            fOverviewRulerHoveringController.install(fOverviewRuler.getControl());
+            fOverviewRulerHoveringController.getInternalAccessor().setInformationControlReplacer(new StickyHoverManager(this));
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.TextViewer#setHoverEnrichMode(org.eclipse.jface.text.ITextViewerExtension8.EnrichMode)
+     * @since 3.4
+     */
+    public void setHoverEnrichMode(ITextViewerExtension8_EnrichMode mode) {
+        super.setHoverEnrichMode(mode);
+        if (fVerticalRulerHoveringController !is null)
+            fVerticalRulerHoveringController.getInternalAccessor().setHoverEnrichMode(mode);
+        if (fOverviewRulerHoveringController !is null)
+            fOverviewRulerHoveringController.getInternalAccessor().setHoverEnrichMode(mode);
+    }
+
+    /*
+     * @see TextViewer#activatePlugins()
+     */
+    public void activatePlugins() {
+        ensureAnnotationHoverManagerInstalled();
+        ensureOverviewHoverManagerInstalled();
+        super.activatePlugins();
+    }
+
+    /*
+     * @see ISourceViewer#setDocument(IDocument, IAnnotationModel)
+     */
+    public void setDocument(IDocument document) {
+        setDocument(document, null, -1, -1);
+    }
+
+    /*
+     * @see ISourceViewer#setDocument(IDocument, IAnnotationModel, int, int)
+     */
+    public void setDocument(IDocument document, int visibleRegionOffset, int visibleRegionLength) {
+        setDocument(document, null, visibleRegionOffset, visibleRegionLength);
+    }
+
+    /*
+     * @see ISourceViewer#setDocument(IDocument, IAnnotationModel)
+     */
+    public void setDocument(IDocument document, IAnnotationModel annotationModel) {
+        setDocument(document, annotationModel, -1, -1);
+    }
+
+    /**
+     * Creates the visual annotation model on top of the given annotation model.
+     *
+     * @param annotationModel the wrapped annotation model
+     * @return the visual annotation model on top of the given annotation model
+     * @since 3.0
+     */
+    protected IAnnotationModel createVisualAnnotationModel(IAnnotationModel annotationModel) {
+        IAnnotationModelExtension model= new AnnotationModel();
+        model.addAnnotationModel(MODEL_ANNOTATION_MODEL, annotationModel);
+        return cast(IAnnotationModel) model;
+    }
+
+    /**
+     * Disposes the visual annotation model.
+     *
+     * @since 3.1
+     */
+    protected void disposeVisualAnnotationModel() {
+        if (fVisualAnnotationModel !is null) {
+            if (getDocument() !is null)
+                fVisualAnnotationModel.disconnect(getDocument());
+
+            if ( cast(IAnnotationModelExtension)fVisualAnnotationModel )
+                (cast(IAnnotationModelExtension)fVisualAnnotationModel).removeAnnotationModel(MODEL_ANNOTATION_MODEL);
+
+            fVisualAnnotationModel= null;
+        }
+    }
+
+    /*
+     * @see ISourceViewer#setDocument(IDocument, IAnnotationModel, int, int)
+     */
+    public void setDocument(IDocument document, IAnnotationModel annotationModel, int modelRangeOffset, int modelRangeLength) {
+        disposeVisualAnnotationModel();
+
+        if (annotationModel !is null && document !is null) {
+            fVisualAnnotationModel= createVisualAnnotationModel(annotationModel);
+            fVisualAnnotationModel.connect(document);
+        }
+
+        if (modelRangeOffset is -1 && modelRangeLength is -1)
+            super.setDocument(document);
+        else
+            super.setDocument(document, modelRangeOffset, modelRangeLength);
+
+        if (fVerticalRuler !is null)
+            fVerticalRuler.setModel(fVisualAnnotationModel);
+
+        if (fOverviewRuler !is null)
+            fOverviewRuler.setModel(fVisualAnnotationModel);
+    }
+
+    /*
+     * @see ISourceViewer#getAnnotationModel()
+     */
+    public IAnnotationModel getAnnotationModel() {
+        if ( cast(IAnnotationModelExtension)fVisualAnnotationModel ) {
+            IAnnotationModelExtension extension= cast(IAnnotationModelExtension) fVisualAnnotationModel;
+            return extension.getAnnotationModel(MODEL_ANNOTATION_MODEL);
+        }
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.ISourceViewerExtension3#getQuickAssistAssistant()
+     * @since 3.2
+     */
+    public IQuickAssistAssistant getQuickAssistAssistant() {
+        return fQuickAssistAssistant;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 3.4
+     */
+    public final ContentAssistantFacade getContentAssistantFacade() {
+        return fContentAssistantFacade;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.ISourceViewerExtension3#getQuickAssistInvocationContext()
+     * @since 3.2
+     */
+    public IQuickAssistInvocationContext getQuickAssistInvocationContext() {
+        Point selection= getSelectedRange();
+        return new TextInvocationContext(this, selection.x, selection.x);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.ISourceViewerExtension2#getVisualAnnotationModel()
+     * @since 3.0
+     */
+    public IAnnotationModel getVisualAnnotationModel() {
+        return fVisualAnnotationModel;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.ISourceViewerExtension2#unconfigure()
+     * @since 3.0
+     */
+    public void unconfigure() {
+        clearRememberedSelection();
+
+        if (fPresentationReconciler !is null) {
+            fPresentationReconciler.uninstall();
+            fPresentationReconciler= null;
+        }
+
+        if (fReconciler !is null) {
+            fReconciler.uninstall();
+            fReconciler= null;
+        }
+
+        if (fContentAssistant !is null) {
+            fContentAssistant.uninstall();
+            fContentAssistantInstalled= false;
+            fContentAssistant= null;
+            if (fContentAssistantFacade !is null)
+                fContentAssistantFacade= null;
+        }
+
+        if (fQuickAssistAssistant !is null) {
+            fQuickAssistAssistant.uninstall();
+            fQuickAssistAssistantInstalled= false;
+            fQuickAssistAssistant= null;
+        }
+
+        fContentFormatter= null;
+
+        if (fInformationPresenter !is null) {
+            fInformationPresenter.uninstall();
+            fInformationPresenter= null;
+        }
+
+        fAutoIndentStrategies= null;
+        fDoubleClickStrategies= null;
+        fTextHovers= null;
+        fIndentChars= null;
+        fDefaultPrefixChars= null;
+
+        if (fVerticalRulerHoveringController !is null) {
+            fVerticalRulerHoveringController.dispose();
+            fVerticalRulerHoveringController= null;
+        }
+
+        if (fOverviewRulerHoveringController !is null) {
+            fOverviewRulerHoveringController.dispose();
+            fOverviewRulerHoveringController= null;
+        }
+
+        if (fUndoManager !is null) {
+            fUndoManager.disconnect();
+            fUndoManager= null;
+        }
+
+        setHyperlinkDetectors(null, SWT.NONE);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.TextViewer#handleDispose()
+     */
+    protected void handleDispose() {
+        unconfigure();
+
+        disposeVisualAnnotationModel();
+
+        fVerticalRuler= null;
+
+        fOverviewRuler= null;
+
+        // http://dev.eclipse.org/bugs/show_bug.cgi?id=15300
+        fComposite= null;
+
+        super.handleDispose();
+    }
+
+    /*
+     * @see ITextOperationTarget#canDoOperation(int)
+     */
+    public bool canDoOperation(int operation) {
+
+        if (getTextWidget() is null || (!redraws() && operation !is FORMAT))
+            return false;
+
+        if (operation is CONTENTASSIST_PROPOSALS)
+            return fContentAssistant !is null && fContentAssistantInstalled && isEditable();
+
+        if (operation is CONTENTASSIST_CONTEXT_INFORMATION)
+            return fContentAssistant !is null && fContentAssistantInstalled && isEditable();
+
+        if (operation is QUICK_ASSIST)
+            return fQuickAssistAssistant !is null && fQuickAssistAssistantInstalled && isEditable();
+
+        if (operation is INFORMATION)
+            return fInformationPresenter !is null;
+
+        if (operation is FORMAT) {
+            return fContentFormatter !is null && isEditable();
+        }
+
+        return super.canDoOperation(operation);
+    }
+
+    /**
+     * Creates a new formatting context for a format operation.
+     * <p>
+     * After the use of the context, clients are required to call
+     * its <code>dispose</code> method.
+     *
+     * @return The new formatting context
+     * @since 3.0
+     */
+    protected IFormattingContext createFormattingContext() {
+        return new FormattingContext();
+    }
+
+    /**
+     * Remembers and returns the current selection. The saved selection can be restored
+     * by calling <code>restoreSelection()</code>.
+     *
+     * @return the current selection
+     * @see org.eclipse.jface.text.ITextViewer#getSelectedRange()
+     * @since 3.0
+     */
+    protected Point rememberSelection() {
+
+        final Point selection= getSelectedRange();
+        final IDocument document= getDocument();
+
+        if (fSelections.isEmpty()) {
+            fSelectionCategory= _SELECTION_POSITION_CATEGORY ~ Integer.toString(toHash());
+            fSelectionUpdater= new NonDeletingPositionUpdater(fSelectionCategory);
+            document.addPositionCategory(fSelectionCategory);
+            document.addPositionUpdater(fSelectionUpdater);
+        }
+
+        try {
+
+            final Position position= new Position(selection.x, selection.y);
+            document.addPosition(fSelectionCategory, position);
+            fSelections.push(position);
+
+        } catch (BadLocationException exception) {
+            // Should not happen
+        } catch (BadPositionCategoryException exception) {
+            // Should not happen
+        }
+
+        return selection;
+    }
+
+    /**
+     * Restores a previously saved selection in the document.
+     * <p>
+     * If no selection was previously saved, nothing happens.
+     *
+     * @since 3.0
+     */
+    protected void restoreSelection() {
+
+        if (!fSelections.isEmpty()) {
+
+            final IDocument document= getDocument();
+            final Position position= cast(Position) fSelections.pop();
+
+            try {
+                document.removePosition(fSelectionCategory, position);
+                Point currentSelection= getSelectedRange();
+                if (currentSelection is null || currentSelection.x !is position.getOffset() || currentSelection.y !is position.getLength())
+                    setSelectedRange(position.getOffset(), position.getLength());
+
+                if (fSelections.isEmpty())
+                    clearRememberedSelection();
+            } catch (BadPositionCategoryException exception) {
+                // Should not happen
+            }
+        }
+    }
+
+    protected void clearRememberedSelection() {
+        if (!fSelections.isEmpty())
+            fSelections.clear();
+
+        IDocument document= getDocument();
+        if (document !is null && fSelectionUpdater !is null) {
+            document.removePositionUpdater(fSelectionUpdater);
+            try {
+                document.removePositionCategory(fSelectionCategory);
+            } catch (BadPositionCategoryException e) {
+                // ignore
+            }
+        }
+        fSelectionUpdater= null;
+        fSelectionCategory= null;
+    }
+
+    /*
+     * @see ITextOperationTarget#doOperation(int)
+     */
+    public void doOperation(int operation) {
+
+        if (getTextWidget() is null || (!redraws() && operation !is FORMAT))
+            return;
+
+        switch (operation) {
+            case CONTENTASSIST_PROPOSALS:
+                fContentAssistant.showPossibleCompletions();
+                return;
+            case CONTENTASSIST_CONTEXT_INFORMATION:
+                fContentAssistant.showContextInformation();
+                return;
+            case QUICK_ASSIST:
+                // FIXME: must find a way to post to the status line
+                /* String msg= */ fQuickAssistAssistant.showPossibleQuickAssists();
+                // setStatusLineErrorMessage(msg);
+                return;
+            case INFORMATION:
+                fInformationPresenter.showInformation();
+                return;
+            case FORMAT:
+                {
+                    final Point selection= rememberSelection();
+                    final IRewriteTarget target= getRewriteTarget();
+                    final IDocument document= getDocument();
+                    IFormattingContext context= null;
+                    DocumentRewriteSession rewriteSession= null;
+
+                    if ( cast(IDocumentExtension4)document ) {
+                        IDocumentExtension4 extension= cast(IDocumentExtension4) document;
+                        DocumentRewriteSessionType type= selection.y is 0 || selection.y > 1000 ? DocumentRewriteSessionType.SEQUENTIAL : DocumentRewriteSessionType.UNRESTRICTED_SMALL;
+                        rewriteSession= extension.startRewriteSession(type);
+                    } else {
+                        setRedraw(false);
+                        target.beginCompoundChange();
+                    }
+
+                    try {
+
+                        final String rememberedContents= document.get();
+
+                        try {
+
+                            if ( cast(IContentFormatterExtension)fContentFormatter ) {
+                                final IContentFormatterExtension extension= cast(IContentFormatterExtension) fContentFormatter;
+                                context= createFormattingContext();
+                                if (selection.y is 0) {
+                                    context.setProperty(stringcast(FormattingContextProperties.CONTEXT_DOCUMENT), Boolean.TRUE);
+                                } else {
+                                    context.setProperty(stringcast(FormattingContextProperties.CONTEXT_DOCUMENT), Boolean.FALSE);
+                                    context.setProperty(stringcast(FormattingContextProperties.CONTEXT_REGION), new Region(selection.x, selection.y));
+                                }
+                                extension.format(document, context);
+                            } else {
+                                IRegion r;
+                                if (selection.y is 0) {
+                                    IRegion coverage= getModelCoverage();
+                                    r= coverage is null ? new Region(0, 0) : coverage;
+                                } else {
+                                    r= new Region(selection.x, selection.y);
+                                }
+                                fContentFormatter.format(document, r);
+                            }
+
+                            updateSlaveDocuments(document);
+
+                        } catch (RuntimeException x) {
+                            // fire wall for https://bugs.eclipse.org/bugs/show_bug.cgi?id=47472
+                            // if something went wrong we undo the changes we just did
+                            // TODO to be removed after 3.0 M8
+                            document.set(rememberedContents);
+                            throw x;
+                        }
+
+                    } finally {
+
+                        if ( cast(IDocumentExtension4)document ) {
+                            IDocumentExtension4 extension= cast(IDocumentExtension4) document;
+                            extension.stopRewriteSession(rewriteSession);
+                        } else {
+                            target.endCompoundChange();
+                            setRedraw(true);
+                        }
+
+                        restoreSelection();
+                        if (context !is null)
+                            context.dispose();
+                    }
+                    return;
+                }
+            default:
+                super.doOperation(operation);
+        }
+    }
+
+    /**
+     * Updates all slave documents of the given document. This default implementation calls <code>updateSlaveDocument</code>
+     * for their current visible range. Subclasses may reimplement.
+     *
+     * @param masterDocument the master document
+     * @since 3.0
+     */
+    protected void updateSlaveDocuments(IDocument masterDocument) {
+        ISlaveDocumentManager manager= getSlaveDocumentManager();
+        if ( cast(ISlaveDocumentManagerExtension)manager ) {
+            ISlaveDocumentManagerExtension extension= cast(ISlaveDocumentManagerExtension) manager;
+            IDocument[] slaves= extension.getSlaveDocuments(masterDocument);
+            if (slaves !is null) {
+                for (int i= 0; i < slaves.length; i++) {
+                    if ( auto child = cast(ChildDocument)slaves[i] ) {
+                        Position p= child.getParentDocumentRange();
+                        try {
+
+                            if (!updateSlaveDocument(child, p.getOffset(), p.getLength()))
+                                child.repairLineInformation();
+
+                        } catch (BadLocationException e) {
+                            // ignore
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /*
+     * @see ITextOperationTargetExtension#enableOperation(int, bool)
+     * @since 2.0
+     */
+    public void enableOperation(int operation, bool enable) {
+
+        switch (operation) {
+            case CONTENTASSIST_PROPOSALS:
+            case CONTENTASSIST_CONTEXT_INFORMATION: {
+
+                if (fContentAssistant is null)
+                    return;
+
+                if (enable) {
+                    if (!fContentAssistantInstalled) {
+                        fContentAssistant.install(this);
+                        fContentAssistantInstalled= true;
+                    }
+                } else if (fContentAssistantInstalled) {
+                    fContentAssistant.uninstall();
+                    fContentAssistantInstalled= false;
+                }
+                break;
+            }
+            case QUICK_ASSIST: {
+
+                if (fQuickAssistAssistant is null)
+                    return;
+
+                if (enable) {
+                    if (!fQuickAssistAssistantInstalled) {
+                        fQuickAssistAssistant.install(this);
+                        fQuickAssistAssistantInstalled= true;
+                    }
+                } else if (fContentAssistantInstalled) {
+                    fQuickAssistAssistant.uninstall();
+                    fContentAssistantInstalled= false;
+                }
+            }
+            default:
+        }
+    }
+
+    /*
+     * @see ISourceViewer#setRangeIndicator(Annotation)
+     */
+    public void setRangeIndicator(Annotation rangeIndicator) {
+        fRangeIndicator= rangeIndicator;
+    }
+
+    /*
+     * @see ISourceViewer#setRangeIndication(int, int, bool)
+     */
+    public void setRangeIndication(int start, int length, bool moveCursor) {
+
+        if (moveCursor) {
+            setSelectedRange(start, 0);
+            revealRange(start, length);
+        }
+
+        if (fRangeIndicator !is null && cast(IAnnotationModelExtension)fVisualAnnotationModel ) {
+            IAnnotationModelExtension extension= cast(IAnnotationModelExtension) fVisualAnnotationModel;
+            extension.modifyAnnotationPosition(fRangeIndicator, new Position(start, length));
+        }
+    }
+
+    /*
+     * @see ISourceViewer#getRangeIndication()
+     */
+    public IRegion getRangeIndication() {
+        if (fRangeIndicator !is null && fVisualAnnotationModel !is null) {
+            Position position= fVisualAnnotationModel.getPosition(fRangeIndicator);
+            if (position !is null)
+                return new Region(position.getOffset(), position.getLength());
+        }
+
+        return null;
+    }
+
+    /*
+     * @see ISourceViewer#removeRangeIndication()
+     */
+    public void removeRangeIndication() {
+        if (fRangeIndicator !is null && fVisualAnnotationModel !is null)
+            fVisualAnnotationModel.removeAnnotation(fRangeIndicator);
+    }
+
+    /*
+     * @see ISourceViewer#showAnnotations(bool)
+     */
+    public void showAnnotations(bool show) {
+        bool old= fIsVerticalRulerVisible;
+
+        fIsVerticalRulerVisible= (fVerticalRuler !is null && (show || !isVerticalRulerOnlyShowingAnnotations()));
+        if (old !is fIsVerticalRulerVisible && fComposite !is null && !fComposite.isDisposed())
+            fComposite.layout();
+
+        if (fIsVerticalRulerVisible && show)
+            ensureAnnotationHoverManagerInstalled();
+        else if (fVerticalRulerHoveringController !is null) {
+            fVerticalRulerHoveringController.dispose();
+            fVerticalRulerHoveringController= null;
+        }
+    }
+
+    /**
+     * Tells whether the vertical ruler only acts as annotation ruler.
+     *
+     * @return <code>true</code> if the vertical ruler only show annotations
+     * @since 3.3
+     */
+    private bool isVerticalRulerOnlyShowingAnnotations() {
+        if ( cast(VerticalRuler)fVerticalRuler )
+            return true;
+
+        if ( cast(CompositeRuler)fVerticalRuler ) {
+            Iterator iter= (cast(CompositeRuler)fVerticalRuler).getDecoratorIterator();
+            return iter.hasNext() && cast(AnnotationRulerColumn)iter.next() && !iter.hasNext();
+        }
+        return false;
+    }
+
+    /**
+     * Returns the vertical ruler of this viewer.
+     *
+     * @return the vertical ruler of this viewer
+     * @since 3.0
+     */
+    protected final IVerticalRuler getVerticalRuler() {
+        return fVerticalRuler;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.ISourceViewerExtension#showAnnotationsOverview(bool)
+     * @since 2.1
+     */
+    public void showAnnotationsOverview(bool show) {
+        bool old= fIsOverviewRulerVisible;
+        fIsOverviewRulerVisible= (show && fOverviewRuler !is null);
+        if (old !is fIsOverviewRulerVisible) {
+            if (fComposite !is null && !fComposite.isDisposed())
+                fComposite.layout();
+            if (fIsOverviewRulerVisible) {
+                ensureOverviewHoverManagerInstalled();
+            } else if (fOverviewRulerHoveringController !is null) {
+                fOverviewRulerHoveringController.dispose();
+                fOverviewRulerHoveringController= null;
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.ISourceViewer#getCurrentAnnotationHover()
+     * @since 3.2
+     */
+    public IAnnotationHover getCurrentAnnotationHover() {
+        if (fVerticalRulerHoveringController is null)
+            return null;
+        return fVerticalRulerHoveringController.getCurrentAnnotationHover();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewerConfiguration.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,487 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.SourceViewerConfiguration;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.jface.text.DefaultAutoIndentStrategy;
+import org.eclipse.jface.text.DefaultInformationControl;
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy;
+import org.eclipse.jface.text.IAutoEditStrategy;
+import org.eclipse.jface.text.IAutoIndentStrategy;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension3;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.ITextDoubleClickStrategy;
+import org.eclipse.jface.text.ITextHover;
+import org.eclipse.jface.text.ITextViewerExtension2;
+import org.eclipse.jface.text.IUndoManager;
+import org.eclipse.jface.text.TextViewerUndoManager;
+import org.eclipse.jface.text.contentassist.IContentAssistant;
+import org.eclipse.jface.text.formatter.IContentFormatter;
+import org.eclipse.jface.text.hyperlink.DefaultHyperlinkPresenter;
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
+import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter;
+import org.eclipse.jface.text.hyperlink.URLHyperlinkDetector;
+import org.eclipse.jface.text.information.IInformationPresenter;
+import org.eclipse.jface.text.presentation.IPresentationReconciler;
+import org.eclipse.jface.text.presentation.PresentationReconciler;
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
+import org.eclipse.jface.text.reconciler.IReconciler;
+
+
+/**
+ * This class bundles the configuration space of a source viewer. Instances of
+ * this class are passed to the <code>configure</code> method of
+ * <code>ISourceViewer</code>.
+ * <p>
+ * Each method in this class get as argument the source viewer for which it
+ * should provide a particular configuration setting such as a presentation
+ * reconciler. Based on its specific knowledge about the returned object, the
+ * configuration might share such objects or compute them according to some
+ * rules.</p>
+ * <p>
+ * Clients should subclass and override just those methods which must be
+ * specific to their needs.</p>
+ *
+ * @see org.eclipse.jface.text.source.ISourceViewer
+ */
+public class SourceViewerConfiguration {
+
+
+    /**
+     * Creates a new source viewer configuration that behaves according to
+     * specification of this class' methods.
+     */
+    public this() {
+//         super();
+    }
+
+    /**
+     * Returns the visual width of the tab character. This implementation always
+     * returns 4.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @return the tab width
+     */
+    public int getTabWidth(ISourceViewer sourceViewer) {
+        return 4;
+    }
+
+    /**
+     * Returns the undo manager for the given source viewer. This implementation
+     * always returns a new instance of <code>DefaultUndoManager</code> whose
+     * history length is set to 25.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @return an undo manager or <code>null</code> if no undo/redo should not be supported
+     */
+    public IUndoManager getUndoManager(ISourceViewer sourceViewer) {
+        return new TextViewerUndoManager(25);
+    }
+
+    /**
+     * Returns the reconciler ready to be used with the given source viewer.
+     * This implementation always returns <code>null</code>.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @return a reconciler or <code>null</code> if reconciling should not be supported
+     */
+    public IReconciler getReconciler(ISourceViewer sourceViewer) {
+        return null;
+    }
+
+    /**
+     * Returns the presentation reconciler ready to be used with the given source viewer.
+     *
+     * @param sourceViewer the source viewer
+     * @return the presentation reconciler or <code>null</code> if presentation reconciling should not be supported
+     */
+    public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) {
+        PresentationReconciler reconciler= new PresentationReconciler();
+        reconciler.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer));
+        return reconciler;
+    }
+
+    /**
+     * Returns the content formatter ready to be used with the given source viewer.
+     * This implementation always returns <code>null</code>.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @return a content formatter or <code>null</code> if formatting should not be supported
+     */
+    public IContentFormatter getContentFormatter(ISourceViewer sourceViewer) {
+        return null;
+    }
+
+    /**
+     * Returns the content assistant ready to be used with the given source viewer.
+     * This implementation always returns <code>null</code>.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @return a content assistant or <code>null</code> if content assist should not be supported
+     */
+    public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) {
+        return null;
+    }
+
+    /**
+     * Returns the quick assist assistant ready to be used with the given
+     * source viewer.
+     * This implementation always returns <code>null</code>.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @return a quick assist assistant or <code>null</code> if quick assist should not be supported
+     * @since 3.2
+     */
+    public IQuickAssistAssistant getQuickAssistAssistant(ISourceViewer sourceViewer) {
+        return null;
+    }
+
+    /**
+     * Returns the auto indentation strategy ready to be used with the given source viewer
+     * when manipulating text of the given content type. This implementation always
+     * returns an new instance of <code>DefaultAutoIndentStrategy</code>.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @param contentType the content type for which the strategy is applicable
+     * @return the auto indent strategy or <code>null</code> if automatic indentation is not to be enabled
+     * @deprecated since 3.1 use {@link #getAutoEditStrategies(ISourceViewer, String)} instead
+     */
+    public IAutoIndentStrategy getAutoIndentStrategy(ISourceViewer sourceViewer, String contentType) {
+        return new DefaultAutoIndentStrategy();
+    }
+
+    /**
+     * Returns the auto edit strategies ready to be used with the given source viewer
+     * when manipulating text of the given content type. For backward compatibility, this implementation always
+     * returns an array containing the result of {@link #getAutoIndentStrategy(ISourceViewer, String)}.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @param contentType the content type for which the strategies are applicable
+     * @return the auto edit strategies or <code>null</code> if automatic editing is not to be enabled
+     * @since 3.1
+     */
+    public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) {
+        return [ getAutoIndentStrategy(sourceViewer, contentType) ];
+    }
+
+    /**
+     * Returns the default prefixes to be used by the line-prefix operation
+     * in the given source viewer for text of the given content type. This implementation always
+     * returns <code>null</code>.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @param contentType the content type for which the prefix is applicable
+     * @return the default prefixes or <code>null</code> if the prefix operation should not be supported
+     * @since 2.0
+     */
+    public String[] getDefaultPrefixes(ISourceViewer sourceViewer, String contentType) {
+        return null;
+    }
+
+    /**
+     * Returns the double-click strategy ready to be used in this viewer when double clicking
+     * onto text of the given content type. This implementation always returns a new instance of
+     * <code>DefaultTextDoubleClickStrategy</code>.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @param contentType the content type for which the strategy is applicable
+     * @return a double-click strategy or <code>null</code> if double clicking should not be supported
+     */
+    public ITextDoubleClickStrategy getDoubleClickStrategy(ISourceViewer sourceViewer, String contentType) {
+        return new DefaultTextDoubleClickStrategy();
+    }
+
+    /**
+     * Returns the prefixes to be used by the line-shift operation. This implementation
+     * always returns <code>new String[] { "\t", "    ", "" }</code>.
+     * <p>
+     * <strong>Note:</strong> <em>This default is incorrect but cannot be changed in order not
+     * to break any existing clients. Subclasses should overwrite this method and
+     * use {@link #getIndentPrefixesForTab(int)} if applicable.</em>
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @param contentType the content type for which the prefix is applicable
+     * @return the prefixes or <code>null</code> if the prefix operation should not be supported
+     */
+    public String[] getIndentPrefixes(ISourceViewer sourceViewer, String contentType) {
+        return [ "\t", "    ", "" ]; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    }
+
+    /**
+     * Computes and returns the indent prefixes for tab indentation
+     * which is represented as <code>tabSizeInSpaces</code>.
+     *
+     * @param tabWidth the display tab width
+     * @return the indent prefixes
+     * @see #getIndentPrefixes(ISourceViewer, String)
+     * @since 3.3
+     */
+    protected String[] getIndentPrefixesForTab(int tabWidth) {
+        String[] indentPrefixes= new String[tabWidth + 2];
+        for (int i= 0; i <= tabWidth; i++) {
+            char[] spaceChars= new char[i];
+            Arrays.fill(spaceChars, ' ');
+            String spaces= new_String(spaceChars);
+            if (i < tabWidth)
+                indentPrefixes[i]= spaces ~ '\t';
+            else
+                indentPrefixes[i]= new_String(spaces);
+        }
+        indentPrefixes[tabWidth + 1]= ""; //$NON-NLS-1$
+        return indentPrefixes;
+    }
+
+    /**
+     * Returns the annotation hover which will provide the information to be
+     * shown in a hover popup window when requested for the given
+     * source viewer. This implementation always returns <code>null</code>.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @return an annotation hover or <code>null</code> if no hover support should be installed
+     */
+    public IAnnotationHover getAnnotationHover(ISourceViewer sourceViewer) {
+        return null;
+    }
+
+    /**
+     * Returns the annotation hover which will provide the information to be
+     * shown in a hover popup window when requested for the overview ruler
+     * of the given source viewer.This implementation always returns the general
+     * annotation hover returned by <code>getAnnotationHover</code>.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @return an annotation hover or <code>null</code> if no hover support should be installed
+     * @since 3.0
+     */
+    public IAnnotationHover getOverviewRulerAnnotationHover(ISourceViewer sourceViewer) {
+        return getAnnotationHover(sourceViewer);
+    }
+
+    /**
+     * Returns the SWT event state masks for which text hover are configured for
+     * the given content type.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @param contentType the content type
+     * @return an <code>int</code> array with the configured SWT event state masks
+     *          or <code>null</code> if text hovers are not supported for the given content type
+     * @since 2.1
+     */
+    public int[] getConfiguredTextHoverStateMasks(ISourceViewer sourceViewer, String contentType) {
+        return null;
+    }
+
+    /**
+     * Returns the text hover which will provide the information to be shown
+     * in a text hover popup window when requested for the given source viewer and
+     * the given content type. This implementation always returns <code>
+     * null</code>.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @param contentType the content type
+     * @param stateMask the SWT event state mask
+     * @return a text hover or <code>null</code> if no hover support should be installed
+     * @since 2.1
+     */
+    public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType, int stateMask) {
+        if (stateMask is ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK)
+            return getTextHover(sourceViewer, contentType);
+        return null;
+    }
+
+    /**
+     * Returns the text hover which will provide the information to be shown
+     * in a text hover popup window when requested for the given source viewer and
+     * the given content type. This implementation always returns <code>
+     * null</code>.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @param contentType the content type
+     * @return a text hover or <code>null</code> if no hover support should be installed
+     */
+    public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) {
+        return null;
+    }
+
+    /**
+     * Returns the information control creator. The creator is a factory creating information
+     * controls for the given source viewer. This implementation always returns a creator for
+     * <code>DefaultInformationControl</code> instances.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @return the information control creator or <code>null</code> if no information support should be installed
+     * @since 2.0
+     */
+    public IInformationControlCreator getInformationControlCreator(ISourceViewer sourceViewer) {
+        return new class()  IInformationControlCreator {
+            public IInformationControl createInformationControl(Shell parent) {
+                return new DefaultInformationControl(parent);
+            }
+        };
+    }
+
+    /**
+     * Returns the information presenter which will determine and shown
+     * information requested for the current cursor position. This implementation
+     * always returns <code>null</code>.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @return an information presenter <code>null</code> if  no information presenter should be installed
+     * @since 2.0
+     */
+    public org.eclipse.jface.text.information.IInformationPresenter.IInformationPresenter getInformationPresenter(ISourceViewer sourceViewer) {
+        return null;
+    }
+
+    /**
+     * Returns all configured content types for the given source viewer. This list
+     * tells the caller which content types must be configured for the given source
+     * viewer, i.e. for which content types the given source viewer's functionalities
+     * must be specified. This implementation always returns <code>
+     * new String[] { IDocument.DEFAULT_CONTENT_TYPE }</code>.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @return the configured content types for the given viewer
+     */
+    public String[] getConfiguredContentTypes(ISourceViewer sourceViewer) {
+        return [ IDocument.DEFAULT_CONTENT_TYPE ];
+    }
+
+    /**
+     * Returns the configured partitioning for the given source viewer. The partitioning is
+     * used when the querying content types from the source viewer's input document.  This
+     * implementation always returns <code>IDocumentExtension3.DEFAULT_PARTITIONING</code>.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @return the configured partitioning
+     * @see #getConfiguredContentTypes(ISourceViewer)
+     * @since 3.0
+     */
+    public String getConfiguredDocumentPartitioning(ISourceViewer sourceViewer) {
+        return IDocumentExtension3.DEFAULT_PARTITIONING;
+    }
+
+    /**
+     * Returns the hyperlink detectors which be used to detect hyperlinks
+     * in the given source viewer. This
+     * implementation always returns an array with an URL hyperlink detector.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @return an array with hyperlink detectors or <code>null</code> if no hyperlink support should be installed
+     * @since 3.1
+     */
+    public IHyperlinkDetector[] getHyperlinkDetectors(ISourceViewer sourceViewer) {
+        if (sourceViewer is null)
+            return null;
+
+        return [ new URLHyperlinkDetector() ];
+    }
+
+    /**
+     * Returns the hyperlink presenter for the given source viewer.
+     * This implementation always returns the {@link DefaultHyperlinkPresenter}.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @return the hyperlink presenter or <code>null</code> if no hyperlink support should be installed
+     * @since 3.1
+     */
+    public IHyperlinkPresenter getHyperlinkPresenter(ISourceViewer sourceViewer) {
+        return new DefaultHyperlinkPresenter(new RGB(0, 0, 255));
+    }
+
+    /**
+     * Returns the SWT event state mask which in combination
+     * with the left mouse button activates hyperlinking.
+     * This implementation always returns the {@link SWT#MOD1}.
+     *
+     * @param sourceViewer the source viewer to be configured by this configuration
+     * @return the SWT event state mask to activate hyperlink mode
+     * @since 3.1
+     */
+    public int getHyperlinkStateMask(ISourceViewer sourceViewer) {
+        return SWT.MOD1;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/TextInvocationContext.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.source.TextInvocationContext;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
+
+
+/**
+ * Text quick assist invocation context.
+ * <p>
+ * Clients may extend this class to add additional context information.
+ * </p>
+ * 
+ * @since 3.3
+ */
+public class TextInvocationContext : IQuickAssistInvocationContext {
+
+    private ISourceViewer fSourceViewer;
+    private int fOffset;
+    private int fLength;
+    
+    public this(ISourceViewer sourceViewer, int offset, int length) {
+        fSourceViewer= sourceViewer;
+        fOffset= offset;
+        fLength= length;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext#getOffset()
+     */
+    public int getOffset() {
+        return fOffset;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext#getLength()
+     */
+    public int getLength() {
+        return fLength;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext#getSourceViewer()
+     */
+    public ISourceViewer getSourceViewer() {
+        return fSourceViewer;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/VerticalRuler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,665 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.VerticalRuler;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Set;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension5;
+import org.eclipse.jface.text.IViewportListener;
+import org.eclipse.jface.text.JFaceTextUtil;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextEvent;
+
+
+/**
+ * A vertical ruler which is connected to a text viewer. Single column standard
+ * implementation of {@link org.eclipse.jface.text.source.IVerticalRuler}.
+ * <p>
+ * The same can be achieved by using <code>CompositeRuler</code> configured
+ * with an <code>AnnotationRulerColumn</code>. Clients may use this class as
+ * is.
+ *
+ * @see org.eclipse.jface.text.ITextViewer
+ */
+public final class VerticalRuler : IVerticalRuler, IVerticalRulerExtension {
+
+    /**
+     * Internal listener class.
+     */
+    class InternalListener : IViewportListener, IAnnotationModelListener, ITextListener {
+
+        /*
+         * @see IViewportListener#viewportChanged(int)
+         */
+        public void viewportChanged(int verticalPosition) {
+            if (verticalPosition !is fScrollPos)
+                redraw();
+        }
+
+        /*
+         * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
+         */
+        public void modelChanged(IAnnotationModel model) {
+            update();
+        }
+
+        /*
+         * @see ITextListener#textChanged(TextEvent)
+         */
+        public void textChanged(TextEvent e) {
+            if (fTextViewer !is null && e.getViewerRedrawState())
+                redraw();
+        }
+    }
+
+    /** The vertical ruler's text viewer */
+    private ITextViewer fTextViewer;
+    /** The ruler's canvas */
+    private Canvas fCanvas;
+    /** The vertical ruler's model */
+    private IAnnotationModel fModel;
+    /** Cache for the actual scroll position in pixels */
+    private int fScrollPos;
+    /** The buffer for double buffering */
+    private Image fBuffer;
+    /** The line of the last mouse button activity */
+    private int fLastMouseButtonActivityLine= -1;
+    /** The internal listener */
+    private InternalListener fInternalListener;
+    /** The width of this vertical ruler */
+    private int fWidth;
+    /**
+     * The annotation access of this vertical ruler
+     * @since 3.0
+     */
+    private IAnnotationAccess fAnnotationAccess;
+
+    /**
+     * Constructs a vertical ruler with the given width.
+     *
+     * @param width the width of the vertical ruler
+     */
+    public this(int width) {
+        this(width, null);
+    }
+
+    /**
+     * Constructs a vertical ruler with the given width and the given annotation
+     * access.
+     *
+     * @param width the width of the vertical ruler
+     * @param annotationAcccess the annotation access
+     * @since 3.0
+     */
+    public this(int width, IAnnotationAccess annotationAcccess) {
+
+        fInternalListener= new InternalListener();
+
+        fWidth= width;
+        fAnnotationAccess= annotationAcccess;
+    }
+
+    /*
+     * @see IVerticalRuler#getControl()
+     */
+    public Control getControl() {
+        return fCanvas;
+    }
+
+    /*
+     * @see IVerticalRuler#createControl(Composite, ITextViewer)
+     */
+    public Control createControl(Composite parent, ITextViewer textViewer) {
+
+        fTextViewer= textViewer;
+
+        fCanvas= new Canvas(parent, SWT.NO_BACKGROUND);
+
+        fCanvas.addPaintListener(new class()  PaintListener {
+            public void paintControl(PaintEvent event) {
+                if (fTextViewer !is null)
+                    doubleBufferPaint(event.gc);
+            }
+        });
+
+        fCanvas.addDisposeListener(new class()  DisposeListener {
+            public void widgetDisposed(DisposeEvent e) {
+                handleDispose();
+                fTextViewer= null;
+            }
+        });
+
+        fCanvas.addMouseListener(new class()  MouseListener {
+            public void mouseUp(MouseEvent event) {
+            }
+
+            public void mouseDown(MouseEvent event) {
+                fLastMouseButtonActivityLine= toDocumentLineNumber(event.y);
+            }
+
+            public void mouseDoubleClick(MouseEvent event) {
+                fLastMouseButtonActivityLine= toDocumentLineNumber(event.y);
+            }
+        });
+
+        if (fTextViewer !is null) {
+            fTextViewer.addViewportListener(fInternalListener);
+            fTextViewer.addTextListener(fInternalListener);
+        }
+
+        return fCanvas;
+    }
+
+    /**
+     * Disposes the ruler's resources.
+     */
+    private void handleDispose() {
+
+        if (fTextViewer !is null) {
+            fTextViewer.removeViewportListener(fInternalListener);
+            fTextViewer.removeTextListener(fInternalListener);
+            fTextViewer= null;
+        }
+
+        if (fModel !is null)
+            fModel.removeAnnotationModelListener(fInternalListener);
+
+        if (fBuffer !is null) {
+            fBuffer.dispose();
+            fBuffer= null;
+        }
+    }
+
+
+    /**
+     * Double buffer drawing.
+     *
+     * @param dest the GC to draw into
+     */
+    private void doubleBufferPaint(GC dest) {
+
+        Point size= fCanvas.getSize();
+
+        if (size.x <= 0 || size.y <= 0)
+            return;
+
+        if (fBuffer !is null) {
+            Rectangle r= fBuffer.getBounds();
+            if (r.width !is size.x || r.height !is size.y) {
+                fBuffer.dispose();
+                fBuffer= null;
+            }
+        }
+        if (fBuffer is null)
+            fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y);
+
+        GC gc= new GC(fBuffer);
+        gc.setFont(fTextViewer.getTextWidget().getFont());
+        try {
+            gc.setBackground(fCanvas.getBackground());
+            gc.fillRectangle(0, 0, size.x, size.y);
+
+            if ( cast(ITextViewerExtension5)fTextViewer )
+                doPaint1(gc);
+            else
+                doPaint(gc);
+
+        } finally {
+            gc.dispose();
+        }
+
+        dest.drawImage(fBuffer, 0, 0);
+    }
+
+    /**
+     * Returns the document offset of the upper left corner of the
+     * widgets view port, possibly including partially visible lines.
+     *
+     * @return the document offset of the upper left corner including partially visible lines
+     * @since 2.0
+     */
+    private int getInclusiveTopIndexStartOffset() {
+
+        StyledText textWidget= fTextViewer.getTextWidget();
+        if (textWidget !is null && !textWidget.isDisposed()) {
+            int top= JFaceTextUtil.getPartialTopIndex(fTextViewer);
+            try {
+                IDocument document= fTextViewer.getDocument();
+                return document.getLineOffset(top);
+            } catch (BadLocationException x) {
+            }
+        }
+
+        return -1;
+    }
+
+
+
+    /**
+     * Draws the vertical ruler w/o drawing the Canvas background.
+     *
+     * @param gc  the GC to draw into
+     */
+    protected void doPaint(GC gc) {
+
+        if (fModel is null || fTextViewer is null)
+            return;
+
+        IAnnotationAccessExtension annotationAccessExtension= null;
+        if ( cast(IAnnotationAccessExtension)fAnnotationAccess )
+            annotationAccessExtension= cast(IAnnotationAccessExtension) fAnnotationAccess;
+
+        StyledText styledText= fTextViewer.getTextWidget();
+        IDocument doc= fTextViewer.getDocument();
+
+        int topLeft= getInclusiveTopIndexStartOffset();
+        int bottomRight= fTextViewer.getBottomIndexEndOffset();
+        int viewPort= bottomRight - topLeft;
+
+        Point d= fCanvas.getSize();
+        fScrollPos= styledText.getTopPixel();
+
+        int topLine= -1, bottomLine= -1;
+        try {
+            IRegion region= fTextViewer.getVisibleRegion();
+            topLine= doc.getLineOfOffset(region.getOffset());
+            bottomLine= doc.getLineOfOffset(region.getOffset() + region.getLength());
+        } catch (BadLocationException x) {
+            return;
+        }
+
+        // draw Annotations
+        Rectangle r= new Rectangle(0, 0, 0, 0);
+        int maxLayer= 1;    // loop at least once though layers.
+
+        for (int layer= 0; layer < maxLayer; layer++) {
+            Iterator iter= fModel.getAnnotationIterator();
+            while (iter.hasNext()) {
+                IAnnotationPresentation annotationPresentation= null;
+                Annotation annotation= cast(Annotation) iter.next();
+
+                int lay= IAnnotationAccessExtension.DEFAULT_LAYER;
+                if (annotationAccessExtension !is null)
+                    lay= annotationAccessExtension.getLayer(annotation);
+                else if ( cast(IAnnotationPresentation)annotation ) {
+                    annotationPresentation= cast(IAnnotationPresentation)annotation;
+                    lay= annotationPresentation.getLayer();
+                }
+                maxLayer= Math.max(maxLayer, lay+1);    // dynamically update layer maximum
+                if (lay !is layer)   // wrong layer: skip annotation
+                    continue;
+
+                Position position= fModel.getPosition(annotation);
+                if (position is null)
+                    continue;
+
+                if (!position.overlapsWith(topLeft, viewPort))
+                    continue;
+
+                try {
+
+                    int offset= position.getOffset();
+                    int length= position.getLength();
+
+                    int startLine= doc.getLineOfOffset(offset);
+                    if (startLine < topLine)
+                        startLine= topLine;
+
+                    int endLine= startLine;
+                    if (length > 0)
+                        endLine= doc.getLineOfOffset(offset + length - 1);
+                    if (endLine > bottomLine)
+                        endLine= bottomLine;
+
+                    startLine -= topLine;
+                    endLine -= topLine;
+
+                    r.x= 0;
+                    r.y= JFaceTextUtil.computeLineHeight(styledText, 0, startLine, startLine)  - fScrollPos;
+
+                    r.width= d.x;
+                    int lines= endLine - startLine;
+
+                    r.height= JFaceTextUtil.computeLineHeight(styledText, startLine, endLine + 1, (lines+1));
+
+                    if (r.y < d.y && annotationAccessExtension !is null)  // annotation within visible area
+                        annotationAccessExtension.paint(annotation, gc, fCanvas, r);
+                    else if (annotationPresentation !is null)
+                        annotationPresentation.paint(gc, fCanvas, r);
+
+                } catch (BadLocationException e) {
+                }
+            }
+        }
+    }
+
+    /**
+     * Draws the vertical ruler w/o drawing the Canvas background. Uses
+     * <code>ITextViewerExtension5</code> for its implementation. Will replace
+     * <code>doPaint(GC)</code>.
+     *
+     * @param gc  the GC to draw into
+     */
+    protected void doPaint1(GC gc) {
+
+        if (fModel is null || fTextViewer is null)
+            return;
+
+        IAnnotationAccessExtension annotationAccessExtension= null;
+        if ( cast(IAnnotationAccessExtension)fAnnotationAccess )
+            annotationAccessExtension= cast(IAnnotationAccessExtension) fAnnotationAccess;
+
+        ITextViewerExtension5 extension= cast(ITextViewerExtension5) fTextViewer;
+        StyledText textWidget= fTextViewer.getTextWidget();
+
+        fScrollPos= textWidget.getTopPixel();
+        Point dimension= fCanvas.getSize();
+
+        // draw Annotations
+        Rectangle r= new Rectangle(0, 0, 0, 0);
+        int maxLayer= 1;    // loop at least once through layers.
+
+        for (int layer= 0; layer < maxLayer; layer++) {
+            Iterator iter= fModel.getAnnotationIterator();
+            while (iter.hasNext()) {
+                IAnnotationPresentation annotationPresentation= null;
+                Annotation annotation= cast(Annotation) iter.next();
+
+                int lay= IAnnotationAccessExtension.DEFAULT_LAYER;
+                if (annotationAccessExtension !is null)
+                    lay= annotationAccessExtension.getLayer(annotation);
+                else if ( cast(IAnnotationPresentation)annotation ) {
+                    annotationPresentation= cast(IAnnotationPresentation)annotation;
+                    lay= annotationPresentation.getLayer();
+                }
+                maxLayer= Math.max(maxLayer, lay+1);    // dynamically update layer maximum
+                if (lay !is layer)   // wrong layer: skip annotation
+                    continue;
+
+                Position position= fModel.getPosition(annotation);
+                if (position is null)
+                    continue;
+
+                IRegion widgetRegion= extension.modelRange2WidgetRange(new Region(position.getOffset(), position.getLength()));
+                if (widgetRegion is null)
+                    continue;
+
+                int startLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset());
+                if (startLine is -1)
+                    continue;
+
+                int endLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset() + Math.max(widgetRegion.getLength() -1, 0));
+                if (endLine is -1)
+                    continue;
+
+                r.x= 0;
+                r.y= JFaceTextUtil.computeLineHeight(textWidget, 0, startLine, startLine)  - fScrollPos;
+
+                r.width= dimension.x;
+                int lines= endLine - startLine;
+
+                r.height= JFaceTextUtil.computeLineHeight(textWidget, startLine, endLine + 1, lines+1);
+
+                if (r.y < dimension.y && annotationAccessExtension !is null)  // annotation within visible area
+                    annotationAccessExtension.paint(annotation, gc, fCanvas, r);
+                else if (annotationPresentation !is null)
+                    annotationPresentation.paint(gc, fCanvas, r);
+            }
+        }
+    }
+
+    /**
+     * Thread-safe implementation.
+     * Can be called from any thread.
+     */
+    /*
+     * @see IVerticalRuler#update()
+     */
+    public void update() {
+        if (fCanvas !is null && !fCanvas.isDisposed()) {
+            Display d= fCanvas.getDisplay();
+            if (d !is null) {
+                d.asyncExec(new class()  Runnable {
+                    public void run() {
+                        redraw();
+                    }
+                });
+            }
+        }
+    }
+
+    /**
+     * Redraws the vertical ruler.
+     */
+    private void redraw() {
+        if (fCanvas !is null && !fCanvas.isDisposed()) {
+            GC gc= new GC(fCanvas);
+            doubleBufferPaint(gc);
+            gc.dispose();
+        }
+    }
+
+    /*
+     * @see IVerticalRuler#setModel(IAnnotationModel)
+     */
+    public void setModel(IAnnotationModel model) {
+        if (model !is fModel) {
+
+            if (fModel !is null)
+                fModel.removeAnnotationModelListener(fInternalListener);
+
+            fModel= model;
+
+            if (fModel !is null)
+                fModel.addAnnotationModelListener(fInternalListener);
+
+            update();
+        }
+    }
+
+    /*
+     * @see IVerticalRuler#getModel()
+     */
+    public IAnnotationModel getModel() {
+        return fModel;
+    }
+
+    /*
+     * @see IVerticalRulerInfo#getWidth()
+     */
+    public int getWidth() {
+        return fWidth;
+    }
+
+    /*
+     * @see IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
+     */
+    public int getLineOfLastMouseButtonActivity() {
+        IDocument doc= fTextViewer.getDocument();
+        if (doc is null || fLastMouseButtonActivityLine >= fTextViewer.getDocument().getNumberOfLines())
+            fLastMouseButtonActivityLine= -1;
+        return fLastMouseButtonActivityLine;
+    }
+
+    /*
+     * @see IVerticalRulerInfo#toDocumentLineNumber(int)
+     */
+    public int toDocumentLineNumber(int y_coordinate) {
+        if (fTextViewer is null  || y_coordinate is -1)
+            return -1;
+
+        StyledText text= fTextViewer.getTextWidget();
+        int line= text.getLineIndex(y_coordinate);
+
+        if (line is text.getLineCount() - 1) {
+            // check whether y_coordinate exceeds last line
+            if (y_coordinate > text.getLinePixel(line + 1))
+                return -1;
+        }
+
+        return widgetLine2ModelLine(fTextViewer, line);
+    }
+
+    /**
+     * Returns the line of the viewer's document that corresponds to the given widget line.
+     *
+     * @param viewer the viewer
+     * @param widgetLine the widget line
+     * @return the corresponding line of the viewer's document
+     * @since 2.1
+     */
+    protected final static int widgetLine2ModelLine(ITextViewer viewer, int widgetLine) {
+
+        if ( cast(ITextViewerExtension5)viewer ) {
+            ITextViewerExtension5 extension= cast(ITextViewerExtension5) viewer;
+            return extension.widgetLine2ModelLine(widgetLine);
+        }
+
+        try {
+            IRegion r= viewer.getVisibleRegion();
+            IDocument d= viewer.getDocument();
+            return widgetLine += d.getLineOfOffset(r.getOffset());
+        } catch (BadLocationException x) {
+        }
+        return widgetLine;
+    }
+
+    /*
+     * @see IVerticalRulerExtension#setFont(Font)
+     * @since 2.0
+     */
+    public void setFont(Font font) {
+    }
+
+    /*
+     * @see IVerticalRulerExtension#setLocationOfLastMouseButtonActivity(int, int)
+     * @since 2.0
+     */
+    public void setLocationOfLastMouseButtonActivity(int x, int y) {
+        fLastMouseButtonActivityLine= toDocumentLineNumber(y);
+    }
+
+    /**
+     * Adds the given mouse listener.
+     *
+     * @param listener the listener to be added
+     * @deprecated will be removed
+     * @since 2.0
+     */
+    public void addMouseListener(MouseListener listener) {
+        if (fCanvas !is null && !fCanvas.isDisposed())
+            fCanvas.addMouseListener(listener);
+    }
+
+    /**
+     * Removes the given mouse listener.
+     *
+     * @param listener the listener to be removed
+     * @deprecated will be removed
+     * @since 2.0
+     */
+    public void removeMouseListener(MouseListener listener) {
+        if (fCanvas !is null && !fCanvas.isDisposed())
+            fCanvas.removeMouseListener(listener);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/VerticalRulerEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.source.VerticalRulerEvent;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * An event sent to {@link org.eclipse.jface.text.source.IVerticalRulerListener} instances when annotation
+ * related event occurs on the vertical ruler.
+ *
+ * @since 3.0
+ */
+public class VerticalRulerEvent {
+
+    private Annotation fAnnotation;
+
+    /**
+     * Creates a new event.
+     *
+     * @param annotation the annotation concerned, or <code>null</code>
+     */
+    public this(Annotation annotation) {
+        fAnnotation= annotation;
+    }
+
+    /**
+     * @return the concerned annotation or <code>null</code>
+     */
+    public Annotation getSelectedAnnotation() {
+        return fAnnotation;
+    }
+
+    /**
+     * @param annotation the concerned annotation, or <code>null</code>
+     */
+    public void setSelectedAnnotation(Annotation annotation) {
+        fAnnotation= annotation;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/VisualAnnotationModel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,212 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.VisualAnnotationModel;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+
+
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.Position;
+
+
+
+/**
+ * Annotation model for visual annotations. Assume a viewer's input element is annotated with
+ * some semantic annotation such as a breakpoint and that it is simultaneously shown in multiple
+ * viewers. A source viewer, e.g., supports visual range indication for which it utilizes
+ * annotations. The range indicating annotation is specific to the visual presentation
+ * of the input element in this viewer and thus should only be visible in this viewer. The
+ * breakpoints however are independent from the input element's presentation and thus should
+ * be shown in all viewers in which the element is shown. As a viewer supports one vertical
+ * ruler which is based on one annotation model, there must be a visual annotation model for
+ * each viewer which all wrap the same element specific model annotation model.
+ */
+class VisualAnnotationModel : AnnotationModel , IAnnotationModelListener {
+
+    /** The wrapped model annotation model */
+    private IAnnotationModel fModel;
+
+    /**
+     * Constructs a visual annotation model which wraps the given
+     * model based annotation model
+     *
+     * @param modelAnnotationModel the model based annotation model
+     */
+    public this(IAnnotationModel modelAnnotationModel) {
+        fModel= modelAnnotationModel;
+    }
+
+    /**
+     * Returns the visual annotation model's wrapped model based annotation model.
+     *
+     * @return the model based annotation model
+     */
+    public IAnnotationModel getModelAnnotationModel() {
+        return fModel;
+    }
+
+    /*
+     * @see IAnnotationModel#addAnnotationModelListener(IAnnotationModelListener)
+     */
+    public void addAnnotationModelListener(IAnnotationModelListener listener) {
+
+        if (fModel !is null && fAnnotationModelListeners.isEmpty())
+            fModel.addAnnotationModelListener(this);
+
+        super.addAnnotationModelListener(listener);
+    }
+
+    /*
+     * @see IAnnotationModel#connect(IDocument)
+     */
+    public void connect(IDocument document) {
+        super.connect(document);
+        if (fModel !is null)
+            fModel.connect(document);
+    }
+
+    /*
+     * @see IAnnotationModel#disconnect(IDocument)
+     */
+    public void disconnect(IDocument document) {
+        super.disconnect(document);
+        if (fModel !is null)
+            fModel.disconnect(document);
+    }
+
+    /*
+     * @see IAnnotationModel#getAnnotationIterator()
+     */
+    public Iterator getAnnotationIterator() {
+
+        if (fModel is null)
+            return super.getAnnotationIterator();
+
+        ArrayList a= new ArrayList(20);
+
+        Iterator e= fModel.getAnnotationIterator();
+        while (e.hasNext())
+            a.add(e.next());
+
+        e= super.getAnnotationIterator();
+        while (e.hasNext())
+            a.add(e.next());
+
+        return a.iterator();
+    }
+
+    /*
+     * @see IAnnotationModel#getPosition(Annotation)
+     */
+    public Position getPosition(Annotation annotation) {
+
+        Position p= cast(Position) getAnnotationMap().get(annotation);
+        if (p !is null)
+            return p;
+
+        if (fModel !is null)
+            return fModel.getPosition(annotation);
+
+        return null;
+    }
+
+    /*
+     * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
+     */
+    public void modelChanged(IAnnotationModel model) {
+        if (model is fModel) {
+            Iterator iter= (new ArrayList(fAnnotationModelListeners)).iterator();
+            while (iter.hasNext()) {
+                IAnnotationModelListener l= cast(IAnnotationModelListener)iter.next();
+                l.modelChanged(this);
+            }
+        }
+    }
+
+    /*
+     * @see IAnnotationModel#removeAnnotationModelListener(IAnnotationModelListener)
+     */
+    public void removeAnnotationModelListener(IAnnotationModelListener listener) {
+        super.removeAnnotationModelListener(listener);
+
+        if (fModel !is null && fAnnotationModelListeners.isEmpty())
+            fModel.removeAnnotationModelListener(this);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/ContextTypeRegistry.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.templates.ContextTypeRegistry;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Map;
+
+
+
+
+/**
+ * A registry for context types. Editor implementors will usually instantiate a
+ * registry and configure the context types available in their editor.
+ * <p>
+ * In order to pick up templates contributed using the <code>org.eclipse.ui.editors.templates</code>
+ * extension point, use a <code>ContributionContextTypeRegistry</code>.
+ * </p>
+ *
+ * @since 3.0
+ */
+public class ContextTypeRegistry {
+
+    /** all known context types */
+    private const Map fContextTypes;
+
+    this(){
+        fContextTypes= new LinkedHashMap();
+    }
+    /**
+     * Adds a context type to the registry. If there already is a context type
+     * with the same ID registered, it is replaced.
+     *
+     * @param contextType the context type to add
+     */
+    public void addContextType(TemplateContextType contextType) {
+        fContextTypes.put(contextType.getId(), contextType);
+    }
+
+    /**
+     * Returns the context type if the id is valid, <code>null</code> otherwise.
+     *
+     * @param id the id of the context type to retrieve
+     * @return the context type if <code>name</code> is valid, <code>null</code> otherwise
+     */
+    public TemplateContextType getContextType(String id) {
+        return cast(TemplateContextType) fContextTypes.get(id);
+    }
+
+    /**
+     * Returns an iterator over all registered context types.
+     *
+     * @return an iterator over all registered context types
+     */
+    public Iterator contextTypes() {
+        return fContextTypes.values().iterator();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/InclusivePositionUpdater.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.templates.InclusivePositionUpdater;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IPositionUpdater;
+import org.eclipse.jface.text.Position;
+
+/**
+ * Position updater that takes any change in [position.offset, position.offset + position.length] as
+ * belonging to the position.
+ *
+ * @since 3.0
+ */
+class InclusivePositionUpdater : IPositionUpdater {
+
+    /** The position category. */
+    private const String fCategory;
+
+    /**
+     * Creates a new updater for the given <code>category</code>.
+     *
+     * @param category the new category.
+     */
+    public this(String category) {
+        fCategory= category;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent)
+     */
+    public void update(DocumentEvent event) {
+
+        int eventOffset= event.getOffset();
+        int eventOldLength= event.getLength();
+        int eventNewLength= event.getText() is null ? 0 : event.getText().length();
+        int deltaLength= eventNewLength - eventOldLength;
+
+        try {
+            Position[] positions= event.getDocument().getPositions(fCategory);
+
+            for (int i= 0; i !is positions.length; i++) {
+
+                Position position= positions[i];
+
+                if (position.isDeleted())
+                    continue;
+
+                int offset= position.getOffset();
+                int length= position.getLength();
+                int end= offset + length;
+
+                if (offset > eventOffset + eventOldLength)
+                    // position comes way
+                    // after change - shift
+                    position.setOffset(offset + deltaLength);
+                else if (end < eventOffset) {
+                    // position comes way before change -
+                    // leave alone
+                } else if (offset <= eventOffset && end >= eventOffset + eventOldLength) {
+                    // event completely internal to the position - adjust length
+                    position.setLength(length + deltaLength);
+                } else if (offset < eventOffset) {
+                    // event extends over end of position - adjust length
+                    int newEnd= eventOffset + eventNewLength;
+                    position.setLength(newEnd - offset);
+                } else if (end > eventOffset + eventOldLength) {
+                    // event extends from before position into it - adjust offset
+                    // and length
+                    // offset becomes end of event, length adjusted accordingly
+                    // we want to recycle the overlapping part
+                    position.setOffset(eventOffset);
+                    int deleted= eventOffset + eventOldLength - offset;
+                    position.setLength(length - deleted + eventNewLength);
+                } else {
+                    // event consumes the position - delete it
+                    position.delete_();
+                }
+            }
+        } catch (BadPositionCategoryException e) {
+            // ignore and return
+        }
+    }
+
+    /**
+     * Returns the position category.
+     *
+     * @return the position category
+     */
+    public String getCategory() {
+        return fCategory;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/JFaceTextTemplateMessages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.templates.JFaceTextTemplateMessages;
+
+import java.lang.all;
+
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+import java.text.MessageFormat;
+
+/**
+ * @since 3.0
+ */
+class JFaceTextTemplateMessages {
+
+    private static ResourceBundle fgResourceBundle_;//= ResourceBundle.getBundle(RESOURCE_BUNDLE);
+
+    private static ResourceBundle fgResourceBundle(){
+        if( fgResourceBundle_ is null ){
+            synchronized(JFaceTextTemplateMessages.classinfo ){
+                if( fgResourceBundle_ is null ){
+                    fgResourceBundle_ = ResourceBundle.getBundle(
+                        getImportData!("org.eclipse.jface.text.templates.JFaceTextTemplateMessages.properties"));
+                }
+            }
+        }
+        return fgResourceBundle_;
+    }
+
+    private this() {
+    }
+
+    public static String getString(String key) {
+        try {
+            return fgResourceBundle.getString(key);
+        } catch (MissingResourceException e) {
+            return '!' ~ key ~ '!';
+        }
+    }
+
+    public static String getFormattedString(String key, Object[] args... ) {
+        return MessageFormat.format(getString(key), args);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/PositionBasedCompletionProposal.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.templates.PositionBasedCompletionProposal;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+
+
+/**
+ * A position based completion proposal.
+ * 
+ * @since 3.0
+ */
+final class PositionBasedCompletionProposal : ICompletionProposal, ICompletionProposalExtension2 {
+
+    /** The string to be displayed in the completion proposal popup */
+    private String fDisplayString;
+    /** The replacement string */
+    private String fReplacementString;
+    /** The replacement position. */
+    private Position fReplacementPosition;
+    /** The cursor position after this proposal has been applied */
+    private int fCursorPosition;
+    /** The image to be displayed in the completion proposal popup */
+    private Image fImage;
+    /** The context information of this proposal */
+    private IContextInformation fContextInformation;
+    /** The additional info of this proposal */
+    private String fAdditionalProposalInfo;
+
+    /**
+     * Creates a new completion proposal based on the provided information.  The replacement string is
+     * considered being the display string too. All remaining fields are set to <code>null</code>.
+     *
+     * @param replacementString the actual string to be inserted into the document
+     * @param replacementPosition the position of the text to be replaced
+     * @param cursorPosition the position of the cursor following the insert relative to replacementOffset
+     */
+    public this(String replacementString, Position replacementPosition, int cursorPosition) {
+        this(replacementString, replacementPosition, cursorPosition, null, null, null, null);
+    }
+
+    /**
+     * Creates a new completion proposal. All fields are initialized based on the provided information.
+     *
+     * @param replacementString the actual string to be inserted into the document
+     * @param replacementPosition the position of the text to be replaced
+     * @param cursorPosition the position of the cursor following the insert relative to replacementOffset
+     * @param image the image to display for this proposal
+     * @param displayString the string to be displayed for the proposal
+     * @param contextInformation the context information associated with this proposal
+     * @param additionalProposalInfo the additional information associated with this proposal
+     */
+    public this(String replacementString, Position replacementPosition, int cursorPosition, Image image, String displayString, IContextInformation contextInformation, String additionalProposalInfo) {
+        Assert.isNotNull(replacementString);
+        Assert.isTrue(replacementPosition !is null);
+
+        fReplacementString= replacementString;
+        fReplacementPosition= replacementPosition;
+        fCursorPosition= cursorPosition;
+        fImage= image;
+        fDisplayString= displayString;
+        fContextInformation= contextInformation;
+        fAdditionalProposalInfo= additionalProposalInfo;
+    }
+
+    /*
+     * @see ICompletionProposal#apply(IDocument)
+     */
+    public void apply(IDocument document) {
+        try {
+            document.replace(fReplacementPosition.getOffset(), fReplacementPosition.getLength(), fReplacementString);
+        } catch (BadLocationException x) {
+            // ignore
+        }
+    }
+
+    /*
+     * @see ICompletionProposal#getSelection(IDocument)
+     */
+    public Point getSelection(IDocument document) {
+        return new Point(fReplacementPosition.getOffset() + fCursorPosition, 0);
+    }
+
+    /*
+     * @see ICompletionProposal#getContextInformation()
+     */
+    public IContextInformation getContextInformation() {
+        return fContextInformation;
+    }
+
+    /*
+     * @see ICompletionProposal#getImage()
+     */
+    public Image getImage() {
+        return fImage;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getDisplayString()
+     */
+    public String getDisplayString() {
+        if (fDisplayString !is null)
+            return fDisplayString;
+        return fReplacementString;
+    }
+
+    /*
+     * @see ICompletionProposal#getAdditionalProposalInfo()
+     */
+    public String getAdditionalProposalInfo() {
+        return fAdditionalProposalInfo;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#apply(org.eclipse.jface.text.ITextViewer, char, int, int)
+     */
+    public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) {
+        apply(viewer.getDocument());
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#selected(org.eclipse.jface.text.ITextViewer, bool)
+     */
+    public void selected(ITextViewer viewer, bool smartToggle) {
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#unselected(org.eclipse.jface.text.ITextViewer)
+     */
+    public void unselected(ITextViewer viewer) {
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#validate(org.eclipse.jface.text.IDocument, int, org.eclipse.jface.text.DocumentEvent)
+     */
+    public bool validate(IDocument document, int offset, DocumentEvent event) {
+        try {
+            String content= document.get(fReplacementPosition.getOffset(), offset - fReplacementPosition.getOffset());
+            if (fReplacementString.startsWith(content))
+                return true;
+        } catch (BadLocationException e) {
+            // ignore concurrently modified document
+        }
+        return false;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/TemplateCompletionProcessor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,287 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.templates.TemplateCompletionProcessor;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+
+import java.lang.all;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+
+
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.contentassist.IContextInformationValidator;
+
+
+/**
+ * A completion processor that computes template proposals. Subclasses need to
+ * provide implementations for {@link #getTemplates(String)},
+ * {@link #getContextType(ITextViewer, IRegion)} and {@link #getImage(Template)}.
+ *
+ * @since 3.0
+ */
+public abstract class TemplateCompletionProcessor : IContentAssistProcessor {
+
+    private static final class ProposalComparator : Comparator {
+        public int compare(Object o1, Object o2) {
+            return (cast(TemplateProposal) o2).getRelevance() - (cast(TemplateProposal) o1).getRelevance();
+        }
+    }
+
+    private static Comparator fgProposalComparator_;
+    private static Comparator fgProposalComparator(){
+        if(fgProposalComparator_ is null ) {
+            synchronized( TemplateCompletionProcessor.classinfo ){
+                if(fgProposalComparator_ is null ) {
+                    fgProposalComparator_ = new ProposalComparator();
+                }
+            }
+        }
+        return fgProposalComparator_;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer,
+     *      int)
+     */
+    public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
+
+        ITextSelection selection= cast(ITextSelection) viewer.getSelectionProvider().getSelection();
+
+        // adjust offset to end of normalized selection
+        if (selection.getOffset() is offset)
+            offset= selection.getOffset() + selection.getLength();
+
+        String prefix= extractPrefix(viewer, offset);
+        Region region= new Region(offset - prefix.length(), prefix.length());
+        TemplateContext context= createContext(viewer, region);
+        if (context is null)
+            return new ICompletionProposal[0];
+
+        context.setVariable("selection", selection.getText()); // name of the selection variables {line, word}_selection //$NON-NLS-1$
+
+        Template[] templates= getTemplates(context.getContextType().getId());
+
+        List matches= new ArrayList();
+        for (int i= 0; i < templates.length; i++) {
+            Template template_= templates[i];
+            try {
+                context.getContextType().validate(template_.getPattern());
+            } catch (TemplateException e) {
+                continue;
+            }
+            if (template_.matches(prefix, context.getContextType().getId()))
+                matches.add( cast(Object) createProposal(template_, context, cast(IRegion) region, getRelevance(template_, prefix)));
+        }
+
+        Collections.sort(matches, fgProposalComparator);
+
+        return arraycast!(ICompletionProposal)( matches.toArray());
+    }
+
+    /**
+     * Creates a new proposal.
+     * <p>
+     * Forwards to {@link #createProposal(Template, TemplateContext, IRegion, int)}.
+     * Do neither call nor override.
+     * </p>
+     *
+     * @param template the template to be applied by the proposal
+     * @param context the context for the proposal
+     * @param region the region the proposal applies to
+     * @param relevance the relevance of the proposal
+     * @return a new <code>ICompletionProposal</code> for
+     *         <code>template</code>
+     * @deprecated use the version specifying <code>IRegion</code> as third parameter
+     * @since 3.1
+     */
+    protected ICompletionProposal createProposal(Template template_, TemplateContext context, Region region, int relevance) {
+        return createProposal(template_, context, cast(IRegion) region, relevance);
+    }
+
+    /**
+     * Creates a new proposal.
+     * <p>
+     * The default implementation returns an instance of
+     * {@link TemplateProposal}. Subclasses may replace this method to provide
+     * their own implementations.
+     * </p>
+     *
+     * @param template the template to be applied by the proposal
+     * @param context the context for the proposal
+     * @param region the region the proposal applies to
+     * @param relevance the relevance of the proposal
+     * @return a new <code>ICompletionProposal</code> for
+     *         <code>template</code>
+     */
+    protected ICompletionProposal createProposal(Template template_, TemplateContext context, IRegion region, int relevance) {
+        return new TemplateProposal(template_, context, region, getImage(template_), relevance);
+    }
+
+    /**
+     * Returns the templates valid for the context type specified by <code>contextTypeId</code>.
+     *
+     * @param contextTypeId the context type id
+     * @return the templates valid for this context type id
+     */
+    protected abstract Template[] getTemplates(String contextTypeId);
+
+    /**
+     * Creates a concrete template context for the given region in the document. This involves finding out which
+     * context type is valid at the given location, and then creating a context of this type. The default implementation
+     * returns a <code>DocumentTemplateContext</code> for the context type at the given location.
+     *
+     * @param viewer the viewer for which the context is created
+     * @param region the region into <code>document</code> for which the context is created
+     * @return a template context that can handle template insertion at the given location, or <code>null</code>
+     */
+    protected TemplateContext createContext(ITextViewer viewer, IRegion region) {
+        TemplateContextType contextType= getContextType(viewer, region);
+        if (contextType !is null) {
+            IDocument document= viewer.getDocument();
+            return new DocumentTemplateContext(contextType, document, region.getOffset(), region.getLength());
+        }
+        return null;
+    }
+
+    /**
+     * Returns the context type that can handle template insertion at the given region
+     * in the viewer's document.
+     *
+     * @param viewer the text viewer
+     * @param region the region into the document displayed by viewer
+     * @return the context type that can handle template expansion for the given location, or <code>null</code> if none exists
+     */
+    protected abstract TemplateContextType getContextType(ITextViewer viewer, IRegion region);
+
+    /**
+     * Returns the relevance of a template given a prefix. The default
+     * implementation returns a number greater than zero if the template name
+     * starts with the prefix, and zero otherwise.
+     *
+     * @param template the template to compute the relevance for
+     * @param prefix the prefix after which content assist was requested
+     * @return the relevance of <code>template</code>
+     * @see #extractPrefix(ITextViewer, int)
+     */
+    protected int getRelevance(Template template_, String prefix) {
+        if (template_.getName().startsWith(prefix))
+            return 90;
+        return 0;
+    }
+
+    /**
+     * Heuristically extracts the prefix used for determining template relevance
+     * from the viewer's document. The default implementation returns the String from
+     * offset backwards that forms a java identifier.
+     *
+     * @param viewer the viewer
+     * @param offset offset into document
+     * @return the prefix to consider
+     * @see #getRelevance(Template, String)
+     */
+    protected String extractPrefix(ITextViewer viewer, int offset) {
+        int i= offset;
+        IDocument document= viewer.getDocument();
+        if (i > document.getLength())
+            return ""; //$NON-NLS-1$
+
+        try {
+            while (i > 0) {
+                char ch= document.getChar(i - 1);
+                if (!Character.isJavaIdentifierPart(ch))
+                    break;
+                i--;
+            }
+
+            return document.get(i, offset - i);
+        } catch (BadLocationException e) {
+            return ""; //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Returns the image to be used for the proposal for <code>template</code>.
+     *
+     * @param template the template for which an image should be returned
+     * @return the image for <code>template</code>
+     */
+    protected abstract Image getImage(Template template_);
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.ITextViewer, int)
+     */
+    public IContextInformation[] computeContextInformation(ITextViewer viewer, int documentOffset) {
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
+     */
+    public char[] getCompletionProposalAutoActivationCharacters() {
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters()
+     */
+    public char[] getContextInformationAutoActivationCharacters() {
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getErrorMessage()
+     */
+    public String getErrorMessage() {
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator()
+     */
+    public IContextInformationValidator getContextInformationValidator() {
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/TemplateProposal.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,492 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.templates.TemplateProposal;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.link.ILinkedModeListener;
+import org.eclipse.jface.text.link.LinkedModeModel;
+import org.eclipse.jface.text.link.LinkedModeUI;
+import org.eclipse.jface.text.link.LinkedPosition;
+import org.eclipse.jface.text.link.LinkedPositionGroup;
+import org.eclipse.jface.text.link.ProposalPosition;
+
+
+/**
+ * A template completion proposal.
+ * <p>
+ * Clients may subclass.</p>
+ *
+ * @since 3.0
+ */
+public class TemplateProposal : ICompletionProposal, ICompletionProposalExtension, ICompletionProposalExtension2, ICompletionProposalExtension3 {
+
+    private const Template fTemplate;
+    private const TemplateContext fContext;
+    private const Image fImage;
+    private const IRegion fRegion;
+    private int fRelevance;
+
+    private IRegion fSelectedRegion; // initialized by apply()
+    private String fDisplayString;
+    private InclusivePositionUpdater fUpdater;
+    private IInformationControlCreator fInformationControlCreator;
+
+    /**
+     * Creates a template proposal with a template and its context.
+     *
+     * @param template  the template
+     * @param context   the context in which the template was requested.
+     * @param region    the region this proposal is applied to
+     * @param image     the icon of the proposal.
+     */
+    public this(Template template_, TemplateContext context, IRegion region, Image image) {
+        this(template_, context, region, image, 0);
+    }
+
+    /**
+     * Creates a template proposal with a template and its context.
+     *
+     * @param template  the template
+     * @param context   the context in which the template was requested.
+     * @param image     the icon of the proposal.
+     * @param region    the region this proposal is applied to
+     * @param relevance the relevance of the proposal
+     */
+    public this(Template template_, TemplateContext context, IRegion region, Image image, int relevance) {
+        Assert.isNotNull(template_);
+        Assert.isNotNull(context);
+        Assert.isNotNull(cast(Object)region);
+
+        fTemplate= template_;
+        fContext= context;
+        fImage= image;
+        fRegion= region;
+
+        fDisplayString= null;
+
+        fRelevance= relevance;
+    }
+
+    /**
+     * Sets the information control creator for this completion proposal.
+     *
+     * @param informationControlCreator the information control creator
+     * @since 3.1
+     */
+    public final void setInformationControlCreator(IInformationControlCreator informationControlCreator) {
+        fInformationControlCreator= informationControlCreator;
+    }
+
+    /**
+     * Returns the template of this proposal.
+     *
+     * @return the template of this proposal
+     * @since 3.1
+     */
+    protected final Template getTemplate() {
+        return fTemplate;
+    }
+
+    /**
+     * Returns the context in which the template was requested.
+     *
+     * @return the context in which the template was requested
+     * @since 3.1
+     */
+    protected final TemplateContext getContext() {
+        return fContext;
+    }
+
+    /*
+     * @see ICompletionProposal#apply(IDocument)
+     */
+    public final void apply(IDocument document) {
+        // not called anymore
+    }
+
+    /**
+     * Inserts the template offered by this proposal into the viewer's document
+     * and sets up a <code>LinkedModeUI</code> on the viewer to edit any of
+     * the template's unresolved variables.
+     *
+     * @param viewer {@inheritDoc}
+     * @param trigger {@inheritDoc}
+     * @param stateMask {@inheritDoc}
+     * @param offset {@inheritDoc}
+     */
+    public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) {
+
+        IDocument document= viewer.getDocument();
+        try {
+            fContext.setReadOnly(false);
+            int start;
+            TemplateBuffer templateBuffer;
+            {
+                int oldReplaceOffset= getReplaceOffset();
+                try {
+                    // this may already modify the document (e.g. add imports)
+                    templateBuffer= fContext.evaluate(fTemplate);
+                } catch (TemplateException e1) {
+                    fSelectedRegion= fRegion;
+                    return;
+                }
+
+                start= getReplaceOffset();
+                int shift= start - oldReplaceOffset;
+                int end= Math.max(getReplaceEndOffset(), offset + shift);
+
+                // insert template string
+                String templateString= templateBuffer.getString();
+                document.replace(start, end - start, templateString);
+            }
+
+            // translate positions
+            LinkedModeModel model= new LinkedModeModel();
+            TemplateVariable[] variables= templateBuffer.getVariables();
+            bool hasPositions= false;
+            for (int i= 0; i !is variables.length; i++) {
+                TemplateVariable variable= variables[i];
+
+                if (variable.isUnambiguous())
+                    continue;
+
+                LinkedPositionGroup group= new LinkedPositionGroup();
+
+                int[] offsets= variable.getOffsets();
+                int length= variable.getLength();
+
+                LinkedPosition first;
+                {
+                    String[] values= variable.getValues();
+                    ICompletionProposal[] proposals= new ICompletionProposal[values.length];
+                    for (int j= 0; j < values.length; j++) {
+                        ensurePositionCategoryInstalled(document, model);
+                        Position pos= new Position(offsets[0] + start, length);
+                        document.addPosition(getCategory(), pos);
+                        proposals[j]= new PositionBasedCompletionProposal(values[j], pos, length);
+                    }
+
+                    if (proposals.length > 1)
+                        first= new ProposalPosition(document, offsets[0] + start, length, proposals);
+                    else
+                        first= new LinkedPosition(document, offsets[0] + start, length);
+                }
+
+                for (int j= 0; j !is offsets.length; j++)
+                    if (j is 0)
+                        group.addPosition(first);
+                    else
+                        group.addPosition(new LinkedPosition(document, offsets[j] + start, length));
+
+                model.addGroup(group);
+                hasPositions= true;
+            }
+
+            if (hasPositions) {
+                model.forceInstall();
+                LinkedModeUI ui= new LinkedModeUI(model, viewer);
+                ui.setExitPosition(viewer, getCaretOffset(templateBuffer) + start, 0, Integer.MAX_VALUE);
+                ui.enter();
+
+                fSelectedRegion= ui.getSelectedRegion();
+            } else {
+                ensurePositionCategoryRemoved(document);
+                fSelectedRegion= new Region(getCaretOffset(templateBuffer) + start, 0);
+            }
+
+        } catch (BadLocationException e) {
+            openErrorDialog(viewer.getTextWidget().getShell(), e);
+            ensurePositionCategoryRemoved(document);
+            fSelectedRegion= fRegion;
+        } catch (BadPositionCategoryException e) {
+            openErrorDialog(viewer.getTextWidget().getShell(), e);
+            fSelectedRegion= fRegion;
+        }
+
+    }
+
+    private void ensurePositionCategoryInstalled(IDocument document, LinkedModeModel model) {
+        if (!document.containsPositionCategory(getCategory())) {
+            document.addPositionCategory(getCategory());
+            fUpdater= new InclusivePositionUpdater(getCategory());
+            document.addPositionUpdater(fUpdater);
+
+            model.addLinkingListener(new class(document)  ILinkedModeListener {
+                IDocument document_;
+                this( IDocument a ){
+                    document_=a;
+                }
+                /*
+                 * @see org.eclipse.jface.text.link.ILinkedModeListener#left(org.eclipse.jface.text.link.LinkedModeModel, int)
+                 */
+                public void left(LinkedModeModel environment, int flags) {
+                    ensurePositionCategoryRemoved(document_);
+                }
+
+                public void suspend(LinkedModeModel environment) {}
+                public void resume(LinkedModeModel environment, int flags) {}
+            });
+        }
+    }
+
+    private void ensurePositionCategoryRemoved(IDocument document) {
+        if (document.containsPositionCategory(getCategory())) {
+            try {
+                document.removePositionCategory(getCategory());
+            } catch (BadPositionCategoryException e) {
+                // ignore
+            }
+            document.removePositionUpdater(fUpdater);
+        }
+    }
+
+    private String getCategory() {
+        return "TemplateProposalCategory_" ~ toString(); //$NON-NLS-1$
+    }
+
+    private int getCaretOffset(TemplateBuffer buffer) {
+
+        TemplateVariable[] variables= buffer.getVariables();
+        for (int i= 0; i !is variables.length; i++) {
+            TemplateVariable variable= variables[i];
+            if (variable.getType().equals(GlobalTemplateVariables.Cursor.NAME))
+                return variable.getOffsets()[0];
+        }
+
+        return buffer.getString().length();
+    }
+
+    /**
+     * Returns the offset of the range in the document that will be replaced by
+     * applying this template.
+     *
+     * @return the offset of the range in the document that will be replaced by
+     *         applying this template
+     * @since 3.1
+     */
+    protected final int getReplaceOffset() {
+        int start;
+        if ( cast(DocumentTemplateContext)fContext ) {
+            DocumentTemplateContext docContext = cast(DocumentTemplateContext)fContext;
+            start= docContext.getStart();
+        } else {
+            start= fRegion.getOffset();
+        }
+        return start;
+    }
+
+    /**
+     * Returns the end offset of the range in the document that will be replaced
+     * by applying this template.
+     *
+     * @return the end offset of the range in the document that will be replaced
+     *         by applying this template
+     * @since 3.1
+     */
+    protected final int getReplaceEndOffset() {
+        int end;
+        if ( cast(DocumentTemplateContext)fContext ) {
+            DocumentTemplateContext docContext = cast(DocumentTemplateContext)fContext;
+            end= docContext.getEnd();
+        } else {
+            end= fRegion.getOffset() + fRegion.getLength();
+        }
+        return end;
+    }
+
+    /*
+     * @see ICompletionProposal#getSelection(IDocument)
+     */
+    public Point getSelection(IDocument document) {
+        return new Point(fSelectedRegion.getOffset(), fSelectedRegion.getLength());
+    }
+
+    /*
+     * @see ICompletionProposal#getAdditionalProposalInfo()
+     */
+    public String getAdditionalProposalInfo() {
+        try {
+            fContext.setReadOnly(true);
+            TemplateBuffer templateBuffer;
+            try {
+                templateBuffer= fContext.evaluate(fTemplate);
+            } catch (TemplateException e) {
+                return null;
+            }
+
+            return templateBuffer.getString();
+
+        } catch (BadLocationException e) {
+            return null;
+        }
+    }
+
+    /*
+     * @see ICompletionProposal#getDisplayString()
+     */
+    public String getDisplayString() {
+        if (fDisplayString is null) {
+            String[] arguments= [ fTemplate.getName(), fTemplate.getDescription() ];
+            fDisplayString= JFaceTextTemplateMessages.getFormattedString("TemplateProposal.displayString", stringcast(arguments)); //$NON-NLS-1$
+        }
+        return fDisplayString;
+    }
+
+    /*
+     * @see ICompletionProposal#getImage()
+     */
+    public Image getImage() {
+        return fImage;
+    }
+
+    /*
+     * @see ICompletionProposal#getContextInformation()
+     */
+    public IContextInformation getContextInformation() {
+        return null;
+    }
+
+    private void openErrorDialog(Shell shell, Exception e) {
+        MessageDialog.openError(shell, JFaceTextTemplateMessages.getString("TemplateProposal.errorDialog.title"), e.msg); //$NON-NLS-1$
+    }
+
+    /**
+     * Returns the relevance.
+     *
+     * @return the relevance
+     */
+    public int getRelevance() {
+        return fRelevance;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getInformationControlCreator()
+     */
+    public IInformationControlCreator getInformationControlCreator() {
+        return fInformationControlCreator;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#selected(org.eclipse.jface.text.ITextViewer, bool)
+     */
+    public void selected(ITextViewer viewer, bool smartToggle) {
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#unselected(org.eclipse.jface.text.ITextViewer)
+     */
+    public void unselected(ITextViewer viewer) {
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#validate(org.eclipse.jface.text.IDocument, int, org.eclipse.jface.text.DocumentEvent)
+     */
+    public bool validate(IDocument document, int offset, DocumentEvent event) {
+        try {
+            int replaceOffset= getReplaceOffset();
+            if (offset >= replaceOffset) {
+                String content= document.get(replaceOffset, offset - replaceOffset);
+                return fTemplate.getName().toLowerCase().startsWith(content.toLowerCase());
+            }
+        } catch (BadLocationException e) {
+            // concurrent modification - ignore
+        }
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getPrefixCompletionText(org.eclipse.jface.text.IDocument, int)
+     */
+    public CharSequence getPrefixCompletionText(IDocument document, int completionOffset) {
+        return new StringCharSequence( fTemplate.getName() );
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getPrefixCompletionStart(org.eclipse.jface.text.IDocument, int)
+     */
+    public int getPrefixCompletionStart(IDocument document, int completionOffset) {
+        return getReplaceOffset();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension#apply(org.eclipse.jface.text.IDocument, char, int)
+     */
+    public void apply(IDocument document, char trigger, int offset) {
+        // not called any longer
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension#isValidFor(org.eclipse.jface.text.IDocument, int)
+     */
+    public bool isValidFor(IDocument document, int offset) {
+        // not called any longer
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension#getTriggerCharacters()
+     */
+    public char[] getTriggerCharacters() {
+        // no triggers
+        return new char[0];
+    }
+
+    /*
+     * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension#getContextInformationPosition()
+     */
+    public int getContextInformationPosition() {
+        return fRegion.getOffset();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplatePersistenceData.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.templates.persistence.TemplatePersistenceData;
+
+import org.eclipse.jface.text.templates.persistence.TemplateReaderWriter; // packageimport
+import org.eclipse.jface.text.templates.persistence.TemplatePersistenceMessages; // packageimport
+import org.eclipse.jface.text.templates.persistence.TemplateStore; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.templates.Template;
+
+
+/**
+ * TemplatePersistenceData stores information about a template. It uniquely
+ * references contributed templates via their id. Contributed templates may be
+ * deleted or modified. All template may be enabled or not.
+ * <p>
+ * Clients may use this class, although this is not usually needed except when
+ * implementing a custom template preference page or template store. This class
+ * is not intended to be subclassed.
+ * </p>
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class TemplatePersistenceData {
+    private const Template fOriginalTemplate;
+    private const String fId;
+    private const bool fOriginalIsEnabled;
+
+    private Template fCustomTemplate= null;
+    private bool fIsDeleted= false;
+    private bool fCustomIsEnabled= true;
+
+    /**
+     * Creates a new, user-added instance that is not linked to a contributed
+     * template.
+     *
+     * @param template the template which is stored by the new instance
+     * @param enabled whether the template is enabled
+     */
+    public this(Template template_, bool enabled) {
+        this(template_, enabled, null);
+    }
+
+    /**
+     * Creates a new instance. If <code>id</code> is not <code>null</code>,
+     * the instance is represents a template that is contributed and can be
+     * identified via its id.
+     *
+     * @param template the template which is stored by the new instance
+     * @param enabled whether the template is enabled
+     * @param id the id of the template, or <code>null</code> if a user-added
+     *        instance should be created
+     */
+    public this(Template template_, bool enabled, String id) {
+        Assert.isNotNull(template_);
+        fOriginalTemplate= template_;
+        fCustomTemplate= template_;
+        fOriginalIsEnabled= enabled;
+        fCustomIsEnabled= enabled;
+        fId= id;
+    }
+
+    /**
+     * Returns the id of this template store, or <code>null</code> if there is none.
+     *
+     * @return the id of this template store
+     */
+    public String getId() {
+        return fId;
+    }
+
+    /**
+     * Returns the deletion state of the stored template. This is only relevant
+     * of contributed templates.
+     *
+     * @return the deletion state of the stored template
+     */
+    public bool isDeleted() {
+        return fIsDeleted;
+    }
+
+    /**
+     * Sets the deletion state of the stored template.
+     *
+     * @param isDeleted the deletion state of the stored template
+     */
+    public void setDeleted(bool isDeleted) {
+        fIsDeleted= isDeleted;
+    }
+
+    /**
+     * Returns the template encapsulated by the receiver.
+     *
+     * @return the template encapsulated by the receiver
+     */
+    public Template getTemplate() {
+        return fCustomTemplate;
+    }
+
+
+    /**
+     * Sets the template encapsulated by the receiver.
+     *
+     * @param template the new template
+     */
+    public void setTemplate(Template template_) {
+        fCustomTemplate= template_;
+    }
+
+    /**
+     * Returns whether the receiver represents a custom template, i.e. is either
+     * a user-added template or a contributed template that has been modified.
+     *
+     * @return <code>true</code> if the contained template is a custom
+     *         template and cannot be reconstructed from the contributed
+     *         templates
+     */
+    public bool isCustom() {
+        return fId is null
+                || fIsDeleted
+                || fOriginalIsEnabled !is fCustomIsEnabled
+                || !fOriginalTemplate.equals(fCustomTemplate);
+    }
+
+    /**
+     * Returns whether the receiver represents a modified template, i.e. a
+     * contributed template that has been changed.
+     *
+     * @return <code>true</code> if the contained template is contributed but has been modified, <code>false</code> otherwise
+     */
+    public bool isModified() {
+        return isCustom() && !isUserAdded();
+    }
+
+    /**
+     * Returns <code>true</code> if the contained template was added by a
+     * user, i.e. does not reference a contributed template.
+     *
+     * @return <code>true</code> if the contained template was added by a user, <code>false</code> otherwise
+     */
+    public bool isUserAdded() {
+        return fId is null;
+    }
+
+
+    /**
+     * Reverts the template to its original setting.
+     */
+    public void revert() {
+        fCustomTemplate= fOriginalTemplate;
+        fCustomIsEnabled= fOriginalIsEnabled;
+        fIsDeleted= false;
+    }
+
+
+    /**
+     * Returns the enablement state of the contained template.
+     *
+     * @return the enablement state of the contained template
+     */
+    public bool isEnabled() {
+        return fCustomIsEnabled;
+    }
+
+    /**
+     * Sets the enablement state of the contained template.
+     *
+     * @param isEnabled the new enablement state of the contained template
+     */
+    public void setEnabled(bool isEnabled) {
+        fCustomIsEnabled= isEnabled;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplatePersistenceMessages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.templates.persistence.TemplatePersistenceMessages;
+
+import org.eclipse.jface.text.templates.persistence.TemplatePersistenceData; // packageimport
+import org.eclipse.jface.text.templates.persistence.TemplateReaderWriter; // packageimport
+import org.eclipse.jface.text.templates.persistence.TemplateStore; // packageimport
+
+
+import java.lang.all;
+
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+import java.text.MessageFormat;
+
+/**
+ * @since 3.0
+ */
+class TemplatePersistenceMessages {
+
+//     private static const String RESOURCE_BUNDLE= TemplatePersistenceMessages.classinfo.getName();
+    private static ResourceBundle fgResourceBundle_;//= ResourceBundle.getBundle(RESOURCE_BUNDLE);
+    private static ResourceBundle fgResourceBundle(){
+        if( fgResourceBundle_ is null ){
+            synchronized(TemplatePersistenceMessages.classinfo ){
+                if( fgResourceBundle_ is null ){
+                    fgResourceBundle_ = ResourceBundle.getBundle(
+                        getImportData!("org.eclipse.jface.text.templates.persistence.TemplatePersistenceMessages.properties"));
+                }
+            }
+        }
+        return fgResourceBundle_;
+    }
+
+    private this() {
+    }
+
+    public static String getString(String key) {
+        try {
+            return fgResourceBundle.getString(key);
+        } catch (MissingResourceException e) {
+            return '!' ~ key ~ '!';
+        }
+    }
+
+    public static String getFormattedString(String key, Object[] args...) {
+        return MessageFormat.format(getString(key), args);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplateReaderWriter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,404 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.templates.persistence.TemplateReaderWriter;
+
+import org.eclipse.jface.text.templates.persistence.TemplatePersistenceData; // packageimport
+import org.eclipse.jface.text.templates.persistence.TemplatePersistenceMessages; // packageimport
+import org.eclipse.jface.text.templates.persistence.TemplateStore; // packageimport
+
+
+import java.lang.all;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.HashSet;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+
+/+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
++/
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.templates.Template;
+
+/**
+ * Serializes templates as character or byte stream and reads the same format
+ * back.
+ * <p>
+ * Clients may instantiate this class, it is not intended to be
+ * subclassed.</p>
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class TemplateReaderWriter {
+
+    private static const String TEMPLATE_ROOT = "templates"; //$NON-NLS-1$
+    private static const String TEMPLATE_ELEMENT = "template"; //$NON-NLS-1$
+    private static const String NAME_ATTRIBUTE= "name"; //$NON-NLS-1$
+    private static const String ID_ATTRIBUTE= "id"; //$NON-NLS-1$
+    private static const String DESCRIPTION_ATTRIBUTE= "description"; //$NON-NLS-1$
+    private static const String CONTEXT_ATTRIBUTE= "context"; //$NON-NLS-1$
+    private static const String ENABLED_ATTRIBUTE= "enabled"; //$NON-NLS-1$
+    private static const String DELETED_ATTRIBUTE= "deleted"; //$NON-NLS-1$
+    /**
+     * @since 3.1
+     */
+    private static const String AUTO_INSERTABLE_ATTRIBUTE= "autoinsert"; //$NON-NLS-1$
+
+    /**
+     * Create a new instance.
+     */
+    public this() {
+    }
+
+    /**
+     * Reads templates from a reader and returns them. The reader must present
+     * a serialized form as produced by the <code>save</code> method.
+     *
+     * @param reader the reader to read templates from
+     * @return the read templates, encapsulated in instances of <code>TemplatePersistenceData</code>
+     * @throws IOException if reading from the stream fails
+     */
+    public TemplatePersistenceData[] read(Reader reader)  {
+        return read(reader, null);
+    }
+
+    /**
+     * Reads the template with identifier <code>id</code> from a reader and
+     * returns it. The reader must present a serialized form as produced by the
+     * <code>save</code> method.
+     *
+     * @param reader the reader to read templates from
+     * @param id the id of the template to return
+     * @return the read template, encapsulated in an instances of
+     *         <code>TemplatePersistenceData</code>
+     * @throws IOException if reading from the stream fails
+     * @since 3.1
+     */
+    public TemplatePersistenceData readSingle(Reader reader, String id)  {
+        implMissing(__FILE__,__LINE__);
+/+        TemplatePersistenceData[] datas= read(new InputSource(reader), null, id);
+        if (datas.length > 0)
+            return datas[0];+/
+        return null;
+    }
+
+    /**
+     * Reads templates from a stream and adds them to the templates.
+     *
+     * @param reader the reader to read templates from
+     * @param bundle a resource bundle to use for translating the read templates, or <code>null</code> if no translation should occur
+     * @return the read templates, encapsulated in instances of <code>TemplatePersistenceData</code>
+     * @throws IOException if reading from the stream fails
+     */
+    public TemplatePersistenceData[] read(Reader reader, ResourceBundle bundle)  {
+        implMissing(__FILE__,__LINE__);
+        return null;
+//         return read(new InputSource(reader), bundle, null);
+    }
+
+    /**
+     * Reads templates from a stream and adds them to the templates.
+     *
+     * @param stream the byte stream to read templates from
+     * @param bundle a resource bundle to use for translating the read templates, or <code>null</code> if no translation should occur
+     * @return the read templates, encapsulated in instances of <code>TemplatePersistenceData</code>
+     * @throws IOException if reading from the stream fails
+     */
+    public TemplatePersistenceData[] read(InputStream stream, ResourceBundle bundle)  {
+        implMissing(__FILE__,__LINE__);
+        return null;
+//         return read(new InputSource(stream), bundle, null);
+    }
+/++
+    /**
+     * Reads templates from an <code>InputSource</code> and adds them to the templates.
+     *
+     * @param source the input source
+     * @param bundle a resource bundle to use for translating the read templates, or <code>null</code> if no translation should occur
+     * @param singleId the template id to extract, or <code>null</code> to read in all templates
+     * @return the read templates, encapsulated in instances of <code>TemplatePersistenceData</code>
+     * @throws IOException if reading from the stream fails
+     */
+    private TemplatePersistenceData[] read(InputSource source, ResourceBundle bundle, String singleId)  {
+        try {
+            Collection templates= new ArrayList();
+            Set ids= new HashSet();
+
+            DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance();
+            DocumentBuilder parser= factory.newDocumentBuilder();
+            Document document= parser.parse(source);
+
+            NodeList elements= document.getElementsByTagName(TEMPLATE_ELEMENT);
+
+            int count= elements.getLength();
+            for (int i= 0; i !is count; i++) {
+                Node node= elements.item(i);
+                NamedNodeMap attributes= node.getAttributes();
+
+                if (attributes is null)
+                    continue;
+
+                String id= getStringValue(attributes, ID_ATTRIBUTE, null);
+                if (id !is null && ids.contains(id))
+                    throw new IOException(TemplatePersistenceMessages.getString("TemplateReaderWriter.duplicate.id")); //$NON-NLS-1$
+
+                if (singleId !is null && !singleId.equals(id))
+                    continue;
+
+                bool deleted = getBooleanValue(attributes, DELETED_ATTRIBUTE, false);
+
+                String name= getStringValue(attributes, NAME_ATTRIBUTE);
+                name= translateString(name, bundle);
+
+                String description= getStringValue(attributes, DESCRIPTION_ATTRIBUTE, ""); //$NON-NLS-1$
+                description= translateString(description, bundle);
+
+                String context= getStringValue(attributes, CONTEXT_ATTRIBUTE);
+
+                if (name is null || context is null)
+                    throw new IOException(TemplatePersistenceMessages.getString("TemplateReaderWriter.error.missing_attribute")); //$NON-NLS-1$
+
+                bool enabled = getBooleanValue(attributes, ENABLED_ATTRIBUTE, true);
+                bool autoInsertable= getBooleanValue(attributes, AUTO_INSERTABLE_ATTRIBUTE, true);
+
+                StringBuffer buffer= new StringBuffer();
+                NodeList children= node.getChildNodes();
+                for (int j= 0; j !is children.getLength(); j++) {
+                    String value= children.item(j).getNodeValue();
+                    if (value !is null)
+                        buffer.append(value);
+                }
+                String pattern= buffer.toString();
+                pattern= translateString(pattern, bundle);
+
+                Template template_= new Template(name, description, context, pattern, autoInsertable);
+                TemplatePersistenceData data= new TemplatePersistenceData(template_, enabled, id);
+                data.setDeleted(deleted);
+
+                templates.add(data);
+
+                if (singleId !is null && singleId.equals(id))
+                    break;
+            }
+
+            return arraycast!(TemplatePersistenceData)( templates.toArray());
+
+        } catch (ParserConfigurationException e) {
+            Assert.isTrue(false);
+        } catch (SAXException e) {
+            Throwable t= e.getCause();
+            if ( cast(IOException)t )
+                throw cast(IOException) t;
+            else if (t !is null)
+                throw new IOException(t.getMessage());
+            else
+                throw new IOException(e.getMessage());
+        }
+
+        return null; // dummy
+    }
+++/
+    /**
+     * Saves the templates as XML, encoded as UTF-8 onto the given byte stream.
+     *
+     * @param templates the templates to save
+     * @param stream the byte output to write the templates to in XML
+     * @throws IOException if writing the templates fails
+     */
+    public void save(TemplatePersistenceData[] templates, OutputStream stream)  {
+        implMissing(__FILE__,__LINE__);
+//         save(templates, new StreamResult(stream));
+    }
+
+    /**
+     * Saves the templates as XML.
+     *
+     * @param templates the templates to save
+     * @param writer the writer to write the templates to in XML
+     * @throws IOException if writing the templates fails
+     */
+    public void save(TemplatePersistenceData[] templates, Writer writer)  {
+        implMissing(__FILE__,__LINE__);
+//         save(templates, new StreamResult(writer));
+    }
+
+/++
+    /**
+     * Saves the templates as XML.
+     *
+     * @param templates the templates to save
+     * @param result the stream result to write to
+     * @throws IOException if writing the templates fails
+     */
+    private void save(TemplatePersistenceData[] templates, StreamResult result)  {
+        try {
+            DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance();
+            DocumentBuilder builder= factory.newDocumentBuilder();
+            Document document= builder.newDocument();
+
+            Node root= document.createElement(TEMPLATE_ROOT);
+            document.appendChild(root);
+
+            for (int i= 0; i < templates.length; i++) {
+                TemplatePersistenceData data= templates[i];
+                Template template_= data.getTemplate();
+
+                Node node= document.createElement(TEMPLATE_ELEMENT);
+                root.appendChild(node);
+
+                NamedNodeMap attributes= node.getAttributes();
+
+                String id= data.getId();
+                if (id !is null) {
+                    Attr idAttr= document.createAttribute(ID_ATTRIBUTE);
+                    idAttr.setValue(id);
+                    attributes.setNamedItem(idAttr);
+                }
+
+                if (template_ !is null) {
+                    Attr name= document.createAttribute(NAME_ATTRIBUTE);
+                    name.setValue(template_.getName());
+                    attributes.setNamedItem(name);
+                }
+
+                if (template_ !is null) {
+                    Attr description= document.createAttribute(DESCRIPTION_ATTRIBUTE);
+                    description.setValue(template_.getDescription());
+                    attributes.setNamedItem(description);
+                }
+
+                if (template_ !is null) {
+                    Attr context= document.createAttribute(CONTEXT_ATTRIBUTE);
+                    context.setValue(template_.getContextTypeId());
+                    attributes.setNamedItem(context);
+                }
+
+                Attr enabled= document.createAttribute(ENABLED_ATTRIBUTE);
+                enabled.setValue(data.isEnabled() ? Boolean.toString(true) : Boolean.toString(false));
+                attributes.setNamedItem(enabled);
+
+                Attr deleted= document.createAttribute(DELETED_ATTRIBUTE);
+                deleted.setValue(data.isDeleted() ? Boolean.toString(true) : Boolean.toString(false));
+                attributes.setNamedItem(deleted);
+
+                if (template_ !is null) {
+                    Attr autoInsertable= document.createAttribute(AUTO_INSERTABLE_ATTRIBUTE);
+                    autoInsertable.setValue(template_.isAutoInsertable() ? Boolean.toString(true) : Boolean.toString(false));
+                    attributes.setNamedItem(autoInsertable);
+                }
+
+                if (template_ !is null) {
+                    Text pattern= document.createTextNode(template_.getPattern());
+                    node.appendChild(pattern);
+                }
+            }
+
+
+            Transformer transformer=TransformerFactory.newInstance().newTransformer();
+            transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
+            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$
+            DOMSource source = new DOMSource(document);
+
+            transformer.transform(source, result);
+
+        } catch (ParserConfigurationException e) {
+            Assert.isTrue(false);
+        } catch (TransformerException e) {
+            if (cast(IOException)e.getException() )
+                throw cast(IOException) e.getException();
+            Assert.isTrue(false);
+        }
+    }
+
+    private bool getBooleanValue(NamedNodeMap attributes, String attribute, bool defaultValue)  {
+        Node enabledNode= attributes.getNamedItem(attribute);
+        if (enabledNode is null)
+            return defaultValue;
+        else if (enabledNode.getNodeValue().equals(Boolean.toString(true)))
+            return true;
+        else if (enabledNode.getNodeValue().equals(Boolean.toString(false)))
+            return false;
+        else
+            throw new SAXException(TemplatePersistenceMessages.getString("TemplateReaderWriter.error.illegal_boolean_attribute")); //$NON-NLS-1$
+    }
+
+    private String getStringValue(NamedNodeMap attributes, String name)  {
+        String val= getStringValue(attributes, name, null);
+        if (val is null)
+            throw new SAXException(TemplatePersistenceMessages.getString("TemplateReaderWriter.error.missing_attribute")); //$NON-NLS-1$
+        return val;
+    }
+
+    private String getStringValue(NamedNodeMap attributes, String name, String defaultValue) {
+        Node node= attributes.getNamedItem(name);
+        return node is null ? defaultValue : node.getNodeValue();
+    }
+
+    private String translateString(String str, ResourceBundle bundle) {
+        if (bundle is null)
+            return str;
+
+        int idx= str.indexOf('%');
+        if (idx is -1) {
+            return str;
+        }
+        StringBuffer buf= new StringBuffer();
+        int k= 0;
+        while (idx !is -1) {
+            buf.append(str.substring(k, idx));
+            for (k= idx + 1; k < str.length() && !Character.isWhitespace(str.charAt(k)); k++) {
+                // loop
+            }
+            String key= str.substring(idx + 1, k);
+            buf.append(getBundleString(key, bundle));
+            idx= str.indexOf('%', k);
+        }
+        buf.append(str.substring(k));
+        return buf.toString();
+    }
+
+    private String getBundleString(String key, ResourceBundle bundle) {
+        if (bundle !is null) {
+            try {
+                return bundle.getString(key);
+            } catch (MissingResourceException e) {
+                return '!' + key + '!';
+            }
+        }
+        return TemplatePersistenceMessages.getString(key); // default messages
+    }
+++/
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/templates/persistence/TemplateStore.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,470 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.templates.persistence.TemplateStore;
+
+import org.eclipse.jface.text.templates.persistence.TemplatePersistenceData; // packageimport
+import org.eclipse.jface.text.templates.persistence.TemplateReaderWriter; // packageimport
+import org.eclipse.jface.text.templates.persistence.TemplatePersistenceMessages; // packageimport
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import org.eclipse.dwtxhelper.StringWriter;
+import org.eclipse.dwtxhelper.StringReader;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.preference.IPersistentPreferenceStore;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.templates.ContextTypeRegistry;
+import org.eclipse.jface.text.templates.Template;
+import org.eclipse.jface.text.templates.TemplateException;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+/**
+ * A collection of templates. Clients may instantiate this class. In order to
+ * load templates contributed using the <code>org.eclipse.ui.editors.templates</code>
+ * extension point, use a <code>ContributionTemplateStore</code>.
+ *
+ * @since 3.0
+ */
+public class TemplateStore {
+    /** The stored templates. */
+    private const List fTemplates;
+    /** The preference store. */
+    private IPreferenceStore fPreferenceStore;
+    /**
+     * The key into <code>fPreferenceStore</code> the value of which holds custom templates
+     * encoded as XML.
+     */
+    private String fKey;
+    /**
+     * The context type registry, or <code>null</code> if all templates regardless
+     * of context type should be loaded.
+     */
+    private ContextTypeRegistry fRegistry;
+    /**
+     * Set to <code>true</code> if property change events should be ignored (e.g. during writing
+     * to the preference store).
+     *
+     * @since 3.2
+     */
+    private bool fIgnorePreferenceStoreChanges= false;
+    /**
+     * The property listener, if any is registered, <code>null</code> otherwise.
+     *
+     * @since 3.2
+     */
+    private IPropertyChangeListener fPropertyListener;
+
+
+    /**
+     * Creates a new template store.
+     *
+     * @param store the preference store in which to store custom templates
+     *        under <code>key</code>
+     * @param key the key into <code>store</code> where to store custom
+     *        templates
+     */
+    public this(IPreferenceStore store, String key) {
+        fTemplates= new ArrayList();
+
+        Assert.isNotNull(cast(Object)store);
+        Assert.isNotNull(key);
+        fPreferenceStore= store;
+        fKey= key;
+    }
+
+    /**
+     * Creates a new template store with a context type registry. Only templates
+     * that specify a context type contained in the registry will be loaded by
+     * this store if the registry is not <code>null</code>.
+     *
+     * @param registry a context type registry, or <code>null</code> if all
+     *        templates should be loaded
+     * @param store the preference store in which to store custom templates
+     *        under <code>key</code>
+     * @param key the key into <code>store</code> where to store custom
+     *        templates
+     */
+    public this(ContextTypeRegistry registry, IPreferenceStore store, String key) {
+        this(store, key);
+        fRegistry= registry;
+    }
+
+    /**
+     * Loads the templates from contributions and preferences.
+     *
+     * @throws IOException if loading fails.
+     */
+    public void load()  {
+        fTemplates.clear();
+        loadContributedTemplates();
+        loadCustomTemplates();
+    }
+
+    /**
+     * Starts listening for property changes on the preference store. If the configured preference
+     * key changes, the template store is {@link #load() reloaded}. Call
+     * {@link #stopListeningForPreferenceChanges()} to remove any listener and stop the
+     * auto-updating behavior.
+     *
+     * @since 3.2
+     */
+    public final void startListeningForPreferenceChanges() {
+        if (fPropertyListener is null) {
+            fPropertyListener= new class()  IPropertyChangeListener {
+                public void propertyChange(PropertyChangeEvent event) {
+                    /*
+                     * Don't load if we are in the process of saving ourselves. We are in sync anyway after the
+                     * save operation, and clients may trigger reloading by listening to preference store
+                     * updates.
+                     */
+                    if (!fIgnorePreferenceStoreChanges && fKey.equals(event.getProperty()))
+                        try {
+                            load();
+                        } catch (IOException x) {
+                            handleException(x);
+                        }
+                }
+            };
+            fPreferenceStore.addPropertyChangeListener(fPropertyListener);
+        }
+
+    }
+
+    /**
+     * Stops the auto-updating behavior started by calling
+     * {@link #startListeningForPreferenceChanges()}.
+     *
+     * @since 3.2
+     */
+    public final void stopListeningForPreferenceChanges() {
+        if (fPropertyListener !is null) {
+            fPreferenceStore.removePropertyChangeListener(fPropertyListener);
+            fPropertyListener= null;
+        }
+    }
+
+    /**
+     * Handles an {@link IOException} thrown during reloading the preferences due to a preference
+     * store update. The default is to write to stderr.
+     *
+     * @param x the exception
+     * @since 3.2
+     */
+    protected void handleException(IOException x) {
+        ExceptionPrintStackTrace(x);
+    }
+
+    /**
+     * Hook method to load contributed templates. Contributed templates are superseded
+     * by customized versions of user added templates stored in the preferences.
+     * <p>
+     * The default implementation does nothing.</p>
+     *
+     * @throws IOException if loading fails
+     */
+    protected void loadContributedTemplates()  {
+    }
+
+    /**
+     * Adds a template to the internal store. The added templates must have
+     * a unique id.
+     *
+     * @param data the template data to add
+     */
+    protected void internalAdd(TemplatePersistenceData data) {
+        if (!data.isCustom()) {
+            // check if the added template is not a duplicate id
+            String id= data.getId();
+            for (Iterator it= fTemplates.iterator(); it.hasNext();) {
+                TemplatePersistenceData d2= cast(TemplatePersistenceData) it.next();
+                if (d2.getId() !is null && d2.getId().equals(id))
+                    return;
+            }
+            fTemplates.add(data);
+        }
+    }
+
+    /**
+     * Saves the templates to the preferences.
+     *
+     * @throws IOException if the templates cannot be written
+     */
+    public void save()  {
+        ArrayList custom= new ArrayList();
+        for (Iterator it= fTemplates.iterator(); it.hasNext();) {
+            TemplatePersistenceData data= cast(TemplatePersistenceData) it.next();
+            if (data.isCustom() && !(data.isUserAdded() && data.isDeleted())) // don't save deleted user-added templates
+                custom.add(data);
+        }
+
+        StringWriter output= new StringWriter();
+        TemplateReaderWriter writer= new TemplateReaderWriter();
+        writer.save(arraycast!(TemplatePersistenceData)( custom.toArray()), output);
+
+        fIgnorePreferenceStoreChanges= true;
+        try {
+            fPreferenceStore.setValue(fKey, output.toString());
+            if ( cast(IPersistentPreferenceStore)fPreferenceStore )
+                (cast(IPersistentPreferenceStore)fPreferenceStore).save();
+        } finally {
+            fIgnorePreferenceStoreChanges= false;
+        }
+    }
+
+    /**
+     * Adds a template encapsulated in its persistent form.
+     *
+     * @param data the template to add
+     */
+    public void add(TemplatePersistenceData data) {
+
+        if (!validateTemplate(data.getTemplate()))
+            return;
+
+        if (data.isUserAdded()) {
+            fTemplates.add(data);
+        } else {
+            for (Iterator it= fTemplates.iterator(); it.hasNext();) {
+                TemplatePersistenceData d2= cast(TemplatePersistenceData) it.next();
+                if (d2.getId() !is null && d2.getId().equals(data.getId())) {
+                    d2.setTemplate(data.getTemplate());
+                    d2.setDeleted(data.isDeleted());
+                    d2.setEnabled(data.isEnabled());
+                    return;
+                }
+            }
+
+            // add an id which is not contributed as add-on
+            if (data.getTemplate() !is null) {
+                TemplatePersistenceData newData= new TemplatePersistenceData(data.getTemplate(), data.isEnabled());
+                fTemplates.add(newData);
+            }
+        }
+    }
+
+    /**
+     * Removes a template from the store.
+     *
+     * @param data the template to remove
+     */
+    public void delete_(TemplatePersistenceData data) {
+        if (data.isUserAdded())
+            fTemplates.remove(data);
+        else
+            data.setDeleted(true);
+    }
+
+    /**
+     * Restores all contributed templates that have been deleted.
+     */
+    public void restoreDeleted() {
+        for (Iterator it= fTemplates.iterator(); it.hasNext();) {
+            TemplatePersistenceData data= cast(TemplatePersistenceData) it.next();
+            if (data.isDeleted())
+                data.setDeleted(false);
+        }
+    }
+
+    /**
+     * Deletes all user-added templates and reverts all contributed templates.
+     */
+    public void restoreDefaults() {
+        try {
+            fIgnorePreferenceStoreChanges= true;
+            fPreferenceStore.setToDefault(fKey);
+        } finally {
+            fIgnorePreferenceStoreChanges= false;
+        }
+        try {
+            load();
+        } catch (IOException x) {
+            // can't log from jface-text
+            ExceptionPrintStackTrace(x);
+        }
+    }
+
+    /**
+     * Returns all enabled templates.
+     *
+     * @return all enabled templates
+     */
+    public Template[] getTemplates() {
+        return getTemplates(null);
+    }
+
+    /**
+     * Returns all enabled templates for the given context type.
+     *
+     * @param contextTypeId the id of the context type of the requested templates, or <code>null</code> if all templates should be returned
+     * @return all enabled templates for the given context type
+     */
+    public Template[] getTemplates(String contextTypeId) {
+        List templates= new ArrayList();
+        for (Iterator it= fTemplates.iterator(); it.hasNext();) {
+            TemplatePersistenceData data= cast(TemplatePersistenceData) it.next();
+            if (data.isEnabled() && !data.isDeleted() && (contextTypeId is null || contextTypeId.equals(data.getTemplate().getContextTypeId())))
+                templates.add(data.getTemplate());
+        }
+
+        return arraycast!(Template)( templates.toArray());
+    }
+
+    /**
+     * Returns the first enabled template that matches the name.
+     *
+     * @param name the name of the template searched for
+     * @return the first enabled template that matches both name and context type id, or <code>null</code> if none is found
+     */
+    public Template findTemplate(String name) {
+        return findTemplate(name, null);
+    }
+
+    /**
+     * Returns the first enabled template that matches both name and context type id.
+     *
+     * @param name the name of the template searched for
+     * @param contextTypeId the context type id to clip unwanted templates, or <code>null</code> if any context type is OK
+     * @return the first enabled template that matches both name and context type id, or <code>null</code> if none is found
+     */
+    public Template findTemplate(String name, String contextTypeId) {
+        Assert.isNotNull(name);
+
+        for (Iterator it= fTemplates.iterator(); it.hasNext();) {
+            TemplatePersistenceData data= cast(TemplatePersistenceData) it.next();
+            Template template_= data.getTemplate();
+            if (data.isEnabled() && !data.isDeleted()
+                    && (contextTypeId is null || contextTypeId.equals(template_.getContextTypeId()))
+                    && name.equals(template_.getName()))
+                return template_;
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the first enabled template that matches the given template id.
+     *
+     * @param id the id of the template searched for
+     * @return the first enabled template that matches id, or <code>null</code> if none is found
+     * @since 3.1
+     */
+    public Template findTemplateById(String id) {
+        TemplatePersistenceData data= getTemplateData(id);
+        if (data !is null && !data.isDeleted())
+            return data.getTemplate();
+
+        return null;
+    }
+
+    /**
+     * Returns all template data.
+     *
+     * @param includeDeleted whether to include deleted data
+     * @return all template data, whether enabled or not
+     */
+    public TemplatePersistenceData[] getTemplateData(bool includeDeleted) {
+        List datas= new ArrayList();
+        for (Iterator it= fTemplates.iterator(); it.hasNext();) {
+            TemplatePersistenceData data= cast(TemplatePersistenceData) it.next();
+            if (includeDeleted || !data.isDeleted())
+                datas.add(data);
+        }
+
+        return arraycast!(TemplatePersistenceData)( datas.toArray());
+    }
+
+    /**
+     * Returns the template data of the template with id <code>id</code> or
+     * <code>null</code> if no such template can be found.
+     *
+     * @param id the id of the template data
+     * @return the template data of the template with id <code>id</code> or <code>null</code>
+     * @since 3.1
+     */
+    public TemplatePersistenceData getTemplateData(String id) {
+        Assert.isNotNull(id);
+        for (Iterator it= fTemplates.iterator(); it.hasNext();) {
+            TemplatePersistenceData data= cast(TemplatePersistenceData) it.next();
+            if (id.equals(data.getId()))
+                return data;
+        }
+
+        return null;
+    }
+
+    private void loadCustomTemplates()  {
+        String pref= fPreferenceStore.getString(fKey);
+        if (pref !is null && pref.trim().length() > 0) {
+            Reader input= new StringReader(pref);
+            TemplateReaderWriter reader= new TemplateReaderWriter();
+            TemplatePersistenceData[] datas= reader.read(input);
+            for (int i= 0; i < datas.length; i++) {
+                TemplatePersistenceData data= datas[i];
+                add(data);
+            }
+        }
+    }
+
+    /**
+     * Validates a template against the context type registered in the context
+     * type registry. Returns always <code>true</code> if no registry is
+     * present.
+     *
+     * @param template the template to validate
+     * @return <code>true</code> if validation is successful or no context
+     *         type registry is specified, <code>false</code> if validation
+     *         fails
+     */
+    private bool validateTemplate(Template template_) {
+        String contextTypeId= template_.getContextTypeId();
+        if (contextExists(contextTypeId)) {
+            if (fRegistry !is null)
+                try {
+                    fRegistry.getContextType(contextTypeId).validate(template_.getPattern());
+                } catch (TemplateException e) {
+                    return false;
+                }
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns <code>true</code> if a context type id specifies a valid context type
+     * or if no context type registry is present.
+     *
+     * @param contextTypeId the context type id to look for
+     * @return <code>true</code> if the context type specified by the id
+     *         is present in the context type registry, or if no registry is
+     *         specified
+     */
+    private bool contextExists(String contextTypeId) {
+        return contextTypeId !is null && (fRegistry is null || fRegistry.getContextType(contextTypeId) !is null);
+    }
+
+    /**
+     * Returns the registry.
+     *
+     * @return Returns the registry
+     */
+    protected final ContextTypeRegistry getRegistry() {
+        return fRegistry;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/AbstractAction.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.jface.action.AbstractAction;
+
+import org.eclipse.jface.action.IAction;
+
+import org.eclipse.core.commands.common.EventManager;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * Some common functionality to share between implementations of
+ * <code>IAction</code>. This functionality deals with the property change
+ * event mechanism.
+ * </p>
+ * <p>
+ * Clients may neither instantiate nor extend this class.
+ * </p>
+ *
+ * @since 3.2
+ */
+public abstract class AbstractAction : EventManager, IAction {
+
+    public void addPropertyChangeListener(IPropertyChangeListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Notifies any property change listeners that a property has changed. Only
+     * listeners registered at the time this method is called are notified.
+     *
+     * @param event
+     *            the property change event
+     *
+     * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(PropertyChangeEvent)
+     */
+    protected final void firePropertyChange(PropertyChangeEvent event) {
+        final Object[] list = getListeners();
+        for (int i = 0; i < list.length; ++i) {
+            (cast(IPropertyChangeListener) list[i]).propertyChange(event);
+        }
+    }
+
+    /**
+     * Notifies any property change listeners that a property has changed. Only
+     * listeners registered at the time this method is called are notified. This
+     * method avoids creating an event object if there are no listeners
+     * registered, but calls
+     * <code>firePropertyChange(PropertyChangeEvent)</code> if there are.
+     *
+     * @param propertyName
+     *            the name of the property that has changed
+     * @param oldValue
+     *            the old value of the property, or <code>null</code> if none
+     * @param newValue
+     *            the new value of the property, or <code>null</code> if none
+     *
+     * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(PropertyChangeEvent)
+     */
+    protected final void firePropertyChange(String propertyName,
+            Object oldValue, Object newValue) {
+        if (isListenerAttached()) {
+            firePropertyChange(new PropertyChangeEvent(this, propertyName,
+                    oldValue, newValue));
+        }
+    }
+
+    public void removePropertyChangeListener(
+            IPropertyChangeListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/AbstractGroupMarker.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.AbstractGroupMarker;
+
+import org.eclipse.jface.action.ContributionItem;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+
+/**
+ * Abstract superclass for group marker classes.
+ * <p>
+ * This class is not intended to be subclassed outside the framework.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public abstract class AbstractGroupMarker : ContributionItem {
+    /**
+     * Constructor for use by subclasses.
+     */
+    protected this() {
+    }
+
+    /**
+     * Create a new group marker with the given name.
+     * The group name must not be <code>null</code> or the empty string.
+     * The group name is also used as the item id.
+     *
+     * @param groupName the name of the group
+     */
+    protected this(String groupName) {
+        super(groupName);
+        Assert.isTrue(groupName !is null && groupName.length > 0);
+    }
+
+    /**
+     * Returns the group name.
+     *
+     * @return the group name
+     */
+    public String getGroupName() {
+        return getId();
+    }
+
+    /**
+     * The <code>AbstractGroupMarker</code> implementation of this <code>IContributionItem</code>
+     * method returns <code>true</code> iff the id is not <code>null</code>. Subclasses may override.
+     */
+    public override bool isGroupMarker() {
+        return getId() !is null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/Action.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,725 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.Action;
+
+import org.eclipse.jface.action.AbstractAction;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuCreator;
+import org.eclipse.jface.action.LegacyActionTools;
+
+import org.eclipse.jface.util.IPropertyChangeListener;
+
+import org.eclipse.swt.events.HelpListener;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The standard abstract implementation of an action.
+ * <p>
+ * Subclasses must implement the <code>IAction.run</code> method to carry out
+ * the action's semantics.
+ * </p>
+ */
+public abstract class Action : AbstractAction, IAction {
+
+    public void addPropertyChangeListener(IPropertyChangeListener listener) {
+        super.addPropertyChangeListener(listener);
+    }
+    public void removePropertyChangeListener( IPropertyChangeListener listener) {
+        super.removePropertyChangeListener(listener);
+    }
+
+
+    private static const IMenuCreator VAL_DROP_DOWN_MENU;
+    static this(){
+        VAL_DROP_DOWN_MENU = new class IMenuCreator {
+            public void dispose() {
+                // do nothing
+            }
+
+            public Menu getMenu(Control parent) {
+                // do nothing
+                return null;
+            }
+
+            public Menu getMenu(Menu parent) {
+                // do nothing
+                return null;
+            }
+        };
+        VAL_RADIO_BTN_OFF = new ValueWrapperInt(0);
+        VAL_RADIO_BTN_ON = new ValueWrapperInt(1);
+        VAL_TOGGLE_BTN_OFF = new ValueWrapperBool(false);
+        VAL_TOGGLE_BTN_ON = new ValueWrapperBool(true);
+    }
+
+    /*
+     * The list of default values the action can have. These values will
+     * determine the style of the action.
+     */
+    private static const String VAL_PUSH_BTN = "PUSH_BTN"; //$NON-NLS-1$
+
+    private static const ValueWrapperInt VAL_RADIO_BTN_OFF;
+
+    private static const ValueWrapperInt VAL_RADIO_BTN_ON;
+
+    private static const ValueWrapperBool VAL_TOGGLE_BTN_OFF;
+
+    private static const ValueWrapperBool VAL_TOGGLE_BTN_ON;
+
+    /**
+     * Converts an accelerator key code to a string representation.
+     *
+     * @param keyCode
+     *            the key code to be translated
+     * @return a string representation of the key code
+     */
+    public static String convertAccelerator(int keyCode) {
+        return LegacyActionTools.convertAccelerator(keyCode);
+    }
+
+    /**
+     * Parses the given accelerator text, and converts it to an accelerator key
+     * code.
+     *
+     * @param acceleratorText
+     *            the accelerator text
+     * @return the SWT key code, or 0 if there is no accelerator
+     */
+    public static int convertAccelerator(String acceleratorText) {
+        return LegacyActionTools.convertAccelerator(acceleratorText);
+    }
+
+    /**
+     * Maps a standard keyboard key name to an SWT key code. Key names are
+     * converted to upper case before comparison. If the key name is a single
+     * letter, for example "S", its character code is returned.
+     * <p>
+     * The following key names are known (case is ignored):
+     * <ul>
+     * <li><code>"BACKSPACE"</code></li>
+     * <li><code>"TAB"</code></li>
+     * <li><code>"RETURN"</code></li>
+     * <li><code>"ENTER"</code></li>
+     * <li><code>"ESC"</code></li>
+     * <li><code>"ESCAPE"</code></li>
+     * <li><code>"DELETE"</code></li>
+     * <li><code>"SPACE"</code></li>
+     * <li><code>"ARROW_UP"</code>, <code>"ARROW_DOWN"</code>,
+     * <code>"ARROW_LEFT"</code>, and <code>"ARROW_RIGHT"</code></li>
+     * <li><code>"PAGE_UP"</code> and <code>"PAGE_DOWN"</code></li>
+     * <li><code>"HOME"</code></li>
+     * <li><code>"END"</code></li>
+     * <li><code>"INSERT"</code></li>
+     * <li><code>"F1"</code>, <code>"F2"</code> through <code>"F12"</code></li>
+     * </ul>
+     * </p>
+     *
+     * @param token
+     *            the key name
+     * @return the SWT key code, <code>-1</code> if no match was found
+     * @see org.eclipse.swt.SWT
+     */
+    public static int findKeyCode(String token) {
+        return LegacyActionTools.findKeyCode(token);
+    }
+
+    /**
+     * Maps an SWT key code to a standard keyboard key name. The key code is
+     * stripped of modifiers (SWT.CTRL, SWT.ALT, SWT.SHIFT, and SWT.COMMAND). If
+     * the key code is not an SWT code (for example if it a key code for the key
+     * 'S'), a string containing a character representation of the key code is
+     * returned.
+     *
+     * @param keyCode
+     *            the key code to be translated
+     * @return the string representation of the key code
+     * @see org.eclipse.swt.SWT
+     * @since 2.0
+     */
+    public static String findKeyString(int keyCode) {
+        return LegacyActionTools.findKeyString(keyCode);
+    }
+
+    /**
+     * Maps standard keyboard modifier key names to the corresponding SWT
+     * modifier bit. The following modifier key names are recognized (case is
+     * ignored): <code>"CTRL"</code>, <code>"SHIFT"</code>,
+     * <code>"ALT"</code>, and <code>"COMMAND"</code>. The given modifier
+     * key name is converted to upper case before comparison.
+     *
+     * @param token
+     *            the modifier key name
+     * @return the SWT modifier bit, or <code>0</code> if no match was found
+     * @see org.eclipse.swt.SWT
+     */
+    public static int findModifier(String token) {
+        return LegacyActionTools.findModifier(token);
+    }
+
+    /**
+     * Returns a string representation of an SWT modifier bit (SWT.CTRL,
+     * SWT.ALT, SWT.SHIFT, and SWT.COMMAND). Returns <code>null</code> if the
+     * key code is not an SWT modifier bit.
+     *
+     * @param keyCode
+     *            the SWT modifier bit to be translated
+     * @return the string representation of the SWT modifier bit, or
+     *         <code>null</code> if the key code was not an SWT modifier bit
+     * @see org.eclipse.swt.SWT
+     * @since 2.0
+     */
+    public static String findModifierString(int keyCode) {
+        return LegacyActionTools.findModifierString(keyCode);
+    }
+
+    /**
+     * Convenience method for removing any optional accelerator text from the
+     * given string. The accelerator text appears at the end of the text, and is
+     * separated from the main part by a single tab character <code>'\t'</code>.
+     *
+     * @param text
+     *            the text
+     * @return the text sans accelerator
+     */
+    public static String removeAcceleratorText(String text) {
+        return LegacyActionTools.removeAcceleratorText(text);
+    }
+
+    /**
+     * Convenience method for removing any mnemonics from the given string. For
+     * example, <code>removeMnemonics("&Open")</code> will return
+     * <code>"Open"</code>.
+     *
+     * @param text
+     *            the text
+     * @return the text sans mnemonics
+     *
+     * @since 3.0
+     */
+    public static String removeMnemonics(String text) {
+        return LegacyActionTools.removeMnemonics(text);
+    }
+
+    /**
+     * This action's accelerator; <code>0</code> means none.
+     */
+    private int accelerator = 0;
+
+    /**
+     * This action's action definition id, or <code>null</code> if none.
+     */
+    private String actionDefinitionId;
+
+    /**
+     * This action's description, or <code>null</code> if none.
+     */
+    private String description;
+
+    /**
+     * This action's disabled image, or <code>null</code> if none.
+     */
+    private ImageDescriptor disabledImage;
+
+    /**
+     * Indicates this action is enabled.
+     */
+    private bool enabled = true;
+
+    /**
+     * An action's help listener, or <code>null</code> if none.
+     */
+    private HelpListener helpListener;
+
+    /**
+     * This action's hover image, or <code>null</code> if none.
+     */
+    private ImageDescriptor hoverImage;
+
+    /**
+     * This action's id, or <code>null</code> if none.
+     */
+    private String id;
+
+    /**
+     * This action's image, or <code>null</code> if none.
+     */
+    private ImageDescriptor image;
+
+    /**
+     * This action's text, or <code>null</code> if none.
+     */
+    private String text;
+
+    /**
+     * This action's tool tip text, or <code>null</code> if none.
+     */
+    private String toolTipText;
+
+    /**
+     * Holds the action's menu creator (an IMenuCreator) or checked state (a
+     * Boolean for toggle button, or an Integer for radio button), or
+     * <code>null</code> if neither have been set.
+     * <p>
+     * The value of this field affects the value of <code>getStyle()</code>.
+     * </p>
+     */
+    private Object value = null;
+
+    /**
+     * Creates a new action with no text and no image.
+     * <p>
+     * Configure the action later using the set methods.
+     * </p>
+     */
+    protected this() {
+        // do nothing
+    }
+
+    /**
+     * Creates a new action with the given text and no image. Calls the zero-arg
+     * constructor, then <code>setText</code>.
+     *
+     * @param text
+     *            the string used as the text for the action, or
+     *            <code>null</code> if there is no text
+     * @see #setText
+     */
+    protected this(String text) {
+        this();
+        setText(text);
+    }
+
+    /**
+     * Creates a new action with the given text and image. Calls the zero-arg
+     * constructor, then <code>setText</code> and
+     * <code>setImageDescriptor</code>.
+     *
+     * @param text
+     *            the action's text, or <code>null</code> if there is no text
+     * @param image
+     *            the action's image, or <code>null</code> if there is no
+     *            image
+     * @see #setText
+     * @see #setImageDescriptor
+     */
+    protected this(String text, ImageDescriptor image) {
+        this(text);
+        setImageDescriptor(image);
+    }
+
+    /**
+     * Creates a new action with the given text and style.
+     *
+     * @param text
+     *            the action's text, or <code>null</code> if there is no text
+     * @param style
+     *            one of <code>AS_PUSH_BUTTON</code>,
+     *            <code>AS_CHECK_BOX</code>, <code>AS_DROP_DOWN_MENU</code>,
+     *            <code>AS_RADIO_BUTTON</code>, and
+     *            <code>AS_UNSPECIFIED</code>.
+     */
+    protected this(String text, int style) {
+        this(text);
+        switch (style) {
+        case AS_PUSH_BUTTON:
+            value = stringcast(VAL_PUSH_BTN);
+            break;
+        case AS_CHECK_BOX:
+            value = VAL_TOGGLE_BTN_OFF;
+            break;
+        case AS_DROP_DOWN_MENU:
+            value = cast(Object)VAL_DROP_DOWN_MENU;
+            break;
+        case AS_RADIO_BUTTON:
+            value = VAL_RADIO_BTN_OFF;
+            break;
+        default:
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public int getAccelerator() {
+        return accelerator;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     *
+     */
+    public String getActionDefinitionId() {
+        return actionDefinitionId;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public String getDescription() {
+        if (description !is null) {
+            return description;
+        }
+        return getToolTipText();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public ImageDescriptor getDisabledImageDescriptor() {
+        return disabledImage;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public HelpListener getHelpListener() {
+        return helpListener;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public ImageDescriptor getHoverImageDescriptor() {
+        return hoverImage;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public String getId() {
+        return id;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public ImageDescriptor getImageDescriptor() {
+        return image;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public IMenuCreator getMenuCreator() {
+        // The default drop down menu value is only used
+        // to mark this action requested style. So do not
+        // return it. For backward compatibility reasons.
+        if (value is cast(Object)VAL_DROP_DOWN_MENU) {
+            return null;
+        }
+        if ( auto v = cast(IMenuCreator) value ) {
+            return v;
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public int getStyle() {
+        // Infer the style from the value field.
+        if (value is stringcast(VAL_PUSH_BTN) || value is null) {
+            return AS_PUSH_BUTTON;
+        }
+        if (value is VAL_TOGGLE_BTN_ON || value is VAL_TOGGLE_BTN_OFF) {
+            return AS_CHECK_BOX;
+        }
+        if (value is VAL_RADIO_BTN_ON || value is VAL_RADIO_BTN_OFF) {
+            return AS_RADIO_BUTTON;
+        }
+        if (cast(IMenuCreator)value ) {
+            return AS_DROP_DOWN_MENU;
+        }
+
+        // We should never get to this line...
+        return AS_PUSH_BUTTON;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public String getText() {
+        return text;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public String getToolTipText() {
+        return toolTipText;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public bool isChecked() {
+        return value is VAL_TOGGLE_BTN_ON || value is VAL_RADIO_BTN_ON;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public bool isEnabled() {
+        return enabled;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public bool isHandled() {
+        return true;
+    }
+
+    /**
+     * Reports the outcome of the running of this action via the
+     * {@link IAction#RESULT} property.
+     *
+     * @param success
+     *            <code>true</code> if the action succeeded and
+     *            <code>false</code> if the action failed or was not completed
+     * @see IAction#RESULT
+     * @since 3.0
+     */
+    public final void notifyResult(bool success) {
+        // avoid Boolean.valueOf(bool) to allow compilation against JCL
+        // Foundation (bug 80059)
+        firePropertyChange(RESULT, null, success ? Boolean.TRUE : Boolean.FALSE);
+    }
+
+    /**
+     * The default implementation of this <code>IAction</code> method does
+     * nothing. Subclasses should override this method if they do not need
+     * information from the triggering event, or override
+     * <code>runWithEvent(Event)</code> if they do.
+     */
+    public void run() {
+        // do nothing
+    }
+
+    /**
+     * The default implementation of this <code>IAction</code> method ignores
+     * the event argument, and simply calls <code>run()</code>. Subclasses
+     * should override this method if they need information from the triggering
+     * event, or override <code>run()</code> if not.
+     *
+     * @param event
+     *            the SWT event which triggered this action being run
+     * @since 2.0
+     */
+    public void runWithEvent(Event event) {
+        run();
+    }
+
+    /*
+     * @see IAction#setAccelerator(int)
+     */
+    public void setAccelerator(int keycode) {
+        this.accelerator = keycode;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public void setActionDefinitionId(String id) {
+        actionDefinitionId = id;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public void setChecked(bool checked) {
+        Object newValue = null;
+
+        // For backward compatibility, if the style is not
+        // set yet, then convert it to a toggle button.
+        if (value is null || value is VAL_TOGGLE_BTN_ON
+                || value is VAL_TOGGLE_BTN_OFF) {
+            newValue = checked ? VAL_TOGGLE_BTN_ON : VAL_TOGGLE_BTN_OFF;
+        } else if (value is VAL_RADIO_BTN_ON || value is VAL_RADIO_BTN_OFF) {
+            newValue = checked ? VAL_RADIO_BTN_ON : VAL_RADIO_BTN_OFF;
+        } else {
+            // Some other style already, so do nothing.
+            return;
+        }
+
+        if (newValue !is value) {
+            value = newValue;
+            if (checked) {
+                firePropertyChange(CHECKED, Boolean.FALSE, Boolean.TRUE);
+            } else {
+                firePropertyChange(CHECKED, Boolean.TRUE, Boolean.FALSE);
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public void setDescription(String text) {
+
+        if ((description is null && text !is null)
+                || (description !is null && text is null)
+                || (description !is null && text !is null && !text
+                        .equals(description))) {
+            String oldDescription = description;
+            description = text;
+            firePropertyChange(DESCRIPTION, stringcast(oldDescription), stringcast(description));
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public void setDisabledImageDescriptor(ImageDescriptor newImage) {
+        if (disabledImage !is newImage) {
+            ImageDescriptor oldImage = disabledImage;
+            disabledImage = newImage;
+            firePropertyChange(IMAGE, oldImage, newImage);
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public void setEnabled(bool enabled) {
+        if (enabled !is this.enabled) {
+            Boolean oldVal = this.enabled ? Boolean.TRUE : Boolean.FALSE;
+            Boolean newVal = enabled ? Boolean.TRUE : Boolean.FALSE;
+            this.enabled = enabled;
+            firePropertyChange(ENABLED, oldVal, newVal);
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public void setHelpListener(HelpListener listener) {
+        helpListener = listener;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public void setHoverImageDescriptor(ImageDescriptor newImage) {
+        if (hoverImage !is newImage) {
+            ImageDescriptor oldImage = hoverImage;
+            hoverImage = newImage;
+            firePropertyChange(IMAGE, oldImage, newImage);
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IAction.
+     */
+    public void setImageDescriptor(ImageDescriptor newImage) {
+        if (image !is newImage) {
+            ImageDescriptor oldImage = image;
+            image = newImage;
+            firePropertyChange(IMAGE, oldImage, newImage);
+        }
+    }
+
+    /**
+     * Sets the menu creator for this action.
+     * <p>
+     * Note that if this method is called, it overrides the check status.
+     * </p>
+     *
+     * @param creator
+     *            the menu creator, or <code>null</code> if none
+     */
+    public void setMenuCreator(IMenuCreator creator) {
+        // For backward compatibility, if the style is not
+        // set yet, then convert it to a drop down menu.
+        if (value is null) {
+            value = cast(Object)creator;
+            return;
+        }
+
+        if ( cast(IMenuCreator)value ) {
+            value = creator is null ? cast(Object)VAL_DROP_DOWN_MENU : cast(Object)creator;
+        }
+    }
+
+    /**
+     * Sets the text for this action.
+     * <p>
+     * Fires a property change event for the <code>TEXT</code> property if the
+     * text actually changes as a consequence.
+     * </p>
+     * <p>
+     * The accelerator is identified by the last index of a tab character. If
+     * there are no tab characters, then it is identified by the last index of a
+     * '@' character. If neither, then there is no accelerator text. Note that
+     * if you want to insert a '@' character into the text (but no accelerator,
+     * you can simply insert a '@' or a tab at the end of the text.
+     * </p>
+     *
+     * @param text
+     *            the text, or <code>null</code> if none
+     */
+    public void setText(String text) {
+        String oldText = this.text;
+        int oldAccel = this.accelerator;
+        this.text = text;
+        if (text !is null) {
+            String acceleratorText = LegacyActionTools
+                    .extractAcceleratorText(text);
+            if (acceleratorText !is null) {
+                int newAccelerator = LegacyActionTools
+                        .convertLocalizedAccelerator(acceleratorText);
+                // Be sure to not wipe out the accelerator if nothing found
+                if (newAccelerator > 0) {
+                    setAccelerator(newAccelerator);
+                }
+            }
+        }
+        if (!(this.accelerator is oldAccel && (oldText is null ? this.text is null
+                : oldText.equals(this.text)))) {
+            firePropertyChange(TEXT, stringcast(oldText), stringcast(this.text));
+        }
+    }
+
+    /**
+     * Sets the tool tip text for this action.
+     * <p>
+     * Fires a property change event for the <code>TOOL_TIP_TEXT</code>
+     * property if the tool tip text actually changes as a consequence.
+     * </p>
+     *
+     * @param toolTipText
+     *            the tool tip text, or <code>null</code> if none
+     */
+    public void setToolTipText(String toolTipText) {
+        String oldToolTipText = this.toolTipText;
+        if (!(oldToolTipText is null ? toolTipText is null : oldToolTipText
+                .equals(toolTipText))) {
+            this.toolTipText = toolTipText;
+            firePropertyChange(TOOL_TIP_TEXT, stringcast(oldToolTipText), stringcast(toolTipText));
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/ActionContributionItem.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1386 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.ActionContributionItem;
+
+import org.eclipse.jface.action.ContributionItem;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.LegacyActionTools;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuCreator;
+import org.eclipse.jface.action.IContributionManagerOverrides;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.jface.action.ExternalActionManager;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.NotEnabledException;
+import org.eclipse.jface.bindings.Trigger;
+import org.eclipse.jface.bindings.TriggerSequence;
+import org.eclipse.jface.bindings.keys.IKeyLookup;
+import org.eclipse.jface.bindings.keys.KeyLookupFactory;
+import org.eclipse.jface.bindings.keys.KeyStroke;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.jface.resource.ResourceManager;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.Policy;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import java.lang.all;
+import java.util.Set;
+import java.lang.JThread;
+import tango.io.Stdout;
+
+/**
+ * A contribution item which delegates to an action.
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ActionContributionItem : ContributionItem {
+    alias ContributionItem.fill fill;
+
+    /**
+     * Mode bit: Show text on tool items or buttons, even if an image is
+     * present. If this mode bit is not set, text is only shown on tool items if
+     * there is no image present.
+     *
+     * @since 3.0
+     */
+    public static int MODE_FORCE_TEXT = 1;
+
+    /** a string inserted in the middle of text that has been shortened */
+    private static const String ellipsis = "..."; //$NON-NLS-1$
+
+    /**
+     * Stores the result of the action. False when the action returned failure.
+     */
+    private Boolean result = null;
+
+    private static bool USE_COLOR_ICONS = true;
+
+    /**
+     * Returns whether color icons should be used in toolbars.
+     *
+     * @return <code>true</code> if color icons should be used in toolbars,
+     *         <code>false</code> otherwise
+     */
+    public static bool getUseColorIconsInToolbars() {
+        return USE_COLOR_ICONS;
+    }
+
+    /**
+     * Sets whether color icons should be used in toolbars.
+     *
+     * @param useColorIcons
+     *            <code>true</code> if color icons should be used in toolbars,
+     *            <code>false</code> otherwise
+     */
+    public static void setUseColorIconsInToolbars(bool useColorIcons) {
+        USE_COLOR_ICONS = useColorIcons;
+    }
+
+    /**
+     * The presentation mode.
+     */
+    private int mode = 0;
+
+    /**
+     * The action.
+     */
+    private IAction action;
+
+    /**
+     * The listener for changes to the text of the action contributed by an
+     * external source.
+     */
+    private const IPropertyChangeListener actionTextListener;
+
+    /**
+     * Remembers all images in use by this contribution item
+     */
+    private LocalResourceManager imageManager;
+
+    /**
+     * Listener for SWT button widget events.
+     */
+    private Listener buttonListener;
+
+    /**
+     * Listener for SWT menu item widget events.
+     */
+    private Listener menuItemListener;
+
+    /**
+     * Listener for action property change notifications.
+     */
+    private const IPropertyChangeListener propertyListener;
+
+    /**
+     * Listener for SWT tool item widget events.
+     */
+    private Listener toolItemListener;
+
+    /**
+     * The widget created for this item; <code>null</code> before creation and
+     * after disposal.
+     */
+    private Widget widget = null;
+
+    private Listener menuCreatorListener;
+
+    /**
+     * Creates a new contribution item from the given action. The id of the
+     * action is used as the id of the item.
+     *
+     * @param action
+     *            the action
+     */
+    public this(IAction action) {
+        super(action.getId());
+        this.action = action;
+        actionTextListener = new class IPropertyChangeListener {
+            /**
+            * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
+            */
+            public void propertyChange(PropertyChangeEvent event) {
+                update(event.getProperty());
+            }
+        };
+        propertyListener = new class IPropertyChangeListener {
+            public void propertyChange(PropertyChangeEvent event) {
+                actionPropertyChange(event);
+            }
+        };
+    }
+
+    /**
+     * Handles a property change event on the action (forwarded by nested
+     * listener).
+     */
+    private void actionPropertyChange(PropertyChangeEvent e) {
+        // This code should be removed. Avoid using free asyncExec
+
+        if (isVisible() && widget !is null) {
+            Display display = widget.getDisplay();
+            if (display.getThread() is JThread.currentThread().nativeThread()) {
+                update(e.getProperty());
+            } else {
+                display.asyncExec(dgRunnable( (PropertyChangeEvent e_) {
+                    update(e_.getProperty());
+                }, e));
+            }
+
+        }
+    }
+
+    /**
+     * Compares this action contribution item with another object. Two action
+     * contribution items are equal if they refer to the identical Action.
+     */
+    public override int opEquals(Object o) {
+        if (!(cast(ActionContributionItem)o )) {
+            return false;
+        }
+        return (cast(Object)action).opEquals(cast(Object)(cast(ActionContributionItem) o).action);
+    }
+
+    /**
+     * The <code>ActionContributionItem</code> implementation of this
+     * <code>IContributionItem</code> method creates an SWT
+     * <code>Button</code> for the action using the action's style. If the
+     * action's checked property has been set, the button is created and primed
+     * to the value of the checked property.
+     */
+    public override void fill(Composite parent) {
+        if (widget is null && parent !is null) {
+            int flags = SWT.PUSH;
+            if (action !is null) {
+                if (action.getStyle() is IAction.AS_CHECK_BOX) {
+                    flags = SWT.TOGGLE;
+                }
+                if (action.getStyle() is IAction.AS_RADIO_BUTTON) {
+                    flags = SWT.RADIO;
+                }
+            }
+
+            Button b = new Button(parent, flags);
+            b.setData(this);
+            b.addListener(SWT.Dispose, getButtonListener());
+            // Don't hook a dispose listener on the parent
+            b.addListener(SWT.Selection, getButtonListener());
+            if (action.getHelpListener() !is null) {
+                b.addHelpListener(action.getHelpListener());
+            }
+            widget = b;
+
+            update(null);
+
+            // Attach some extra listeners.
+            action.addPropertyChangeListener(propertyListener);
+            if (action !is null) {
+                String commandId = action.getActionDefinitionId();
+                ExternalActionManager.ICallback callback = ExternalActionManager
+                        .getInstance().getCallback();
+
+                if ((callback !is null) && (commandId !is null)) {
+                    callback.addPropertyChangeListener(commandId,
+                            actionTextListener);
+                }
+            }
+        }
+    }
+
+    /**
+     * The <code>ActionContributionItem</code> implementation of this
+     * <code>IContributionItem</code> method creates an SWT
+     * <code>MenuItem</code> for the action using the action's style. If the
+     * action's checked property has been set, a button is created and primed to
+     * the value of the checked property. If the action's menu creator property
+     * has been set, a cascading submenu is created.
+     */
+    public override void fill(Menu parent, int index) {
+        if (widget is null && parent !is null) {
+            int flags = SWT.PUSH;
+            if (action !is null) {
+                int style = action.getStyle();
+                if (style is IAction.AS_CHECK_BOX) {
+                    flags = SWT.CHECK;
+                } else if (style is IAction.AS_RADIO_BUTTON) {
+                    flags = SWT.RADIO;
+                } else if (style is IAction.AS_DROP_DOWN_MENU) {
+                    flags = SWT.CASCADE;
+                }
+            }
+
+            MenuItem mi = null;
+            if (index >= 0) {
+                mi = new MenuItem(parent, flags, index);
+            } else {
+                mi = new MenuItem(parent, flags);
+            }
+            widget = mi;
+
+            mi.setData(this);
+            mi.addListener(SWT.Dispose, getMenuItemListener());
+            mi.addListener(SWT.Selection, getMenuItemListener());
+            if (action.getHelpListener() !is null) {
+                mi.addHelpListener(action.getHelpListener());
+            }
+
+            if (flags is SWT.CASCADE) {
+                // just create a proxy for now, if the user shows it then
+                // fill it in
+                Menu subMenu = new Menu(parent);
+                subMenu.addListener(SWT.Show, getMenuCreatorListener());
+                subMenu.addListener(SWT.Hide, getMenuCreatorListener());
+                mi.setMenu(subMenu);
+            }
+
+            update(null);
+
+            // Attach some extra listeners.
+            action.addPropertyChangeListener(propertyListener);
+            if (action !is null) {
+                String commandId = action.getActionDefinitionId();
+                ExternalActionManager.ICallback callback = ExternalActionManager
+                        .getInstance().getCallback();
+
+                if ((callback !is null) && (commandId !is null)) {
+                    callback.addPropertyChangeListener(commandId,
+                            actionTextListener);
+                }
+            }
+        }
+    }
+
+    /**
+     * The <code>ActionContributionItem</code> implementation of this ,
+     * <code>IContributionItem</code> method creates an SWT
+     * <code>ToolItem</code> for the action using the action's style. If the
+     * action's checked property has been set, a button is created and primed to
+     * the value of the checked property. If the action's menu creator property
+     * has been set, a drop-down tool item is created.
+     */
+    public override void fill(ToolBar parent, int index) {
+        if (widget is null && parent !is null) {
+            int flags = SWT.PUSH;
+            if (action !is null) {
+                int style = action.getStyle();
+                if (style is IAction.AS_CHECK_BOX) {
+                    flags = SWT.CHECK;
+                } else if (style is IAction.AS_RADIO_BUTTON) {
+                    flags = SWT.RADIO;
+                } else if (style is IAction.AS_DROP_DOWN_MENU) {
+                    flags = SWT.DROP_DOWN;
+                }
+            }
+
+            ToolItem ti = null;
+            if (index >= 0) {
+                ti = new ToolItem(parent, flags, index);
+            } else {
+                ti = new ToolItem(parent, flags);
+            }
+            ti.setData(this);
+            ti.addListener(SWT.Selection, getToolItemListener());
+            ti.addListener(SWT.Dispose, getToolItemListener());
+
+            widget = ti;
+
+            update(null);
+
+            // Attach some extra listeners.
+            action.addPropertyChangeListener(propertyListener);
+            if (action !is null) {
+                String commandId = action.getActionDefinitionId();
+                ExternalActionManager.ICallback callback = ExternalActionManager
+                        .getInstance().getCallback();
+
+                if ((callback !is null) && (commandId !is null)) {
+                    callback.addPropertyChangeListener(commandId,
+                            actionTextListener);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the action associated with this contribution item.
+     *
+     * @return the action
+     */
+    public IAction getAction() {
+        return action;
+    }
+
+    /**
+     * Returns the listener for SWT button widget events.
+     *
+     * @return a listener for button events
+     */
+    private Listener getButtonListener() {
+        if (buttonListener is null) {
+            buttonListener = new class Listener {
+                public void handleEvent(Event event) {
+                    switch (event.type) {
+                    case SWT.Dispose:
+                        handleWidgetDispose(event);
+                        break;
+                    case SWT.Selection:
+                        Widget ew = event.widget;
+                        if (ew !is null) {
+                            handleWidgetSelection(event, (cast(Button) ew)
+                                    .getSelection());
+                        }
+                        break;
+                    default:
+                    }
+                }
+            };
+        }
+        return buttonListener;
+    }
+
+    /**
+     * Returns the listener for SWT menu item widget events.
+     *
+     * @return a listener for menu item events
+     */
+    private Listener getMenuItemListener() {
+        if (menuItemListener is null) {
+            menuItemListener = new class Listener {
+                public void handleEvent(Event event) {
+                    switch (event.type) {
+                    case SWT.Dispose:
+                        handleWidgetDispose(event);
+                        break;
+                    case SWT.Selection:
+                        Widget ew = event.widget;
+                        if (ew !is null) {
+                            handleWidgetSelection(event, (cast(MenuItem) ew)
+                                    .getSelection());
+                        }
+                        break;
+                    default:
+                    }
+                }
+            };
+        }
+        return menuItemListener;
+    }
+
+    /**
+     * Returns the presentation mode, which is the bitwise-or of the
+     * <code>MODE_*</code> constants. The default mode setting is 0, meaning
+     * that for menu items, both text and image are shown (if present), but for
+     * tool items, the text is shown only if there is no image.
+     *
+     * @return the presentation mode settings
+     *
+     * @since 3.0
+     */
+    public int getMode() {
+        return mode;
+    }
+
+    /**
+     * Returns the listener for SWT tool item widget events.
+     *
+     * @return a listener for tool item events
+     */
+    private Listener getToolItemListener() {
+        if (toolItemListener is null) {
+            toolItemListener = new class Listener {
+                public void handleEvent(Event event) {
+                    switch (event.type) {
+                    case SWT.Dispose:
+                        handleWidgetDispose(event);
+                        break;
+                    case SWT.Selection:
+                        Widget ew = event.widget;
+                        if (ew !is null) {
+                            handleWidgetSelection(event, (cast(ToolItem) ew)
+                                    .getSelection());
+                        }
+                        break;
+                    default:
+                    }
+                }
+            };
+        }
+        return toolItemListener;
+    }
+
+    /**
+     * Handles a widget dispose event for the widget corresponding to this item.
+     */
+    private void handleWidgetDispose(Event e) {
+        // Check if our widget is the one being disposed.
+        if (e.widget is widget) {
+            // Dispose of the menu creator.
+            if (action.getStyle() is IAction.AS_DROP_DOWN_MENU
+                    && menuCreatorCalled) {
+                IMenuCreator mc = action.getMenuCreator();
+                if (mc !is null) {
+                    mc.dispose();
+                }
+            }
+
+            // Unhook all of the listeners.
+            action.removePropertyChangeListener(propertyListener);
+            if (action !is null) {
+                String commandId = action.getActionDefinitionId();
+                ExternalActionManager.ICallback callback = ExternalActionManager
+                        .getInstance().getCallback();
+
+                if ((callback !is null) && (commandId !is null)) {
+                    callback.removePropertyChangeListener(commandId,
+                            actionTextListener);
+                }
+            }
+
+            // Clear the widget field.
+            widget = null;
+
+            disposeOldImages();
+        }
+    }
+
+    /**
+     * Handles a widget selection event.
+     */
+    private void handleWidgetSelection(Event e, bool selection) {
+
+        Widget item = e.widget;
+        if (item !is null) {
+            int style = item.getStyle();
+
+            if ((style & (SWT.TOGGLE | SWT.CHECK)) !is 0) {
+                if (action.getStyle() is IAction.AS_CHECK_BOX) {
+                    action.setChecked(selection);
+                }
+            } else if ((style & SWT.RADIO) !is 0) {
+                if (action.getStyle() is IAction.AS_RADIO_BUTTON) {
+                    action.setChecked(selection);
+                }
+            } else if ((style & SWT.DROP_DOWN) !is 0) {
+                if (e.detail is 4) { // on drop-down button
+                    if (action.getStyle() is IAction.AS_DROP_DOWN_MENU) {
+                        IMenuCreator mc = action.getMenuCreator();
+                        menuCreatorCalled = true;
+                        ToolItem ti = cast(ToolItem) item;
+                        // we create the menu as a sub-menu of "dummy" so that
+                        // we can use
+                        // it in a cascading menu too.
+                        // If created on a SWT control we would get an SWT
+                        // error...
+                        // Menu dummy= new Menu(ti.getParent());
+                        // Menu m= mc.getMenu(dummy);
+                        // dummy.dispose();
+                        if (mc !is null) {
+                            Menu m = mc.getMenu(ti.getParent());
+                            if (m !is null) {
+                                // position the menu below the drop down item
+                                Point point = ti.getParent().toDisplay(
+                                        new Point(e.x, e.y));
+                                m.setLocation(point.x, point.y); // waiting
+                                                                    // for SWT
+                                // 0.42
+                                m.setVisible(true);
+                                return; // we don't fire the action
+                            }
+                        }
+                    }
+                }
+            }
+
+            ExternalActionManager.IExecuteCallback callback = null;
+            String actionDefinitionId = action.getActionDefinitionId();
+            if (actionDefinitionId !is null) {
+                Object obj = cast(Object) ExternalActionManager.getInstance()
+                        .getCallback();
+                if (null !is cast(ExternalActionManager.IExecuteCallback)obj ) {
+                    callback = cast(ExternalActionManager.IExecuteCallback) obj;
+                }
+            }
+
+            // Ensure action is enabled first.
+            // See 1GAN3M6: ITPUI:WINNT - Any IAction in the workbench can be
+            // executed while disabled.
+            if (action.isEnabled()) {
+                bool trace = Policy.TRACE_ACTIONS;
+
+                long ms = 0L;
+                if (trace) {
+                    ms = System.currentTimeMillis();
+                    Stdout.formatln("Running action: {}", action.getText()); //$NON-NLS-1$
+                }
+
+                IPropertyChangeListener resultListener = null;
+                if (callback !is null) {
+                    resultListener = new class IPropertyChangeListener {
+                        public void propertyChange(PropertyChangeEvent event) {
+                            // Check on result
+                            if (event.getProperty().equals(IAction.RESULT)) {
+                                if (null !is cast(Boolean)event.getNewValue() ) {
+                                    result = cast(Boolean) event.getNewValue();
+                                }
+                            }
+                        }
+                    };
+                    action.addPropertyChangeListener(resultListener);
+                    callback.preExecute(action, e);
+                }
+
+                action.runWithEvent(e);
+
+                if (callback !is null) {
+                    if (result is null || result.opEquals(Boolean.TRUE)) {
+                        callback.postExecuteSuccess(action, Boolean.TRUE);
+                    } else {
+                        callback.postExecuteFailure(action,
+                                new ExecutionException(action.getText()
+                                        ~ " returned failure.")); //$NON-NLS-1$
+                    }
+                }
+
+                if (resultListener !is null) {
+                    result = null;
+                    action.removePropertyChangeListener(resultListener);
+                }
+                if (trace) {
+                    Stdout.formatln("{} ms to run action: {}",(System.currentTimeMillis() - ms), action.getText()); //$NON-NLS-1$
+                }
+            } else {
+                if (callback !is null) {
+                    callback.notEnabled(action, new NotEnabledException(action
+                            .getText()
+                            ~ " is not enabled.")); //$NON-NLS-1$
+                }
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Object.
+     */
+    public override hash_t toHash() {
+        return (cast(Object)action).toHash();
+    }
+
+    /**
+     * Returns whether the given action has any images.
+     *
+     * @param actionToCheck
+     *            the action
+     * @return <code>true</code> if the action has any images,
+     *         <code>false</code> if not
+     */
+    private bool hasImages(IAction actionToCheck) {
+        return actionToCheck.getImageDescriptor() !is null
+                || actionToCheck.getHoverImageDescriptor() !is null
+                || actionToCheck.getDisabledImageDescriptor() !is null;
+    }
+
+    /**
+     * Returns whether the command corresponding to this action is active.
+     */
+    private bool isCommandActive() {
+        IAction actionToCheck = getAction();
+
+        if (actionToCheck !is null) {
+            String commandId = actionToCheck.getActionDefinitionId();
+            ExternalActionManager.ICallback callback = ExternalActionManager
+                    .getInstance().getCallback();
+
+            if (callback !is null) {
+                return callback.isActive(commandId);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * The action item implementation of this <code>IContributionItem</code>
+     * method returns <code>true</code> for menu items and <code>false</code>
+     * for everything else.
+     */
+    public override bool isDynamic() {
+        if (cast(MenuItem)widget ) {
+            // Optimization. Only recreate the item is the check or radio style
+            // has changed.
+            bool itemIsCheck = (widget.getStyle() & SWT.CHECK) !is 0;
+            bool actionIsCheck = getAction() !is null
+                    && getAction().getStyle() is IAction.AS_CHECK_BOX;
+            bool itemIsRadio = (widget.getStyle() & SWT.RADIO) !is 0;
+            bool actionIsRadio = getAction() !is null
+                    && getAction().getStyle() is IAction.AS_RADIO_BUTTON;
+            return (itemIsCheck !is actionIsCheck)
+                    || (itemIsRadio !is actionIsRadio);
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionItem.
+     */
+    public override bool isEnabled() {
+        return action !is null && action.isEnabled();
+    }
+
+    /**
+     * Returns <code>true</code> if this item is allowed to enable,
+     * <code>false</code> otherwise.
+     *
+     * @return if this item is allowed to be enabled
+     * @since 2.0
+     */
+    protected bool isEnabledAllowed() {
+        if (getParent() is null) {
+            return true;
+        }
+        auto value = getParent().getOverrides().getEnabled(this);
+        return (value is null) ? true : value.value;
+    }
+
+    /**
+     * The <code>ActionContributionItem</code> implementation of this
+     * <code>ContributionItem</code> method extends the super implementation
+     * by also checking whether the command corresponding to this action is
+     * active.
+     */
+    public override bool isVisible() {
+        return super.isVisible() && isCommandActive();
+    }
+
+    /**
+     * Sets the presentation mode, which is the bitwise-or of the
+     * <code>MODE_*</code> constants.
+     *
+     * @param mode
+     *            the presentation mode settings
+     *
+     * @since 3.0
+     */
+    public void setMode(int mode) {
+        this.mode = mode;
+        update();
+    }
+
+    /**
+     * The action item implementation of this <code>IContributionItem</code>
+     * method calls <code>update(null)</code>.
+     */
+    public override final void update() {
+        update(null);
+    }
+
+    /**
+     * Synchronizes the UI with the given property.
+     *
+     * @param propertyName
+     *            the name of the property, or <code>null</code> meaning all
+     *            applicable properties
+     */
+    public override void update(String propertyName) {
+        if (widget !is null) {
+            // determine what to do
+            bool textChanged = propertyName is null
+                    || propertyName.equals(IAction.TEXT);
+            bool imageChanged = propertyName is null
+                    || propertyName.equals(IAction.IMAGE);
+            bool tooltipTextChanged = propertyName is null
+                    || propertyName.equals(IAction.TOOL_TIP_TEXT);
+            bool enableStateChanged = propertyName is null
+                    || propertyName.equals(IAction.ENABLED)
+                    || propertyName
+                            .equals(IContributionManagerOverrides.P_ENABLED);
+            bool checkChanged = (action.getStyle() is IAction.AS_CHECK_BOX || action
+                    .getStyle() is IAction.AS_RADIO_BUTTON)
+                    && (propertyName is null || propertyName
+                            .equals(IAction.CHECKED));
+
+            if (cast(ToolItem)widget ) {
+                ToolItem ti = cast(ToolItem) widget;
+                String text = action.getText();
+                // the set text is shown only if there is no image or if forced
+                // by MODE_FORCE_TEXT
+                bool showText = text !is null
+                        && ((getMode() & MODE_FORCE_TEXT) !is 0 || !hasImages(action));
+
+                // only do the trimming if the text will be used
+                if (showText && text !is null) {
+                    text = Action.removeAcceleratorText(text);
+                    text = Action.removeMnemonics(text);
+                }
+
+                if (textChanged) {
+                    String textToSet = showText ? text : ""; //$NON-NLS-1$
+                    bool rightStyle = (ti.getParent().getStyle() & SWT.RIGHT) !is 0;
+                    if (rightStyle || !ti.getText().equals(textToSet)) {
+                        // In addition to being required to update the text if
+                        // it
+                        // gets nulled out in the action, this is also a
+                        // workaround
+                        // for bug 50151: Using SWT.RIGHT on a ToolBar leaves
+                        // blank space
+                        ti.setText(textToSet);
+                    }
+                }
+
+                if (imageChanged) {
+                    // only substitute a missing image if it has no text
+                    updateImages(!showText);
+                }
+
+                if (tooltipTextChanged || textChanged) {
+                    String toolTip = action.getToolTipText();
+                    if ((toolTip is null) || (toolTip.length is 0)) {
+                        toolTip = text;
+                    }
+
+                    ExternalActionManager.ICallback callback = ExternalActionManager
+                            .getInstance().getCallback();
+                    String commandId = action.getActionDefinitionId();
+                    if ((callback !is null) && (commandId !is null)
+                            && (toolTip !is null)) {
+                        String acceleratorText = callback
+                                .getAcceleratorText(commandId);
+                        if (acceleratorText !is null
+                                && acceleratorText.length !is 0) {
+                            toolTip = JFaceResources.format(
+                                    "Toolbar_Tooltip_Accelerator", //$NON-NLS-1$
+                                    [ toolTip, acceleratorText ]);
+                        }
+                    }
+
+                    // if the text is showing, then only set the tooltip if
+                    // different
+                    if (!showText || toolTip !is null && !toolTip.equals(text)) {
+                        ti.setToolTipText(toolTip);
+                    } else {
+                        ti.setToolTipText(null);
+                    }
+                }
+
+                if (enableStateChanged) {
+                    bool shouldBeEnabled = action.isEnabled()
+                            && isEnabledAllowed();
+
+                    if (ti.getEnabled() !is shouldBeEnabled) {
+                        ti.setEnabled(shouldBeEnabled);
+                    }
+                }
+
+                if (checkChanged) {
+                    bool bv = action.isChecked();
+
+                    if (ti.getSelection() !is bv) {
+                        ti.setSelection(bv);
+                    }
+                }
+                return;
+            }
+
+            if (cast(MenuItem)widget ) {
+                MenuItem mi = cast(MenuItem) widget;
+
+                if (textChanged) {
+                    int accelerator = 0;
+                    String acceleratorText = null;
+                    IAction updatedAction = getAction();
+                    String text = null;
+                    accelerator = updatedAction.getAccelerator();
+                    ExternalActionManager.ICallback callback = ExternalActionManager
+                            .getInstance().getCallback();
+
+                    // Block accelerators that are already in use.
+                    if ((accelerator !is 0) && (callback !is null)
+                            && (callback.isAcceleratorInUse(accelerator))) {
+                        accelerator = 0;
+                    }
+
+                    /*
+                     * Process accelerators on GTK in a special way to avoid Bug
+                     * 42009. We will override the native input method by
+                     * allowing these reserved accelerators to be placed on the
+                     * menu. We will only do this for "Ctrl+Shift+[0-9A-FU]".
+                     */
+                    String commandId = updatedAction
+                            .getActionDefinitionId();
+                    if (("gtk".equals(SWT.getPlatform())) && (cast(ExternalActionManager.IBindingManagerCallback)callback ) //$NON-NLS-1$
+                            && (commandId !is null)) {
+                        ExternalActionManager.IBindingManagerCallback bindingManagerCallback = cast(ExternalActionManager.IBindingManagerCallback) callback;
+                        IKeyLookup lookup = KeyLookupFactory.getDefault();
+                        TriggerSequence[] triggerSequences = bindingManagerCallback
+                                .getActiveBindingsFor(commandId);
+                        for (int i = 0; i < triggerSequences.length; i++) {
+                            TriggerSequence triggerSequence = triggerSequences[i];
+                            Trigger[] triggers = triggerSequence
+                                    .getTriggers();
+                            if (triggers.length is 1) {
+                                Trigger trigger = triggers[0];
+                                if (cast(KeyStroke)trigger ) {
+                                    KeyStroke currentKeyStroke = cast(KeyStroke) trigger;
+                                    int currentNaturalKey = currentKeyStroke
+                                            .getNaturalKey();
+                                    if ((currentKeyStroke.getModifierKeys() is (lookup
+                                            .getCtrl() | lookup.getShift()))
+                                            && ((currentNaturalKey >= '0' && currentNaturalKey <= '9')
+                                                    || (currentNaturalKey >= 'A' && currentNaturalKey <= 'F') || (currentNaturalKey is 'U'))) {
+                                        accelerator = currentKeyStroke
+                                                .getModifierKeys()
+                                                | currentNaturalKey;
+                                        acceleratorText = triggerSequence
+                                                .format();
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                    if (accelerator is 0) {
+                        if ((callback !is null) && (commandId !is null)) {
+                            acceleratorText = callback
+                                    .getAcceleratorText(commandId);
+                        }
+                    }
+
+                    IContributionManagerOverrides overrides = null;
+
+                    if (getParent() !is null) {
+                        overrides = getParent().getOverrides();
+                    }
+
+                    if (overrides !is null) {
+                        text = getParent().getOverrides().getText(this);
+                    }
+
+                    mi.setAccelerator(accelerator);
+
+                    if (text is null) {
+                        text = updatedAction.getText();
+                    }
+
+                    if (text !is null && acceleratorText is null) {
+                        // use extracted accelerator text in case accelerator
+                        // cannot be fully represented in one int (e.g.
+                        // multi-stroke keys)
+                        acceleratorText = LegacyActionTools
+                                .extractAcceleratorText(text);
+                        if (acceleratorText is null && accelerator !is 0) {
+                            acceleratorText = Action
+                                    .convertAccelerator(accelerator);
+                        }
+                    }
+
+                    if (text is null) {
+                        text = ""; //$NON-NLS-1$
+                    } else {
+                        text = Action.removeAcceleratorText(text);
+                    }
+
+                    if (acceleratorText is null) {
+                        mi.setText(text);
+                    } else {
+                        mi.setText(text ~ '\t' ~ acceleratorText);
+                    }
+                }
+
+                if (imageChanged) {
+                    updateImages(false);
+                }
+
+                if (enableStateChanged) {
+                    bool shouldBeEnabled = action.isEnabled()
+                            && isEnabledAllowed();
+
+                    if (mi.getEnabled() !is shouldBeEnabled) {
+                        mi.setEnabled(shouldBeEnabled);
+                    }
+                }
+
+                if (checkChanged) {
+                    bool bv = action.isChecked();
+
+                    if (mi.getSelection() !is bv) {
+                        mi.setSelection(bv);
+                    }
+                }
+
+                return;
+            }
+
+            if (cast(Button)widget ) {
+                Button button = cast(Button) widget;
+
+                if (imageChanged) {
+                    updateImages(false);
+                }
+
+                if (textChanged) {
+                    String text = action.getText();
+                    bool showText = text !is null && ((getMode() & MODE_FORCE_TEXT) !is 0 || !hasImages(action));
+                    // only do the trimming if the text will be used
+                    if (showText) {
+                        text = Action.removeAcceleratorText(text);
+                    }
+                    String textToSet = showText ? text : ""; //$NON-NLS-1$
+                    button.setText(textToSet);
+                }
+
+                if (tooltipTextChanged) {
+                    button.setToolTipText(action.getToolTipText());
+                }
+
+                if (enableStateChanged) {
+                    bool shouldBeEnabled = action.isEnabled()
+                            && isEnabledAllowed();
+
+                    if (button.getEnabled() !is shouldBeEnabled) {
+                        button.setEnabled(shouldBeEnabled);
+                    }
+                }
+
+                if (checkChanged) {
+                    bool bv = action.isChecked();
+
+                    if (button.getSelection() !is bv) {
+                        button.setSelection(bv);
+                    }
+                }
+                return;
+            }
+        }
+    }
+
+    /**
+     * Updates the images for this action.
+     *
+     * @param forceImage
+     *            <code>true</code> if some form of image is compulsory, and
+     *            <code>false</code> if it is acceptable for this item to have
+     *            no image
+     * @return <code>true</code> if there are images for this action,
+     *         <code>false</code> if not
+     */
+    private bool updateImages(bool forceImage) {
+
+        ResourceManager parentResourceManager = JFaceResources.getResources();
+
+        if (cast(ToolItem)widget ) {
+            if (USE_COLOR_ICONS) {
+                ImageDescriptor image = action.getHoverImageDescriptor();
+                if (image is null) {
+                    image = action.getImageDescriptor();
+                }
+                ImageDescriptor disabledImage = action
+                        .getDisabledImageDescriptor();
+
+                // Make sure there is a valid image.
+                if (image is null && forceImage) {
+                    image = ImageDescriptor.getMissingImageDescriptor();
+                }
+
+                LocalResourceManager localManager = new LocalResourceManager(
+                        parentResourceManager);
+
+                // performance: more efficient in SWT to set disabled and hot
+                // image before regular image
+                (cast(ToolItem) widget)
+                        .setDisabledImage(disabledImage is null ? null
+                                : localManager
+                                        .createImageWithDefault(disabledImage));
+                (cast(ToolItem) widget).setImage(image is null ? null
+                        : localManager.createImageWithDefault(image));
+
+                disposeOldImages();
+                imageManager = localManager;
+
+                return image !is null;
+            }
+            ImageDescriptor image = action.getImageDescriptor();
+            ImageDescriptor hoverImage = action.getHoverImageDescriptor();
+            ImageDescriptor disabledImage = action.getDisabledImageDescriptor();
+
+            // If there is no regular image, but there is a hover image,
+            // convert the hover image to gray and use it as the regular image.
+            if (image is null && hoverImage !is null) {
+                image = ImageDescriptor.createWithFlags(action
+                        .getHoverImageDescriptor(), SWT.IMAGE_GRAY);
+            } else {
+                // If there is no hover image, use the regular image as the
+                // hover image,
+                // and convert the regular image to gray
+                if (hoverImage is null && image !is null) {
+                    hoverImage = image;
+                    image = ImageDescriptor.createWithFlags(action
+                            .getImageDescriptor(), SWT.IMAGE_GRAY);
+                }
+            }
+
+            // Make sure there is a valid image.
+            if (hoverImage is null && image is null && forceImage) {
+                image = ImageDescriptor.getMissingImageDescriptor();
+            }
+
+            // Create a local resource manager to remember the images we've
+            // allocated for this tool item
+            LocalResourceManager localManager = new LocalResourceManager(
+                    parentResourceManager);
+
+            // performance: more efficient in SWT to set disabled and hot image
+            // before regular image
+            (cast(ToolItem) widget).setDisabledImage(disabledImage is null ? null
+                    : localManager.createImageWithDefault(disabledImage));
+            (cast(ToolItem) widget).setHotImage(hoverImage is null ? null
+                    : localManager.createImageWithDefault(hoverImage));
+            (cast(ToolItem) widget).setImage(image is null ? null : localManager
+                    .createImageWithDefault(image));
+
+            // Now that we're no longer referencing the old images, clear them
+            // out.
+            disposeOldImages();
+            imageManager = localManager;
+
+            return image !is null;
+        } else if (cast(Item)widget  || cast(Button)widget ) {
+
+            // Use hover image if there is one, otherwise use regular image.
+            ImageDescriptor image = action.getHoverImageDescriptor();
+            if (image is null) {
+                image = action.getImageDescriptor();
+            }
+            // Make sure there is a valid image.
+            if (image is null && forceImage) {
+                image = ImageDescriptor.getMissingImageDescriptor();
+            }
+
+            // Create a local resource manager to remember the images we've
+            // allocated for this widget
+            LocalResourceManager localManager = new LocalResourceManager(
+                    parentResourceManager);
+
+            if (cast(Item)widget) {
+                (cast(Item) widget).setImage(image is null ? null : localManager
+                        .createImageWithDefault(image));
+            } else if (cast(Button)widget) {
+                (cast(Button) widget).setImage(image is null ? null : localManager
+                        .createImageWithDefault(image));
+            }
+
+            // Now that we're no longer referencing the old images, clear them
+            // out.
+            disposeOldImages();
+            imageManager = localManager;
+
+            return image !is null;
+        }
+        return false;
+    }
+
+    /**
+     * Dispose any images allocated for this contribution item
+     */
+    private void disposeOldImages() {
+        if (imageManager !is null) {
+            imageManager.dispose();
+            imageManager = null;
+        }
+    }
+
+    /**
+     * Shorten the given text <code>t</code> so that its length doesn't exceed
+     * the width of the given ToolItem.The default implementation replaces
+     * characters in the center of the original string with an ellipsis ("...").
+     * Override if you need a different strategy.
+     *
+     * @param textValue
+     *            the text to shorten
+     * @param item
+     *            the tool item the text belongs to
+     * @return the shortened string
+     *
+     */
+    protected String shortenText(String textValue, ToolItem item) {
+        if (textValue is null) {
+            return null;
+        }
+
+        GC gc = new GC(item.getParent());
+
+        int maxWidth = item.getImage().getBounds().width * 4;
+
+        if (gc.textExtent(textValue).x < maxWidth) {
+            gc.dispose();
+            return textValue;
+        }
+
+        for (int i = textValue.length; i > 0; i--) {
+            String test = textValue.substring(0, i);
+            test = test ~ ellipsis;
+            if (gc.textExtent(test).x < maxWidth) {
+                gc.dispose();
+                return test;
+            }
+
+        }
+        gc.dispose();
+        // If for some reason we fall through abort
+        return textValue;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.action.ContributionItem#dispose()
+     */
+    public void dispose() {
+        if (widget !is null) {
+            widget.dispose();
+            widget = null;
+        }
+        holdMenu = null;
+    }
+
+    /**
+     * Handle show and hide on the proxy menu for IAction.AS_DROP_DOWN_MENU
+     * actions.
+     *
+     * @return the appropriate listener
+     * @since 3.4
+     */
+    private Listener getMenuCreatorListener() {
+        if (menuCreatorListener is null) {
+            menuCreatorListener = new class Listener {
+                public void handleEvent(Event event) {
+                    switch (event.type) {
+                    case SWT.Show:
+                        handleShowProxy(cast(Menu) event.widget);
+                        break;
+                    case SWT.Hide:
+                        handleHideProxy(cast(Menu) event.widget);
+                        break;
+                    default:
+                    }
+                }
+            };
+        }
+        return menuCreatorListener;
+    }
+
+    /**
+     * This is the easiest way to hold the menu until we can swap it in to the
+     * proxy.
+     */
+    private Menu holdMenu = null;
+
+    private bool menuCreatorCalled = false;
+
+    /**
+     * The proxy menu is being shown, we better get the real menu.
+     *
+     * @param proxy
+     *            the proxy menu
+     * @since 3.4
+     */
+    private void handleShowProxy(Menu proxy) {
+        proxy.removeListener(SWT.Show, getMenuCreatorListener());
+        IMenuCreator mc = action.getMenuCreator();
+        menuCreatorCalled  = true;
+        if (mc is null) {
+            return;
+        }
+        holdMenu = mc.getMenu(proxy.getParentMenu());
+        if (holdMenu is null) {
+            return;
+        }
+        copyMenu(holdMenu, proxy);
+    }
+
+    /**
+     * Create MenuItems in the proxy menu that can execute the real menu items
+     * if selected. Create proxy menus for any real item submenus.
+     *
+     * @param realMenu
+     *            the real menu to copy from
+     * @param proxy
+     *            the proxy menu to populate
+     * @since 3.4
+     */
+    private void copyMenu(Menu realMenu, Menu proxy) {
+        if (realMenu.isDisposed() || proxy.isDisposed()) {
+            return;
+        }
+
+        // we notify the real menu so it can populate itself if it was
+        // listening for SWT.Show
+        realMenu.notifyListeners(SWT.Show, null);
+
+        final Listener passThrough = new class Listener {
+            public void handleEvent(Event event) {
+                if (!event.widget.isDisposed()) {
+                    Widget realItem = cast(Widget) event.widget.getData();
+                    if (!realItem.isDisposed()) {
+                        int style = event.widget.getStyle();
+                        if (event.type is SWT.Selection
+                                && ((style & (SWT.TOGGLE | SWT.CHECK)) !is 0)
+                                && (null !is cast(MenuItem)realItem )) {
+                            (cast(MenuItem) realItem)
+                                    .setSelection((cast(MenuItem) event.widget)
+                                            .getSelection());
+                        }
+                        event.widget = realItem;
+                        realItem.notifyListeners(event.type, event);
+                    }
+                }
+            }
+        };
+
+        MenuItem[] items = realMenu.getItems();
+        for (int i = 0; i < items.length; i++) {
+            final MenuItem realItem = items[i];
+            final MenuItem proxyItem = new MenuItem(proxy, realItem.getStyle());
+            proxyItem.setData(realItem);
+            proxyItem.setAccelerator(realItem.getAccelerator());
+            proxyItem.setEnabled(realItem.getEnabled());
+            proxyItem.setImage(realItem.getImage());
+            proxyItem.setSelection(realItem.getSelection());
+            proxyItem.setText(realItem.getText());
+
+            // pass through any events
+            proxyItem.addListener(SWT.Selection, passThrough);
+            proxyItem.addListener(SWT.Arm, passThrough);
+            proxyItem.addListener(SWT.Help, passThrough);
+
+            final Menu itemMenu = realItem.getMenu();
+            if (itemMenu !is null) {
+                // create a proxy for any sub menu items
+                final Menu subMenu = new Menu(proxy);
+                subMenu.setData(itemMenu);
+                proxyItem.setMenu(subMenu);
+                subMenu.addListener(SWT.Show, new class(subMenu, itemMenu) Listener {
+                    Menu subMenu_;
+                    Menu itemMenu_;
+                    this(Menu a,Menu b){
+                        subMenu_=a;
+                        itemMenu_=b;
+                    }
+                    void handleEvent(Event event){
+                        event.widget.removeListener(SWT.Show, this);
+                        if (event.type is SWT.Show) {
+                            copyMenu(itemMenu_, subMenu_);
+                        }
+                    }
+                });
+                subMenu.addListener(SWT.Help, passThrough);
+                subMenu.addListener(SWT.Hide, passThrough);
+            }
+        }
+    }
+
+    /**
+     * The proxy menu is being hidden, so we need to make it go away.
+     *
+     * @param proxy
+     *            the proxy menu
+     * @since 3.4
+     */
+    private void handleHideProxy(Menu proxy) {
+        proxy.removeListener(SWT.Hide, getMenuCreatorListener());
+        proxy.getDisplay().asyncExec(dgRunnable( (Menu proxy_) {
+                if (!proxy_.isDisposed()) {
+                    MenuItem parentItem = proxy_.getParentItem();
+                    proxy_.dispose();
+                    parentItem.setMenu(holdMenu);
+                }
+                if (holdMenu !is null && !holdMenu.isDisposed()) {
+                    holdMenu.notifyListeners(SWT.Hide, null);
+                }
+                holdMenu = null;
+        }, proxy ));
+    }
+
+    /**
+     * Return the widget associated with this contribution item. It should not
+     * be cached, as it can be disposed and re-created by its containing
+     * ContributionManager, which controls all of the widgets lifecycle methods.
+     * <p>
+     * This can be used to set layout data on the widget if appropriate. The
+     * actual type of the widget can be any valid control for this
+     * ContributionItem's current ContributionManager.
+     * </p>
+     *
+     * @return the widget, or <code>null</code> depending on the lifecycle.
+     * @since 3.4
+     */
+    public Widget getWidget() {
+        return widget;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/ContributionItem.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.ContributionItem;
+
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.IContributionManager;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.CoolBar;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.ToolBar;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * An abstract base implementation for contribution items.
+ */
+public abstract class ContributionItem : IContributionItem {
+
+    /**
+     * The identifier for this contribution item, of <code>null</code> if none.
+     */
+    private String id = null;
+
+    /**
+     * Indicates this item is visible in its manager; <code>true</code>
+     * by default.
+     */
+    private bool visible = true;
+
+    /**
+     * The parent contribution manager for this item
+     */
+    private IContributionManager parent;
+
+    /**
+     * Creates a contribution item with a <code>null</code> id.
+     * Calls <code>this(String)</code> with <code>null</code>.
+     */
+    protected this() {
+        this(null);
+    }
+
+    /**
+     * Creates a contribution item with the given (optional) id.
+     * The given id is used to find items in a contribution manager,
+     * and for positioning items relative to other items.
+     *
+     * @param id the contribution item identifier, or <code>null</code>
+     */
+    protected this(String id) {
+        this.id = id;
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * method does nothing. Subclasses may override.
+     */
+    public void dispose() {
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * method does nothing. Subclasses may override.
+     */
+    public void fill(Composite parent) {
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * method does nothing. Subclasses may override.
+     */
+    public void fill(Menu menu, int index) {
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * method does nothing. Subclasses may override.
+     */
+    public void fill(ToolBar parent, int index) {
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * method does nothing. Subclasses may override.
+     *
+     * @since 3.0
+     */
+    public void fill(CoolBar parent, int index) {
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * method does nothing. Subclasses may override.
+     *
+     * @since 3.0
+     */
+    public void saveWidgetState() {
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * Returns the parent contribution manager, or <code>null</code> if this
+     * contribution item is not currently added to a contribution manager.
+     *
+     * @return the parent contribution manager, or <code>null</code>
+     * @since 2.0
+     */
+    public IContributionManager getParent() {
+        return parent;
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * method returns <code>false</code>. Subclasses may override.
+     */
+    public bool isDirty() {
+        // @issue should this be false instead of calling isDynamic()?
+        return isDynamic();
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * method returns <code>true</code>. Subclasses may override.
+     */
+    public bool isEnabled() {
+        return true;
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * method returns <code>false</code>. Subclasses may override.
+     */
+    public bool isDynamic() {
+        return false;
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * method returns <code>false</code>. Subclasses may override.
+     */
+    public bool isGroupMarker() {
+        return false;
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * method returns <code>false</code>. Subclasses may override.
+     */
+    public bool isSeparator() {
+        return false;
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * method returns the value recorded in an internal state variable,
+     * which is <code>true</code> by default. <code>setVisible</code>
+     * should be used to change this setting.
+     */
+    public bool isVisible() {
+        return visible;
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * method stores the value in an internal state variable,
+     * which is <code>true</code> by default.
+     */
+    public void setVisible(bool visible) {
+        this.visible = visible;
+    }
+
+    /**
+     * Returns a string representation of this contribution item
+     * suitable only for debugging.
+     */
+    public override String toString() {
+        return this.classinfo.name ~ "(id=" ~ getId() ~ ")";//$NON-NLS-2$//$NON-NLS-1$
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * method does nothing. Subclasses may override.
+     */
+    public void update() {
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public void setParent(IContributionManager parent) {
+        this.parent = parent;
+    }
+
+    /**
+     * The <code>ContributionItem</code> implementation of this
+     * method declared on <code>IContributionItem</code> does nothing.
+     * Subclasses should override to update their state.
+     */
+    public void update(String id) {
+    }
+    
+    /**
+     * The ID for this contribution item. It should be set once either in the
+     * constructor or using this method.
+     * 
+     * @param itemId
+     * @since 3.4
+     * @see #getId()
+     */
+    public void setId(String itemId) {
+        id = itemId;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/ContributionManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,587 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.ContributionManager;
+
+import org.eclipse.jface.action.ActionContributionItem;
+
+import org.eclipse.jface.action.IContributionManager;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.IContributionManagerOverrides;
+import org.eclipse.jface.action.IAction;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.util.Policy;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import tango.io.Stdout;
+import tango.core.Exception;
+import tango.text.convert.Format;
+
+/**
+ * Abstract base class for all contribution managers, and standard
+ * implementation of <code>IContributionManager</code>. This class provides
+ * functionality common across the specific managers defined by this framework.
+ * <p>
+ * This class maintains a list of contribution items and a dirty flag, both as
+ * internal state. In addition to providing implementations of most
+ * <code>IContributionManager</code> methods, this class automatically
+ * coalesces adjacent separators, hides beginning and ending separators, and
+ * deals with dynamically changing sets of contributions. When the set of
+ * contributions does change dynamically, the changes are propagated to the
+ * control via the <code>update</code> method, which subclasses must
+ * implement.
+ * </p>
+ * <p>
+ * Note: A <code>ContributionItem</code> cannot be shared between different
+ * <code>ContributionManager</code>s.
+ * </p>
+ */
+public abstract class ContributionManager : IContributionManager {
+
+    // Internal debug flag.
+    // protected static final bool DEBUG = false;
+
+    /**
+     * The list of contribution items.
+     */
+    private List contributions;
+
+    /**
+     * Indicates whether the widgets are in sync with the contributions.
+     */
+    private bool isDirty_ = true;
+
+    /**
+     * Number of dynamic contribution items.
+     */
+    private int dynamicItems = 0;
+
+    /**
+     * The overrides for items of this manager
+     */
+    private IContributionManagerOverrides overrides;
+
+    /**
+     * Creates a new contribution manager.
+     */
+    protected this() {
+        contributions = new ArrayList();
+        // Do nothing.
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public void add(IAction action) {
+        Assert.isNotNull( cast(Object)action, "Action must not be null"); //$NON-NLS-1$
+        add(new ActionContributionItem(action));
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public void add(IContributionItem item) {
+        Assert.isNotNull( cast(Object)item, "Item must not be null"); //$NON-NLS-1$
+        if (allowItem(item)) {
+            contributions.add(cast(Object)item);
+            itemAdded(item);
+        }
+    }
+
+    /**
+     * Adds a contribution item to the start or end of the group with the given
+     * name.
+     *
+     * @param groupName
+     *            the name of the group
+     * @param item
+     *            the contribution item
+     * @param append
+     *            <code>true</code> to add to the end of the group, and
+     *            <code>false</code> to add the beginning of the group
+     * @exception IllegalArgumentException
+     *                if there is no group with the given name
+     */
+    private void addToGroup(String groupName, IContributionItem item,
+            bool append) {
+        int i;
+        auto items = contributions.iterator();
+        for (i = 0; items.hasNext(); i++) {
+            IContributionItem o = cast(IContributionItem) items.next();
+            if (o.isGroupMarker()) {
+                String id = o.getId();
+                if (id !is null && id.equalsIgnoreCase(groupName)) {
+                    i++;
+                    if (append) {
+                        for (; items.hasNext(); i++) {
+                            IContributionItem ci = cast(IContributionItem) items
+                                    .next();
+                            if (ci.isGroupMarker()) {
+                                break;
+                            }
+                        }
+                    }
+                    if (allowItem(item)) {
+                        contributions.add(i, cast(Object)item);
+                        itemAdded(item);
+                    }
+                    return;
+                }
+            }
+        }
+        throw new IllegalArgumentException("Group not found: " ~ groupName);//$NON-NLS-1$
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public void appendToGroup(String groupName, IAction action) {
+        addToGroup(groupName, new ActionContributionItem(action), true);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public void appendToGroup(String groupName, IContributionItem item) {
+        addToGroup(groupName, item, true);
+    }
+
+    /**
+     * This method allows subclasses of <code>ContributionManager</code> to
+     * prevent certain items in the contributions list.
+     * <code>ContributionManager</code> will either block or allow an addition
+     * based on the result of this method call. This can be used to prevent
+     * duplication, for example.
+     *
+     * @param itemToAdd
+     *            The contribution item to be added; may be <code>null</code>.
+     * @return <code>true</code> if the addition should be allowed;
+     *         <code>false</code> otherwise. The default implementation allows
+     *         all items.
+     * @since 3.0
+     */
+    protected bool allowItem(IContributionItem itemToAdd) {
+        return true;
+    }
+
+    /**
+     * Internal debug method for printing statistics about this manager to
+     * <code>System.out</code>.
+     */
+    protected void dumpStatistics() {
+        int size = 0;
+        if (contributions !is null) {
+            size = contributions.size();
+        }
+
+        Stdout.formatln(this.toString());
+        Stdout.formatln("   Number of elements: {}", size);//$NON-NLS-1$
+        int sum = 0;
+        for (int i = 0; i < size; i++) {
+            if ((cast(IContributionItem) contributions.get(i)).isVisible()) {
+                sum++;
+            }
+        }
+        Stdout.formatln("   Number of visible elements: {}", sum);//$NON-NLS-1$
+        Stdout.formatln("   Is dirty: {}", isDirty()); //$NON-NLS-1$
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public IContributionItem find(String id) {
+        auto e = contributions.iterator();
+        while (e.hasNext()) {
+            IContributionItem item = cast(IContributionItem) e.next();
+            String itemId = item.getId();
+            if (itemId !is null && itemId.equalsIgnoreCase(id)) {
+                return item;
+            }
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public IContributionItem[] getItems() {
+        IContributionItem[] items = arraycast!(IContributionItem)(contributions.toArray());
+        return items;
+    }
+
+    /**
+     * Return the number of contributions in this manager.
+     *
+     * @return the number of contributions in this manager
+     * @since 3.3
+     */
+    public int getSize() {
+        return contributions.size();
+    }
+
+    /**
+     * The <code>ContributionManager</code> implementation of this method
+     * declared on <code>IContributionManager</code> returns the current
+     * overrides. If there is no overrides it lazily creates one which overrides
+     * no item state.
+     *
+     * @since 2.0
+     */
+    public IContributionManagerOverrides getOverrides() {
+        if (overrides is null) {
+            overrides = new class IContributionManagerOverrides {
+                public Boolean getEnabled(IContributionItem item) {
+                    return null;
+                }
+
+                public Integer getAccelerator(IContributionItem item) {
+                    return null;
+                }
+
+                public String getAcceleratorText(IContributionItem item) {
+                    return null;
+                }
+
+                public String getText(IContributionItem item) {
+                    return null;
+                }
+            };
+        }
+        return overrides;
+    }
+
+    /**
+     * Returns whether this contribution manager contains dynamic items. A
+     * dynamic contribution item contributes items conditionally, dependent on
+     * some internal state.
+     *
+     * @return <code>true</code> if this manager contains dynamic items, and
+     *         <code>false</code> otherwise
+     */
+    protected bool hasDynamicItems() {
+        return (dynamicItems > 0);
+    }
+
+    /**
+     * Returns the index of the item with the given id.
+     *
+     * @param id
+     *            The id of the item whose index is requested.
+     *
+     * @return <code>int</code> the index or -1 if the item is not found
+     */
+    public int indexOf(String id) {
+        for (int i = 0; i < contributions.size(); i++) {
+            IContributionItem item = cast(IContributionItem) contributions.get(i);
+            String itemId = item.getId();
+            if (itemId !is null && itemId.equalsIgnoreCase(id)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns the index of the object in the internal structure. This is
+     * different from <code>indexOf(String id)</code> since some contribution
+     * items may not have an id.
+     *
+     * @param item
+     *            The contribution item
+     * @return the index, or -1 if the item is not found
+     * @since 3.0
+     */
+    protected int indexOf(IContributionItem item) {
+        return contributions.indexOf(cast(Object)item);
+    }
+
+    /**
+     * Insert the item at the given index.
+     *
+     * @param index
+     *            The index to be used for insertion
+     * @param item
+     *            The item to be inserted
+     */
+    public void insert(int index, IContributionItem item) {
+        if (index > contributions.size()) {
+            throw new IndexOutOfBoundsException( Format(
+                    "inserting {} at {}", item.getId(), index)); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+        if (allowItem(item)) {
+            contributions.add(index, cast(Object)item);
+            itemAdded(item);
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public void insertAfter(String ID, IAction action) {
+        insertAfter(ID, new ActionContributionItem(action));
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public void insertAfter(String ID, IContributionItem item) {
+        IContributionItem ci = find(ID);
+        if (ci is null) {
+            throw new IllegalArgumentException(Format("can't find ID{}", ID));//$NON-NLS-1$
+        }
+        int ix = contributions.indexOf(cast(Object)ci);
+        if (ix >= 0) {
+            // System.out.println("insert after: " + ix);
+            if (allowItem(item)) {
+                contributions.add(ix + 1,cast(Object) item);
+                itemAdded(item);
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public void insertBefore(String ID, IAction action) {
+        insertBefore(ID, new ActionContributionItem(action));
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public void insertBefore(String ID, IContributionItem item) {
+        IContributionItem ci = find(ID);
+        if (ci is null) {
+            throw new IllegalArgumentException(Format("can't find ID {}", ID));//$NON-NLS-1$
+        }
+        int ix = contributions.indexOf(cast(Object)ci);
+        if (ix >= 0) {
+            // System.out.println("insert before: " + ix);
+            if (allowItem(item)) {
+                contributions.add(ix, cast(Object)item);
+                itemAdded(item);
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public bool isDirty() {
+        if (isDirty_) {
+            return true;
+        }
+        if (hasDynamicItems()) {
+            for (Iterator iter = contributions.iterator(); iter.hasNext();) {
+                IContributionItem item = cast(IContributionItem) iter.next();
+                if (item.isDirty()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public bool isEmpty() {
+        return contributions.isEmpty();
+    }
+
+    /**
+     * The given item was added to the list of contributions. Marks the manager
+     * as dirty and updates the number of dynamic items, and the memento.
+     *
+     * @param item
+     *            the item to be added
+     *
+     */
+    protected void itemAdded(IContributionItem item) {
+        item.setParent(this);
+        markDirty();
+        if (item.isDynamic()) {
+            dynamicItems++;
+        }
+    }
+
+    /**
+     * The given item was removed from the list of contributions. Marks the
+     * manager as dirty and updates the number of dynamic items.
+     *
+     * @param item
+     *            remove given parent from list of contributions
+     */
+    protected void itemRemoved(IContributionItem item) {
+        item.setParent(null);
+        markDirty();
+        if (item.isDynamic()) {
+            dynamicItems--;
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public void markDirty() {
+        setDirty(true);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public void prependToGroup(String groupName, IAction action) {
+        addToGroup(groupName, new ActionContributionItem(action), false);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public void prependToGroup(String groupName, IContributionItem item) {
+        addToGroup(groupName, item, false);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public IContributionItem remove(String ID) {
+        IContributionItem ci = find(ID);
+        if (ci is null) {
+            return null;
+        }
+        return remove(ci);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public IContributionItem remove(IContributionItem item) {
+        if (contributions.remove(cast(Object)item)) {
+            itemRemoved(item);
+            return item;
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public void removeAll() {
+        IContributionItem[] items = getItems();
+        contributions.clear();
+        for (int i = 0; i < items.length; i++) {
+            IContributionItem item = items[i];
+            itemRemoved(item);
+        }
+        dynamicItems = 0;
+        markDirty();
+    }
+
+    /**
+     * Replaces the item of the given identifier with another contribution item.
+     * This can be used, for example, to replace large contribution items with
+     * placeholders to avoid memory leaks. If the identifier cannot be found in
+     * the current list of items, then this does nothing. If multiple
+     * occurrences are found, then the replacement items is put in the first
+     * position and the other positions are removed.
+     *
+     * @param identifier
+     *            The identifier to look for in the list of contributions;
+     *            should not be <code>null</code>.
+     * @param replacementItem
+     *            The contribution item to replace the old item; must not be
+     *            <code>null</code>. Use
+     *            {@link org.eclipse.jface.action.ContributionManager#remove(java.lang.String) remove}
+     *            if that is what you want to do.
+     * @return <code>true</code> if the given identifier can be; <code>
+     * @since 3.0
+     */
+    public bool replaceItem(String identifier,
+            IContributionItem replacementItem) {
+        if (identifier is null) {
+            return false;
+        }
+
+        int index = indexOf(identifier);
+        if (index < 0) {
+            return false; // couldn't find the item.
+        }
+
+        // Remove the old item.
+        IContributionItem oldItem = cast(IContributionItem) contributions
+                .get(index);
+        itemRemoved(oldItem);
+
+        // Add the new item.
+        contributions.set(index, cast(Object)replacementItem);
+        itemAdded(replacementItem); // throws NPE if (replacementItem is null)
+
+        // Go through and remove duplicates.
+        for (int i = contributions.size() - 1; i > index; i--) {
+            IContributionItem item = cast(IContributionItem) contributions.get(i);
+            if ((item !is null) && (identifier.equals(item.getId()))) {
+                if (Policy.TRACE_TOOLBAR) {
+                    Stdout.formatln("Removing duplicate on replace: {}", identifier); //$NON-NLS-1$
+                }
+                contributions.remove(i);
+                itemRemoved(item);
+            }
+        }
+
+        return true; // success
+    }
+
+    /**
+     * Sets whether this manager is dirty. When dirty, the list of contributions
+     * is not accurately reflected in the corresponding widgets.
+     *
+     * @param dirty
+     *            <code>true</code> if this manager is dirty, and
+     *            <code>false</code> if it is up-to-date
+     */
+    protected void setDirty(bool dirty) {
+        isDirty_ = dirty;
+    }
+
+    /**
+     * Sets the overrides for this contribution manager
+     *
+     * @param newOverrides
+     *            the overrides for the items of this manager
+     * @since 2.0
+     */
+    public void setOverrides(IContributionManagerOverrides newOverrides) {
+        overrides = newOverrides;
+    }
+
+    /**
+     * An internal method for setting the order of the contribution items.
+     *
+     * @param items
+     *            the contribution items in the specified order
+     * @since 3.0
+     */
+    protected void internalSetItems(IContributionItem[] items) {
+        contributions.clear();
+        for (int i = 0; i < items.length; i++) {
+            if (allowItem(items[i])) {
+                contributions.add(cast(Object)items[i]);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/ControlContribution.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.action.ControlContribution;
+
+import org.eclipse.jface.action.ContributionItem;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * An abstract contribution item implementation for adding an arbitrary
+ * SWT control to a tool bar.
+ * Note, however, that these items cannot be contributed to menu bars.
+ * <p>
+ * The <code>createControl</code> framework method must be implemented
+ * by concrete subclasses.
+ * </p>
+ */
+public abstract class ControlContribution : ContributionItem {
+    alias ContributionItem.fill fill;
+    /**
+     * Creates a control contribution item with the given id.
+     *
+     * @param id the contribution item id
+     */
+    protected this(String id) {
+        super(id);
+    }
+
+    /**
+     * Computes the width of the given control which is being added
+     * to a tool bar.  This is needed to determine the width of the tool bar item
+     * containing the given control.
+     * <p>
+     * The default implementation of this framework method returns
+     * <code>control.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x</code>.
+     * Subclasses may override if required.
+     * </p>
+     *
+     * @param control the control being added
+     * @return the width of the control
+     */
+    protected int computeWidth(Control control) {
+        return control.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x;
+    }
+
+    /**
+     * Creates and returns the control for this contribution item
+     * under the given parent composite.
+     * <p>
+     * This framework method must be implemented by concrete
+     * subclasses.
+     * </p>
+     *
+     * @param parent the parent composite
+     * @return the new control
+     */
+    protected abstract Control createControl(Composite parent);
+
+    /**
+     * The control item implementation of this <code>IContributionItem</code>
+     * method calls the <code>createControl</code> framework method.
+     * Subclasses must implement <code>createControl</code> rather than
+     * overriding this method.
+     */
+    public override final void fill(Composite parent) {
+        createControl(parent);
+    }
+
+    /**
+     * The control item implementation of this <code>IContributionItem</code>
+     * method throws an exception since controls cannot be added to menus.
+     */
+    public override final void fill(Menu parent, int index) {
+        Assert.isTrue(false, "Can't add a control to a menu");//$NON-NLS-1$
+    }
+
+    /**
+     * The control item implementation of this <code>IContributionItem</code>
+     * method calls the <code>createControl</code> framework method to
+     * create a control under the given parent, and then creates
+     * a new tool item to hold it.
+     * Subclasses must implement <code>createControl</code> rather than
+     * overriding this method.
+     */
+    public override final void fill(ToolBar parent, int index) {
+        Control control = createControl(parent);
+        ToolItem ti = new ToolItem(parent, SWT.SEPARATOR, index);
+        ti.setControl(control);
+        ti.setWidth(computeWidth(control));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/CoolBarManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1054 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.CoolBarManager;
+
+import org.eclipse.jface.action.ToolBarContributionItem;
+import org.eclipse.jface.action.Separator;
+
+import org.eclipse.jface.action.ContributionManager;
+import org.eclipse.jface.action.IContributionManager;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.ICoolBarManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.CoolBar;
+import org.eclipse.swt.widgets.CoolItem;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.util.Policy;
+
+import java.lang.all;
+import java.util.ListIterator;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.Set;
+import tango.io.Stdout;
+
+/**
+ * A cool bar manager is a contribution manager which realizes itself and its
+ * items in a cool bar control.
+ * <p>
+ * This class may be instantiated; it may also be subclassed.
+ * </p>
+ *
+ * @since 3.0
+ */
+public class CoolBarManager : ContributionManager,
+        ICoolBarManager {
+    alias ContributionManager.add add;
+
+    /**
+     * A separator created by the end user.
+     */
+    public final static String USER_SEPARATOR = "UserSeparator"; //$NON-NLS-1$
+
+    /**
+     * The original creation order of the contribution items.
+     */
+    private ArrayList cbItemsCreationOrder;
+
+    /**
+     * MenuManager for cool bar pop-up menu, or null if none.
+     */
+    private MenuManager contextMenuManager = null;
+
+    /**
+     * The cool bar control; <code>null</code> before creation and after
+     * disposal.
+     */
+    private CoolBar coolBar = null;
+
+    /**
+     * The cool bar items style; <code>SWT.NONE</code> by default.
+     */
+    private int itemStyle = SWT.NONE;
+
+    /**
+     * Creates a new cool bar manager with the default style. Equivalent to
+     * <code>CoolBarManager(SWT.NONE)</code>.
+     */
+    public this() {
+        cbItemsCreationOrder = new ArrayList();
+        // do nothing
+    }
+
+    /**
+     * Creates a cool bar manager for an existing cool bar control. This
+     * manager becomes responsible for the control, and will dispose of it when
+     * the manager is disposed.
+     *
+     * @param coolBar
+     *            the cool bar control
+     */
+    public this(CoolBar coolBar) {
+        this();
+        Assert.isNotNull(coolBar);
+        this.coolBar = coolBar;
+        itemStyle = coolBar.getStyle();
+    }
+
+    /**
+     * Creates a cool bar manager with the given SWT style. Calling <code>createControl</code>
+     * will create the cool bar control.
+     *
+     * @param style
+     *            the cool bar item style; see
+     *            {@link org.eclipse.swt.widgets.CoolBar CoolBar}for for valid
+     *            style bits
+     */
+    public this(int style) {
+        cbItemsCreationOrder = new ArrayList();
+        itemStyle = style;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.action.ICoolBarManager#add(org.eclipse.jface.action.IToolBarManager)
+     */
+    public void add(IToolBarManager toolBarManager) {
+        Assert.isNotNull(cast(Object)toolBarManager);
+        super.add(new ToolBarContributionItem(toolBarManager));
+    }
+
+    /**
+     * Collapses consecutive separators and removes a separator from the
+     * beginning and end of the list.
+     *
+     * @param contributionList
+     *            the list of contributions; must not be <code>null</code>.
+     * @return The contribution list provided with extraneous separators
+     *         removed; this value is never <code>null</code>, but may be
+     *         empty.
+     */
+    private ArrayList adjustContributionList(ArrayList contributionList) {
+        IContributionItem item;
+        // Fist remove a separator if it is the first element of the list
+        if (contributionList.size() !is 0) {
+            item = cast(IContributionItem) contributionList.get(0);
+            if (item.isSeparator()) {
+                contributionList.remove(0);
+            }
+
+            auto iterator = contributionList.listIterator();
+            // collapse consecutive separators
+            while (iterator.hasNext()) {
+                item = cast(IContributionItem) iterator.next();
+                if (item.isSeparator()) {
+                    while (iterator.hasNext()) {
+                        item = cast(IContributionItem) iterator.next();
+                        if (item.isSeparator()) {
+                            iterator.remove();
+                        } else {
+                            break;
+                        }
+                    }
+
+                }
+            }
+            // Now check last element to see if there is a separator
+            item = cast(IContributionItem) contributionList.get(contributionList
+                    .size() - 1);
+            if (item.isSeparator()) {
+                contributionList.remove(contributionList.size() - 1);
+            }
+        }
+        return contributionList;
+
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.ContributionManager#checkDuplication(org.eclipse.jface.action.IContributionItem)
+     */
+    protected override bool allowItem(IContributionItem itemToAdd) {
+        /* We will allow as many null entries as they like, though there should
+         * be none.
+         */
+        if (itemToAdd is null) {
+            return true;
+        }
+
+        /* Null identifiers can be expected in generic contribution items.
+         */
+        String firstId = itemToAdd.getId();
+        if (firstId is null) {
+            return true;
+        }
+
+        // Cycle through the current list looking for duplicates.
+        IContributionItem[] currentItems = getItems();
+        for (int i = 0; i < currentItems.length; i++) {
+            IContributionItem currentItem = currentItems[i];
+
+            // We ignore null entries.
+            if (currentItem is null) {
+                continue;
+            }
+
+            String secondId = currentItem.getId();
+            if (firstId.equals(secondId)) {
+                if (Policy.TRACE_TOOLBAR) {
+                    Stdout.formatln("Trying to add a duplicate item."); //$NON-NLS-1$
+                    ExceptionPrintStackTrace(new Exception(null), Stdout );
+                    Stdout.formatln("DONE --------------------------"); //$NON-NLS-1$
+                }
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Positions the list iterator to the end of all the separators. Calling
+     * <code>next()</code> the iterator should return the immediate object
+     * following the last separator.
+     *
+     * @param iterator
+     *            the list iterator.
+     */
+    private void collapseSeparators(ListIterator iterator) {
+
+        while (iterator.hasNext()) {
+            IContributionItem item = cast(IContributionItem) iterator.next();
+            if (!item.isSeparator()) {
+                iterator.previous();
+                return;
+            }
+        }
+    }
+
+    /**
+     * Returns whether the cool bar control has been created and not yet
+     * disposed.
+     *
+     * @return <code>true</code> if the control has been created and not yet
+     *         disposed, <code>false</code> otherwise
+     */
+    private bool coolBarExist() {
+        return coolBar !is null && !coolBar.isDisposed();
+    }
+
+    /**
+     * Creates and returns this manager's cool bar control. Does not create a
+     * new control if one already exists.
+     *
+     * @param parent
+     *            the parent control
+     * @return the cool bar control
+     */
+    public CoolBar createControl(Composite parent) {
+        Assert.isNotNull(parent);
+        if (!coolBarExist()) {
+            coolBar = new CoolBar(parent, itemStyle);
+            coolBar.setMenu(getContextMenuControl());
+            coolBar.setLocked(false);
+            update(false);
+        }
+        return coolBar;
+    }
+
+    /**
+     * Disposes of this cool bar manager and frees all allocated SWT resources.
+     * Notifies all contribution items of the dispose. Note that this method
+     * does not clean up references between this cool bar manager and its
+     * associated contribution items. Use <code>removeAll</code> for that
+     * purpose.
+     */
+    public void dispose() {
+        if (coolBarExist()) {
+            coolBar.dispose();
+            coolBar = null;
+        }
+        IContributionItem[] items = getItems();
+        for (int i = 0; i < items.length; i++) {
+            // Disposes of the contribution item.
+            // If Contribution Item is a toolbar then it will dispose of
+            // all the nested
+            // contribution items.
+            items[i].dispose();
+        }
+        // If a context menu existed then dispose of it.
+        if (contextMenuManager !is null) {
+            contextMenuManager.dispose();
+            contextMenuManager = null;
+        }
+
+    }
+
+    /**
+     * Disposes the given cool item.
+     *
+     * @param item
+     *            the cool item to dispose
+     */
+    private void dispose(CoolItem item) {
+        if ((item !is null) && !item.isDisposed()) {
+
+            item.setData(null);
+            Control control = item.getControl();
+            // if the control is already disposed, setting the coolitem
+            // control to null will cause an SWT exception, workaround
+            // for 19630
+            if ((control !is null) && !control.isDisposed()) {
+                item.setControl(null);
+            }
+            item.dispose();
+        }
+    }
+
+    /**
+     * Finds the cool item associated with the given contribution item.
+     *
+     * @param item
+     *            the contribution item
+     * @return the associated cool item, or <code>null</code> if not found
+     */
+    private CoolItem findCoolItem(IContributionItem item) {
+        CoolItem[] coolItems = (coolBar is null) ? null : coolBar.getItems();
+        return findCoolItem(coolItems, item);
+    }
+
+    private CoolItem findCoolItem(CoolItem[] items, IContributionItem item) {
+        if (items is null) {
+            return null;
+        }
+
+        for (int i = 0; i < items.length; i++) {
+            CoolItem coolItem = items[i];
+            IContributionItem data = cast(IContributionItem) coolItem.getData();
+            if (data !is null && (cast(Object)data).opEquals(cast(Object)item)) {
+                return coolItem;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Return a consistent set of wrap indices. The return value will always
+     * include at least one entry and the first entry will always be zero.
+     * CoolBar.getWrapIndices() is inconsistent in whether or not it returns an
+     * index for the first row.
+     *
+     * @param wraps
+     *            the wrap indicies from the cool bar widget
+     * @return the adjusted wrap indicies.
+     */
+    private int[] getAdjustedWrapIndices(int[] wraps) {
+        int[] adjustedWrapIndices;
+        if (wraps.length is 0) {
+            adjustedWrapIndices = [ 0 ];
+        } else {
+            if (wraps[0] !is 0) {
+                adjustedWrapIndices = new int[wraps.length + 1];
+                adjustedWrapIndices[0] = 0;
+                for (int i = 0; i < wraps.length; i++) {
+                    adjustedWrapIndices[i + 1] = wraps[i];
+                }
+            } else {
+                adjustedWrapIndices = wraps;
+            }
+        }
+        return adjustedWrapIndices;
+    }
+
+    /**
+     * Returns the control of the Menu Manager. If the menu manager does not
+     * have a control then one is created.
+     *
+     * @return menu control associated with manager, or null if none
+     */
+    private Menu getContextMenuControl() {
+        if ((contextMenuManager !is null) && (coolBar !is null)) {
+            Menu menuWidget = contextMenuManager.getMenu();
+            if ((menuWidget is null) || (menuWidget.isDisposed())) {
+                menuWidget = contextMenuManager.createContextMenu(coolBar);
+            }
+            return menuWidget;
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.action.ICoolBarManager#isLayoutLocked()
+     */
+    public IMenuManager getContextMenuManager() {
+        return contextMenuManager;
+    }
+
+    /**
+     * Returns the cool bar control for this manager.
+     *
+     * @return the cool bar control, or <code>null</code> if none
+     */
+    public CoolBar getControl() {
+        return coolBar;
+    }
+
+    /**
+     * Returns an array list of all the contribution items in the manager.
+     *
+     * @return an array list of contribution items.
+     */
+    private ArrayList getItemList() {
+        IContributionItem[] cbItems = getItems();
+        ArrayList list = new ArrayList(cbItems.length);
+        for (int i = 0; i < cbItems.length; i++) {
+            list.add( cast(Object) cbItems[i]);
+        }
+        return list;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.action.ICoolBarManager#isLayoutLocked()
+     */
+    public bool getLockLayout() {
+        if (!coolBarExist()) {
+            return false;
+        }
+        return coolBar.getLocked();
+    }
+
+    /**
+     * Returns the number of rows that should be displayed visually.
+     *
+     * @param items
+     *            the array of contributin items
+     * @return the number of rows
+     */
+    private int getNumRows(IContributionItem[] items) {
+        int numRows = 1;
+        bool separatorFound = false;
+        for (int i = 0; i < items.length; i++) {
+            if (items[i].isSeparator()) {
+                separatorFound = true;
+            }
+            if ((separatorFound) && (items[i].isVisible())
+                    && (!items[i].isGroupMarker()) && (!items[i].isSeparator())) {
+                numRows++;
+                separatorFound = false;
+            }
+        }
+        return numRows;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.action.ICoolBarManager#getStyle()
+     */
+    public int getStyle() {
+        return itemStyle;
+    }
+
+    /**
+     * Subclasses may extend this <code>ContributionManager</code> method,
+     * but must call <code>super.itemAdded</code>.
+     *
+     * @see org.eclipse.jface.action.ContributionManager#itemAdded(org.eclipse.jface.action.IContributionItem)
+     */
+    protected override void itemAdded(IContributionItem item) {
+        Assert.isNotNull(cast(Object)item);
+        super.itemAdded(item);
+        int insertedAt = indexOf(item);
+        bool replaced = false;
+        final int size = cbItemsCreationOrder.size();
+        for (int i = 0; i < size; i++) {
+            IContributionItem created = cast(IContributionItem) cbItemsCreationOrder
+                    .get(i);
+            if (created.getId() !is null && created.getId().equals(item.getId())) {
+                cbItemsCreationOrder.set(i, cast(Object) item);
+                replaced = true;
+                break;
+            }
+        }
+
+        if (!replaced) {
+            cbItemsCreationOrder.add(Math.min(Math.max(insertedAt, 0),
+                    cbItemsCreationOrder.size()), cast(Object) item);
+        }
+    }
+
+    /**
+     * Subclasses may extend this <code>ContributionManager</code> method,
+     * but must call <code>super.itemRemoved</code>.
+     *
+     * @see org.eclipse.jface.action.ContributionManager#itemRemoved(org.eclipse.jface.action.IContributionItem)
+     */
+    protected override void itemRemoved(IContributionItem item) {
+        Assert.isNotNull(cast(Object)item);
+        super.itemRemoved(item);
+        CoolItem coolItem = findCoolItem(item);
+        if (coolItem !is null) {
+            coolItem.setData(null);
+        }
+    }
+
+    /**
+     * Positions the list iterator to the starting of the next row. By calling
+     * next on the returned iterator, it will return the first element of the
+     * next row.
+     *
+     * @param iterator
+     *            the list iterator of contribution items
+     * @param ignoreCurrentItem
+     *            Whether the current item in the iterator should be considered
+     *            (as well as subsequent items).
+     */
+    private void nextRow(ListIterator iterator, bool ignoreCurrentItem) {
+
+        IContributionItem currentElement = null;
+        if (!ignoreCurrentItem && iterator.hasPrevious()) {
+            currentElement = cast(IContributionItem) iterator.previous();
+            iterator.next();
+        }
+
+        if ((currentElement !is null) && (currentElement.isSeparator())) {
+            collapseSeparators(iterator);
+            return;
+        }
+
+        //Find next separator
+        while (iterator.hasNext()) {
+            IContributionItem item = cast(IContributionItem) iterator.next();
+            if (item.isSeparator()) {
+                // we we find a separator, collapse any consecutive
+                // separators
+                // and return
+                collapseSeparators(iterator);
+                return;
+            }
+        }
+    }
+
+    /*
+     * Used for debuging. Prints all the items in the internal structures.
+     */
+    //    private void printContributions(ArrayList contributionList) {
+    //        int index = 0;
+    //        System.out.println("----------------------------------\n"); //$NON-NLS-1$
+    //        for (Iterator i = contributionList.iterator(); i.hasNext(); index++) {
+    //            IContributionItem item = (IContributionItem) i.next();
+    //            if (item.isSeparator()) {
+    //                System.out.println("Separator"); //$NON-NLS-1$
+    //            } else {
+    //                System.out.println(index + ". Item id: " + item.getId() //$NON-NLS-1$
+    //                        + " - is Visible: " //$NON-NLS-1$
+    //                        + item.isVisible());
+    //            }
+    //        }
+    //    }
+    /**
+     * Synchronizes the visual order of the cool items in the control with this
+     * manager's internal data structures. This method should be called before
+     * requesting the order of the contribution items to ensure that the order
+     * is accurate.
+     * <p>
+     * Note that <code>update()</code> and <code>refresh()</code> are
+     * converses: <code>update()</code> changes the visual order to match the
+     * internal structures, and <code>refresh</code> changes the internal
+     * structures to match the visual order.
+     * </p>
+     */
+    public void refresh() {
+        if (!coolBarExist()) {
+            return;
+        }
+
+        // Retreives the list of contribution items as an array list
+        auto contributionList = getItemList();
+
+        // Check the size of the list
+        if (contributionList.size() is 0) {
+            return;
+        }
+
+        // The list of all the cool items in their visual order
+        CoolItem[] coolItems = coolBar.getItems();
+        // The wrap indicies of the coolbar
+        int[] wrapIndicies = getAdjustedWrapIndices(coolBar.getWrapIndices());
+
+        int row = 0;
+        int coolItemIndex = 0;
+
+        // Traverse through all cool items in the coolbar add them to a new
+        // data structure
+        // in the correct order
+        auto displayedItems = new ArrayList(coolBar.getItemCount());
+        for (int i = 0; i < coolItems.length; i++) {
+            CoolItem coolItem = coolItems[i];
+            if ( auto cbItem = cast(IContributionItem) coolItem.getData() ) {
+                displayedItems.add(Math.min(i, displayedItems.size()), cast(Object) cbItem);
+            }
+        }
+
+        // Add separators to the displayed Items data structure
+        int offset = 0;
+        for (int i = 1; i < wrapIndicies.length; i++) {
+            int insertAt = wrapIndicies[i] + offset;
+            displayedItems.add(insertAt, new Separator(USER_SEPARATOR));
+            offset++;
+        }
+
+        // Determine which rows are invisible
+        auto existingVisibleRows = new ArrayList(4);
+        ListIterator rowIterator = contributionList.listIterator();
+        collapseSeparators(rowIterator);
+        int numRow = 0;
+        while (rowIterator.hasNext()) {
+            // Scan row
+            while (rowIterator.hasNext()) {
+                IContributionItem cbItem = cast(IContributionItem) rowIterator
+                        .next();
+                if (displayedItems.contains(cast(Object) cbItem)) {
+                    existingVisibleRows.add(new Integer(numRow));
+                    break;
+                }
+                if (cbItem.isSeparator()) {
+                    break;
+                }
+            }
+            nextRow(rowIterator, false);
+            numRow++;
+        }
+
+        auto existingRows = existingVisibleRows.iterator();
+        // Adjust row number to the first visible
+        if (existingRows.hasNext()) {
+            row = (cast(Integer) existingRows.next()).intValue();
+        }
+
+        auto itemLocation = new HashMap();
+        for (ListIterator locationIterator = displayedItems.listIterator(); locationIterator
+                .hasNext();) {
+            IContributionItem item = cast(IContributionItem) locationIterator
+                    .next();
+            if (item.isSeparator()) {
+                if (existingRows.hasNext()) {
+                    Integer value = cast(Integer) existingRows.next();
+                    row = value.intValue();
+                } else {
+                    row++;
+                }
+            } else {
+                itemLocation.put(cast(Object)item, new Integer(row));
+            }
+
+        }
+
+        // Insert the contribution items in their correct location
+        for (ListIterator iterator = displayedItems.listIterator(); iterator
+                .hasNext();) {
+            IContributionItem cbItem = cast(IContributionItem) iterator.next();
+            if (cbItem.isSeparator()) {
+                coolItemIndex = 0;
+            } else {
+                relocate(cbItem, coolItemIndex, contributionList, itemLocation);
+                cbItem.saveWidgetState();
+                coolItemIndex++;
+            }
+        }
+
+        if (contributionList.size() !is 0) {
+            contributionList = adjustContributionList(contributionList);
+            IContributionItem[] array = arraycast!(IContributionItem)( contributionList.toArray());
+            internalSetItems(array);
+        }
+
+    }
+
+    /**
+     * Relocates the given contribution item to the specified index.
+     *
+     * @param cbItem
+     *            the conribution item to relocate
+     * @param index
+     *            the index to locate this item
+     * @param contributionList
+     *            the current list of conrtributions
+     * @param itemLocation
+     */
+    private void relocate(IContributionItem cbItem, int index,
+            ArrayList contributionList, HashMap itemLocation) {
+
+        if (!(cast(Integer)itemLocation.get(cast(Object)cbItem) )) {
+            return;
+        }
+        int targetRow = (cast(Integer) itemLocation.get(cast(Object)cbItem)).intValue();
+
+        int cbInternalIndex = contributionList.indexOf(cast(Object)cbItem);
+
+        //  by default add to end of list
+        int insertAt = contributionList.size();
+        // Find the row to place this item in.
+        ListIterator iterator = contributionList.listIterator();
+        // bypass any separators at the begining
+        collapseSeparators(iterator);
+        int currentRow = -1;
+        while (iterator.hasNext()) {
+
+            currentRow++;
+            if (currentRow is targetRow) {
+                // We found the row to insert the item
+                int virtualIndex = 0;
+                insertAt = iterator.nextIndex();
+                // first check the position of the current element (item)
+                // then get the next element
+                while (iterator.hasNext()) {
+                    IContributionItem item = cast(IContributionItem) iterator
+                            .next();
+                    Integer itemRow = cast(Integer) itemLocation.get( cast(Object) item);
+                    if (item.isSeparator()) {
+                        break;
+                    }
+                    // if the item has an associate widget
+                    if ((itemRow !is null) && (itemRow.intValue() is targetRow)) {
+                        // if the next element is the index we are looking for
+                        // then break
+                        if (virtualIndex >= index) {
+                            break;
+                        }
+                        virtualIndex++;
+
+                    }
+                    insertAt++;
+                }
+                // If we don't need to move it then we return
+                if (cbInternalIndex is insertAt) {
+                    return;
+                }
+                break;
+            }
+            nextRow(iterator, true);
+        }
+        contributionList.remove(cast(Object) cbItem);
+
+        // Adjust insertAt index
+        if (cbInternalIndex < insertAt) {
+            insertAt--;
+        }
+
+        // if we didn't find the row then add a new row
+        if (currentRow !is targetRow) {
+            contributionList.add(new Separator(USER_SEPARATOR));
+            insertAt = contributionList.size();
+        }
+        insertAt = Math.min(insertAt, contributionList.size());
+        contributionList.add(insertAt, cast(Object) cbItem);
+
+    }
+
+    /**
+     * Restores the canonical order of this cool bar manager. The canonical
+     * order is the order in which the contribution items where added.
+     */
+    public void resetItemOrder() {
+        for (ListIterator iterator = cbItemsCreationOrder.listIterator(); iterator
+                .hasNext();) {
+            IContributionItem item = cast(IContributionItem) iterator.next();
+            // if its a user separator then do not include in original order.
+            if ((item.getId() !is null) && (item.getId().equals(USER_SEPARATOR))) {
+                iterator.remove();
+            }
+        }
+        IContributionItem[] itemsToSet = arraycast!(IContributionItem)(cbItemsCreationOrder.toArray());
+        setItems(itemsToSet);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.action.ICoolBarManager#setContextMenuManager(org.eclipse.jface.action.IMenuManager)
+     */
+    public void setContextMenuManager(IMenuManager contextMenuManager) {
+        this.contextMenuManager = cast(MenuManager) contextMenuManager;
+        if (coolBar !is null) {
+            coolBar.setMenu(getContextMenuControl());
+        }
+    }
+
+    /**
+     * Replaces the current items with the given items.
+     * Forces an update.
+     *
+     * @param newItems the items with which to replace the current items
+     */
+    public void setItems(IContributionItem[] newItems) {
+        // dispose of all the cool items on the cool bar manager
+        if (coolBar !is null) {
+            CoolItem[] coolItems = coolBar.getItems();
+            for (int i = 0; i < coolItems.length; i++) {
+                dispose(coolItems[i]);
+            }
+        }
+        // Set the internal structure to this order
+        internalSetItems(newItems);
+        // Force and update
+        update(true);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.action.ICoolBarManager#lockLayout(bool)
+     */
+    public void setLockLayout(bool value) {
+        if (!coolBarExist()) {
+            return;
+        }
+        coolBar.setLocked(value);
+    }
+
+    /**
+     * Subclasses may extend this <code>IContributionManager</code> method,
+     * but must call <code>super.update</code>.
+     *
+     * @see org.eclipse.jface.action.IContributionManager#update(bool)
+     */
+    public void update(bool force) {
+        if ((!isDirty() && !force) || (!coolBarExist())) {
+            return;
+        }
+
+        bool relock = false;
+        bool changed = false;
+
+        try {
+            coolBar.setRedraw(false);
+
+            // Refresh the widget data with the internal data structure.
+            refresh();
+
+            if (coolBar.getLocked()) {
+                coolBar.setLocked(false);
+                relock = true;
+            }
+
+            /*
+             * Make a list of items including only those items that are
+             * visible. Separators should stay because they mark line breaks in
+             * a cool bar.
+             */
+            IContributionItem[] items = getItems();
+            List visibleItems = new ArrayList(items.length);
+            for (int i = 0; i < items.length; i++) {
+                IContributionItem item = items[i];
+                if (item.isVisible()) {
+                    visibleItems.add(cast(Object)item);
+                }
+            }
+
+            /*
+             * Make a list of CoolItem widgets in the cool bar for which there
+             * is no current visible contribution item. These are the widgets
+             * to be disposed. Dynamic items are also removed.
+             */
+            CoolItem[] coolItems = coolBar.getItems();
+            ArrayList coolItemsToRemove = new ArrayList(coolItems.length);
+            for (int i = 0; i < coolItems.length; i++) {
+                Object data = coolItems[i].getData();
+                if ((data is null)
+                        || (!visibleItems.contains(data))
+                        || ((cast(IContributionItem)data ) && (cast(IContributionItem) data)
+                                .isDynamic())) {
+                    coolItemsToRemove.add(coolItems[i]);
+                }
+            }
+
+            // Dispose of any items in the list to be removed.
+            for (int i = coolItemsToRemove.size() - 1; i >= 0; i--) {
+                CoolItem coolItem = cast(CoolItem) coolItemsToRemove.get(i);
+                if (!coolItem.isDisposed()) {
+                    Control control = coolItem.getControl();
+                    if (control !is null) {
+                        coolItem.setControl(null);
+                        control.dispose();
+                    }
+                    coolItem.dispose();
+                }
+            }
+
+            // Add any new items by telling them to fill.
+            coolItems = coolBar.getItems();
+            IContributionItem sourceItem;
+            IContributionItem destinationItem;
+            int sourceIndex = 0;
+            int destinationIndex = 0;
+            Iterator visibleItemItr = visibleItems.iterator();
+            while (visibleItemItr.hasNext()) {
+                sourceItem = cast(IContributionItem) visibleItemItr.next();
+
+                // Retrieve the corresponding contribution item from SWT's
+                // data.
+                if (sourceIndex < coolItems.length) {
+                    destinationItem = cast(IContributionItem) coolItems[sourceIndex]
+                            .getData();
+                } else {
+                    destinationItem = null;
+                }
+
+                // The items match is they are equal or both separators.
+                if (destinationItem !is null) {
+                    if ((cast(Object)sourceItem).opEquals(cast(Object)destinationItem)) {
+                        sourceIndex++;
+                        destinationIndex++;
+                        sourceItem.update();
+                        continue;
+
+                    } else if ((destinationItem.isSeparator())
+                            && (sourceItem.isSeparator())) {
+                        coolItems[sourceIndex].setData(cast(Object)sourceItem);
+                        sourceIndex++;
+                        destinationIndex++;
+                        sourceItem.update();
+                        continue;
+
+                    }
+                }
+
+                // Otherwise, a new item has to be added.
+                int start = coolBar.getItemCount();
+                sourceItem.fill(coolBar, destinationIndex);
+                int newItems = coolBar.getItemCount() - start;
+                for (int i = 0; i < newItems; i++) {
+                    coolBar.getItem(destinationIndex++).setData(cast(Object)sourceItem);
+                }
+                changed = true;
+            }
+
+            // Remove any old widgets not accounted for.
+            for (int i = coolItems.length - 1; i >= sourceIndex; i--) {
+                final CoolItem item = coolItems[i];
+                if (!item.isDisposed()) {
+                    Control control = item.getControl();
+                    if (control !is null) {
+                        item.setControl(null);
+                        control.dispose();
+                    }
+                    item.dispose();
+                    changed = true;
+                }
+            }
+
+            // Update wrap indices.
+            updateWrapIndices();
+
+            // Update the sizes.
+            for (int i = 0; i < items.length; i++) {
+                IContributionItem item = items[i];
+                item.update(SIZE);
+            }
+
+            // if the coolBar was previously locked then lock it
+            if (relock) {
+                coolBar.setLocked(true);
+            }
+
+            if (changed) {
+                updateTabOrder();
+            }
+
+            // We are no longer dirty.
+            setDirty(false);
+        } finally {
+            coolBar.setRedraw(true);
+        }
+    }
+
+    /**
+     * Sets the tab order of the coolbar to the visual order of its items.
+     */
+    /* package */void updateTabOrder() {
+        if (coolBar !is null) {
+            CoolItem[] items = coolBar.getItems();
+            if (items !is null) {
+                ArrayList children = new ArrayList(items.length);
+                for (int i = 0; i < items.length; i++) {
+                    if ((items[i].getControl() !is null)
+                            && (!items[i].getControl().isDisposed())) {
+                        children.add(items[i].getControl());
+                    }
+                }
+                // Convert array
+                Control[] childrenArray;
+                childrenArray =  arraycast!(Control)(children.toArray());
+
+                if (childrenArray !is null) {
+                    coolBar.setTabList(childrenArray);
+                }
+
+            }
+        }
+    }
+
+    /**
+     * Updates the indices at which the cool bar should wrap.
+     */
+    private void updateWrapIndices() {
+        final IContributionItem[] items = getItems();
+        final int numRows = getNumRows(items) - 1;
+
+        // Generate the list of wrap indices.
+        final int[] wrapIndices = new int[numRows];
+        bool foundSeparator = false;
+        int j = 0;
+        CoolItem[] coolItems = (coolBar is null) ? null : coolBar.getItems();
+
+        for (int i = 0; i < items.length; i++) {
+            IContributionItem item = items[i];
+            CoolItem coolItem = findCoolItem(coolItems, item);
+            if (item.isSeparator()) {
+                foundSeparator = true;
+            }
+            if ((!item.isSeparator()) && (!item.isGroupMarker())
+                    && (item.isVisible()) && (coolItem !is null)
+                    && (foundSeparator)) {
+                wrapIndices[j] = coolBar.indexOf(coolItem);
+                j++;
+                foundSeparator = false;
+            }
+        }
+
+        /*
+         * Check to see if these new wrap indices are different than the old
+         * ones.
+         */
+        final int[] oldIndices = coolBar.getWrapIndices();
+        bool shouldUpdate = false;
+        if (oldIndices.length is wrapIndices.length) {
+            for (int i = 0; i < oldIndices.length; i++) {
+                if (oldIndices[i] !is wrapIndices[i]) {
+                    shouldUpdate = true;
+                    break;
+                }
+            }
+        } else {
+            shouldUpdate = true;
+        }
+
+        if (shouldUpdate) {
+            coolBar.setWrapIndices(wrapIndices);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/ExternalActionManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,804 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.ExternalActionManager;
+
+import org.eclipse.jface.action.IAction;
+
+
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.CommandEvent;
+import org.eclipse.core.commands.CommandManager;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.ICommandListener;
+import org.eclipse.core.commands.NotEnabledException;
+import org.eclipse.core.commands.ParameterizedCommand;
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.bindings.BindingManager;
+import org.eclipse.jface.bindings.BindingManagerEvent;
+import org.eclipse.jface.bindings.IBindingManagerListener;
+import org.eclipse.jface.bindings.Trigger;
+import org.eclipse.jface.bindings.TriggerSequence;
+import org.eclipse.jface.bindings.keys.KeySequence;
+import org.eclipse.jface.bindings.keys.KeyStroke;
+import org.eclipse.jface.bindings.keys.SWTKeySupport;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.Policy;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.ResourceBundle;
+import tango.text.convert.Format;
+
+/**
+ * <p>
+ * A manager for a callback facility which is capable of querying external
+ * interfaces for additional information about actions and action contribution
+ * items. This information typically includes things like accelerators and
+ * textual representations.
+ * </p>
+ * <p>
+ * <em>It is only necessary to use this mechanism if you will be using a mix of
+ * actions and commands, and wish the interactions to work properly.</em>
+ * </p>
+ * <p>
+ * For example, in the Eclipse workbench, this mechanism is used to allow the
+ * command architecture to override certain values in action contribution items.
+ * </p>
+ * <p>
+ * This class is not intended to be called or extended by any external clients.
+ * </p>
+ *
+ * @since 3.0
+ */
+public final class ExternalActionManager {
+
+    /**
+     * A simple implementation of the <code>ICallback</code> mechanism that
+     * simply takes a <code>BindingManager</code> and a
+     * <code>CommandManager</code>.
+     * <p>
+     * <b>Note:</b> this class is not intended to be subclassed by clients.
+     * </p>
+     *
+     * @since 3.1
+     */
+    public static class CommandCallback :
+            IBindingManagerListener, IBindingManagerCallback, IExecuteCallback {
+
+        /**
+         * The internationalization bundle for text produced by this class.
+         */
+        private static const ResourceBundle RESOURCE_BUNDLE;
+
+        /**
+         * The callback capable of responding to whether a command is active.
+         */
+        private const IActiveChecker activeChecker;
+
+        /**
+         * Check the applicability of firing an execution event for an action.
+         */
+        private /+final+/ IExecuteApplicable applicabilityChecker;
+
+        /**
+         * The binding manager for your application. Must not be
+         * <code>null</code>.
+         */
+        private const BindingManager bindingManager;
+
+        /**
+         * Whether a listener has been attached to the binding manager yet.
+         */
+        private bool bindingManagerListenerAttached = false;
+
+        /**
+         * The command manager for your application. Must not be
+         * <code>null</code>.
+         */
+        private const CommandManager commandManager;
+
+        /**
+         * A set of all the command identifiers that have been logged as broken
+         * so far. For each of these, there will be a listener on the
+         * corresponding command. If the command ever becomes defined, the item
+         * will be removed from this set and the listener removed. This value
+         * may be empty, but never <code>null</code>.
+         */
+        private const Set loggedCommandIds;
+
+        /**
+         * The list of listeners that have registered for property change
+         * notification. This is a map of command identifiers (<code>String</code>)
+         * to listeners (<code>IPropertyChangeListener</code> or
+         * <code>ListenerList</code> of <code>IPropertyChangeListener</code>).
+         */
+        private const Map registeredListeners;
+
+        static this(){
+            RESOURCE_BUNDLE = ResourceBundle.getBundle(
+                getImportData!("org.eclipse.jface.action.ExternalActionManager.properties"));
+        }
+        /**
+         * Constructs a new instance of <code>CommandCallback</code> with the
+         * workbench it should be using. All commands will be considered active.
+         *
+         * @param bindingManager
+         *            The binding manager which will provide the callback; must
+         *            not be <code>null</code>.
+         * @param commandManager
+         *            The command manager which will provide the callback; must
+         *            not be <code>null</code>.
+         *
+         * @since 3.1
+         */
+        public this(BindingManager bindingManager,
+                CommandManager commandManager) {
+            this(bindingManager, commandManager, new class IActiveChecker {
+                public bool isActive(String commandId) {
+                    return true;
+                }
+
+            }, new class IExecuteApplicable {
+                public bool isApplicable(IAction action) {
+                    return true;
+                }
+            });
+        }
+        /**
+         * Constructs a new instance of <code>CommandCallback</code> with the
+         * workbench it should be using.
+         *
+         * @param bindingManager
+         *            The binding manager which will provide the callback; must
+         *            not be <code>null</code>.
+         * @param commandManager
+         *            The command manager which will provide the callback; must
+         *            not be <code>null</code>.
+         * @param activeChecker
+         *            The callback mechanism for checking whether a command is
+         *            active; must not be <code>null</code>.
+         *
+         * @since 3.1
+         */
+        public this(BindingManager bindingManager,
+                CommandManager commandManager,
+                IActiveChecker activeChecker) {
+            this(bindingManager, commandManager, activeChecker,
+                    new class IExecuteApplicable {
+                public bool isApplicable(IAction action) {
+                    return true;
+                }
+            });
+        }
+        /**
+         * Constructs a new instance of <code>CommandCallback</code> with the
+         * workbench it should be using.
+         *
+         * @param bindingManager
+         *            The binding manager which will provide the callback; must
+         *            not be <code>null</code>.
+         * @param commandManager
+         *            The command manager which will provide the callback; must
+         *            not be <code>null</code>.
+         * @param activeChecker
+         *            The callback mechanism for checking whether a command is
+         *            active; must not be <code>null</code>.
+         * @param checker
+         *            The callback to check if an IAction should fire execution
+         *            events.
+         *
+         * @since 3.4
+         */
+        public this(BindingManager bindingManager,
+                CommandManager commandManager,
+                IActiveChecker activeChecker,
+                IExecuteApplicable checker) {
+            loggedCommandIds = new HashSet();
+            registeredListeners = new HashMap();
+            if (bindingManager is null) {
+                throw new NullPointerException(
+                        "The callback needs a binding manager"); //$NON-NLS-1$
+            }
+
+            if (commandManager is null) {
+                throw new NullPointerException(
+                        "The callback needs a command manager"); //$NON-NLS-1$
+            }
+
+            if (activeChecker is null) {
+                throw new NullPointerException(
+                        "The callback needs an active callback"); //$NON-NLS-1$
+            }
+            if (checker is null) {
+                throw new NullPointerException(
+                        "The callback needs an applicable callback"); //$NON-NLS-1$
+            }
+
+            this.activeChecker = activeChecker;
+            this.bindingManager = bindingManager;
+            this.commandManager = commandManager;
+            this.applicabilityChecker = checker;
+        }
+
+        /**
+         * @see org.eclipse.jface.action.ExternalActionManager.ICallback#addPropertyChangeListener(String,
+         *      IPropertyChangeListener)
+         */
+        public final void addPropertyChangeListener(String commandId,
+                IPropertyChangeListener listener) {
+            auto existing = cast(Object)registeredListeners.get(commandId);
+            if (null !is cast(ListenerList)existing ) {
+                (cast(ListenerList) existing).add(cast(Object)listener);
+            } else if (existing !is null) {
+                ListenerList listeners = new ListenerList(ListenerList.IDENTITY);
+                listeners.add(existing);
+                listeners.add(cast(Object)listener);
+            } else {
+                registeredListeners.put(commandId, cast(Object)listener);
+            }
+            if (!bindingManagerListenerAttached) {
+                bindingManager.addBindingManagerListener(this);
+                bindingManagerListenerAttached = true;
+            }
+        }
+
+        public final void bindingManagerChanged(BindingManagerEvent event) {
+            if (event.isActiveBindingsChanged()) {
+                final Iterator listenerItr = registeredListeners.entrySet()
+                        .iterator();
+                while (listenerItr.hasNext()) {
+                    Map.Entry entry = cast(Map.Entry) listenerItr.next();
+                    String commandId = stringcast( entry.getKey());
+                    Command command = commandManager
+                            .getCommand(commandId);
+                    ParameterizedCommand parameterizedCommand = new ParameterizedCommand(
+                            command, null);
+                    if (event.isActiveBindingsChangedFor(parameterizedCommand)) {
+                        Object value = entry.getValue();
+                        PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(event
+                                .getManager(), IAction.TEXT, null, null);
+                        if (null !is cast(ListenerList)value ) {
+                            Object[] listeners= (cast(ListenerList) value).getListeners();
+                            for (int i = 0; i < listeners.length; i++) {
+                                final IPropertyChangeListener listener = cast(IPropertyChangeListener) listeners[i];
+                                listener.propertyChange(propertyChangeEvent);
+                            }
+                        } else {
+                            final IPropertyChangeListener listener = cast(IPropertyChangeListener) value;
+                            listener.propertyChange(propertyChangeEvent);
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * @see org.eclipse.jface.action.ExternalActionManager.ICallback#getAccelerator(String)
+         */
+        public Integer getAccelerator(String commandId) {
+            TriggerSequence triggerSequence = bindingManager
+                    .getBestActiveBindingFor(commandId);
+            if (triggerSequence !is null) {
+                Trigger[] triggers = triggerSequence.getTriggers();
+                if (triggers.length is 1) {
+                    Trigger trigger = triggers[0];
+                    if ( auto keyStroke = cast(KeyStroke) trigger ) {
+                        int accelerator = SWTKeySupport
+                                .convertKeyStrokeToAccelerator(keyStroke);
+                        return new Integer(accelerator);
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        /**
+         * @see org.eclipse.jface.action.ExternalActionManager.ICallback#getAcceleratorText(String)
+         */
+        public final String getAcceleratorText(String commandId) {
+            TriggerSequence triggerSequence = bindingManager
+                    .getBestActiveBindingFor(commandId);
+            if (triggerSequence is null) {
+                return null;
+            }
+
+            return triggerSequence.format();
+        }
+
+        /**
+         * Returns the active bindings for a particular command identifier.
+         *
+         * @param commandId
+         *            The identifier of the command whose bindings are
+         *            requested. This argument may be <code>null</code>. It
+         *            is assumed that the command has no parameters.
+         * @return The array of active triggers (<code>TriggerSequence</code>)
+         *         for a particular command identifier. This value is guaranteed
+         *         not to be <code>null</code>, but it may be empty.
+         * @since 3.2
+         */
+        public final TriggerSequence[] getActiveBindingsFor(
+                String commandId) {
+            return bindingManager.getActiveBindingsFor(commandId);
+        }
+
+        /**
+         * @see org.eclipse.jface.action.ExternalActionManager.ICallback#isAcceleratorInUse(int)
+         */
+        public final bool isAcceleratorInUse(int accelerator) {
+            KeySequence keySequence = KeySequence
+                    .getInstance(SWTKeySupport
+                            .convertAcceleratorToKeyStroke(accelerator));
+            return bindingManager.isPerfectMatch(keySequence)
+                    || bindingManager.isPartialMatch(keySequence);
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * Calling this method with an undefined command id will generate a log
+         * message.
+         */
+        public final bool isActive(String commandId) {
+            if (commandId !is null) {
+                Command command = commandManager.getCommand(commandId);
+
+                if (!command.isDefined()
+                        && (!loggedCommandIds.contains(commandId))) {
+                    // The command is not yet defined, so we should log this.
+                    String message = Format(Util
+                            .translateString(RESOURCE_BUNDLE,
+                                    "undefinedCommand.WarningMessage", null), //$NON-NLS-1$
+                            [ command.getId() ]);
+                    IStatus status = new Status(IStatus.ERROR,
+                            "org.eclipse.jface", //$NON-NLS-1$
+                            0, message, new Exception(null));
+                    Policy.getLog().log(status);
+
+                    // And remember this item so we don't log it again.
+                    loggedCommandIds.add(commandId);
+                    command.addCommandListener(new class(command,commandId) ICommandListener {
+                        Command command_;
+                        String commandId_;
+                        this(Command a,String b){
+                            command_=a;
+                            commandId_=b;
+                        }
+                        /*
+                         * (non-Javadoc)
+                         *
+                         * @see org.eclipse.ui.commands.ICommandListener#commandChanged(org.eclipse.ui.commands.CommandEvent)
+                         */
+                        public final void commandChanged(
+                                CommandEvent commandEvent) {
+                            if (command_.isDefined()) {
+                                command_.removeCommandListener(this);
+                                loggedCommandIds.remove(commandId_);
+                            }
+                        }
+                    });
+
+                    return true;
+                }
+
+                return activeChecker.isActive(commandId);
+            }
+
+            return true;
+        }
+
+        /**
+         * @see org.eclipse.jface.action.ExternalActionManager.ICallback#removePropertyChangeListener(String,
+         *      IPropertyChangeListener)
+         */
+        public final void removePropertyChangeListener(String commandId,
+                IPropertyChangeListener listener) {
+            Object existing = cast(Object) registeredListeners.get(commandId);
+            if (existing is cast(Object)listener) {
+                registeredListeners.remove(commandId);
+                if (registeredListeners.isEmpty()) {
+                    bindingManager.removeBindingManagerListener(this);
+                    bindingManagerListenerAttached = false;
+                }
+            } else if (null !is cast(ListenerList)existing ) {
+                ListenerList existingList = cast(ListenerList) existing;
+                existingList.remove(cast(Object)listener);
+                if (existingList.size() is 1) {
+                    registeredListeners.put(commandId, existingList.getListeners()[0]);
+                }
+            }
+        }
+
+        /**
+         * @since 3.4
+         */
+        public void preExecute(IAction action, Event event) {
+            String actionDefinitionId = action.getActionDefinitionId();
+            if (actionDefinitionId is null
+                    || !applicabilityChecker.isApplicable(action)) {
+                return;
+            }
+            Command command = commandManager.getCommand(actionDefinitionId);
+            ExecutionEvent executionEvent = new ExecutionEvent(command,
+                    Collections.EMPTY_MAP, event, null);
+
+            commandManager.firePreExecute(actionDefinitionId, executionEvent);
+        }
+
+        /**
+         * @since 3.4
+         */
+        public void postExecuteSuccess(IAction action, Object returnValue) {
+            String actionDefinitionId = action.getActionDefinitionId();
+            if (actionDefinitionId is null
+                    || !applicabilityChecker.isApplicable(action)) {
+                return;
+            }
+            commandManager.firePostExecuteSuccess(actionDefinitionId, returnValue);
+        }
+
+        /**
+         * @since 3.4
+         */
+        public void postExecuteFailure(IAction action,
+                ExecutionException exception) {
+            String actionDefinitionId = action.getActionDefinitionId();
+            if (actionDefinitionId is null
+                    || !applicabilityChecker.isApplicable(action)) {
+                return;
+            }
+            commandManager.firePostExecuteFailure(actionDefinitionId, exception);
+        }
+
+        /**
+         * @since 3.4
+         */
+        public void notDefined(IAction action, NotDefinedException exception) {
+            String actionDefinitionId = action.getActionDefinitionId();
+            if (actionDefinitionId is null
+                    || !applicabilityChecker.isApplicable(action)) {
+                return;
+            }
+            commandManager.fireNotDefined(actionDefinitionId, exception);
+        }
+
+        /**
+         * @since 3.4
+         */
+        public void notEnabled(IAction action, NotEnabledException exception) {
+            String actionDefinitionId = action.getActionDefinitionId();
+            if (actionDefinitionId is null
+                    || !applicabilityChecker.isApplicable(action)) {
+                return;
+            }
+            commandManager.fireNotEnabled(actionDefinitionId, exception);
+        }
+    }
+
+    /**
+     * Defines a callback mechanism for developer who wish to further control
+     * the visibility of legacy action-based contribution items.
+     *
+     * @since 3.1
+     */
+    public interface IActiveChecker {
+        /**
+         * Checks whether the command with the given identifier should be
+         * considered active. This can be used in systems using some kind of
+         * user interface filtering (e.g., activities in the Eclipse workbench).
+         *
+         * @param commandId
+         *            The identifier for the command; must not be
+         *            <code>null</code>
+         * @return <code>true</code> if the command is active;
+         *         <code>false</code> otherwise.
+         */
+        public bool isActive(String commandId);
+    }
+
+    /**
+     * <p>
+     * A callback which communicates with the applications binding manager. This
+     * interface provides more information from the binding manager, which
+     * allows greater integration. Implementing this interface is preferred over
+     * {@link ExternalActionManager.ICallback}.
+     * </p>
+     * <p>
+     * Clients may implement this interface, but must not extend.
+     * </p>
+     *
+     * @since 3.2
+     */
+    public interface IBindingManagerCallback : ICallback {
+
+        /**
+         * <p>
+         * Returns the active bindings for a particular command identifier.
+         * </p>
+         *
+         * @param commandId
+         *            The identifier of the command whose bindings are
+         *            requested. This argument may be <code>null</code>. It
+         *            is assumed that the command has no parameters.
+         * @return The array of active triggers (<code>TriggerSequence</code>)
+         *         for a particular command identifier. This value is guaranteed
+         *         not to be <code>null</code>, but it may be empty.
+         */
+        public TriggerSequence[] getActiveBindingsFor(String commandId);
+    }
+
+    /**
+     * An overridable mechanism to filter certain IActions from the execution
+     * bridge.
+     *
+     * @since 3.4
+     */
+    public interface IExecuteApplicable {
+        /**
+         * Allow the callback to filter out actions that should not fire
+         * execution events.
+         *
+         * @param action
+         *            The action with an actionDefinitionId
+         * @return true if this action should be considered.
+         */
+        public bool isApplicable(IAction action);
+    }
+
+    /**
+     * <p>
+     * A callback for executing execution events. Allows
+     * <code>ActionContributionItems</code> to fire useful events.
+     * </p>
+     * <p>
+     * Clients must not implement this interface and must not extend.
+     * </p>
+     *
+     * @since 3.4
+     *
+     */
+    public interface IExecuteCallback {
+
+        /**
+         * Fires a <code>NotEnabledException</code> because the action was not
+         * enabled.
+         *
+         * @param action
+         *          The action contribution that caused the exception,
+         *          never <code>null</code>.
+         * @param exception
+         *          The <code>NotEnabledException</code>, never <code>null</code>.
+         */
+        public void notEnabled(IAction action, NotEnabledException exception);
+
+        /**
+         * Fires a <code>NotDefinedException</code> because the action was not
+         * defined.
+         *
+         * @param action
+         *          The action contribution that caused the exception,
+         *          never <code>null</code>.
+         * @param exception
+         *          The <code>NotDefinedException</code>, never <code>null</code>.
+         */
+        public void notDefined(IAction action, NotDefinedException exception);
+
+        /**
+         * Fires an execution event before an action is run.
+         *
+         * @param action
+         *            The action contribution that requires an
+         *            execution event to be fired. Cannot be <code>null</code>.
+         * @param e
+         *            The SWT Event, may be <code>null</code>.
+         *
+         */
+        public void preExecute(IAction action,
+                Event e);
+
+        /**
+         * Fires an execution event when the action returned a success.
+         *
+         * @param action
+         *            The action contribution that requires an
+         *            execution event to be fired. Cannot be <code>null</code>.
+         * @param returnValue
+         *            The command's result, may be <code>null</code>.
+         *
+         */
+        public void postExecuteSuccess(IAction action,
+                Object returnValue);
+
+        /**
+         * Creates an <code>ExecutionException</code> when the action returned
+         * a failure.
+         *
+         * @param action
+         *          The action contribution that caused the exception,
+         *          never <code>null</code>.
+         * @param exception
+         *          The <code>ExecutionException</code>, never <code>null</code>.
+         */
+        public void postExecuteFailure(IAction action,
+                ExecutionException exception);
+    }
+
+    /**
+     * A callback mechanism for some external tool to communicate extra
+     * information to actions and action contribution items.
+     *
+     * @since 3.0
+     */
+    public interface ICallback {
+
+        /**
+         * <p>
+         * Adds a listener to the object referenced by <code>identifier</code>.
+         * This listener will be notified if a property of the item is to be
+         * changed. This identifier is specific to mechanism being used. In the
+         * case of the Eclipse workbench, this is the command identifier.
+         * </p>
+         * <p>
+         * Has no effect if an identical listener has already been added for
+         * the <code>identifier</code>.
+         * </p>
+         *
+         * @param identifier
+         *            The identifier of the item to which the listener should be
+         *            attached; must not be <code>null</code>.
+         * @param listener
+         *            The listener to be added; must not be <code>null</code>.
+         */
+        public void addPropertyChangeListener(String identifier,
+                IPropertyChangeListener listener);
+
+        /**
+         * An accessor for the accelerator associated with the item indicated by
+         * the identifier. This identifier is specific to mechanism being used.
+         * In the case of the Eclipse workbench, this is the command identifier.
+         *
+         * @param identifier
+         *            The identifier of the item from which the accelerator
+         *            should be obtained ; must not be <code>null</code>.
+         * @return An integer representation of the accelerator. This is the
+         *         same accelerator format used by SWT.
+         */
+        public Integer getAccelerator(String identifier);
+
+        /**
+         * An accessor for the accelerator text associated with the item
+         * indicated by the identifier. This identifier is specific to mechanism
+         * being used. In the case of the Eclipse workbench, this is the command
+         * identifier.
+         *
+         * @param identifier
+         *            The identifier of the item from which the accelerator text
+         *            should be obtained ; must not be <code>null</code>.
+         * @return A string representation of the accelerator. This is the
+         *         string representation that should be displayed to the user.
+         */
+        public String getAcceleratorText(String identifier);
+
+        /**
+         * Checks to see whether the given accelerator is being used by some
+         * other mechanism (outside of the menus controlled by JFace). This is
+         * used to keep JFace from trying to grab accelerators away from someone
+         * else.
+         *
+         * @param accelerator
+         *            The accelerator to check -- in SWT's internal accelerator
+         *            format.
+         * @return <code>true</code> if the accelerator is already being used
+         *         and shouldn't be used again; <code>false</code> otherwise.
+         */
+        public bool isAcceleratorInUse(int accelerator);
+
+        /**
+         * Checks whether the item matching this identifier is active. This is
+         * used to decide whether a contribution item with this identifier
+         * should be made visible. An inactive item is not visible.
+         *
+         * @param identifier
+         *            The identifier of the item from which the active state
+         *            should be retrieved; must not be <code>null</code>.
+         * @return <code>true</code> if the item is active; <code>false</code>
+         *         otherwise.
+         */
+        public bool isActive(String identifier);
+
+        /**
+         * Removes a listener from the object referenced by
+         * <code>identifier</code>. This identifier is specific to mechanism
+         * being used. In the case of the Eclipse workbench, this is the command
+         * identifier.
+         *
+         * @param identifier
+         *            The identifier of the item to from the listener should be
+         *            removed; must not be <code>null</code>.
+         * @param listener
+         *            The listener to be removed; must not be <code>null</code>.
+         */
+        public void removePropertyChangeListener(String identifier,
+                IPropertyChangeListener listener);
+
+    }
+
+    /**
+     * The singleton instance of this class. This value may be <code>null</code>--
+     * if it has not yet been initialized.
+     */
+    private static ExternalActionManager instance;
+
+    /**
+     * Retrieves the current singleton instance of this class.
+     *
+     * @return The singleton instance; this value is never <code>null</code>.
+     */
+    public static ExternalActionManager getInstance() {
+        if (instance is null) {
+            instance = new ExternalActionManager();
+        }
+
+        return instance;
+    }
+
+    /**
+     * The callback mechanism to use to retrieve extra information.
+     */
+    private ICallback callback;
+
+    /**
+     * Constructs a new instance of <code>ExternalActionManager</code>.
+     */
+    private this() {
+        // This is a singleton class. Only this class should create an instance.
+    }
+
+    /**
+     * An accessor for the current call back.
+     *
+     * @return The current callback mechanism being used. This is the callback
+     *         that should be queried for extra information about actions and
+     *         action contribution items. This value may be <code>null</code>
+     *         if there is no extra information.
+     */
+    public ICallback getCallback() {
+        return callback;
+    }
+
+    /**
+     * A mutator for the current call back
+     *
+     * @param callbackToUse
+     *            The new callback mechanism to use; this value may be
+     *            <code>null</code> if the default is acceptable (i.e., no
+     *            extra information will provided to actions).
+     */
+    public void setCallback(ICallback callbackToUse) {
+        callback = callbackToUse;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/ExternalActionManager.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,12 @@
+###############################################################################
+# 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
+###############################################################################
+
+undefinedCommand.WarningMessage = The command ("{0}") is undefined
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/GroupMarker.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.GroupMarker;
+
+import org.eclipse.jface.action.AbstractGroupMarker;
+
+import java.lang.all;
+
+/**
+ * A group marker is a special kind of contribution item denoting
+ * the beginning of a group. These groups are used to structure
+ * the list of items. Unlike regular contribution items and
+ * separators, group markers have no visual representation.
+ * The name of the group is synonymous with the contribution item id.
+ * <p>
+ * This class may be instantiated; it is not intended to be
+ * subclassed outside the framework.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class GroupMarker : AbstractGroupMarker {
+    /**
+     * Create a new group marker with the given name.
+     * The group name must not be <code>null</code> or the empty string.
+     * The group name is also used as the item id.
+     *
+     * @param groupName the name of the group
+     */
+    public this(String groupName) {
+        super(groupName);
+    }
+
+    /**
+     * The <code>GroupMarker</code> implementation of this method
+     * returns <code>false</code> since group markers are always invisible.
+     */
+    public override bool isVisible() {
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/IAction.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,524 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.IAction;
+
+import org.eclipse.jface.action.IMenuCreator;
+
+import org.eclipse.swt.events.HelpListener;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.core.commands.IHandlerAttributes;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.util.IPropertyChangeListener;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * An action represents the non-UI side of a command which can be triggered
+ * by the end user. Actions are typically associated with buttons, menu items,
+ * and items in tool bars. The controls for a command are built by some container,
+ * which furnished the context where these controls appear and configures
+ * them with data from properties declared by the action. When the end user
+ * triggers the command via its control, the action's <code>run</code>
+ * method is invoked to do the real work.
+ * <p>
+ * Actions support a predefined set of properties (and possibly others as well).
+ * Clients of an action may register property change listeners so that they get
+ * notified whenever the value of a property changes.
+ * </p>
+ * <p>
+ * Clients should subclass the abstract base class <code>Action</code> to define
+ * concrete actions rather than implementing <code>IAction</code> from scratch.
+ * </p>
+ * <p>
+ * This interface exists only to define the API for actions.
+ * It is not intended to be implemented by clients.
+ * </p>
+ *
+ * @see Action
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IAction {
+
+    /**
+     * Action style constant (value <code>0</code>) indicating action style
+     * is not specified yet. By default, the action will assume a push button
+     * style. If <code>setChecked</code> is called, then the style will change
+     * to a check box, or if <code>setMenuCreator</code> is called, then the
+     * style will change to a drop down menu.
+     *
+     * @since 2.1
+     */
+    public static const int AS_UNSPECIFIED = 0x00;
+
+    /**
+     * Action style constant (value <code>1</code>) indicating action is
+     * a simple push button.
+     */
+    public static const int AS_PUSH_BUTTON = 0x01;
+
+    /**
+     * Action style constant (value <code>2</code>) indicating action is
+     * a check box (or a toggle button).
+     */
+    public static const int AS_CHECK_BOX = 0x02;
+
+    /**
+     * Action style constant (value <code>4</code>) indicating action is
+     * a drop down menu.
+     */
+    public static const int AS_DROP_DOWN_MENU = 0x04;
+
+    /**
+     * Action style constant (value <code>8</code>) indicating action is
+     * a radio button.
+     *
+     * @since 2.1
+     */
+    public static const int AS_RADIO_BUTTON = 0x08;
+
+    /**
+     * Property name of an action's text (value <code>"text"</code>).
+     */
+    public static const String TEXT = "text"; //$NON-NLS-1$
+
+    /**
+     * Property name of an action's enabled state
+     * (value <code>"enabled"</code>).
+     */
+    public static const String ENABLED = "enabled"; //$NON-NLS-1$
+
+    /**
+     * Property name of an action's image (value <code>"image"</code>).
+     */
+    public static const String IMAGE = "image"; //$NON-NLS-1$
+
+    /**
+     * Property name of an action's tooltip text (value <code>"toolTipText"</code>).
+     */
+    public static const String TOOL_TIP_TEXT = "toolTipText"; //$NON-NLS-1$
+
+    /**
+     * Property name of an action's description (value <code>"description"</code>).
+     * Typically the description is shown as a (longer) help text in the status line.
+     */
+    public static const String DESCRIPTION = "description"; //$NON-NLS-1$
+
+    /**
+     * Property name of an action's checked status (value
+     * <code>"checked"</code>). Applicable when the style is
+     * <code>AS_CHECK_BOX</code> or <code>AS_RADIO_BUTTON</code>.
+     */
+    public static const String CHECKED = "checked"; //$NON-NLS-1$
+
+    /**
+     * Property name of an action's success/fail result
+     * (value <code>"result"</code>). The values are
+     * <code>Boolean.TRUE</code> if running the action succeeded and 
+     * <code>Boolean.FALSE</code> if running the action failed or did not
+     * complete.
+     * <p>
+     * Not all actions report whether they succeed or fail. This property
+     * is provided for use by actions that may be invoked by clients that can
+     * take advantage of this information when present (for example, actions
+     * used in cheat sheets). Clients should always assume that running the
+     * action succeeded in the absence of notification to the contrary.
+     * </p>
+     *
+     * @since 3.0
+     */
+    public static const String RESULT = "result"; //$NON-NLS-1$
+
+    /**
+     * Property name of an action's handler. Some actions delegate some or all
+     * of their behaviour or state to another object. In this case, if the
+     * object to which behaviour has been delegated changes, then a property
+     * change event should be sent with this name.
+     *
+     * This is used to support backward compatibility of actions within the
+     * commands framework.
+     *
+     * @since 3.1
+     */
+    public static const String HANDLED = IHandlerAttributes.ATTRIBUTE_HANDLED;
+
+    /**
+     * Adds a property change listener to this action.
+     * Has no effect if an identical listener is already registered.
+     *
+     * @param listener a property change listener
+     */
+    public void addPropertyChangeListener(IPropertyChangeListener listener);
+
+    /**
+     * Returns the accelerator keycode for this action.
+     * The result is the bit-wise OR of zero or more modifier masks
+     * and a key, as explained in <code>MenuItem.getAccelerator</code>.
+     *
+     * @return the accelerator keycode
+     * @see org.eclipse.swt.widgets.MenuItem#getAccelerator()
+     */
+    public int getAccelerator();
+
+    /**
+     * Returns the action definition id of this action.
+     *
+     * @return the action definition id of this action, or
+     * <code>null</code> if none
+     * @since 2.0
+     */
+    public String getActionDefinitionId();
+
+    /**
+     * Returns the action's description if it has one.
+     * Otherwise it returns <code>getToolTipText()</code>.
+     *
+     * @return a description for the action; may be <code>null</code>
+     */
+    public String getDescription();
+
+    /**
+     * Returns the disabled image for this action as an image descriptor.
+     * <p>
+     * This method is associated with the <code>IMAGE</code> property;
+     * property change events are reported when its value changes.
+     * </p>
+     *
+     * @return the image, or <code>null</code> if this action has no image
+     * @see #IMAGE
+     */
+    public ImageDescriptor getDisabledImageDescriptor();
+
+    /**
+     * Returns a help listener for this action.
+     *
+     * @return a help listener for this action
+     */
+    public HelpListener getHelpListener();
+
+    /**
+     * Returns the hover image for this action as an image descriptor.
+     * <p>
+     * Hover images will be used on platforms that support changing the image
+     * when the user hovers over the item. This method is associated with
+     * the <code>IMAGE</code> property;
+     * property change events are reported when its value changes.
+     * </p>
+     *
+     * @return the image, or <code>null</code> if this action has no image
+     * @see #IMAGE
+     */
+    public ImageDescriptor getHoverImageDescriptor();
+
+    /**
+     * Returns a unique identifier for this action, or <code>null</code> if it has
+     * none.
+     *
+     * @return the action id, or <code>null</code> if none
+     */
+    public String getId();
+
+    /**
+     * Returns the image for this action as an image descriptor.
+     * <p>
+     * This method is associated with the <code>IMAGE</code> property;
+     * property change events are reported when its value changes.
+     * </p>
+     *
+     * @return the image, or <code>null</code> if this action has no image
+     * @see #IMAGE
+     */
+    public ImageDescriptor getImageDescriptor();
+
+    /**
+     * Returns the menu creator for this action.
+     *
+     * @return the menu creator, or <code>null</code> if none
+     */
+    public IMenuCreator getMenuCreator();
+
+    /**
+     * Return this action's style.
+     *
+     * @return one of <code>AS_PUSH_BUTTON</code>, <code>AS_CHECK_BOX</code>,
+     * <code>AS_RADIO_BUTTON</code> and <code>AS_DROP_DOWN_MENU</code>.
+     */
+    public int getStyle();
+
+    /**
+     * Returns the text for this action.
+     * <p>
+     * This method is associated with the <code>TEXT</code> property;
+     * property change events are reported when its value changes.
+     * </p>
+     *
+     * @return the text, or <code>null</code> if none
+     * @see #TEXT
+     */
+    public String getText();
+
+    /**
+     * Returns the tool tip text for this action.
+     * <p>
+     * This method is associated with the <code>TOOL_TIP_TEXT</code> property;
+     * property change events are reported when its value changes.
+     * </p>
+     *
+     * @return the tool tip text, or <code>null</code> if none
+     * @see #TOOL_TIP_TEXT
+     */
+    public String getToolTipText();
+
+    /**
+     * Returns the checked status of this action. Applicable only if the style is
+     * <code>AS_CHECK_BOX</code> or <code>AS_RADIO_BUTTON</code>.
+     * <p>
+     * This method is associated with the <code>CHECKED</code> property;
+     * property change events are reported when its value changes.
+     * </p>
+     *
+     * @return the checked status
+     * @see #CHECKED
+     */
+    public bool isChecked();
+
+    /**
+     * Returns whether this action is enabled.
+     * <p>
+     * This method is associated with the <code>ENABLED</code> property;
+     * property change events are reported when its value changes.
+     * </p>
+     *
+     * @return <code>true</code> if enabled, and
+     *   <code>false</code> if disabled
+     * @see #ENABLED
+     */
+    public bool isEnabled();
+
+    /**
+     * Returns whether this action is handled. In the default case, this is
+     * always <code>true</code>. However, if the action delegates some of its
+     * behaviour to some other object, then this method should answer whether
+     * such an object is currently available.
+     *
+     * @return <code>true</code> if all of the action's behaviour is
+     *         available; <code>false</code> otherwise.
+     * @since 3.1
+     */
+    public bool isHandled();
+
+    /**
+     * Removes the given listener from this action.
+     * Has no effect if an identical listener is not registered.
+     *
+     * @param listener a property change listener
+     */
+    public void removePropertyChangeListener(IPropertyChangeListener listener);
+
+    /**
+     * Runs this action.
+     * Each action implementation must define the steps needed to carry out this action.
+     * The default implementation of this method in <code>Action</code>
+     * does nothing.
+     */
+    public void run();
+
+    /**
+     * Runs this action, passing the triggering SWT event.
+     * As of 2.0, <code>ActionContributionItem</code> calls this method
+     * instead of <code>run()</code>.
+     * The default implementation of this method in <code>Action</code>
+     * simply calls <code>run()</code> for backwards compatibility.
+     *
+     * @param event the SWT event which triggered this action being run
+     * @since 2.0
+     */
+    public void runWithEvent(Event event);
+
+    /**
+     * Sets the action definition id of this action.
+     *
+     * @param id the action definition id
+     * @since 2.0
+     */
+    public void setActionDefinitionId(String id);
+
+    /**
+     * Sets the checked status of this action. Applicable for the styles
+     * <code>AS_CHECK_BOX</code> or <code>AS_RADIO_BUTTON</code>.
+     * <p>
+     * Fires a property change event for the <code>CHECKED</code> property
+     * if the checked status actually changes as a consequence.
+     * </p>
+     *
+     * @param checked the new checked status
+     * @see #CHECKED
+     */
+    public void setChecked(bool checked);
+
+    /**
+     * Sets this action's description.
+     * Typically the description is shown as a (longer) help text in the status line.
+     * <p>
+     * Fires a property change event for the <code>DESCRIPTION</code> property
+     * if the description actually changes as a consequence.
+     * </p>
+     *
+     * @param text the description, or <code>null</code> to clear the description
+     * @see #DESCRIPTION
+     */
+    public void setDescription(String text);
+
+    /**
+     * Sets the disabled image for this action, as an image descriptor.
+     * <p>
+     * Disabled images will be used on platforms that support changing the image
+     * when the item is disabled.Fires a property change event for
+     * the <code>IMAGE</code> property
+     * if the image actually changes as a consequence.
+     * </p>
+     *
+     * @param newImage the image, or <code>null</code> if this
+     *   action should not have an image
+     * @see #IMAGE
+     */
+    public void setDisabledImageDescriptor(ImageDescriptor newImage);
+
+    /**
+     * Sets the enabled state of this action.
+     * <p>
+     * When an action is in the enabled state, the control associated with
+     * it is active; triggering it will end up inkoking this action's
+     * <code>run</code> method.
+     * </p>
+     * <p>
+     * Fires a property change event for the <code>ENABLED</code> property
+     * if the enabled state actually changes as a consequence.
+     * </p>
+     *
+     * @param enabled <code>true</code> to enable, and
+     *   <code>false</code> to disable
+     * @see #ENABLED
+     */
+    public void setEnabled(bool enabled);
+
+    /**
+     * Sets a help listener for this action.
+     *
+     * @param listener a help listener for this action
+     */
+    public void setHelpListener(HelpListener listener);
+
+    /**
+     * Sets the hover image for this action, as an image descriptor.
+     * <p>
+     * Hover images will be used on platforms that support changing the image
+     * when the user hovers over the item.Fires a property change event for
+     * the <code>IMAGE</code> property
+     * if the image actually changes as a consequence.
+     * </p>
+     *
+     * @param newImage the image, or <code>null</code> if this
+     *   action should not have an image
+     * @see #IMAGE
+     */
+    public void setHoverImageDescriptor(ImageDescriptor newImage);
+
+    /**
+     * Sets the unique identifier for this action. This is used to identify actions
+     * when added to a contribution manager.
+     * It should be set when the action is created.  It should not be modified once
+     * the action is part of an action contribution item.
+     *
+     * @param id the action id
+     *
+     * @see ActionContributionItem
+     * @see IContributionItem#getId
+     */
+    public void setId(String id);
+
+    /**
+     * Sets the image for this action, as an image descriptor.
+     * <p>
+     * Fires a property change event for the <code>IMAGE</code> property
+     * if the image actually changes as a consequence.
+     * </p>
+     *
+     * @param newImage the image, or <code>null</code> if this
+     *   action should not have an image
+     * @see #IMAGE
+     */
+    public void setImageDescriptor(ImageDescriptor newImage);
+
+    /**
+     * Sets the menu creator for this action. Applicable for style
+     * <code>AS_DROP_DOWN_MENU</code>.
+     *
+     * @param creator the menu creator, or <code>null</code> if none
+     */
+    public void setMenuCreator(IMenuCreator creator);
+
+    /**
+     * Sets the text for this action.
+     * <p>
+     * An accelerator specification may follow the actual text, separated from it by
+     * an '@' or a '\t' character.  An accelerator specification consists of zero or more
+     * modifier tokens followed by a key code token.  The tokens are separated by a '+' character.
+     * </p>
+     * <p>
+     * Fires a property change event for the <code>TEXT</code> property
+     * if the text actually changes as a consequence.
+     * </p>
+     *
+     * @param text the text, or <code>null</code> if none
+     * @see #TEXT
+     * @see Action#findModifier
+     * @see Action#findKeyCode
+     */
+    public void setText(String text);
+
+    /**
+     * Sets the tool tip text for this action.
+     * <p>
+     * Fires a property change event for the <code>TOOL_TIP_TEXT</code> property
+     * if the tool tip text actually changes as a consequence.
+     * </p>
+     *
+     * @param text the tool tip text, or <code>null</code> if none
+     * @see #TOOL_TIP_TEXT
+     */
+    public void setToolTipText(String text);
+
+    /**
+     * <p>
+     * Sets the accelerator keycode that this action maps to. This is a bitwise OR
+     * of zero or more SWT key modifier masks (i.e. SWT.CTRL or SWT.ALT) and a
+     * character code. For example, for Ctrl+Z, use <code>SWT.CTRL | 'Z'</code>.
+     * Use 0 for no accelerator.
+     * </p>
+     * <p>
+     * This method should no longer be used for actions in the Eclipse workbench.
+     * <code>IWorkbenchCommandSupport</code> and
+     * <code>IWorkbenchContextSupport</code> provide all the functionality
+     * required for key bindings. If you set an accelerator using this method, then
+     * it will not work in the workbench if it conflicts any existing key binding,
+     * or if there is a different key binding defined for this action's definition
+     * id. The definition id should be used instead -- referring to the command in
+     * the workbench from which the key binding should be retrieved.
+     * </p>
+     *
+     * @param keycode
+     *            the keycode to be accepted.
+     */
+    public void setAccelerator(int keycode);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/IContributionItem.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.IContributionItem;
+
+import org.eclipse.jface.action.IContributionManager;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.CoolBar;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.ToolBar;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A contribution item represents a contribution to a shared UI resource such as a
+ * menu or tool bar. More generally, contribution items are managed by a contribution
+ * manager.
+ * For instance, in a tool bar a contribution item is a tool bar button or a separator.
+ * In a menu bar a contribution item is a menu, and in a menu a contribution item
+ * is a menu item or separator.
+ * <p>
+ * A contribution item can realize itself in different SWT widgets, using the different
+ * <code>fill</code> methods.  The same type of contribution item can be used with a
+ * <code>MenuBarManager</code>, <code>ToolBarManager</code>, <code>CoolBarManager</code>,
+ * </code>or a <code>StatusLineManager</code>.
+ * </p>
+ * <p>
+ * This interface is internal to the framework; it should not be implemented outside
+ * the framework.
+ * </p>
+ *
+ * @see IContributionManager
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IContributionItem {
+
+    /**
+     * Disposes of this contribution item. Called by the parent
+     * contribution manager when the manager is being disposed.
+     * Clients should not call this method directly, unless they
+     * have removed this contribution item from the containing
+     * IContributionManager before the contribution lifecycle
+     * has ended.
+     *
+     * @since 2.1
+     */
+    public void dispose();
+
+    /**
+     * Fills the given composite control with controls representing this
+     * contribution item.  Used by <code>StatusLineManager</code>.
+     *
+     * @param parent the parent control
+     */
+    public void fill(Composite parent);
+
+    /**
+     * Fills the given menu with controls representing this contribution item.
+     * Used by <code>MenuManager</code>.
+     *
+     * @param parent the parent menu
+     * @param index the index where the controls are inserted,
+     *   or <code>-1</code> to insert at the end
+     */
+    public void fill(Menu parent, int index);
+
+    /**
+     * Fills the given tool bar with controls representing this contribution item.
+     * Used by <code>ToolBarManager</code>.
+     *
+     * @param parent the parent tool bar
+     * @param index the index where the controls are inserted,
+     *   or <code>-1</code> to insert at the end
+     */
+    public void fill(ToolBar parent, int index);
+
+    /**
+     * Fills the given cool bar with controls representing this contribution item.
+     * Used by <code>CoolBarManager</code>.
+     *
+     * @param parent the parent cool bar
+     * @param index the index where the controls are inserted,
+     *   or <code>-1</code> to insert at the end
+     * @since 3.0
+     */
+    public void fill(CoolBar parent, int index);
+
+    /**
+     * Returns the identifier of this contribution item.
+     * The id is used for retrieving an item from its manager.
+     *
+     * @return the contribution item identifier, or <code>null</code>
+     *   if none
+     */
+    public String getId();
+
+    /**
+     * Returns whether this contribution item is enabled.
+     *
+     * @return <code>true</code> if this item is enabled
+     */
+    public bool isEnabled();
+
+    /**
+     * Returns whether this contribution item is dirty. A dirty item will be
+     * recreated when the action bar is updated.
+     *
+     * @return <code>true</code> if this item is dirty
+     */
+    public bool isDirty();
+
+    /**
+     * Returns whether this contribution item is dynamic. A dynamic contribution
+     * item contributes items conditionally, dependent on some internal state.
+     *
+     * @return <code>true</code> if this item is dynamic, and
+     *  <code>false</code> for normal items
+     */
+    public bool isDynamic();
+
+    /**
+     * Returns whether this contribution item is a group marker.
+     * This information is used when adding items to a group.
+     *
+     * @return <code>true</code> if this item is a group marker, and
+     *  <code>false</code> for normal items
+     *
+     * @see GroupMarker
+     * @see IContributionManager#appendToGroup(String, IContributionItem)
+     * @see IContributionManager#prependToGroup(String, IContributionItem)
+     */
+    public bool isGroupMarker();
+
+    /**
+     * Returns whether this contribution item is a separator.
+     * This information is used to enable hiding of unnecessary separators.
+     *
+     * @return <code>true</code> if this item is a separator, and
+     *  <code>false</code> for normal items
+     * @see Separator
+     */
+    public bool isSeparator();
+
+    /**
+     * Returns whether this contribution item is visibile within its manager.
+     *
+     * @return <code>true</code> if this item is visible, and
+     *  <code>false</code> otherwise
+     */
+    public bool isVisible();
+
+    /**
+     * Saves any state information of the control(s) owned by this contribution item.
+     * The contribution manager calls this method before disposing of the controls.
+     *
+     * @since 3.0
+     */
+    public void saveWidgetState();
+
+    /**
+     * Sets the parent manager of this item
+     *
+     * @param parent the parent contribution manager
+     * @since 2.0
+     */
+    public void setParent(IContributionManager parent);
+
+    /**
+     * Sets whether this contribution item is visibile within its manager.
+     *
+     * @param visible <code>true</code> if this item should be visible, and
+     *  <code>false</code> otherwise
+     */
+    public void setVisible(bool visible);
+
+    /**
+     * Updates any SWT controls cached by this contribution item with any
+     * changes which have been made to this contribution item since the last update.
+     * Called by contribution manager update methods.
+     */
+    public void update();
+
+    /**
+     * Updates any SWT controls cached by this contribution item with changes
+     * for the the given property.
+     *
+     * @param id the id of the changed property
+     * @since 2.0
+     */
+    public void update(String id);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/IContributionManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.IContributionManager;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.IContributionManagerOverrides;
+
+import java.lang.all;
+
+/**
+ * A contribution manager organizes contributions to such UI components
+ * as menus, toolbars and status lines.
+ * <p>
+ * A contribution manager keeps track of a list of contribution
+ * items. Each contribution item may has an optional identifier, which can be used
+ * to retrieve items from a manager, and for positioning items relative to
+ * each other. The list of contribution items can be subdivided into named groups
+ * using special contribution items that serve as group markers.
+ * </p>
+ * <p>
+ * The <code>IContributionManager</code> interface provides general
+ * protocol for adding, removing, and retrieving contribution items.
+ * It also provides convenience methods that make it convenient
+ * to contribute actions. This interface should be implemented
+ * by all objects that wish to manage contributions.
+ * </p>
+ * <p>
+ * There are several implementions of this interface in this package,
+ * including ones for menus ({@link MenuManager <code>MenuManager</code>}),
+ * tool bars ({@link ToolBarManager <code>ToolBarManager</code>}),
+ * and status lines ({@link StatusLineManager <code>StatusLineManager</code>}).
+ * </p>
+ */
+public interface IContributionManager {
+    /**
+     * Adds an action as a contribution item to this manager.
+     * Equivalent to <code>add(new ActionContributionItem(action))</code>.
+     *
+     * @param action the action, this cannot be <code>null</code>
+     */
+    public void add(IAction action);
+
+    /**
+     * Adds a contribution item to this manager.
+     *
+     * @param item the contribution item, this cannot be <code>null</code>
+     */
+    public void add(IContributionItem item);
+
+    /**
+     * Adds a contribution item for the given action at the end of the group
+     * with the given name.
+     * Equivalent to
+     * <code>appendToGroup(groupName,new ActionContributionItem(action))</code>.
+     *
+     * @param groupName the name of the group
+     * @param action the action
+     * @exception IllegalArgumentException if there is no group with
+     *   the given name
+     */
+    public void appendToGroup(String groupName, IAction action);
+
+    /**
+     * Adds a contribution item to this manager at the end of the group
+     * with the given name.
+     *
+     * @param groupName the name of the group
+     * @param item the contribution item
+     * @exception IllegalArgumentException if there is no group with
+     *   the given name
+     */
+    public void appendToGroup(String groupName, IContributionItem item);
+
+    /**
+     * Finds the contribution item with the given id.
+     *
+     * @param id the contribution item id
+     * @return the contribution item, or <code>null</code> if
+     *   no item with the given id can be found
+     */
+    public IContributionItem find(String id);
+
+    /**
+     * Returns all contribution items known to this manager.
+     *
+     * @return a list of contribution items
+     */
+    public IContributionItem[] getItems();
+
+    /**
+     * Returns the overrides for the items of this manager.
+     *
+     * @return the overrides for the items of this manager
+     * @since 2.0
+     */
+    public IContributionManagerOverrides getOverrides();
+
+    /**
+     * Inserts a contribution item for the given action after the item
+     * with the given id.
+     * Equivalent to
+     * <code>insertAfter(id,new ActionContributionItem(action))</code>.
+     *
+     * @param id the contribution item id
+     * @param action the action to insert
+     * @exception IllegalArgumentException if there is no item with
+     *   the given id
+     */
+    public void insertAfter(String id, IAction action);
+
+    /**
+     * Inserts a contribution item after the item with the given id.
+     *
+     * @param id the contribution item id
+     * @param item the contribution item to insert
+     * @exception IllegalArgumentException if there is no item with
+     *   the given id
+     */
+    public void insertAfter(String id, IContributionItem item);
+
+    /**
+     * Inserts a contribution item for the given action before the item
+     * with the given id.
+     * Equivalent to
+     * <code>insertBefore(id,new ActionContributionItem(action))</code>.
+     *
+     * @param id the contribution item id
+     * @param action the action to insert
+     * @exception IllegalArgumentException if there is no item with
+     *   the given id
+     */
+    public void insertBefore(String id, IAction action);
+
+    /**
+     * Inserts a contribution item before the item with the given id.
+     *
+     * @param id the contribution item id
+     * @param item the contribution item to insert
+     * @exception IllegalArgumentException if there is no item with
+     *   the given id
+     */
+    public void insertBefore(String id, IContributionItem item);
+
+    /**
+     * Returns whether the list of contributions has recently changed and
+     * has yet to be reflected in the corresponding widgets.
+     *
+     * @return <code>true</code> if this manager is dirty, and <code>false</code>
+     *   if it is up-to-date
+     */
+    public bool isDirty();
+
+    /**
+     * Returns whether this manager has any contribution items.
+     *
+     * @return <code>true</code> if there are no items, and
+     *   <code>false</code> otherwise
+     */
+    public bool isEmpty();
+
+    /**
+     * Marks this contribution manager as dirty.
+     */
+    public void markDirty();
+
+    /**
+     * Adds a contribution item for the given action at the beginning of the
+     * group with the given name.
+     * Equivalent to
+     * <code>prependToGroup(groupName,new ActionContributionItem(action))</code>.
+     *
+     * @param groupName the name of the group
+     * @param action the action
+     * @exception IllegalArgumentException if there is no group with
+     *   the given name
+     */
+    public void prependToGroup(String groupName, IAction action);
+
+    /**
+     * Adds a contribution item to this manager at the beginning of the
+     * group with the given name.
+     *
+     * @param groupName the name of the group
+     * @param item the contribution item
+     * @exception IllegalArgumentException if there is no group with
+     *   the given name
+     */
+    public void prependToGroup(String groupName, IContributionItem item);
+
+    /**
+     * Removes and returns the contribution item with the given id from this manager.
+     * Returns <code>null</code> if this manager has no contribution items
+     * with the given id.
+     *
+     * @param id the contribution item id
+     * @return the item that was found and removed, or <code>null</code> if none
+     */
+    public IContributionItem remove(String id);
+
+    /**
+     * Removes the given contribution item from the contribution items
+     * known to this manager.
+     *
+     * @param item the contribution item
+     * @return the <code>item</code> parameter if the item was removed,
+     *   and <code>null</code> if it was not found
+     */
+    public IContributionItem remove(IContributionItem item);
+
+    /**
+     * Removes all contribution items from this manager.
+     */
+    public void removeAll();
+
+    /**
+     * Updates this manager's underlying widget(s) with any changes which
+     * have been made to it or its items.  Normally changes to a contribution
+     * manager merely mark it as dirty, without updating the underlying widgets.
+     * This brings the underlying widgets up to date with any changes.
+     *
+     * @param force <code>true</code> means update even if not dirty,
+     *   and <code>false</code> for normal incremental updating
+     */
+    public void update(bool force);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/IContributionManagerOverrides.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.IContributionManagerOverrides;
+
+import org.eclipse.jface.action.IContributionItem;
+
+import java.lang.all;
+
+/**
+ * This interface is used by instances of <code>IContributionItem</code>
+ * to determine if the values for certain properties have been overriden
+ * by their manager.
+ * <p>
+ * This interface is internal to the framework; it should not be implemented outside
+ * the framework.
+ * </p>
+ *
+ * @since 2.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IContributionManagerOverrides {
+    /**
+     * Id for the enabled property. Value is <code>"enabled"</code>.
+     *
+     * @since 2.0
+     */
+    public const static String P_ENABLED = "enabled"; //$NON-NLS-1$
+
+    /**
+     * Find out the enablement of the item
+     * @param item the contribution item for which the enable override value is
+     * determined
+     * @return <ul>
+     *              <li><code>Boolean.TRUE</code> if the given contribution item should be enabled</li>
+     *              <li><code>Boolean.FALSE</code> if the item should be disabled</li>
+     *              <li><code>null</code> if the item may determine its own enablement</li>
+     *          </ul>
+     * @since 2.0
+     */
+    public Boolean getEnabled(IContributionItem item);
+
+    /**
+     * This is not intended to be called outside of the workbench. This method
+     * is intended to be deprecated in 3.1.
+     *
+     * TODO deprecate for 3.1
+     * @param item the contribution item for which the accelerator value is determined
+     * @return the accelerator
+     */
+    public ValueWrapperInt getAccelerator(IContributionItem item);
+
+    /**
+     * This is not intended to be called outside of the workbench. This method
+     * is intended to be deprecated in 3.1.
+     *
+     * TODO deprecate for 3.1
+     * @param item the contribution item for which the accelerator text is determined
+     * @return the text for the accelerator
+     */
+    public String getAcceleratorText(IContributionItem item);
+
+    /**
+     * This is not intended to be called outside of the workbench. This method
+     * is intended to be deprecated in 3.1.
+     *
+     * TODO deprecate for 3.1
+     * @param item the contribution item for which the text is determined
+     * @return the text
+     */
+    public String getText(IContributionItem item);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/ICoolBarManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 org.eclipse.jface.action.ICoolBarManager;
+
+import org.eclipse.jface.action.IContributionManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.IMenuManager;
+
+import org.eclipse.swt.widgets.CoolBar;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The <code>ICoolBarManager</code> interface provides protocol for managing
+ * contributions to a cool bar. A cool bar manager delegates responsibility for
+ * creating child controls to its contribution items by calling
+ * {@link IContributionItem#fill(CoolBar, int)}.
+ * <p>
+ * This interface is internal to the framework; it should not be implemented
+ * outside the framework. This package provides a concrete cool bar manager
+ * implementation, {@link CoolBarManager}, which
+ * clients may instantiate or subclass.
+ * </p>
+ *
+ * @see ToolBarContributionItem
+ * @since 3.0
+ */
+public interface ICoolBarManager : IContributionManager {
+
+    /**
+     * Property name of a cool item's size (value <code>"size"</code>).
+     * <p>
+     * The cool bar manager uses this property to tell its cool items to update
+     * their size.
+     * </p>
+     *
+     * @see IContributionItem#update(String) @issue consider declaring this
+     *      constant elsewhere
+     */
+    public static const String SIZE = "size"; //$NON-NLS-1$
+
+    /**
+     * A convenience method to add a tool bar as a contribution item to this
+     * cool bar manager. Equivalent to <code>add(new ToolBarContributionManager(toolBarManager))</code>.
+     *
+     * @param toolBarManager
+     *            the tool bar manager to be added
+     * @see ToolBarContributionItem
+     */
+    public void add(IToolBarManager toolBarManager);
+
+    /**
+     * Returns the context menu manager used by this cool bar manager. This
+     * context menu manager is used by the cool bar manager except for cool
+     * items that provide their own.
+     *
+     * @return the context menu manager, or <code>null</code> if none
+     * @see #setContextMenuManager
+     */
+    public IMenuManager getContextMenuManager();
+
+    /**
+     * Returns whether the layout of the underlying cool bar widget is locked.
+     *
+     * @return <code>true</code> if cool bar layout is locked, <code>false</code>
+     *         otherwise
+     */
+    public bool getLockLayout();
+
+    /**
+     * Returns the style of the underlying cool bar widget.
+     *
+     * @return the style of the cool bar
+     */
+    public int getStyle();
+
+    /**
+     * Sets the context menu of this cool bar manager to the given menu
+     * manager.
+     *
+     * @param menuManager
+     *            the context menu manager, or <code>null</code> if none
+     * @see #getContextMenuManager
+     */
+    public void setContextMenuManager(IMenuManager menuManager);
+
+    /**
+     * Locks or unlocks the layout of the underlying cool bar widget. Once the
+     * cool bar is locked, cool items cannot be repositioned by the user.
+     * <p>
+     * Note that items can be added or removed programmatically even while the
+     * cool bar is locked.
+     * </p>
+     *
+     * @param value
+     *            <code>true</code> to lock the cool bar, <code>false</code>
+     *            to unlock
+     */
+    public void setLockLayout(bool value);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/IMenuCreator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.action.IMenuCreator;
+
+import java.lang.all;
+
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Menu;
+
+/**
+ * Interface for something that creates and disposes of SWT menus.  Note that
+ * it is the responsibility of the implementor to dispose of SWT menus it
+ * creates.
+ */
+public interface IMenuCreator {
+    /**
+     * Disposes the menu returned by <code>getMenu</code>. Does nothing
+     * if there is no menu.  This method will be executed only when the
+     * parent of the menu is disposed.
+     */
+    public void dispose();
+
+    /**
+     * Returns the SWT menu, created as a pop up menu parented by the
+     * given control.  In most cases, this menu can be created once, cached and reused
+     * when the pop-up/drop-down action occurs.  If the menu must be dynamically
+     * created (i.e., each time it is popped up or dropped down), the old menu
+     * should be disposed of before replacing it with the new menu.
+     *
+     * @param parent the parent control
+     * @return the menu, or <code>null</code> if the menu could not
+     *  be created
+     */
+    public Menu getMenu(Control parent);
+
+    /**
+     * Returns an SWT menu created as a drop down menu parented by the
+     * given menu.  In most cases, this menu can be created once, cached and reused
+     * when the pop-up/drop-down action occurs.  If the menu must be dynamically
+     * created (i.e., each time it is popped up or dropped down), the old menu
+     * should be disposed of before replacing it with the new menu.
+     *
+     * @param parent the parent menu
+     * @return the menu, or <code>null</code> if the menu could not
+     *  be created
+     */
+    public Menu getMenu(Menu parent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/IMenuListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.action.IMenuListener;
+
+import java.lang.all;
+
+import org.eclipse.jface.action.IMenuManager;
+
+/**
+ * A menu listener that gets informed when a menu is about to show.
+ *
+ * @see MenuManager#addMenuListener
+ */
+public interface IMenuListener {
+    /**
+     * Notifies this listener that the menu is about to be shown by
+     * the given menu manager.
+     *
+     * @param manager the menu manager
+     */
+    public void menuAboutToShow(IMenuManager manager);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/IMenuListener2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.action.IMenuListener2;
+
+import java.lang.all;
+
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+
+/**
+ * A menu listener that gets informed when a menu is about to hide.
+ *
+ * @see MenuManager#addMenuListener
+ * @since 3.2
+ */
+public interface IMenuListener2 : IMenuListener {
+    /**
+     * Notifies this listener that the menu is about to be hidden by
+     * the given menu manager.
+     *
+     * @param manager the menu manager
+     */
+    public void menuAboutToHide(IMenuManager manager);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/IMenuManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.IMenuManager;
+
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.IContributionManager;
+import org.eclipse.jface.action.IMenuListener;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The <code>IMenuManager</code> interface provides protocol for managing
+ * contributions to a menu bar and its sub menus.
+ * An <code>IMenuManager</code> is also an <code>IContributionItem</code>,
+ * allowing sub-menus to be nested in parent menus.
+ * <p>
+ * This interface is internal to the framework; it should not be implemented outside
+ * the framework.
+ * </p>
+ * <p>
+ * This package provides a concrete menu manager implementation,
+ * {@link MenuManager <code>MenuManager</code>}.
+ * </p>
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IMenuManager : IContributionManager, IContributionItem {
+
+    alias IContributionItem.update update;
+    alias IContributionManager.update update;
+    /**
+     * Adds a menu listener to this menu.
+     * Has no effect if an identical listener is already registered.
+     *
+     * @param listener a menu listener
+     */
+    public void addMenuListener(IMenuListener listener);
+
+    /**
+     * Finds the manager for the menu at the given path. A path
+     * consists of contribution item ids separated by the separator
+     * character.  The path separator character is <code>'/'</code>.
+     * <p>
+     * Convenience for <code>findUsingPath(path)</code> which
+     * extracts an <code>IMenuManager</code> if possible.
+     * </p>
+     *
+     * @param path the path string
+     * @return the menu contribution item, or <code>null</code>
+     *   if there is no such contribution item or if the item does
+     *   not have an associated menu manager
+     */
+    public IMenuManager findMenuUsingPath(String path);
+
+    /**
+     * Finds the contribution item at the given path. A path
+     * consists of contribution item ids separated by the separator
+     * character. The path separator character is <code>'/'</code>.
+     *
+     * @param path the path string
+     * @return the contribution item, or <code>null</code> if there is no
+     *   such contribution item
+     */
+    public IContributionItem findUsingPath(String path);
+
+    /**
+     * Returns whether all items should be removed when the menu is about to
+     * show, but before notifying menu listeners. The default is
+     * <code>false</code>.
+     *
+     * @return <code>true</code> if all items should be removed when shown,
+     *         <code>false</code> if not
+     */
+    public bool getRemoveAllWhenShown();
+
+    /**
+     * Returns whether this menu should be enabled or not.
+     *
+     * @return <code>true</code> if enabled, and
+     *   <code>false</code> if disabled
+     */
+    public bool isEnabled();
+
+    /**
+     * Removes the given menu listener from this menu.
+     * Has no effect if an identical listener is not registered.
+     *
+     * @param listener the menu listener
+     */
+    public void removeMenuListener(IMenuListener listener);
+
+    /**
+     * Sets whether all items should be removed when the menu is about to show,
+     * but before notifying menu listeners.
+     *
+     * @param removeAll
+     *            <code>true</code> if all items should be removed when shown,
+     *            <code>false</code> if not
+     */
+    public void setRemoveAllWhenShown(bool removeAll);
+
+    /**
+     * Incrementally builds the menu from the contribution items, and
+     * does so recursively for all submenus.
+     *
+     * @param force <code>true</code> means update even if not dirty,
+     *   and <code>false</code> for normal incremental updating
+     */
+    public void updateAll(bool force);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/IStatusLineManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.action.IStatusLineManager;
+
+import org.eclipse.jface.action.IContributionManager;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The <code>IStatusLineManager</code> interface provides protocol
+ * for displaying messages on a status line, for monitoring progress,
+ * and for managing contributions to the status line.
+ * <p>
+ * <b>Note:</b> An error message overrides the current message until
+ * the error message is cleared.
+ * </p><p>
+ * This package also provides a concrete status line manager implementation,
+ * {@link StatusLineManager <code>StatusLineManager</code>}.
+ * </p>
+ */
+public interface IStatusLineManager : IContributionManager {
+    /**
+     * Returns a progress monitor which reports progress
+     * in the status line.
+     *
+     * @return the progress monitor
+     *
+     * Note: There is a delay after a beginTask message before the monitor is shown.
+     *   This may not be appropriate for all apps.
+     */
+    public IProgressMonitor getProgressMonitor();
+
+    /**
+     * Returns whether the cancel button on the status line's progress monitor
+     * is enabled.
+     *
+     * @return <code>true</code> if the cancel button is enabled, or <code>false</code> if not
+     */
+    public bool isCancelEnabled();
+
+    /**
+     * Sets whether the cancel button on the status line's progress monitor
+     * is enabled.
+     *
+     * @param enabled <code>true</code> if the cancel button is enabled, or <code>false</code> if not
+     */
+    public void setCancelEnabled(bool enabled);
+
+    /**
+     * Sets the error message text to be displayed on the status line.
+     * The image on the status line is cleared.
+     * <p>
+     * An error message overrides the current message until the error
+     * message is cleared (set to <code>null</code>).
+     * </p>
+     *
+     * @param message the error message, or <code>null</code> to clear
+     *      the current error message.
+     */
+    public void setErrorMessage(String message);
+
+    /**
+     * Sets the image and error message to be displayed on the status line.
+     * <p>
+     * An error message overrides the current message until the error
+     * message is cleared (set to <code>null</code>).
+     * </p>
+     *
+     * @param image the image to use, or <code>null</code> for no image
+     * @param message the error message, or <code>null</code> to clear
+     *      the current error message.
+     */
+    public void setErrorMessage(Image image, String message);
+
+    /**
+     * Sets the message text to be displayed on the status line.
+     * The image on the status line is cleared.
+     * <p>
+     * This method replaces the current message but does not affect the
+     * error message. That is, the error message, if set, will continue
+     * to be displayed until it is cleared (set to <code>null</code>).
+     * </p>
+     *
+     * @param message the message, or <code>null</code> for no message
+     */
+    public void setMessage(String message);
+
+    /**
+     * Sets the image and message to be displayed on the status line.
+     * <p>
+     * This method replaces the current message but does not affect the
+     * error message. That is, the error message, if set, will continue
+     * to be displayed until it is cleared (set to <code>null</code>).
+     * </p>
+     *
+     * @param image the image to use, or <code>null</code> for no image
+     * @param message the message, or <code>null</code> for no message
+     */
+    public void setMessage(Image image, String message);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/IToolBarManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.action.IToolBarManager;
+
+import java.lang.all;
+
+import org.eclipse.jface.action.IContributionManager;
+
+/**
+ * The <code>IToolBarManager</code> interface provides protocol for managing
+ * contributions to a tool bar. It extends <code>IContributionManager</code>
+ * but does not declare any new members; it exists only to increase the
+ * readability of code using tool bars.
+ * <p>
+ * This package also provides a concrete tool bar manager implementation,
+ * {@link ToolBarManager <code>ToolBarManager</code>}.
+ * </p>
+ */
+public interface IToolBarManager : IContributionManager {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/LegacyActionTools.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,746 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.LegacyActionTools;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+static import tango.text.Text;
+alias tango.text.Text.Text!(char) StringBuffer;
+
+/**
+ * <p>
+ * Some static utility methods for handling labels on actions. This includes
+ * mnemonics and accelerators.
+ * </p>
+ * <p>
+ * Clients may neither instantiate this class nor extend.
+ * </p>
+ *
+ * @since 3.2
+ */
+public final class LegacyActionTools {
+
+    /**
+     * Table of key codes (key type: <code>String</code>, value type:
+     * <code>Integer</code>); <code>null</code> if not yet initialized.
+     *
+     * @see #findKeyCode
+     */
+    private static Map keyCodes = null;
+
+    /**
+     * Table of string representations of keys (key type: <code>Integer</code>,
+     * value type: <code>String</code>); <code>null</code>> if not yet
+     * initialized.
+     *
+     * @see #findKeyString
+     */
+    private static Map keyStrings = null;
+
+    /**
+     * The localized uppercase version of ALT
+     */
+    private static String localizedAlt;
+
+    /**
+     * The localized uppercase version of COMMAND
+     */
+    private static String localizedCommand;
+
+    /**
+     * The localized uppercase version of CTRL
+     */
+    private static String localizedCtrl;
+
+    /**
+     * Table of key codes (key type: <code>String</code>, value type:
+     * <code>Integer</code>); <code>null</code> if not yet initialized. The
+     * key is the localalized name of the key as it appears in menus.
+     *
+     * @see #findLocalizedKeyCode
+     */
+    private static Map localizedKeyCodes = null;
+
+    /**
+     * The localized uppercase version of SHIFT
+     */
+    private static String localizedShift;
+
+    /**
+     * The constant to use if there is no mnemonic for this location.
+     */
+    public static const char MNEMONIC_NONE = 0;
+
+    /**
+     * Converts an accelerator key code to a string representation.
+     *
+     * @param keyCode
+     *            the key code to be translated
+     * @return a string representation of the key code
+     */
+    public static final String convertAccelerator(int keyCode) {
+        String modifier = getModifierString(keyCode);
+        String fullKey;
+        if (modifier.equals("")) { //$NON-NLS-1$
+            fullKey = findKeyString(keyCode);
+        } else {
+            fullKey = modifier ~ "+" ~ findKeyString(keyCode); //$NON-NLS-1$
+        }
+        return fullKey;
+    }
+
+    /**
+     * Parses the given accelerator text, and converts it to an accelerator key
+     * code.
+     *
+     * @param acceleratorText
+     *            the accelerator text
+     * @return the SWT key code, or 0 if there is no accelerator
+     */
+    public static final int convertAccelerator(String acceleratorText) {
+        int accelerator = 0;
+        auto tokens = tango.text.Util.delimit( acceleratorText, "+" );
+
+        int keyCode = -1;
+        foreach( idx, token; tokens ){
+//         bool hasMoreTokens = stok.hasMoreTokens();
+//         while (hasMoreTokens) {
+//             String token = stok.nextToken();
+//             bool hasMoreTokens = idx+1 < tokens.length;//stok.hasMoreTokens();
+            // Every token except the last must be one of the modifiers
+            // Ctrl, Shift, Alt, or Command
+            if (idx+1 < tokens.length) {
+                int modifier = findModifier(token);
+                if (modifier !is 0) {
+                    accelerator |= modifier;
+                } else { // Leave if there are none
+                    return 0;
+                }
+            } else {
+                keyCode = findKeyCode(token);
+            }
+        }
+        if (keyCode !is -1) {
+            accelerator |= keyCode;
+        }
+        return accelerator;
+    }
+
+    /**
+     * Parses the given accelerator text, and converts it to an accelerator key
+     * code.
+     *
+     * Support for localized modifiers is for backwards compatibility with 1.0.
+     * Use setAccelerator(int) to set accelerators programatically or the
+     * <code>accelerator</code> tag in action definitions in plugin.xml.
+     *
+     * @param acceleratorText
+     *            the accelerator text localized to the current locale
+     * @return the SWT key code, or 0 if there is no accelerator
+     */
+    static final int convertLocalizedAccelerator(String acceleratorText) {
+        int accelerator = 0;
+        auto tokens = tango.text.Util.delimit( acceleratorText, "+" );
+//         StringTokenizer stok = new StringTokenizer(acceleratorText, "+"); //$NON-NLS-1$
+
+        int keyCode = -1;
+
+        foreach( idx, token; tokens ){
+//         bool hasMoreTokens = stok.hasMoreTokens();
+//         while (hasMoreTokens) {
+//             String token = stok.nextToken();
+//             hasMoreTokens = stok.hasMoreTokens();
+            // Every token except the last must be one of the modifiers
+            // Ctrl, Shift, Alt, or Command
+            if (idx+1 < tokens.length) {
+                int modifier = findLocalizedModifier(token);
+                if (modifier !is 0) {
+                    accelerator |= modifier;
+                } else { // Leave if there are none
+                    return 0;
+                }
+            } else {
+                keyCode = findLocalizedKeyCode(token);
+            }
+        }
+        if (keyCode !is -1) {
+            accelerator |= keyCode;
+        }
+        return accelerator;
+    }
+
+    /**
+     * Extracts the accelerator text from the given text. Returns
+     * <code>null</code> if there is no accelerator text, and the empty string
+     * if there is no text after the accelerator delimeter (tab or '@').
+     *
+     * @param text
+     *            the text for the action; may be <code>null</code>.
+     * @return the accelerator text, or <code>null</code>
+     */
+    public static final String extractAcceleratorText(String text) {
+        if (text is null) {
+            return null;
+        }
+
+        int index = text.lastIndexOf('\t');
+        if (index is -1) {
+            index = text.lastIndexOf('@');
+        }
+        if (index >= 0) {
+            return text.substring(index + 1);
+        }
+        return null;
+    }
+
+    /**
+     * Extracts the mnemonic text from the given string.
+     *
+     * @param text
+     *            The text from which the mnemonic should be extracted; may be
+     *            <code>null</code>
+     * @return The text of the mnemonic; will be {@link #MNEMONIC_NONE} if there
+     *         is no mnemonic;
+     */
+    public static final char extractMnemonic(String text) {
+        if (text is null) {
+            return MNEMONIC_NONE;
+        }
+
+        int index = text.indexOf('&');
+        if (index is -1) {
+            return MNEMONIC_NONE;
+        }
+
+        int textLength = text.length;
+
+        // Ignore '&' at the end of the string.
+        if (index is textLength - 1) {
+            return MNEMONIC_NONE;
+        }
+
+        // Ignore two consecutive ampersands.
+        while (text.charAt(index + 1) is '&') {
+            index = text.indexOf('&', ++index);
+            if (index is textLength - 1) {
+                return MNEMONIC_NONE;
+            }
+        }
+
+        return text.charAt(index + 1);
+    }
+
+    /**
+     * Maps a standard keyboard key name to an SWT key code. Key names are
+     * converted to upper case before comparison. If the key name is a single
+     * letter, for example "S", its character code is returned.
+     * <p>
+     * The following key names are known (case is ignored):
+     * <ul>
+     * <li><code>"BACKSPACE"</code></li>
+     * <li><code>"TAB"</code></li>
+     * <li><code>"RETURN"</code></li>
+     * <li><code>"ENTER"</code></li>
+     * <li><code>"ESC"</code></li>
+     * <li><code>"ESCAPE"</code></li>
+     * <li><code>"DELETE"</code></li>
+     * <li><code>"SPACE"</code></li>
+     * <li><code>"ARROW_UP"</code>, <code>"ARROW_DOWN"</code>,
+     * <code>"ARROW_LEFT"</code>, and <code>"ARROW_RIGHT"</code></li>
+     * <li><code>"PAGE_UP"</code> and <code>"PAGE_DOWN"</code></li>
+     * <li><code>"HOME"</code></li>
+     * <li><code>"END"</code></li>
+     * <li><code>"INSERT"</code></li>
+     * <li><code>"F1"</code>, <code>"F2"</code> through <code>"F12"</code></li>
+     * </ul>
+     * </p>
+     *
+     * @param token
+     *            the key name
+     * @return the SWT key code, <code>-1</code> if no match was found
+     * @see SWT
+     */
+    public static final int findKeyCode(String token) {
+        if (keyCodes is null) {
+            initKeyCodes();
+        }
+        token = token.toUpperCase();
+        Integer i = cast(Integer) keyCodes.get(token);
+        if (i !is null) {
+            return i.intValue();
+        }
+        if (token.length is 1) {
+            return token.charAt(0);
+        }
+        return -1;
+    }
+
+    /**
+     * Maps an SWT key code to a standard keyboard key name. The key code is
+     * stripped of modifiers (SWT.CTRL, SWT.ALT, SWT.SHIFT, and SWT.COMMAND). If
+     * the key code is not an SWT code (for example if it a key code for the key
+     * 'S'), a string containing a character representation of the key code is
+     * returned.
+     *
+     * @param keyCode
+     *            the key code to be translated
+     * @return the string representation of the key code
+     * @see SWT
+     * @since 2.0
+     */
+    public static final String findKeyString( int keyCode) {
+        if (keyStrings is null) {
+            initKeyStrings();
+        }
+        int i = keyCode & ~(SWT.CTRL | SWT.ALT | SWT.SHIFT | SWT.COMMAND);
+        Integer integer = new Integer(i);
+        String result = stringcast(keyStrings.get(integer));
+        if (result !is null) {
+            return result;
+        }
+        result = dcharToString( cast(dchar) i );
+        return result;
+    }
+
+    /**
+     * Find the supplied code for a localized key. As #findKeyCode but localized
+     * to the current locale.
+     *
+     * Support for localized modifiers is for backwards compatibility with 1.0.
+     * Use setAccelerator(int) to set accelerators programatically or the
+     * <code>accelerator</code> tag in action definitions in plugin.xml.
+     *
+     * @param token
+     *            the localized key name
+     * @return the SWT key code, <code>-1</code> if no match was found
+     * @see #findKeyCode
+     */
+    private static final int findLocalizedKeyCode(String token) {
+        if (localizedKeyCodes is null) {
+            initLocalizedKeyCodes();
+        }
+        token = token.toUpperCase();
+        Integer i;
+        if (localizedKeyCodes.containsKey(token) && (i = cast(Integer) localizedKeyCodes.get(token)) !is null ) {
+            return i.intValue();
+        }
+        if (token.length is 1) {
+            return token.charAt(0);
+        }
+        return -1;
+    }
+
+    /**
+     * Maps the localized modifier names to a code in the same manner as
+     * #findModifier.
+     *
+     * Support for localized modifiers is for backwards compatibility with 1.0.
+     * Use setAccelerator(int) to set accelerators programatically or the
+     * <code>accelerator</code> tag in action definitions in plugin.xml.
+     *
+     * @see #findModifier
+     */
+    private static final int findLocalizedModifier(String token) {
+        if (localizedCtrl is null) {
+            initLocalizedModifiers();
+        }
+
+        token = token.toUpperCase();
+        if (token.equals(localizedCtrl)) {
+            return SWT.CTRL;
+        }
+        if (token.equals(localizedShift)) {
+            return SWT.SHIFT;
+        }
+        if (token.equals(localizedAlt)) {
+            return SWT.ALT;
+        }
+        if (token.equals(localizedCommand)) {
+            return SWT.COMMAND;
+        }
+        return 0;
+    }
+
+    /**
+     * Maps standard keyboard modifier key names to the corresponding SWT
+     * modifier bit. The following modifier key names are recognized (case is
+     * ignored): <code>"CTRL"</code>, <code>"SHIFT"</code>,
+     * <code>"ALT"</code>, and <code>"COMMAND"</code>. The given modifier
+     * key name is converted to upper case before comparison.
+     *
+     * @param token
+     *            the modifier key name
+     * @return the SWT modifier bit, or <code>0</code> if no match was found
+     * @see SWT
+     */
+    public static final int findModifier(String token) {
+        token = token.toUpperCase();
+        if (token.equals("CTRL")) { //$NON-NLS-1$
+            return SWT.CTRL;
+        }
+        if (token.equals("SHIFT")) { //$NON-NLS-1$
+            return SWT.SHIFT;
+        }
+        if (token.equals("ALT")) { //$NON-NLS-1$
+            return SWT.ALT;
+        }
+        if (token.equals("COMMAND")) { //$NON-NLS-1$
+            return SWT.COMMAND;
+        }
+        return 0;
+    }
+
+    /**
+     * Returns a string representation of an SWT modifier bit (SWT.CTRL,
+     * SWT.ALT, SWT.SHIFT, and SWT.COMMAND). Returns <code>null</code> if the
+     * key code is not an SWT modifier bit.
+     *
+     * @param keyCode
+     *            the SWT modifier bit to be translated
+     * @return the string representation of the SWT modifier bit, or
+     *         <code>null</code> if the key code was not an SWT modifier bit
+     * @see SWT
+     */
+    public static final String findModifierString(int keyCode) {
+        if (keyCode is SWT.CTRL) {
+            return JFaceResources.getString("Ctrl"); //$NON-NLS-1$
+        }
+        if (keyCode is SWT.ALT) {
+            return JFaceResources.getString("Alt"); //$NON-NLS-1$
+        }
+        if (keyCode is SWT.SHIFT) {
+            return JFaceResources.getString("Shift"); //$NON-NLS-1$
+        }
+        if (keyCode is SWT.COMMAND) {
+            return JFaceResources.getString("Command"); //$NON-NLS-1$
+        }
+        return null;
+    }
+
+    /**
+     * Returns the string representation of the modifiers (Ctrl, Alt, Shift,
+     * Command) of the key event.
+     *
+     * @param keyCode
+     *            The key code for which the modifier string is desired.
+     * @return The string representation of the key code; never
+     *         <code>null</code>.
+     */
+    private static String getModifierString(int keyCode) {
+        String modString = ""; //$NON-NLS-1$
+
+        if ((keyCode & SWT.CTRL) !is 0) {
+            modString = findModifierString(keyCode & SWT.CTRL);
+        }
+
+        if ((keyCode & SWT.ALT) !is 0) {
+            if (modString.equals("")) { //$NON-NLS-1$
+                modString = findModifierString(keyCode & SWT.ALT);
+            } else {
+                modString = modString
+                        ~ "+" ~ findModifierString(keyCode & SWT.ALT); //$NON-NLS-1$
+            }
+        }
+
+        if ((keyCode & SWT.SHIFT) !is 0) {
+            if (modString.equals("")) { //$NON-NLS-1$
+                modString = findModifierString(keyCode & SWT.SHIFT);
+            } else {
+                modString = modString
+                        ~ "+" ~ findModifierString(keyCode & SWT.SHIFT); //$NON-NLS-1$
+            }
+        }
+
+        if ((keyCode & SWT.COMMAND) !is 0) {
+            if (modString.equals("")) { //$NON-NLS-1$
+                modString = findModifierString(keyCode & SWT.COMMAND);
+            } else {
+                modString = modString
+                        ~ "+" ~ findModifierString(keyCode & SWT.COMMAND); //$NON-NLS-1$
+            }
+        }
+
+        return modString;
+    }
+
+    /**
+     * Initializes the internal key code table.
+     */
+    private static final void initKeyCodes() {
+        keyCodes = new HashMap();
+
+        keyCodes.put(stringcast("BACKSPACE"), new Integer(8)); //$NON-NLS-1$
+        keyCodes.put(stringcast("TAB"), new Integer(9)); //$NON-NLS-1$
+        keyCodes.put(stringcast("RETURN"), new Integer(13)); //$NON-NLS-1$
+        keyCodes.put(stringcast("ENTER"), new Integer(13)); //$NON-NLS-1$
+        keyCodes.put(stringcast("ESCAPE"), new Integer(27)); //$NON-NLS-1$
+        keyCodes.put(stringcast("ESC"), new Integer(27)); //$NON-NLS-1$
+        keyCodes.put(stringcast("DELETE"), new Integer(127)); //$NON-NLS-1$
+
+        keyCodes.put(stringcast("SPACE"), new Integer(' ')); //$NON-NLS-1$
+        keyCodes.put(stringcast("ARROW_UP"), new Integer(SWT.ARROW_UP)); //$NON-NLS-1$
+        keyCodes.put(stringcast("ARROW_DOWN"), new Integer(SWT.ARROW_DOWN)); //$NON-NLS-1$
+        keyCodes.put(stringcast("ARROW_LEFT"), new Integer(SWT.ARROW_LEFT)); //$NON-NLS-1$
+        keyCodes.put(stringcast("ARROW_RIGHT"), new Integer(SWT.ARROW_RIGHT)); //$NON-NLS-1$
+        keyCodes.put(stringcast("PAGE_UP"), new Integer(SWT.PAGE_UP)); //$NON-NLS-1$
+        keyCodes.put(stringcast("PAGE_DOWN"), new Integer(SWT.PAGE_DOWN)); //$NON-NLS-1$
+        keyCodes.put(stringcast("HOME"), new Integer(SWT.HOME)); //$NON-NLS-1$
+        keyCodes.put(stringcast("END"), new Integer(SWT.END)); //$NON-NLS-1$
+        keyCodes.put(stringcast("INSERT"), new Integer(SWT.INSERT)); //$NON-NLS-1$
+        keyCodes.put(stringcast("F1"), new Integer(SWT.F1)); //$NON-NLS-1$
+        keyCodes.put(stringcast("F2"), new Integer(SWT.F2)); //$NON-NLS-1$
+        keyCodes.put(stringcast("F3"), new Integer(SWT.F3)); //$NON-NLS-1$
+        keyCodes.put(stringcast("F4"), new Integer(SWT.F4)); //$NON-NLS-1$
+        keyCodes.put(stringcast("F5"), new Integer(SWT.F5)); //$NON-NLS-1$
+        keyCodes.put(stringcast("F6"), new Integer(SWT.F6)); //$NON-NLS-1$
+        keyCodes.put(stringcast("F7"), new Integer(SWT.F7)); //$NON-NLS-1$
+        keyCodes.put(stringcast("F8"), new Integer(SWT.F8)); //$NON-NLS-1$
+        keyCodes.put(stringcast("F9"), new Integer(SWT.F9)); //$NON-NLS-1$
+        keyCodes.put(stringcast("F10"), new Integer(SWT.F10)); //$NON-NLS-1$
+        keyCodes.put(stringcast("F11"), new Integer(SWT.F11)); //$NON-NLS-1$
+        keyCodes.put(stringcast("F12"), new Integer(SWT.F12)); //$NON-NLS-1$
+    }
+
+    /**
+     * Initializes the internal key string table.
+     */
+    private static void initKeyStrings() {
+        keyStrings = new HashMap();
+
+        keyStrings.put(new Integer(8), stringcast(JFaceResources.getString("Backspace"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(9), stringcast(JFaceResources.getString("Tab"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(13), stringcast(JFaceResources.getString("Return"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(13), stringcast(JFaceResources.getString("Enter"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(27), stringcast(JFaceResources.getString("Escape"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(27), stringcast(JFaceResources.getString("Esc"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(127), stringcast(JFaceResources.getString("Delete"))); //$NON-NLS-1$
+
+        keyStrings.put(new Integer(cast(int)' '), stringcast(JFaceResources.getString("Space"))); //$NON-NLS-1$
+
+        keyStrings.put(new Integer(SWT.ARROW_UP), stringcast(JFaceResources
+                .getString("Arrow_Up"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.ARROW_DOWN), stringcast(JFaceResources
+                .getString("Arrow_Down"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.ARROW_LEFT), stringcast(JFaceResources
+                .getString("Arrow_Left"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.ARROW_RIGHT), stringcast(JFaceResources
+                .getString("Arrow_Right"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.PAGE_UP), stringcast(JFaceResources
+                .getString("Page_Up"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.PAGE_DOWN), stringcast(JFaceResources
+                .getString("Page_Down"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.HOME), stringcast(JFaceResources.getString("Home"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.END), stringcast(JFaceResources.getString("End"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.INSERT), stringcast(JFaceResources
+                .getString("Insert"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.F1), stringcast(JFaceResources.getString("F1"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.F2), stringcast(JFaceResources.getString("F2"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.F3), stringcast(JFaceResources.getString("F3"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.F4), stringcast(JFaceResources.getString("F4"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.F5), stringcast(JFaceResources.getString("F5"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.F6), stringcast(JFaceResources.getString("F6"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.F7), stringcast(JFaceResources.getString("F7"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.F8), stringcast(JFaceResources.getString("F8"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.F9), stringcast(JFaceResources.getString("F9"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.F10), stringcast(JFaceResources.getString("F10"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.F11), stringcast(JFaceResources.getString("F11"))); //$NON-NLS-1$
+        keyStrings.put(new Integer(SWT.F12), stringcast(JFaceResources.getString("F12"))); //$NON-NLS-1$
+    }
+
+    /**
+     * Initializes the localized internal key code table.
+     */
+    private static void initLocalizedKeyCodes() {
+        localizedKeyCodes = new HashMap();
+
+        localizedKeyCodes.put(stringcast(JFaceResources
+                .getString("Backspace").toUpperCase()), new Integer(8)); //$NON-NLS-1$
+        localizedKeyCodes.put(
+                stringcast(JFaceResources.getString("Tab").toUpperCase()), new Integer(9)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("Return").toUpperCase()), new Integer(13)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("Enter").toUpperCase()), new Integer(13)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("Escape").toUpperCase()), new Integer(27)); //$NON-NLS-1$
+        localizedKeyCodes.put(
+                stringcast(JFaceResources.getString("Esc").toUpperCase()), new Integer(27)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("Delete").toUpperCase()), new Integer(127)); //$NON-NLS-1$
+
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("Space").toUpperCase()), new Integer(' ')); //$NON-NLS-1$
+
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("Arrow_Up").toUpperCase()), new Integer(SWT.ARROW_UP)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("Arrow_Down").toUpperCase()), new Integer(SWT.ARROW_DOWN)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("Arrow_Left").toUpperCase()), new Integer(SWT.ARROW_LEFT)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("Arrow_Right").toUpperCase()), new Integer(SWT.ARROW_RIGHT)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("Page_Up").toUpperCase()), new Integer(SWT.PAGE_UP)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("Page_Down").toUpperCase()), new Integer(SWT.PAGE_DOWN)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("Home").toUpperCase()), new Integer(SWT.HOME)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("End").toUpperCase()), new Integer(SWT.END)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("Insert").toUpperCase()), new Integer(SWT.INSERT)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("F1").toUpperCase()), new Integer(SWT.F1)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("F2").toUpperCase()), new Integer(SWT.F2)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("F3").toUpperCase()), new Integer(SWT.F3)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("F4").toUpperCase()), new Integer(SWT.F4)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("F5").toUpperCase()), new Integer(SWT.F5)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("F6").toUpperCase()), new Integer(SWT.F6)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("F7").toUpperCase()), new Integer(SWT.F7)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("F8").toUpperCase()), new Integer(SWT.F8)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("F9").toUpperCase()), new Integer(SWT.F9)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("F10").toUpperCase()), new Integer(SWT.F10)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("F11").toUpperCase()), new Integer(SWT.F11)); //$NON-NLS-1$
+        localizedKeyCodes
+                .put(
+                        stringcast(JFaceResources.getString("F12").toUpperCase()), new Integer(SWT.F12)); //$NON-NLS-1$
+    }
+
+    /**
+     * Initialize the list of localized modifiers
+     */
+    private static void initLocalizedModifiers() {
+        localizedCtrl = JFaceResources.getString("Ctrl").toUpperCase(); //$NON-NLS-1$
+        localizedShift = JFaceResources.getString("Shift").toUpperCase(); //$NON-NLS-1$
+        localizedAlt = JFaceResources.getString("Alt").toUpperCase(); //$NON-NLS-1$
+        localizedCommand = JFaceResources.getString("Command").toUpperCase(); //$NON-NLS-1$
+    }
+
+    /**
+     * Convenience method for removing any optional accelerator text from the
+     * given string. The accelerator text appears at the end of the text, and is
+     * separated from the main part by a single tab character <code>'\t'</code>.
+     *
+     * @param text
+     *            the text
+     * @return the text sans accelerator
+     */
+    public static final String removeAcceleratorText(String text) {
+        int index = text.lastIndexOf('\t');
+        if (index is -1) {
+            index = text.lastIndexOf('@');
+        }
+        if (index >= 0) {
+            return text.substring(0, index);
+        }
+        return text;
+    }
+
+    /**
+     * Convenience method for removing any mnemonics from the given string. For
+     * example, <code>removeMnemonics("&Open")</code> will return
+     * <code>"Open"</code>.
+     *
+     * @param text
+     *            the text
+     * @return the text sans mnemonics
+     */
+    public static final String removeMnemonics(String text) {
+        int index = text.indexOf('&');
+        if (index is -1) {
+            return text;
+        }
+        int len = text.length;
+        StringBuffer sb = new StringBuffer(len);
+        int lastIndex = 0;
+        while (index !is -1) {
+            // ignore & at the end
+            if (index is len - 1) {
+                break;
+            }
+            // handle the && case
+            if (text.charAt(index + 1) is '&') {
+                ++index;
+            }
+
+            // DBCS languages use "(&X)" format
+            if (index > 0 && text.charAt(index - 1) is '('
+                    && text.length >= index + 3
+                    && text.charAt(index + 2) is ')') {
+                sb.append(text.substring(lastIndex, index - 1));
+                index += 3;
+            } else {
+                sb.append(text.substring(lastIndex, index));
+                // skip the &
+                ++index;
+            }
+
+            lastIndex = index;
+            index = text.indexOf('&', index);
+        }
+        if (lastIndex < len) {
+            sb.append(text.substring(lastIndex, len));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * This class cannot be instantiated.
+     */
+    private this() {
+        // Does nothing
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/MenuManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,993 @@
+/*******************************************************************************
+ * 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
+ *     Remy Chi Jian Suen <remy.suen@gmail.com> - Bug 12116 [Contributions] widgets: MenuManager.setImageDescriptor() method needed
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.action.MenuManager;
+
+import org.eclipse.jface.action.ContributionManager;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IContributionManagerOverrides;
+import org.eclipse.jface.action.IContributionManager;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuListener2;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.SubContributionItem;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.ExternalActionManager;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MenuAdapter;
+import org.eclipse.swt.events.MenuEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.CoolBar;
+import org.eclipse.swt.widgets.Decorations;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.LocalResourceManager;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+
+/**
+ * A menu manager is a contribution manager which realizes itself and its items
+ * in a menu control; either as a menu bar, a sub-menu, or a context menu.
+ * <p>
+ * This class may be instantiated; it may also be subclassed.
+ * </p>
+ */
+public class MenuManager : ContributionManager, IMenuManager {
+
+    public bool isDirty(){
+        return super.isDirty();
+    }
+
+    /**
+     * The menu id.
+     */
+    private String id;
+
+    /**
+     * List of registered menu listeners (element type: <code>IMenuListener</code>).
+     */
+    private ListenerList listeners;
+
+    /**
+     * The menu control; <code>null</code> before
+     * creation and after disposal.
+     */
+    private Menu menu = null;
+
+    /**
+     * The menu item widget; <code>null</code> before
+     * creation and after disposal. This field is used
+     * when this menu manager is a sub-menu.
+     */
+    private MenuItem menuItem;
+
+    /**
+     * The text for a sub-menu.
+     */
+    private String menuText;
+
+    /**
+     * The image for a sub-menu.
+     */
+    private ImageDescriptor image;
+
+    /**
+     * A resource manager to remember all of the images that have been used by this menu.
+     */
+    private LocalResourceManager imageManager;
+
+    /**
+     * The overrides for items of this manager
+     */
+    private IContributionManagerOverrides overrides;
+
+    /**
+     * The parent contribution manager.
+     */
+    private IContributionManager parent;
+
+    /**
+     * Indicates whether <code>removeAll</code> should be
+     * called just before the menu is displayed.
+     */
+    private bool removeAllWhenShown = false;
+
+    /**
+     * Indicates this item is visible in its manager; <code>true</code>
+     * by default.
+     * @since 3.3
+     */
+    protected bool visible = true;
+
+    /**
+     * allows a submenu to display a shortcut key. This is often used with the
+     * QuickMenu command or action which can pop up a menu using the shortcut.
+     */
+    private String definitionId = null;
+
+    /**
+     * Creates a menu manager.  The text and id are <code>null</code>.
+     * Typically used for creating a context menu, where it doesn't need to be referred to by id.
+     */
+    public this() {
+        this(null, null, null);
+    }
+
+    /**
+     * Creates a menu manager with the given text. The id of the menu
+     * is <code>null</code>.
+     * Typically used for creating a sub-menu, where it doesn't need to be referred to by id.
+     *
+     * @param text the text for the menu, or <code>null</code> if none
+     */
+    public this(String text) {
+        this(text, null, null);
+    }
+
+    /**
+     * Creates a menu manager with the given text and id.
+     * Typically used for creating a sub-menu, where it needs to be referred to by id.
+     *
+     * @param text the text for the menu, or <code>null</code> if none
+     * @param id the menu id, or <code>null</code> if it is to have no id
+     */
+    public this(String text, String id) {
+        this(text, null, id);
+    }
+
+    /**
+     * Creates a menu manager with the given text, image, and id.
+     * Typically used for creating a sub-menu, where it needs to be referred to by id.
+     *
+     * @param text the text for the menu, or <code>null</code> if none
+     * @param image the image for the menu, or <code>null</code> if none
+     * @param id the menu id, or <code>null</code> if it is to have no id
+     * @since 3.4
+     */
+    public this(String text, ImageDescriptor image, String id) {
+        listeners = new ListenerList();
+        this.menuText = text;
+        this.image = image;
+        this.id = id;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IMenuManager#addMenuListener(org.eclipse.jface.action.IMenuListener)
+     */
+    public void addMenuListener(IMenuListener listener) {
+        listeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Creates and returns an SWT context menu control for this menu,
+     * and installs all registered contributions.
+     * Does not create a new control if one already exists.
+     * <p>
+     * Note that the menu is not expected to be dynamic.
+     * </p>
+     *
+     * @param parent the parent control
+     * @return the menu control
+     */
+    public Menu createContextMenu(Control parent) {
+        if (!menuExist()) {
+            menu = new Menu(parent);
+            initializeMenu();
+        }
+        return menu;
+    }
+
+    /**
+     * Creates and returns an SWT menu bar control for this menu,
+     * for use in the given <code>Decorations</code>, and installs all registered
+     * contributions. Does not create a new control if one already exists.
+     *
+     * @param parent the parent decorations
+     * @return the menu control
+     * @since 2.1
+     */
+    public Menu createMenuBar(Decorations parent) {
+        if (!menuExist()) {
+            menu = new Menu(parent, SWT.BAR);
+            update(false);
+        }
+        return menu;
+    }
+
+    /**
+     * Creates and returns an SWT menu bar control for this menu, for use in the
+     * given <code>Shell</code>, and installs all registered contributions. Does not
+     * create a new control if one already exists. This implementation simply calls
+     * the <code>createMenuBar(Decorations)</code> method
+     *
+     * @param parent the parent decorations
+     * @return the menu control
+     * @deprecated use <code>createMenuBar(Decorations)</code> instead.
+     */
+    public Menu createMenuBar(Shell parent) {
+        return createMenuBar(cast(Decorations) parent);
+    }
+
+    /**
+     * Disposes of this menu manager and frees all allocated SWT resources.
+     * Notifies all contribution items of the dispose. Note that this method does
+     * not clean up references between this menu manager and its associated
+     * contribution items. Use <code>removeAll</code> for that purpose.
+     */
+    public void dispose() {
+        if (menuExist()) {
+            menu.dispose();
+        }
+        menu = null;
+
+        if (menuItem !is null) {
+            menuItem.dispose();
+            menuItem = null;
+        }
+
+        disposeOldImages();
+
+        IContributionItem[] items = getItems();
+        for (int i = 0; i < items.length; i++) {
+            items[i].dispose();
+        }
+
+        markDirty();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#fill(org.eclipse.swt.widgets.Composite)
+     */
+    public void fill(Composite parent) {
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#fill(org.eclipse.swt.widgets.CoolBar, int)
+     */
+    public void fill(CoolBar parent, int index) {
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#fill(org.eclipse.swt.widgets.Menu, int)
+     */
+    public void fill(Menu parent, int index) {
+        if (menuItem is null || menuItem.isDisposed()) {
+            if (index >= 0) {
+                menuItem = new MenuItem(parent, SWT.CASCADE, index);
+            } else {
+                menuItem = new MenuItem(parent, SWT.CASCADE);
+            }
+
+            menuItem.setText(getMenuText());
+
+            if (image !is null) {
+                LocalResourceManager localManager = new LocalResourceManager(
+                        JFaceResources.getResources());
+                menuItem.setImage(localManager.createImage(image));
+                disposeOldImages();
+                imageManager = localManager;
+            }
+
+            if (!menuExist()) {
+                menu = new Menu(parent);
+            }
+
+            menuItem.setMenu(menu);
+
+            initializeMenu();
+
+            setDirty(true);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#fill(org.eclipse.swt.widgets.ToolBar, int)
+     */
+    public void fill(ToolBar parent, int index) {
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IMenuManager#findMenuUsingPath(java.lang.String)
+     */
+    public IMenuManager findMenuUsingPath(String path) {
+        IContributionItem item = findUsingPath(path);
+        if (auto mm = cast(IMenuManager)item ) {
+            return mm;
+        }
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IMenuManager#findUsingPath(java.lang.String)
+     */
+    public IContributionItem findUsingPath(String path) {
+        String id = path;
+        String rest = null;
+        int separator = java.lang.all.indexOf( path, '/');
+        if (separator !is -1) {
+            id = path.substring(0, separator);
+            rest = path.substring(separator + 1);
+        } else {
+            return super.find(path);
+        }
+
+        IContributionItem item = super.find(id);
+        if (auto manager = cast(IMenuManager)item ) {
+            return manager.findUsingPath(rest);
+        }
+        return null;
+    }
+
+    /**
+     * Notifies any menu listeners that a menu is about to show.
+     * Only listeners registered at the time this method is called are notified.
+     *
+     * @param manager the menu manager
+     *
+     * @see IMenuListener#menuAboutToShow
+     */
+    private void fireAboutToShow(IMenuManager manager) {
+        Object[] listeners = this.listeners.getListeners();
+        for (int i = 0; i < listeners.length; ++i) {
+            (cast(IMenuListener) listeners[i]).menuAboutToShow(manager);
+        }
+    }
+
+    /**
+     * Notifies any menu listeners that a menu is about to hide.
+     * Only listeners registered at the time this method is called are notified.
+     *
+     * @param manager the menu manager
+     *
+     */
+    private void fireAboutToHide(IMenuManager manager) {
+        final Object[] listeners = this.listeners.getListeners();
+        for (int i = 0; i < listeners.length; ++i) {
+            final Object listener = listeners[i];
+            if (auto listener2 = cast(IMenuListener2)listener) {
+                listener2.menuAboutToHide(manager);
+            }
+        }
+    }
+
+    /**
+     * Returns the menu id. The menu id is used when creating a contribution
+     * item for adding this menu as a sub menu of another.
+     *
+     * @return the menu id
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * Returns the SWT menu control for this menu manager.
+     *
+     * @return the menu control
+     */
+    public Menu getMenu() {
+        return menu;
+    }
+
+    /**
+     * Returns the text shown in the menu, potentially with a shortcut
+     * appended.
+     *
+     * @return the menu text
+     */
+    public String getMenuText() {
+        if (definitionId is null) {
+            return menuText;
+        }
+        ExternalActionManager.ICallback callback = ExternalActionManager
+                .getInstance().getCallback();
+        if (callback !is null) {
+            String shortCut = callback.getAcceleratorText(definitionId);
+            if (shortCut is null) {
+                return menuText;
+            }
+            return menuText ~ "\t" ~ shortCut; //$NON-NLS-1$
+        }
+        return menuText;
+    }
+
+    /**
+     * Returns the image for this menu as an image descriptor.
+     *
+     * @return the image, or <code>null</code> if this menu has no image
+     * @since 3.4
+     */
+    public ImageDescriptor getImageDescriptor() {
+        return image;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionManager#getOverrides()
+     */
+    public override IContributionManagerOverrides getOverrides() {
+        if (overrides is null) {
+            if (parent is null) {
+                overrides = new class IContributionManagerOverrides {
+                    public Integer getAccelerator(IContributionItem item) {
+                        return null;
+                    }
+
+                    public String getAcceleratorText(IContributionItem item) {
+                        return null;
+                    }
+
+                    public Boolean getEnabled(IContributionItem item) {
+                        return null;
+                    }
+
+                    public String getText(IContributionItem item) {
+                        return null;
+                    }
+                };
+            } else {
+                overrides = parent.getOverrides();
+            }
+            super.setOverrides(overrides);
+        }
+        return overrides;
+    }
+
+    /**
+     * Returns the parent contribution manager of this manger.
+     *
+     * @return the parent contribution manager
+     * @since 2.0
+     */
+    public IContributionManager getParent() {
+        return parent;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IMenuManager#getRemoveAllWhenShown()
+     */
+    public bool getRemoveAllWhenShown() {
+        return removeAllWhenShown;
+    }
+
+    /**
+     * Notifies all listeners that this menu is about to appear.
+     */
+    private void handleAboutToShow() {
+        if (removeAllWhenShown) {
+            removeAll();
+        }
+        fireAboutToShow(this);
+        update(false, false);
+    }
+
+    /**
+     * Notifies all listeners that this menu is about to disappear.
+     */
+    private void handleAboutToHide() {
+        fireAboutToHide(this);
+    }
+
+    /**
+     * Initializes the menu control.
+     */
+    private void initializeMenu() {
+        menu.addMenuListener(new class MenuAdapter {
+            public void menuHidden(MenuEvent e) {
+                //          ApplicationWindow.resetDescription(e.widget);
+                handleAboutToHide();
+            }
+
+            public void menuShown(MenuEvent e) {
+                handleAboutToShow();
+            }
+        });
+        // Don't do an update(true) here, in case menu is never opened.
+        // Always do it lazily in handleAboutToShow().
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#isDynamic()
+     */
+    public bool isDynamic() {
+        return false;
+    }
+
+    /**
+     * Returns whether this menu should be enabled or not.
+     * Used to enable the menu item containing this menu when it is realized as a sub-menu.
+     * <p>
+     * The default implementation of this framework method
+     * returns <code>true</code>. Subclasses may reimplement.
+     * </p>
+     *
+     * @return <code>true</code> if enabled, and
+     *   <code>false</code> if disabled
+     */
+    public bool isEnabled() {
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#isGroupMarker()
+     */
+    public bool isGroupMarker() {
+        return false;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#isSeparator()
+     */
+    public bool isSeparator() {
+        return false;
+    }
+
+    /**
+     * Check if the contribution is item is a subsitute for ourselves
+     *
+     * @param item the contribution item
+     * @return <code>true</code> if give item is a substitution for ourselves
+     * @deprecated this method is no longer a part of the
+     *   {@link org.eclipse.jface.action.IContributionItem} API.
+     */
+    public bool isSubstituteFor(IContributionItem item) {
+        return this.opEquals(cast(Object)item) !is 0;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#isVisible()
+     */
+    public bool isVisible() {
+        if (!visible) {
+            return false; // short circuit calculations in this case
+        }
+
+        if (removeAllWhenShown) {
+            // we have no way of knowing if the menu has children
+            return true;
+        }
+
+        // menus aren't visible if all of its children are invisible (or only contains visible separators).
+        IContributionItem[] childItems = getItems();
+        bool visibleChildren = false;
+        for (int j = 0; j < childItems.length; j++) {
+            if (childItems[j].isVisible() && !childItems[j].isSeparator()) {
+                visibleChildren = true;
+                break;
+            }
+        }
+
+        return visibleChildren;
+    }
+
+
+    /**
+     * The <code>MenuManager</code> implementation of this <code>ContributionManager</code> method
+     * also propagates the dirty flag up the parent chain.
+     *
+     * @since 3.1
+     */
+    public override void markDirty() {
+        super.markDirty();
+        // Can't optimize by short-circuiting when the first dirty manager is encountered,
+        // since non-visible children are not even processed.
+        // That is, it's possible to have a dirty sub-menu under a non-dirty parent menu
+        // even after the parent menu has been updated.
+        // If items are added/removed in the sub-menu, we still need to propagate the dirty flag up,
+        // even if the sub-menu is already dirty, since the result of isVisible() may change
+        // due to the added/removed items.
+        IContributionManager parent = getParent();
+        if (parent !is null) {
+            parent.markDirty();
+        }
+    }
+
+    /**
+     * Returns whether the menu control is created
+     * and not disposed.
+     *
+     * @return <code>true</code> if the control is created
+     *  and not disposed, <code>false</code> otherwise
+     * @since 3.4 protected, was added in 3.1 as private method
+     */
+    protected bool menuExist() {
+        return menu !is null && !menu.isDisposed();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IMenuManager#removeMenuListener(org.eclipse.jface.action.IMenuListener)
+     */
+    public void removeMenuListener(IMenuListener listener) {
+        listeners.remove(cast(Object)listener);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#saveWidgetState()
+     */
+    public void saveWidgetState() {
+    }
+
+    /**
+     * Sets the overrides for this contribution manager
+     *
+     * @param newOverrides the overrides for the items of this manager
+     * @since 2.0
+     */
+    public override void setOverrides(IContributionManagerOverrides newOverrides) {
+        overrides = newOverrides;
+        super.setOverrides(overrides);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#setParent(org.eclipse.jface.action.IContributionManager)
+     */
+    public void setParent(IContributionManager manager) {
+        parent = manager;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IMenuManager#setRemoveAllWhenShown(bool)
+     */
+    public void setRemoveAllWhenShown(bool removeAll) {
+        this.removeAllWhenShown = removeAll;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#setVisible(bool)
+     */
+    public void setVisible(bool visible) {
+        this.visible = visible;
+    }
+
+    /**
+     * Sets the action definition id of this action. This simply allows the menu
+     * item text to include a short cut if available.  It can be used to
+     * notify a user of a key combination that will open a quick menu.
+     *
+     * @param definitionId
+     *            the command definition id
+     * @since 3.4
+     */
+    public void setActionDefinitionId(String definitionId) {
+        this.definitionId = definitionId;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#update()
+     */
+    public void update() {
+        updateMenuItem();
+    }
+
+    /**
+     * The <code>MenuManager</code> implementation of this <code>IContributionManager</code>
+     * updates this menu, but not any of its submenus.
+     *
+     * @see #updateAll
+     */
+    public void update(bool force) {
+        update(force, false);
+    }
+
+    /**
+     * Get all the items from the implementation's widget.
+     *
+     * @return the menu items
+     * @since 3.4
+     */
+    protected Item[] getMenuItems() {
+        if (menu !is null) {
+            return menu.getItems();
+        }
+        return null;
+    }
+
+    /**
+     * Get an item from the implementation's widget.
+     *
+     * @param index
+     *            of the item
+     * @return the menu item
+     * @since 3.4
+     */
+    protected Item getMenuItem(int index) {
+        if (menu !is null) {
+            return menu.getItem(index);
+        }
+        return null;
+    }
+
+    /**
+     * Get the menu item count for the implementation's widget.
+     *
+     * @return the number of items
+     * @since 3.4
+     */
+    protected int getMenuItemCount() {
+        if (menu !is null) {
+            return menu.getItemCount();
+        }
+        return 0;
+    }
+
+    /**
+     * Call an <code>IContributionItem</code>'s fill method with the
+     * implementation's widget. The default is to use the <code>Menu</code>
+     * widget.<br>
+     * <code>fill(Menu menu, int index)</code>
+     *
+     * @param ci
+     *            An <code>IContributionItem</code> whose <code>fill()</code>
+     *            method should be called.
+     * @param index
+     *            The position the <code>fill()</code> method should start
+     *            inserting at.
+     * @since 3.4
+     */
+    protected void doItemFill(IContributionItem ci, int index) {
+        ci.fill(menu, index);
+    }
+
+    /**
+     * Incrementally builds the menu from the contribution items.
+     * This method leaves out double separators and separators in the first
+     * or last position.
+     *
+     * @param force <code>true</code> means update even if not dirty,
+     *   and <code>false</code> for normal incremental updating
+     * @param recursive <code>true</code> means recursively update
+     *   all submenus, and <code>false</code> means just this menu
+     */
+    protected void update(bool force, bool recursive) {
+        if (isDirty() || force) {
+            if (menuExist()) {
+                // clean contains all active items without double separators
+                IContributionItem[] items = getItems();
+                List clean = new ArrayList(items.length);
+                IContributionItem separator = null;
+                for (int i = 0; i < items.length; ++i) {
+                    IContributionItem ci = items[i];
+                    if (!ci.isVisible()) {
+                        continue;
+                    }
+                    if (ci.isSeparator()) {
+                        // delay creation until necessary
+                        // (handles both adjacent separators, and separator at end)
+                        separator = ci;
+                    } else {
+                        if (separator !is null) {
+                            if (clean.size() > 0) {
+                                clean.add(cast(Object)separator);
+                            }
+                            separator = null;
+                        }
+                        clean.add(cast(Object)ci);
+                    }
+                }
+
+                // remove obsolete (removed or non active)
+                Item[] mi = getMenuItems();
+
+                for (int i = 0; i < mi.length; i++) {
+                    Object data = mi[i].getData();
+
+                    if (data is null || !clean.contains(data)) {
+                        mi[i].dispose();
+                    } else if (cast(IContributionItem)data
+                            && (cast(IContributionItem) data).isDynamic()
+                            && (cast(IContributionItem) data).isDirty()) {
+                        mi[i].dispose();
+                    }
+                }
+
+                // add new
+                mi = getMenuItems();
+                int srcIx = 0;
+                int destIx = 0;
+
+                for (Iterator e = clean.iterator(); e.hasNext();) {
+                    IContributionItem src = cast(IContributionItem) e.next();
+                    IContributionItem dest;
+
+                    // get corresponding item in SWT widget
+                    if (srcIx < mi.length) {
+                        dest = cast(IContributionItem) mi[srcIx].getData();
+                    } else {
+                        dest = null;
+                    }
+
+                    if (dest !is null && (cast(Object)src).opEquals(cast(Object)dest)) {
+                        srcIx++;
+                        destIx++;
+                    } else if (dest !is null && dest.isSeparator()
+                            && src.isSeparator()) {
+                        mi[srcIx].setData(cast(Object)src);
+                        srcIx++;
+                        destIx++;
+                    } else {
+                        int start = getMenuItemCount();
+                        doItemFill(src, destIx);
+                        int newItems = getMenuItemCount() - start;
+                        for (int i = 0; i < newItems; i++) {
+                            Item item = getMenuItem(destIx++);
+                            item.setData(cast(Object)src);
+                        }
+                    }
+
+                    // May be we can optimize this call. If the menu has just
+                    // been created via the call src.fill(fMenuBar, destIx) then
+                    // the menu has already been updated with update(true)
+                    // (see MenuManager). So if force is true we do it again. But
+                    // we can't set force to false since then information for the
+                    // sub sub menus is lost.
+                    if (recursive) {
+                        IContributionItem item = src;
+                        if ( auto sub = cast(SubContributionItem)item ) {
+                            item = sub.getInnerItem();
+                        }
+                        if (auto mm = cast(IMenuManager)item ) {
+                            mm.updateAll(force);
+                        }
+                    }
+
+                }
+
+                // remove any old menu items not accounted for
+                for (; srcIx < mi.length; srcIx++) {
+                    mi[srcIx].dispose();
+                }
+
+                setDirty(false);
+            }
+        } else {
+            // I am not dirty. Check if I must recursivly walk down the hierarchy.
+            if (recursive) {
+                IContributionItem[] items = getItems();
+                for (int i = 0; i < items.length; ++i) {
+                    IContributionItem ci = items[i];
+                    if ( auto mm = cast(IMenuManager) ci ) {
+                        if (mm.isVisible()) {
+                            mm.updateAll(force);
+                        }
+                    }
+                }
+            }
+        }
+        updateMenuItem();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#update(java.lang.String)
+     */
+    public void update(String property) {
+        IContributionItem items[] = getItems();
+
+        for (int i = 0; i < items.length; i++) {
+            items[i].update(property);
+        }
+
+        if (menu !is null && !menu.isDisposed() && menu.getParentItem() !is null) {
+            if (IAction.TEXT.equals(property)) {
+                String text = getOverrides().getText(this);
+
+                if (text is null) {
+                    text = getMenuText();
+                }
+
+                if (text !is null) {
+                    ExternalActionManager.ICallback callback = ExternalActionManager
+                            .getInstance().getCallback();
+
+                    if (callback !is null) {
+                        int index = .indexOf( text, '&' ); // SWT collision with local indexOf
+
+                        if (index >= 0 && index < text.length - 1) {
+
+                            dchar character = CharacterToUpper(text
+                                [index + 1 .. $].firstCodePoint());
+
+                            if (callback.isAcceleratorInUse(SWT.ALT | character)) {
+                                if (index is 0) {
+                                    text = text.substring(1);
+                                } else {
+                                    text = text.substring(0, index)
+                                        ~ text.substring(index + 1);
+                                }
+                            }
+                        }
+                    }
+
+                    menu.getParentItem().setText(text);
+                }
+            } else if (IAction.IMAGE.equals(property) && image !is null) {
+                LocalResourceManager localManager = new LocalResourceManager(JFaceResources
+                        .getResources());
+                menu.getParentItem().setImage(localManager.createImage(image));
+                disposeOldImages();
+                imageManager = localManager;
+            }
+        }
+    }
+
+    /**
+     * Dispose any images allocated for this menu
+     */
+    private void disposeOldImages() {
+        if (imageManager !is null) {
+            imageManager.dispose();
+            imageManager = null;
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IMenuManager#updateAll(bool)
+     */
+    public void updateAll(bool force) {
+        update(force, true);
+    }
+
+    /**
+     * Updates the menu item for this sub menu.
+     * The menu item is disabled if this sub menu is empty.
+     * Does nothing if this menu is not a submenu.
+     */
+    private void updateMenuItem() {
+        /*
+         * Commented out until proper solution to enablement of
+         * menu item for a sub-menu is found. See bug 30833 for
+         * more details.
+         *
+         if (menuItem !is null && !menuItem.isDisposed() && menuExist()) {
+         IContributionItem items[] = getItems();
+         bool enabled = false;
+         for (int i = 0; i < items.length; i++) {
+         IContributionItem item = items[i];
+         enabled = item.isEnabled();
+         if(enabled) break;
+         }
+         // Workaround for 1GDDCN2: SWT:Linux - MenuItem.setEnabled() always causes a redraw
+         if (menuItem.getEnabled() !is enabled)
+         menuItem.setEnabled(enabled);
+         }
+         */
+        // Partial fix for bug #34969 - diable the menu item if no
+        // items in sub-menu (for context menus).
+        if (menuItem !is null && !menuItem.isDisposed() && menuExist()) {
+            bool enabled = removeAllWhenShown || menu.getItemCount() > 0;
+            // Workaround for 1GDDCN2: SWT:Linux - MenuItem.setEnabled() always causes a redraw
+            if (menuItem.getEnabled() !is enabled) {
+                // We only do this for context menus (for bug #34969)
+                Menu topMenu = menu;
+                while (topMenu.getParentMenu() !is null) {
+                    topMenu = topMenu.getParentMenu();
+                }
+                if ((topMenu.getStyle() & SWT.BAR) is 0) {
+                    menuItem.setEnabled(enabled);
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/Separator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.Separator;
+
+import org.eclipse.jface.action.AbstractGroupMarker;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+
+import java.lang.all;
+
+/**
+ * A separator is a special kind of contribution item which acts
+ * as a visual separator and, optionally, acts as a group marker.
+ * Unlike group markers, separators do have a visual representation for
+ * menus and toolbars.
+ * <p>
+ * This class may be instantiated; it is not intended to be
+ * subclassed outside the framework.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class Separator : AbstractGroupMarker {
+    alias AbstractGroupMarker.fill fill;
+    /**
+     * Creates a separator which does not start a new group.
+     */
+    public this() {
+        super();
+    }
+
+    /**
+     * Creates a new separator which also defines a new group having the given group name.
+     * The group name must not be <code>null</code> or the empty string.
+     * The group name is also used as the item id.
+     *
+     * @param groupName the group name of the separator
+     */
+    public this(String groupName) {
+        super(groupName);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     * Fills the given menu with a SWT separator MenuItem.
+     */
+    public override void fill(Menu menu, int index) {
+        if (index >= 0) {
+            new MenuItem(menu, SWT.SEPARATOR, index);
+        } else {
+            new MenuItem(menu, SWT.SEPARATOR);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     * Fills the given tool bar with a SWT separator ToolItem.
+     */
+    public override void fill(ToolBar toolbar, int index) {
+        if (index >= 0) {
+            new ToolItem(toolbar, SWT.SEPARATOR, index);
+        } else {
+            new ToolItem(toolbar, SWT.SEPARATOR);
+        }
+    }
+
+    /**
+     * The <code>Separator</code> implementation of this <code>IContributionItem</code>
+     * method returns <code>true</code>
+     */
+    public override bool isSeparator() {
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/StatusLine.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,700 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.StatusLine;
+
+import org.eclipse.jface.action.StatusLineLayoutData;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.ProgressIndicator;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.JFaceColors;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A StatusLine control is a SWT Composite with a horizontal layout which hosts
+ * a number of status indication controls. Typically it is situated below the
+ * content area of the window.
+ * <p>
+ * By default a StatusLine has two predefined status controls: a MessageLine and
+ * a ProgressIndicator and it provides API for easy access.
+ * </p>
+ * <p>
+ * This is an internal class, not intended to be used outside the JFace
+ * framework.
+ * </p>
+ */
+/* package */class StatusLine : Composite, IProgressMonitor {
+
+    /** Horizontal gaps between items. */
+    public static const int GAP = 3;
+
+    /** Progress bar creation is delayed by this ms */
+    public static const int DELAY_PROGRESS = 500;
+
+    /** visibility state of the progressbar */
+    protected bool fProgressIsVisible = false;
+
+    /** visibility state of the cancle button */
+    protected bool fCancelButtonIsVisible = false;
+
+    /** enablement state of the cancle button */
+    protected bool fCancelEnabled = false;
+
+    /** name of the task */
+    protected String fTaskName;
+
+    /** is the task is cancled */
+    protected bool fIsCanceled;
+
+    /** the start time of the task */
+    protected long fStartTime;
+
+    private Cursor fStopButtonCursor;
+
+    /** the message text */
+    protected String fMessageText;
+
+    /** the message image */
+    protected Image fMessageImage;
+
+    /** the error text */
+    protected String fErrorText;
+
+    /** the error image */
+    protected Image fErrorImage;
+
+    /** the message label */
+    protected CLabel fMessageLabel;
+
+    /** the composite parent of the progress bar */
+    protected Composite fProgressBarComposite;
+
+    /** the progress bar */
+    protected ProgressIndicator fProgressBar;
+
+    /** the toolbar */
+    protected ToolBar fToolBar;
+
+    /** the cancle button */
+    protected ToolItem fCancelButton;
+
+    /** stop image descriptor */
+    protected static ImageDescriptor fgStopImage;
+
+    static this() {
+        fgStopImage = ImageDescriptor.createFromFile(
+            getImportData!("org.eclipse.jface.action.images.stop.gif"));//$NON-NLS-1$
+            //getImportData!("file.png"));//$NON-NLS-1$
+    //SWT Note: Not used in jface, but needs Display instance, which is not yet available.
+    //    JFaceResources.getImageRegistry().put(
+    //    "org.eclipse.jface.parts.StatusLine.stopImage", fgStopImage);//$NON-NLS-1$
+    }
+
+    /**
+     * Layout the contribution item controls on the status line.
+     */
+    public class StatusLineLayout : Layout {
+        private const StatusLineLayoutData DEFAULT_DATA;
+
+        this(){
+            DEFAULT_DATA = new StatusLineLayoutData();
+        }
+
+        public override Point computeSize(Composite composite, int wHint, int hHint,
+                bool changed) {
+
+            if (wHint !is SWT.DEFAULT && hHint !is SWT.DEFAULT) {
+                return new Point(wHint, hHint);
+            }
+
+            Control[] children = composite.getChildren();
+            int totalWidth = 0;
+            int maxHeight = 0;
+            int totalCnt = 0;
+            for (int i = 0; i < children.length; i++) {
+                bool useWidth = true;
+                Control w = children[i];
+                if (w is fProgressBarComposite && !fProgressIsVisible) {
+                    useWidth = false;
+                } else if (w is fToolBar && !fCancelButtonIsVisible) {
+                    useWidth = false;
+                }
+                StatusLineLayoutData data = cast(StatusLineLayoutData) w
+                        .getLayoutData();
+                if (data is null) {
+                    data = DEFAULT_DATA;
+                }
+                Point e = w.computeSize(data.widthHint, data.heightHint,
+                        changed);
+                if (useWidth) {
+                    totalWidth += e.x;
+                    totalCnt++;
+                }
+                maxHeight = Math.max(maxHeight, e.y);
+            }
+            if (totalCnt > 0) {
+                totalWidth += (totalCnt - 1) * GAP;
+            }
+            if (totalWidth <= 0) {
+                totalWidth = maxHeight * 4;
+            }
+            return new Point(totalWidth, maxHeight);
+        }
+
+        public override void layout(Composite composite, bool flushCache) {
+
+            if (composite is null) {
+                return;
+            }
+
+            // StatusLineManager skips over the standard status line widgets
+            // in its update method. There is thus a dependency
+            // between the layout of the standard widgets and the update method.
+
+            // Make sure cancel button and progress bar are before
+            // contributions.
+            fMessageLabel.moveAbove(null);
+            fToolBar.moveBelow(fMessageLabel);
+            fProgressBarComposite.moveBelow(fToolBar);
+
+            Rectangle rect = composite.getClientArea();
+            Control[] children = composite.getChildren();
+            int count = children.length;
+
+            int ws[] = new int[count];
+
+            int h = rect.height;
+            int totalWidth = -GAP;
+            for (int i = 0; i < count; i++) {
+                Control w = children[i];
+                if (w is fProgressBarComposite && !fProgressIsVisible) {
+                    continue;
+                }
+                if (w is fToolBar && !fCancelButtonIsVisible) {
+                    continue;
+                }
+                StatusLineLayoutData data = cast(StatusLineLayoutData) w
+                        .getLayoutData();
+                if (data is null) {
+                    data = DEFAULT_DATA;
+                }
+                int width = w.computeSize(data.widthHint, h, flushCache).x;
+                ws[i] = width;
+                totalWidth += width + GAP;
+            }
+
+            int diff = rect.width - totalWidth;
+            ws[0] += diff; // make the first StatusLabel wider
+
+            // Check against minimum recommended width
+            final int msgMinWidth = rect.width / 3;
+            if (ws[0] < msgMinWidth) {
+                diff = ws[0] - msgMinWidth;
+                ws[0] = msgMinWidth;
+            } else {
+                diff = 0;
+            }
+
+            // Take space away from the contributions first.
+            for (int i = count - 1; i >= 0 && diff < 0; --i) {
+                int min = Math.min(ws[i], -diff);
+                ws[i] -= min;
+                diff += min + GAP;
+            }
+
+            int x = rect.x;
+            int y = rect.y;
+            for (int i = 0; i < count; i++) {
+                Control w = children[i];
+                /*
+                 * Workaround for Linux Motif: Even if the progress bar and
+                 * cancel button are not set to be visible ad of width 0, they
+                 * still draw over the first pixel of the editor contributions.
+                 *
+                 * The fix here is to draw the progress bar and cancel button
+                 * off screen if they are not visible.
+                 */
+                if (w is fProgressBarComposite && !fProgressIsVisible
+                        || w is fToolBar && !fCancelButtonIsVisible) {
+                    w.setBounds(x + rect.width, y, ws[i], h);
+                    continue;
+                }
+                w.setBounds(x, y, ws[i], h);
+                if (ws[i] > 0) {
+                    x += ws[i] + GAP;
+                }
+            }
+        }
+    }
+
+    /**
+     * Create a new StatusLine as a child of the given parent.
+     *
+     * @param parent
+     *            the parent for this Composite
+     * @param style
+     *            the style used to create this widget
+     */
+    public this(Composite parent, int style) {
+        super(parent, style);
+
+        addDisposeListener(new class DisposeListener {
+            public void widgetDisposed(DisposeEvent e) {
+                handleDispose();
+            }
+        });
+
+        // StatusLineManager skips over the standard status line widgets
+        // in its update method. There is thus a dependency
+        // between this code defining the creation and layout of the standard
+        // widgets and the update method.
+
+        setLayout(new StatusLineLayout());
+
+        fMessageLabel = new CLabel(this, SWT.NONE);// SWT.SHADOW_IN);
+        // Color[] colors = new Color[2];
+        // colors[0] =
+        // parent.getDisplay().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW);
+        // colors[1] = fMessageLabel.getBackground();
+        // int[] gradient = new int[] {JFaceColors.STATUS_PERCENT};
+        // fMessageLabel.setBackground(colors, gradient);
+
+        fProgressIsVisible = false;
+        fCancelEnabled = false;
+
+        fToolBar = new ToolBar(this, SWT.FLAT);
+        fCancelButton = new ToolItem(fToolBar, SWT.PUSH);
+        fCancelButton.setImage(fgStopImage.createImage());
+        fCancelButton.setToolTipText(JFaceResources
+                .getString("Cancel_Current_Operation")); //$NON-NLS-1$
+        fCancelButton.addSelectionListener(new class SelectionAdapter {
+            public void widgetSelected(SelectionEvent e) {
+                setCanceled(true);
+            }
+        });
+        fCancelButton.addDisposeListener(new class DisposeListener {
+            public void widgetDisposed(DisposeEvent e) {
+                Image i = fCancelButton.getImage();
+                if ((i !is null) && (!i.isDisposed())) {
+                    i.dispose();
+                }
+            }
+        });
+
+        // We create a composite to create the progress bar in
+        // so that it can be centered. See bug #32331
+        fProgressBarComposite = new Composite(this, SWT.NONE);
+        GridLayout layout = new GridLayout();
+        layout.horizontalSpacing = 0;
+        layout.verticalSpacing = 0;
+        layout.marginHeight = 0;
+        layout.marginWidth = 0;
+        fProgressBarComposite.setLayout(layout);
+        fProgressBar = new ProgressIndicator(fProgressBarComposite);
+        fProgressBar.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
+                | GridData.GRAB_VERTICAL));
+
+        fStopButtonCursor = new Cursor(getDisplay(), SWT.CURSOR_ARROW);
+    }
+
+    /**
+     * Notifies that the main task is beginning.
+     *
+     * @param name
+     *            the name (or description) of the main task
+     * @param totalWork
+     *            the total number of work units into which the main task is
+     *            been subdivided. If the value is 0 or UNKNOWN the
+     *            implemenation is free to indicate progress in a way which
+     *            doesn't require the total number of work units in advance. In
+     *            general users should use the UNKNOWN value if they don't know
+     *            the total amount of work units.
+     */
+    public void beginTask(String name, int totalWork) {
+        long timestamp = System.currentTimeMillis();
+        fStartTime = timestamp;
+        bool animated = (totalWork is UNKNOWN || totalWork is 0);
+        // make sure the progress bar is made visible while
+        // the task is running. Fixes bug 32198 for the non-animated case.
+        Runnable timer = new class(animated,timestamp) Runnable {
+            bool animated_;
+            long timestamp_;
+            this(bool a,long b){
+                animated_=a;
+                timestamp_=b;
+            }
+            public void run() {
+                this.outer.startTask(timestamp_, animated_);
+            }
+        };
+        if (fProgressBar is null) {
+            return;
+        }
+
+        fProgressBar.getDisplay().timerExec(DELAY_PROGRESS, timer);
+        if (!animated) {
+            fProgressBar.beginTask(totalWork);
+        }
+        if (name is null) {
+            fTaskName = Util.ZERO_LENGTH_STRING;
+        } else {
+            fTaskName = name;
+        }
+        setMessage(fTaskName);
+    }
+
+    /**
+     * Notifies that the work is done; that is, either the main task is
+     * completed or the user cancelled it. Done() can be called more than once;
+     * an implementation should be prepared to handle this case.
+     */
+    public void done() {
+
+        fStartTime = 0;
+
+        if (fProgressBar !is null) {
+            fProgressBar.sendRemainingWork();
+            fProgressBar.done();
+        }
+        setMessage(null);
+
+        hideProgress();
+    }
+
+    /**
+     * Returns the status line's progress monitor
+     * 
+     * @return {@link IProgressMonitor} the progress monitor
+     */
+    public IProgressMonitor getProgressMonitor() {
+        return this;
+    }
+
+    /**
+     * @private
+     */
+    protected void handleDispose() {
+        if (fStopButtonCursor !is null) {
+            fStopButtonCursor.dispose();
+            fStopButtonCursor = null;
+        }
+        if (fProgressBar !is null) {
+            fProgressBar.dispose();
+            fProgressBar = null;
+        }
+    }
+
+    /**
+     * Hides the Cancel button and ProgressIndicator.
+     * 
+     */
+    protected void hideProgress() {
+
+        if (fProgressIsVisible && !isDisposed()) {
+            fProgressIsVisible = false;
+            fCancelEnabled = false;
+            fCancelButtonIsVisible = false;
+            if (fToolBar !is null && !fToolBar.isDisposed()) {
+                fToolBar.setVisible(false);
+            }
+            if (fProgressBarComposite !is null
+                    && !fProgressBarComposite.isDisposed()) {
+                fProgressBarComposite.setVisible(false);
+            }
+            layout();
+        }
+    }
+
+    /**
+     * @see IProgressMonitor#internalWorked(double)
+     */
+    public void internalWorked(double work) {
+        if (!fProgressIsVisible) {
+            if (System.currentTimeMillis() - fStartTime > DELAY_PROGRESS) {
+                showProgress();
+            }
+        }
+
+        if (fProgressBar !is null) {
+            fProgressBar.worked(work);
+        }
+    }
+
+    /**
+     * Returns true if the user does some UI action to cancel this operation.
+     * (like hitting the Cancel button on the progress dialog). The long running
+     * operation typically polls isCanceled().
+     */
+    public bool isCanceled() {
+        return fIsCanceled;
+    }
+
+    /**
+     * Returns
+     * <code>true</true> if the ProgressIndication provides UI for canceling
+     * a long running operation.
+     * @return <code>true</true> if the ProgressIndication provides UI for canceling
+     */
+    public bool isCancelEnabled() {
+        return fCancelEnabled;
+    }
+
+    /**
+     * Sets the cancel status. This method is usually called with the argument
+     * false if a client wants to abort a cancel action.
+     */
+    public void setCanceled(bool b) {
+        fIsCanceled = b;
+        if (fCancelButton !is null) {
+            fCancelButton.setEnabled(!b);
+        }
+    }
+
+    /**
+     * Controls whether the ProgressIndication provides UI for canceling a long
+     * running operation. If the ProgressIndication is currently visible calling
+     * this method may have a direct effect on the layout because it will make a
+     * cancel button visible.
+     * 
+     * @param enabled
+     *            <code>true</true> if cancel should be enabled
+     */
+    public void setCancelEnabled(bool enabled) {
+        fCancelEnabled = enabled;
+        if (fProgressIsVisible && !fCancelButtonIsVisible && enabled) {
+            showButton();
+            layout();
+        }
+        if (fCancelButton !is null && !fCancelButton.isDisposed()) {
+            fCancelButton.setEnabled(enabled);
+        }
+    }
+
+    /**
+     * Sets the error message text to be displayed on the status line. The image
+     * on the status line is cleared.
+     *
+     * @param message
+     *            the error message, or <code>null</code> for no error message
+     */
+    public void setErrorMessage(String message) {
+        setErrorMessage(null, message);
+    }
+
+    /**
+     * Sets an image and error message text to be displayed on the status line.
+     *
+     * @param image
+     *            the image to use, or <code>null</code> for no image
+     * @param message
+     *            the error message, or <code>null</code> for no error message
+     */
+    public void setErrorMessage(Image image, String message) {
+        fErrorText = trim(message);
+        fErrorImage = image;
+        updateMessageLabel();
+    }
+
+    /**
+     * Applies the given font to this status line.
+     */
+    public override void setFont(Font font) {
+        super.setFont(font);
+        Control[] children = getChildren();
+        for (int i = 0; i < children.length; i++) {
+            children[i].setFont(font);
+        }
+    }
+
+    /**
+     * Sets the message text to be displayed on the status line. The image on
+     * the status line is cleared.
+     *
+     * @param message
+     *            the error message, or <code>null</code> for no error message
+     */
+    public void setMessage(String message) {
+        setMessage(null, message);
+    }
+
+    /**
+     * Sets an image and a message text to be displayed on the status line.
+     *
+     * @param image
+     *            the image to use, or <code>null</code> for no image
+     * @param message
+     *            the message, or <code>null</code> for no message
+     */
+    public void setMessage(Image image, String message) {
+        fMessageText = trim(message);
+        fMessageImage = image;
+        updateMessageLabel();
+    }
+
+    /**
+     * @see IProgressMonitor#setTaskName(java.lang.String)
+     */
+    public void setTaskName(String name) {
+        if (name is null)
+            fTaskName = Util.ZERO_LENGTH_STRING;
+        else
+            fTaskName = name;
+    }
+
+    /**
+     * Makes the Cancel button visible.
+     * 
+     */
+    protected void showButton() {
+        if (fToolBar !is null && !fToolBar.isDisposed()) {
+            fToolBar.setVisible(true);
+            fToolBar.setEnabled(true);
+            fToolBar.setCursor(fStopButtonCursor);
+            fCancelButtonIsVisible = true;
+        }
+    }
+
+    /**
+     * Shows the Cancel button and ProgressIndicator.
+     * 
+     */
+    protected void showProgress() {
+        if (!fProgressIsVisible && !isDisposed()) {
+            fProgressIsVisible = true;
+            if (fCancelEnabled) {
+                showButton();
+            }
+            if (fProgressBarComposite !is null
+                    && !fProgressBarComposite.isDisposed()) {
+                fProgressBarComposite.setVisible(true);
+            }
+            layout();
+        }
+    }
+
+    /**
+     * @private
+     */
+    void startTask(long timestamp, bool animated) {
+        if (!fProgressIsVisible && fStartTime is timestamp) {
+            showProgress();
+            if (animated) {
+                if (fProgressBar !is null && !fProgressBar.isDisposed()) {
+                    fProgressBar.beginAnimatedTask();
+                }
+            }
+        }
+    }
+
+    /**
+     * Notifies that a subtask of the main task is beginning. Subtasks are
+     * optional; the main task might not have subtasks.
+     * 
+     * @param name
+     *            the name (or description) of the subtask
+     * @see IProgressMonitor#subTask(String)
+     */
+    public void subTask(String name) {
+
+        String newName;
+        if (name is null)
+            newName = Util.ZERO_LENGTH_STRING;
+        else
+            newName = name;
+
+        String text;
+        if (fTaskName.length is 0) {
+            text = newName;
+        } else {
+            text = JFaceResources.format(
+                    "Set_SubTask", [ fTaskName, newName ]);//$NON-NLS-1$
+        }
+        setMessage(text);
+    }
+
+    /**
+     * Trims the message to be displayable in the status line. This just pulls
+     * out the first line of the message. Allows null.
+     */
+    String trim(String message) {
+        if (message is null) {
+            return null;
+        }
+        message = Util.replaceAll(message, "&", "&&"); //$NON-NLS-1$//$NON-NLS-2$
+        int cr = message.indexOf('\r');
+        int lf = message.indexOf('\n');
+        if (cr is -1 && lf is -1) {
+            return message;
+        }
+        int len;
+        if (cr is -1) {
+            len = lf;
+        } else if (lf is -1) {
+            len = cr;
+        } else {
+            len = Math.min(cr, lf);
+        }
+        return message.substring(0, len);
+    }
+
+    /**
+     * Updates the message label widget.
+     */
+    protected void updateMessageLabel() {
+        if (fMessageLabel !is null && !fMessageLabel.isDisposed()) {
+            Display display = fMessageLabel.getDisplay();
+            if ((fErrorText !is null && fErrorText.length > 0)
+                    || fErrorImage !is null) {
+                fMessageLabel.setForeground(JFaceColors.getErrorText(display));
+                fMessageLabel.setText(fErrorText);
+                fMessageLabel.setImage(fErrorImage);
+            } else {
+                fMessageLabel.setForeground(display
+                        .getSystemColor(SWT.COLOR_WIDGET_FOREGROUND));
+                fMessageLabel.setText(fMessageText is null ? "" : fMessageText); //$NON-NLS-1$
+                fMessageLabel.setImage(fMessageImage);
+            }
+        }
+    }
+
+    /**
+     * @see IProgressMonitor#worked(int)
+     */
+    public void worked(int work) {
+        internalWorked(work);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/StatusLineContributionItem.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.StatusLineContributionItem;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.util.Util;
+
+import org.eclipse.jface.action.ContributionItem;
+import org.eclipse.jface.action.StatusLineLayoutData;
+import org.eclipse.jface.action.IContributionManager;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A contribution item to be used with status line managers.
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.
+ * </p>
+ *
+ * @since 3.4
+ */
+public class StatusLineContributionItem : ContributionItem {
+
+    private const static int DEFAULT_CHAR_WIDTH = 40;
+
+    private int charWidth;
+
+    private CLabel label;
+
+    /**
+     * The composite into which this contribution item has been placed. This
+     * will be <code>null</code> if this instance has not yet been
+     * initialized.
+     */
+    private Composite statusLine = null;
+
+    private String text;
+
+    private int widthHint = -1;
+
+    private int heightHint = -1;
+
+    /**
+     * Creates a status line contribution item with the given id.
+     *
+     * @param id
+     *            the contribution item's id, or <code>null</code> if it is to
+     *            have no id
+     */
+    public this(String id) {
+        this(id, DEFAULT_CHAR_WIDTH);
+    }
+
+    /**
+     * Creates a status line contribution item with the given id that displays
+     * the given number of characters.
+     *
+     * @param id
+     *            the contribution item's id, or <code>null</code> if it is to
+     *            have no id
+     * @param charWidth
+     *            the number of characters to display
+     */
+    public this(String id, int charWidth) {
+        text = Util.ZERO_LENGTH_STRING;
+        super(id);
+        this.charWidth = charWidth;
+        setVisible(false); // no text to start with
+    }
+
+    public void fill(Composite parent) {
+        statusLine = parent;
+
+        Label sep = new Label(parent, SWT.SEPARATOR);
+        label = new CLabel(statusLine, SWT.SHADOW_NONE);
+
+        if (widthHint < 0) {
+            GC gc = new GC(statusLine);
+            gc.setFont(statusLine.getFont());
+            FontMetrics fm = gc.getFontMetrics();
+            widthHint = fm.getAverageCharWidth() * charWidth;
+            heightHint = fm.getHeight();
+            gc.dispose();
+        }
+
+        StatusLineLayoutData data = new StatusLineLayoutData();
+        data.widthHint = widthHint;
+        label.setLayoutData(data);
+        label.setText(text);
+
+        data = new StatusLineLayoutData();
+        data.heightHint = heightHint;
+        sep.setLayoutData(data);
+    }
+
+    /**
+     * An accessor for the current location of this status line contribution
+     * item -- relative to the display.
+     *
+     * @return The current location of this status line; <code>null</code> if
+     *         not yet initialized.
+     */
+    public Point getDisplayLocation() {
+        if ((label !is null) && (statusLine !is null)) {
+            return statusLine.toDisplay(label.getLocation());
+        }
+
+        return null;
+    }
+
+    /**
+     * Retrieves the text that is being displayed in the status line.
+     *
+     * @return the text that is currently being displayed
+     */
+    public String getText() {
+        return text;
+    }
+
+    /**
+     * Sets the text to be displayed in the status line.
+     *
+     * @param text
+     *            the text to be displayed, must not be <code>null</code>
+     */
+    public void setText(String text) {
+        Assert.isNotNull(text);
+
+        this.text = escape(text);
+
+        if (label !is null && !label.isDisposed()) {
+            label.setText(this.text);
+        }
+
+        if (this.text.length is 0) {
+            if (isVisible()) {
+                setVisible(false);
+                IContributionManager contributionManager = getParent();
+
+                if (contributionManager !is null) {
+                    contributionManager.update(true);
+                }
+            }
+        } else {
+            if (!isVisible()) {
+                setVisible(true);
+                IContributionManager contributionManager = getParent();
+
+                if (contributionManager !is null) {
+                    contributionManager.update(true);
+                }
+            }
+        }
+    }
+
+    private String escape(String text) {
+        return Util.replaceAll(text, "&", "&&");  //$NON-NLS-1$//$NON-NLS-2$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/StatusLineLayoutData.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.action.StatusLineLayoutData;
+
+import org.eclipse.swt.SWT;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Represents the layout data object for <code>Control</code> within the status line.
+ * To set a <code>StatusLineLayoutData</code> object into a <code>Control</code>, use
+ * the <code>setLayoutData()</code> method.
+ * <p>
+ * NOTE: Do not reuse <code>StatusLineLayoutData</code> objects. Every control in the
+ * status line must have a unique <code>StatusLineLayoutData</code> instance or
+ * <code>null</code>.
+ * </p>
+ *
+ * @since 2.1
+ */
+public class StatusLineLayoutData {
+    /**
+     * The <code>widthHint</code> specifies a minimum width for
+     * the <code>Control</code>. A value of <code>SWT.DEFAULT</code>
+     * indicates that no minimum width is specified.
+     *
+     * The default value is <code>SWT.DEFAULT</code>.
+     */
+    public int widthHint = SWT.DEFAULT;
+
+    /**
+     * The <code>heightHint</code> specifies a minimum height for
+     * the <code>Control</code>. A value of <code>SWT.DEFAULT</code>
+     * indicates that no minimum height is specified.
+     *
+     * The default value is <code>SWT.DEFAULT</code>.
+     */
+    public int heightHint = SWT.DEFAULT;
+
+    /**
+     * Creates an initial status line layout data object.
+     */
+    public this() {
+        //super();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/StatusLineManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,361 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.action.StatusLineManager;
+
+import org.eclipse.jface.action.ContributionManager;
+import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.action.GroupMarker;
+import org.eclipse.jface.action.StatusLine;
+import org.eclipse.jface.action.IContributionManager;
+import org.eclipse.jface.action.IContributionItem;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IProgressMonitorWithBlocking;
+import org.eclipse.core.runtime.IStatus;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A status line manager is a contribution manager which realizes itself and its items
+ * in a status line control.
+ * <p>
+ * This class may be instantiated; it may also be subclassed if a more
+ * sophisticated layout is required.
+ * </p>
+ */
+public class StatusLineManager : ContributionManager,
+        IStatusLineManager {
+
+    /**
+     * Identifier of group marker used to position contributions at the beginning
+     * of the status line.
+     *
+     * @since 3.0
+     */
+    public static const String BEGIN_GROUP = "BEGIN_GROUP"; //$NON-NLS-1$
+
+    /**
+     * Identifier of group marker used to position contributions in the middle
+     * of the status line.
+     *
+     * @since 3.0
+     */
+    public static const String MIDDLE_GROUP = "MIDDLE_GROUP"; //$NON-NLS-1$
+
+    /**
+     * Identifier of group marker used to position contributions at the end
+     * of the status line.
+     *
+     * @since 3.0
+     */
+    public static const String END_GROUP = "END_GROUP"; //$NON-NLS-1$
+
+    /**
+     * The status line control; <code>null</code> before
+     * creation and after disposal.
+     */
+    private Composite statusLine = null;
+
+    /**
+     * Creates a new status line manager.
+     * Use the <code>createControl</code> method to create the
+     * status line control.
+     */
+    public this() {
+        add(new GroupMarker(BEGIN_GROUP));
+        add(new GroupMarker(MIDDLE_GROUP));
+        add(new GroupMarker(END_GROUP));
+    }
+
+    /**
+     * Creates and returns this manager's status line control.
+     * Does not create a new control if one already exists.
+     * <p>
+     * Note: Since 3.0 the return type is <code>Control</code>.  Before 3.0, the return type was
+     *   the package-private class <code>StatusLine</code>.
+     * </p>
+     *
+     * @param parent the parent control
+     * @return the status line control
+     */
+    public Control createControl(Composite parent) {
+        return createControl(parent, SWT.NONE);
+    }
+
+    /**
+     * Creates and returns this manager's status line control.
+     * Does not create a new control if one already exists.
+     *
+     * @param parent the parent control
+     * @param style the style for the control
+     * @return the status line control
+     * @since 3.0
+     */
+    public Control createControl(Composite parent, int style) {
+        if (!statusLineExist() && parent !is null) {
+            statusLine = new StatusLine(parent, style);
+            update(false);
+        }
+        return statusLine;
+    }
+
+    /**
+     * Disposes of this status line manager and frees all allocated SWT resources.
+     * Notifies all contribution items of the dispose. Note that this method does
+     * not clean up references between this status line manager and its associated
+     * contribution items. Use <code>removeAll</code> for that purpose.
+     */
+    public void dispose() {
+        if (statusLineExist()) {
+            statusLine.dispose();
+        }
+        statusLine = null;
+
+        IContributionItem items[] = getItems();
+        for (int i = 0; i < items.length; i++) {
+            items[i].dispose();
+        }
+    }
+
+    /**
+     * Returns the control used by this StatusLineManager.
+     *
+     * @return the control used by this manager
+     */
+    public Control getControl() {
+        return statusLine;
+    }
+
+    /**
+     * Returns the progress monitor delegate. Override this method
+     * to provide your own object used to handle progress.
+     *
+     * @return the IProgressMonitor delegate
+     * @since 3.0
+     */
+    protected IProgressMonitor getProgressMonitorDelegate() {
+        return cast(IProgressMonitor) getControl();
+    }
+
+    /*
+     * (non-Javadoc)
+     * Method declared on IStatusLineManager
+     */
+    public IProgressMonitor getProgressMonitor() {
+
+        return new class(getProgressMonitorDelegate()) IProgressMonitorWithBlocking {
+
+            IProgressMonitor progressDelegate;
+            this(IProgressMonitor a){
+                progressDelegate = a;
+            }
+
+            /* (non-Javadoc)
+             * @see org.eclipse.core.runtime.IProgressMonitor#beginTask(java.lang.String, int)
+             */
+            public void beginTask(String name, int totalWork) {
+                progressDelegate.beginTask(name, totalWork);
+
+            }
+
+            /* (non-Javadoc)
+             * @see org.eclipse.core.runtime.IProgressMonitor#done()
+             */
+            public void done() {
+                progressDelegate.done();
+            }
+
+            /* (non-Javadoc)
+             * @see org.eclipse.core.runtime.IProgressMonitor#internalWorked(double)
+             */
+            public void internalWorked(double work) {
+                progressDelegate.internalWorked(work);
+
+            }
+
+            /* (non-Javadoc)
+             * @see org.eclipse.core.runtime.IProgressMonitor#isCanceled()
+             */
+            public bool isCanceled() {
+                return progressDelegate.isCanceled();
+            }
+
+            /* (non-Javadoc)
+             * @see org.eclipse.core.runtime.IProgressMonitor#setCanceled(bool)
+             */
+            public void setCanceled(bool value) {
+                //Don't bother updating for disposed status
+                if (statusLine.isDisposed()) {
+                    return;
+                }
+                progressDelegate.setCanceled(value);
+
+            }
+
+            /* (non-Javadoc)
+             * @see org.eclipse.core.runtime.IProgressMonitor#setTaskName(java.lang.String)
+             */
+            public void setTaskName(String name) {
+                progressDelegate.setTaskName(name);
+
+            }
+
+            /* (non-Javadoc)
+             * @see org.eclipse.core.runtime.IProgressMonitor#subTask(java.lang.String)
+             */
+            public void subTask(String name) {
+                progressDelegate.subTask(name);
+
+            }
+
+            /* (non-Javadoc)
+             * @see org.eclipse.core.runtime.IProgressMonitor#worked(int)
+             */
+            public void worked(int work) {
+                progressDelegate.worked(work);
+            }
+
+            /* (non-Javadoc)
+             * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#clearBlocked()
+             */
+            public void clearBlocked() {
+                //Do nothing here as we let the modal context handle it
+            }
+
+            /* (non-Javadoc)
+             * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#setBlocked(org.eclipse.core.runtime.IStatus)
+             */
+            public void setBlocked(IStatus reason) {
+                //          Do nothing here as we let the modal context handle it
+            }
+        };
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IStatueLineManager
+     */
+    public bool isCancelEnabled() {
+        return statusLineExist() && (cast(StatusLine) statusLine).isCancelEnabled();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IStatueLineManager
+     */
+    public void setCancelEnabled(bool enabled) {
+        if (statusLineExist()) {
+            (cast(StatusLine) statusLine).setCancelEnabled(enabled);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IStatusLineManager.
+     */
+    public void setErrorMessage(String message) {
+        if (statusLineExist()) {
+            (cast(StatusLine) statusLine).setErrorMessage(message);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IStatusLineManager.
+     */
+    public void setErrorMessage(Image image, String message) {
+        if (statusLineExist()) {
+            (cast(StatusLine) statusLine).setErrorMessage(image, message);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IStatusLineManager.
+     */
+    public void setMessage(String message) {
+        if (statusLineExist()) {
+            (cast(StatusLine) statusLine).setMessage(message);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IStatusLineManager.
+     */
+    public void setMessage(Image image, String message) {
+        if (statusLineExist()) {
+            (cast(StatusLine) statusLine).setMessage(image, message);
+        }
+    }
+
+    /**
+     * Returns whether the status line control is created
+     * and not disposed.
+     *
+     * @return <code>true</code> if the control is created
+     *  and not disposed, <code>false</code> otherwise
+     */
+    private bool statusLineExist() {
+        return statusLine !is null && !statusLine.isDisposed();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public void update(bool force) {
+
+        //bool DEBUG= false;
+
+        if (isDirty() || force) {
+
+            if (statusLineExist()) {
+                statusLine.setRedraw(false);
+
+                // NOTE: the update algorithm is non-incremental.
+                // An incremental algorithm requires that SWT items can be created in the middle of the list
+                // but the ContributionItem.fill(Composite) method used here does not take an index, so this
+                // is not possible.
+
+                Control ws[] = statusLine.getChildren();
+                for (int i = 0; i < ws.length; i++) {
+                    Control w = ws[i];
+                    Object data = w.getData();
+                    if (cast(IContributionItem) data ) {
+                        w.dispose();
+                    }
+                }
+
+                int oldChildCount = statusLine.getChildren().length;
+                IContributionItem[] items = getItems();
+                for (int i = 0; i < items.length; ++i) {
+                    IContributionItem ci = items[i];
+                    if (ci.isVisible()) {
+                        ci.fill(statusLine);
+                        // associate controls with contribution item
+                        Control[] newChildren = statusLine.getChildren();
+                        for (int j = oldChildCount; j < newChildren.length; j++) {
+                            newChildren[j].setData(cast(Object)ci);
+                        }
+                        oldChildCount = newChildren.length;
+                    }
+                }
+
+                setDirty(false);
+
+                statusLine.layout();
+                statusLine.setRedraw(true);
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/SubContributionItem.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,191 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.SubContributionItem;
+
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.IContributionManager;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.CoolBar;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.ToolBar;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A <code>SubContributionItem</code> is a wrapper for an <code>IContributionItem</code>.
+ * It is used within a <code>SubContributionManager</code> to control the visibility
+ * of items.
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class SubContributionItem : IContributionItem {
+    /**
+     * The visibility of the item.
+     */
+    private bool visible;
+
+    /**
+     * The inner item for this contribution.
+     */
+    private IContributionItem innerItem;
+
+    /**
+     * Creates a new <code>SubContributionItem</code>.
+     * @param item the contribution item to be wrapped
+     */
+    public this(IContributionItem item) {
+        innerItem = item;
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * delegates to the inner item. Subclasses may override.
+     */
+    public void dispose() {
+        innerItem.dispose();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public void fill(Composite parent) {
+        if (visible) {
+            innerItem.fill(parent);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public void fill(Menu parent, int index) {
+        if (visible) {
+            innerItem.fill(parent, index);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public void fill(ToolBar parent, int index) {
+        if (visible) {
+            innerItem.fill(parent, index);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public String getId() {
+        return innerItem.getId();
+    }
+
+    /**
+     * Returns the inner contribution item.
+     *
+     * @return the inner contribution item
+     */
+    public IContributionItem getInnerItem() {
+        return innerItem;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public bool isEnabled() {
+        return innerItem.isEnabled();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public bool isDirty() {
+        return innerItem.isDirty();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public bool isDynamic() {
+        return innerItem.isDynamic();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public bool isGroupMarker() {
+        return innerItem.isGroupMarker();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public bool isSeparator() {
+        return innerItem.isSeparator();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public bool isVisible() {
+        return visible && innerItem.isVisible();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public void setParent(IContributionManager parent) {
+        // do nothing, the parent of our inner item
+        // is its SubContributionManager
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public void setVisible(bool visible) {
+        this.visible = visible;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public void update() {
+        innerItem.update();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionItem.
+     */
+    public void update(String id) {
+        innerItem.update(id);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#fill(org.eclipse.swt.widgets.CoolBar, int)
+     */
+    public void fill(CoolBar parent, int index) {
+        if (visible) {
+            innerItem.fill(parent, index);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#saveWidgetState()
+     */
+    public void saveWidgetState() {
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/SubContributionManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,377 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.action.SubContributionManager;
+
+import org.eclipse.jface.action.IContributionManager;
+import org.eclipse.jface.action.ActionContributionItem;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.SubContributionItem;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.IContributionManagerOverrides;
+
+
+import java.lang.all;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+/**
+ * A <code>SubContributionManager</code> is used to define a set of contribution
+ * items within a parent manager.  Once defined, the visibility of the entire set can
+ * be changed as a unit.
+ */
+public abstract class SubContributionManager : IContributionManager {
+    /**
+     * The parent contribution manager.
+     */
+    private IContributionManager parentMgr;
+
+    /**
+     * Maps each item in the manager to a wrapper.  The wrapper is used to
+     * control the visibility of each item.
+     */
+    private Map mapItemToWrapper;
+
+    /**
+     * The visibility of the manager,
+     */
+    private bool visible = false;
+
+    /**
+     * Constructs a new <code>SubContributionManager</code>
+     *
+     * @param mgr the parent contribution manager.  All contributions made to the
+     *      <code>SubContributionManager</code> are forwarded and appear in the
+     *      parent manager.
+     */
+    public this(IContributionManager mgr) {
+        mapItemToWrapper = new HashMap();
+        //super();
+        parentMgr = mgr;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public void add(IAction action) {
+        add(new ActionContributionItem(action));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public void add(IContributionItem item) {
+        item.setParent(this);
+        SubContributionItem wrap = wrap(item);
+        wrap.setVisible(visible);
+        parentMgr.add(wrap);
+        itemAdded(item, wrap);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public void appendToGroup(String groupName, IAction action) {
+        appendToGroup(groupName, new ActionContributionItem(action));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public void appendToGroup(String groupName, IContributionItem item) {
+        item.setParent(this);
+        SubContributionItem wrap = wrap(item);
+        wrap.setVisible(visible);
+        parentMgr.appendToGroup(groupName, wrap);
+        itemAdded(item, wrap);
+    }
+
+    /**
+     * Disposes this sub contribution manager, removing all its items
+     * and cleaning up any other resources allocated by it.
+     * This must leave no trace of this sub contribution manager
+     * in the parent manager.  Subclasses may extend.
+     *
+     * @since 3.0
+     */
+    public void disposeManager() {
+        Iterator it = mapItemToWrapper.values().iterator();
+        // Dispose items in addition to removing them.
+        // See bugs 64024 and 73715 for details.
+        // Do not use getItems() here as subclasses can override that in bad ways.
+        while (it.hasNext()) {
+            IContributionItem item = cast(IContributionItem) it.next();
+            item.dispose();
+        }
+        removeAll();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     *
+     * Returns the item passed to us, not the wrapper.
+     */
+    public IContributionItem find(String id) {
+        IContributionItem item = parentMgr.find(id);
+        // Return the item passed to us, not the wrapper.
+        item = unwrap(item);
+        return item;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     *
+     * Returns the items passed to us, not the wrappers.
+     */
+    public IContributionItem[] getItems() {
+        IContributionItem[] result = arraycast!(IContributionItem)(mapItemToWrapper.keySet().toArray());
+        return result;
+    }
+
+    /**
+     * Returns the parent manager.
+     *
+     * @return the parent manager
+     */
+    public IContributionManager getParent() {
+        return parentMgr;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public IContributionManagerOverrides getOverrides() {
+        return parentMgr.getOverrides();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public void insertAfter(String id, IAction action) {
+        insertAfter(id, new ActionContributionItem(action));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public void insertAfter(String id, IContributionItem item) {
+        item.setParent(this);
+        SubContributionItem wrap = wrap(item);
+        wrap.setVisible(visible);
+        parentMgr.insertAfter(id, wrap);
+        itemAdded(item, wrap);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public void insertBefore(String id, IAction action) {
+        insertBefore(id, new ActionContributionItem(action));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public void insertBefore(String id, IContributionItem item) {
+        item.setParent(this);
+        SubContributionItem wrap = wrap(item);
+        wrap.setVisible(visible);
+        parentMgr.insertBefore(id, wrap);
+        itemAdded(item, wrap);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public bool isDirty() {
+        return parentMgr.isDirty();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public bool isEmpty() {
+        return parentMgr.isEmpty();
+    }
+
+    /**
+     * Returns whether the contribution list is visible.
+     * If the visibility is <code>true</code> then each item within the manager
+     * appears within the parent manager.  Otherwise, the items are not visible.
+     *
+     * @return <code>true</code> if the manager is visible
+     */
+    public bool isVisible() {
+        return visible;
+    }
+
+    /**
+     * Notifies that an item has been added.
+     * <p>
+     * Subclasses are not expected to override this method.
+     * </p>
+     *
+     * @param item the item contributed by the client
+     * @param wrap the item contributed to the parent manager as a proxy for the item
+     *      contributed by the client
+     */
+    protected void itemAdded(IContributionItem item, SubContributionItem wrap) {
+        mapItemToWrapper.put(cast(Object)item, wrap);
+    }
+
+    /**
+     * Notifies that an item has been removed.
+     * <p>
+     * Subclasses are not expected to override this method.
+     * </p>
+     *
+     * @param item the item contributed by the client
+     */
+    protected void itemRemoved(IContributionItem item) {
+        mapItemToWrapper.remove(cast(Object)item);
+    }
+
+    /**
+     * @return fetch all enumeration of wrappers for the item
+     * @deprecated Use getItems(String value) instead.
+     */
+    public Enumeration items() {
+        return new class(mapItemToWrapper.values().iterator()) Enumeration {
+            Iterator i;
+            this(Iterator i_){
+                i = i_;
+            }
+            public bool hasMoreElements() {
+                return i.hasNext();
+            }
+
+            public Object nextElement() {
+                return i.next();
+            }
+        };
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public void markDirty() {
+        parentMgr.markDirty();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public void prependToGroup(String groupName, IAction action) {
+        prependToGroup(groupName, new ActionContributionItem(action));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public void prependToGroup(String groupName, IContributionItem item) {
+        item.setParent(this);
+        SubContributionItem wrap = wrap(item);
+        wrap.setVisible(visible);
+        parentMgr.prependToGroup(groupName, wrap);
+        itemAdded(item, wrap);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public IContributionItem remove(String id) {
+        IContributionItem result = parentMgr.remove(id);
+        if (result !is null) {
+            itemRemoved(result);
+        }
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public IContributionItem remove(IContributionItem item) {
+        SubContributionItem wrap = cast(SubContributionItem) mapItemToWrapper
+                .get(cast(Object)item);
+        if (wrap is null) {
+            return null;
+        }
+        IContributionItem result = parentMgr.remove(wrap);
+        if (result is null) {
+            return null;
+        }
+        itemRemoved(item);
+        return item;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     */
+    public void removeAll() {
+        Iterator it = mapItemToWrapper.values().iterator();
+        while (it.hasNext()) {
+            IContributionItem item = cast(IContributionItem) it.next();
+            parentMgr.remove(item);
+        }
+        mapItemToWrapper.clear();
+    }
+
+    /**
+     * Sets the visibility of the manager.  If the visibility is <code>true</code>
+     * then each item within the manager appears within the parent manager.
+     * Otherwise, the items are not visible.
+     *
+     * @param visible the new visibility
+     */
+    public void setVisible(bool visible) {
+        this.visible = visible;
+        if (mapItemToWrapper.size() > 0) {
+            Iterator it = mapItemToWrapper.values().iterator();
+            while (it.hasNext()) {
+                IContributionItem item = cast(IContributionItem) it.next();
+                item.setVisible(visible);
+            }
+            parentMgr.markDirty();
+        }
+    }
+
+    /**
+     * Wraps a contribution item in a sub contribution item, and returns the new wrapper.
+     * @param item the contribution item to be wrapped
+     * @return the wrapped item
+     */
+    protected SubContributionItem wrap(IContributionItem item) {
+        return new SubContributionItem(item);
+    }
+
+    /**
+     * Unwraps a nested contribution item. If the contribution item is an
+     * instance of <code>SubContributionItem</code>, then its inner item is
+     * returned. Otherwise, the item itself is returned.
+     *
+     * @param item
+     *            The item to unwrap; may be <code>null</code>.
+     * @return The inner item of <code>item</code>, if <code>item</code> is
+     *         a <code>SubContributionItem</code>;<code>item</code>
+     *         otherwise.
+     */
+    protected IContributionItem unwrap(IContributionItem item) {
+        if (cast(SubContributionItem)item ) {
+            return (cast(SubContributionItem) item).getInnerItem();
+        }
+
+        return item;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/SubCoolBarManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 org.eclipse.jface.action.SubCoolBarManager;
+
+import org.eclipse.jface.action.ICoolBarManager;
+import org.eclipse.jface.action.SubContributionManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.ToolBarContributionItem;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A <code>SubCoolBarManager</code> monitors the additional and removal of
+ * items from a parent manager so that visibility of the entire set can be changed as a
+ * unit.
+ *
+ * @since 3.0
+ */
+public class SubCoolBarManager : SubContributionManager,
+        ICoolBarManager {
+    alias SubContributionManager.add add;
+
+    /**
+     * Constructs a new manager.
+     *
+     * @param mgr the parent manager.  All contributions made to the
+     *      <code>SubCoolBarManager</code> are forwarded and appear in the
+     *      parent manager.
+     */
+    public this(ICoolBarManager mgr) {
+        super(mgr);
+        Assert.isNotNull(cast(Object)mgr);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.ICoolBarManager#add(org.eclipse.jface.action.IToolBarManager)
+     */
+    public void add(IToolBarManager toolBarManager) {
+        Assert.isNotNull(cast(Object)toolBarManager);
+        super.add(new ToolBarContributionItem(toolBarManager));
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.ICoolBarManager#getStyle()
+     */
+    public int getStyle() {
+        // It is okay to cast down since we only accept coolBarManager objects in the
+        // constructor
+        return (cast(ICoolBarManager) getParent()).getStyle();
+    }
+
+    /**
+     * Returns the parent cool bar manager that this sub-manager contributes to.
+     *
+     * @return the parent cool bar manager
+     */
+    protected final ICoolBarManager getParentCoolBarManager() {
+        // Cast is ok because that's the only
+        // thing we accept in the construtor.
+        return cast(ICoolBarManager) getParent();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.ICoolBarManager#isLayoutLocked()
+     */
+    public bool getLockLayout() {
+        return getParentCoolBarManager().getLockLayout();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.ICoolBarManager#lockLayout(bool)
+     */
+    public void setLockLayout(bool value) {
+    }
+
+    /* (non-Javadoc)
+     * SubCoolBarManagers do not have control of the global context menu.
+     */
+    public IMenuManager getContextMenuManager() {
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * In SubCoolBarManager we do nothing.
+     */
+    public void setContextMenuManager(IMenuManager menuManager) {
+        // do nothing
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionManager#update(bool)
+     */
+    public void update(bool force) {
+        // This method is not governed by visibility.  The client may
+        // call <code>setVisible</code> and then force an update.  At that
+        // point we need to update the parent.
+        getParentCoolBarManager().update(force);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/SubMenuManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,415 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.SubMenuManager;
+
+import org.eclipse.jface.action.SubContributionManager;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.IContributionManager;
+import org.eclipse.jface.action.SubContributionItem;
+
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.CoolBar;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+
+import java.lang.all;
+import java.util.List;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+/**
+ * A <code>SubMenuManager</code> is used to define a set of contribution
+ * items within a parent manager.  Once defined, the visibility of the entire set can
+ * be changed as a unit.
+ * <p>
+ * A client may ask for and make additions to a submenu.  The visibility of these items
+ * is also controlled by the visibility of the <code>SubMenuManager</code>.
+ * </p>
+ */
+public class SubMenuManager : SubContributionManager,
+        IMenuManager {
+
+    public bool isDirty() {
+        return super.isDirty();
+    }
+    public bool isVisible() {
+        return super.isVisible();
+    }
+
+    /**
+     * Maps each submenu in the manager to a wrapper.  The wrapper is used to
+     * monitor additions and removals.  If the visibility of the manager is modified
+     * the visibility of the submenus is also modified.
+     */
+    private Map mapMenuToWrapper;
+
+    /**
+     * List of registered menu listeners (element type: <code>IMenuListener</code>).
+     */
+    private ListenerList menuListeners;
+
+    /**
+     * The menu listener added to the parent.  Lazily initialized
+     * in addMenuListener.
+     */
+    private IMenuListener menuListener;
+
+    /**
+     * Constructs a new manager.
+     *
+     * @param mgr the parent manager.  All contributions made to the
+     *      <code>SubMenuManager</code> are forwarded and appear in the
+     *      parent manager.
+     */
+    public this(IMenuManager mgr) {
+        menuListeners = new ListenerList();
+        super(mgr);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IMenuManager#addMenuListener(org.eclipse.jface.action.IMenuListener)
+     */
+    public void addMenuListener(IMenuListener listener) {
+        menuListeners.add(cast(Object)listener);
+        if (menuListener is null) {
+            menuListener = new class IMenuListener {
+                public void menuAboutToShow(IMenuManager manager) {
+                    Object[] listeners = menuListeners.getListeners();
+                    for (int i = 0; i < listeners.length; ++i) {
+                        (cast(IMenuListener) listeners[i])
+                                .menuAboutToShow(this.outer);
+                    }
+                }
+            };
+        }
+        getParentMenuManager().addMenuListener(menuListener);
+    }
+
+    /**
+     * The default implementation of this <code>IContributionItem</code>
+     * method does nothing. Subclasses may override.
+     */
+    public void dispose() {
+        // do nothing
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.SubContributionManager#disposeManager()
+     */
+    public override void disposeManager() {
+        if (menuListener !is null) {
+            getParentMenuManager().removeMenuListener(menuListener);
+            menuListener = null;
+            menuListeners.clear();
+        }
+        // Dispose wrapped menus in addition to removing them.
+        // See bugs 64024 and 73715 for details.
+        // important to dispose menu wrappers before call to super,
+        // otherwise super's call to removeAll will remove them
+        // before they can be disposed
+        if (mapMenuToWrapper !is null) {
+            Iterator iter = mapMenuToWrapper.values().iterator();
+            while (iter.hasNext()) {
+                SubMenuManager wrapper = cast(SubMenuManager) iter.next();
+                wrapper.disposeManager();
+            }
+            mapMenuToWrapper.clear();
+            mapMenuToWrapper = null;
+        }
+        super.disposeManager();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#fill(org.eclipse.swt.widgets.Composite)
+     */
+    public void fill(Composite parent) {
+        if (isVisible()) {
+            getParentMenuManager().fill(parent);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#fill(org.eclipse.swt.widgets.CoolBar, int)
+     */
+    public void fill(CoolBar parent, int index) {
+        // do nothing
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#fill(org.eclipse.swt.widgets.Menu, int)
+     */
+    public void fill(Menu parent, int index) {
+        if (isVisible()) {
+            getParentMenuManager().fill(parent, index);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#fill(org.eclipse.swt.widgets.ToolBar, int)
+     */
+    public void fill(ToolBar parent, int index) {
+        if (isVisible()) {
+            getParentMenuManager().fill(parent, index);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IContributionManager.
+     *
+     * Returns the item passed to us, not the wrapper.
+     * In the case of menu's not added by this manager,
+     * ensure that we return a wrapper for the menu.
+     */
+    public override IContributionItem find(String id) {
+        IContributionItem item = getParentMenuManager().find(id);
+        if (cast(SubContributionItem)item ) {
+            // Return the item passed to us, not the wrapper.
+            item = unwrap(item);
+        }
+
+        if (cast(IMenuManager)item ) {
+            // if it is a menu manager wrap it before returning
+            IMenuManager menu = cast(IMenuManager) item;
+            item = getWrapper(menu);
+        }
+
+        return item;
+    }
+
+    /**
+     * <p>
+     * The menu returned is wrapped within a <code>SubMenuManager</code> to
+     * monitor additions and removals.  If the visibility of this menu is modified
+     * the visibility of the submenus is also modified.
+     * </p>
+     */
+    public IMenuManager findMenuUsingPath(String path) {
+        IContributionItem item = findUsingPath(path);
+        if (cast(IMenuManager)item ) {
+            return cast(IMenuManager) item;
+        }
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IMenuManager.
+     *
+     * Returns the item passed to us, not the wrapper.
+     *
+     * We use use the same algorithm as MenuManager.findUsingPath, but unwrap
+     * submenus along so that SubMenuManagers are visible.
+     */
+    public IContributionItem findUsingPath(String path) {
+        String id = path;
+        String rest = null;
+        int separator = path.indexOf('/');
+        if (separator !is -1) {
+            id = path.substring(0, separator);
+            rest = path.substring(separator + 1);
+        }
+        IContributionItem item = find(id); // unwraps item
+        if (rest !is null && cast(IMenuManager)item ) {
+            IMenuManager menu = cast(IMenuManager) item;
+            item = menu.findUsingPath(rest);
+        }
+        return item;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#getId()
+     */
+    public String getId() {
+        return getParentMenuManager().getId();
+    }
+
+    /**
+     * @return the parent menu manager that this sub-manager contributes to.
+     */
+    protected final IMenuManager getParentMenuManager() {
+        // Cast is ok because that's the only
+        // thing we accept in the construtor.
+        return cast(IMenuManager) getParent();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IMenuManager#getRemoveAllWhenShown()
+     */
+    public bool getRemoveAllWhenShown() {
+        return false;
+    }
+
+    /**
+     * Returns the menu wrapper for a menu manager.
+     * <p>
+     * The sub menus within this menu are wrapped within a <code>SubMenuManager</code> to
+     * monitor additions and removals.  If the visibility of this menu is modified
+     * the visibility of the sub menus is also modified.
+     * <p>
+     * @param mgr the menu manager to be wrapped
+     *
+     * @return the menu wrapper
+     */
+    protected IMenuManager getWrapper(IMenuManager mgr) {
+        if (mapMenuToWrapper is null) {
+            mapMenuToWrapper = new HashMap(4);
+        }
+        SubMenuManager wrapper = cast(SubMenuManager) mapMenuToWrapper.get(cast(Object)mgr);
+        if (wrapper is null) {
+            wrapper = wrapMenu(mgr);
+            mapMenuToWrapper.put(cast(Object)mgr, wrapper);
+        }
+        return wrapper;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#isDynamic()
+     */
+    public bool isDynamic() {
+        return getParentMenuManager().isDynamic();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#isEnabled()
+     */
+    public bool isEnabled() {
+        return isVisible() && getParentMenuManager().isEnabled();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#isGroupMarker()
+     */
+    public bool isGroupMarker() {
+        return getParentMenuManager().isGroupMarker();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#isSeparator()
+     */
+    public bool isSeparator() {
+        return getParentMenuManager().isSeparator();
+    }
+
+    /**
+     * Remove all contribution items.
+     */
+    public override void removeAll() {
+        super.removeAll();
+        if (mapMenuToWrapper !is null) {
+            Iterator iter = mapMenuToWrapper.values().iterator();
+            while (iter.hasNext()) {
+                SubMenuManager wrapper = cast(SubMenuManager) iter.next();
+                wrapper.removeAll();
+            }
+            mapMenuToWrapper.clear();
+            mapMenuToWrapper = null;
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IMenuManager#removeMenuListener(org.eclipse.jface.action.IMenuListener)
+     */
+    public void removeMenuListener(IMenuListener listener) {
+        menuListeners.remove(cast(Object)listener);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#saveWidgetState()
+     */
+    public void saveWidgetState() {
+        // do nothing
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#setParent(org.eclipse.jface.action.IContributionManager)
+     */
+    public void setParent(IContributionManager parent) {
+        // do nothing, our "parent manager's" parent
+        // is set when it is added to a manager
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IMenuManager#setRemoveAllWhenShown(bool)
+     */
+    public void setRemoveAllWhenShown(bool removeAll) {
+        Assert.isTrue(false, "Should not be called on submenu manager"); //$NON-NLS-1$
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.SubContributionManager#setVisible(bool)
+     */
+    public override void setVisible(bool visible) {
+        super.setVisible(visible);
+        if (mapMenuToWrapper !is null) {
+            Iterator iter = mapMenuToWrapper.values().iterator();
+            while (iter.hasNext()) {
+                SubMenuManager wrapper = cast(SubMenuManager) iter.next();
+                wrapper.setVisible(visible);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#update()
+     */
+    public void update() {
+        // This method is not governed by visibility.  The client may
+        // call <code>setVisible</code> and then force an update.  At that
+        // point we need to update the parent.
+        getParentMenuManager().update();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionManager#update(bool)
+     */
+    public void update(bool force) {
+        // This method is not governed by visibility.  The client may
+        // call <code>setVisible</code> and then force an update.  At that
+        // point we need to update the parent.
+        getParentMenuManager().update(force);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IContributionItem#update(java.lang.String)
+     */
+    public void update(String id) {
+        getParentMenuManager().update(id);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IMenuManager#updateAll(bool)
+     */
+    public void updateAll(bool force) {
+        // This method is not governed by visibility.  The client may
+        // call <code>setVisible</code> and then force an update.  At that
+        // point we need to update the parent.
+        getParentMenuManager().updateAll(force);
+    }
+
+    /**
+     * Wraps a menu manager in a sub menu manager, and returns the new wrapper.
+     * @param menu the menu manager to wrap
+     * @return the new wrapped menu manager
+     */
+    protected SubMenuManager wrapMenu(IMenuManager menu) {
+        SubMenuManager mgr = new SubMenuManager(menu);
+        mgr.setVisible(isVisible());
+        return mgr;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/SubStatusLineManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.action.SubStatusLineManager;
+
+import org.eclipse.jface.action.SubContributionManager;
+import org.eclipse.jface.action.IStatusLineManager;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A <code>SubStatusLineManager</code> is used to define a set of contribution
+ * items within a parent manager.  Once defined, the visibility of the entire set can
+ * be changed as a unit.
+ */
+public class SubStatusLineManager : SubContributionManager,
+        IStatusLineManager {
+    /**
+     * Current status line message.
+     */
+    private String message;
+
+    /**
+     * Current status line error message.
+     */
+    private String errorMessage;
+
+    /**
+     * Current status line message image.
+     */
+    private Image messageImage;
+
+    /**
+     * Current status line error image
+     */
+    private Image errorImage;
+
+    /**
+     * Constructs a new manager.
+     *
+     * @param mgr the parent manager.  All contributions made to the
+     *      <code>SubStatusLineManager</code> are forwarded and appear in the
+     *      parent manager.
+     */
+    public this(IStatusLineManager mgr) {
+        super(mgr);
+    }
+
+    /**
+     * @return the parent status line manager that this sub-manager contributes
+     * to
+     */
+    protected final IStatusLineManager getParentStatusLineManager() {
+        // Cast is ok because that's the only
+        // thing we accept in the construtor.
+        return cast(IStatusLineManager) getParent();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IStatusLineManager.
+     */
+    public IProgressMonitor getProgressMonitor() {
+        return getParentStatusLineManager().getProgressMonitor();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IStatusLineManager.
+     */
+    public bool isCancelEnabled() {
+        return getParentStatusLineManager().isCancelEnabled();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IStatusLineManager.
+     */
+    public void setCancelEnabled(bool enabled) {
+        getParentStatusLineManager().setCancelEnabled(enabled);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IStatusLineManager.
+     */
+    public void setErrorMessage(String message) {
+        this.errorImage = null;
+        this.errorMessage = message;
+        if (isVisible()) {
+            getParentStatusLineManager().setErrorMessage(errorMessage);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IStatusLineManager.
+     */
+    public void setErrorMessage(Image image, String message) {
+        this.errorImage = image;
+        this.errorMessage = message;
+        if (isVisible()) {
+            getParentStatusLineManager().setErrorMessage(errorImage,
+                    errorMessage);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IStatusLineManager.
+     */
+    public void setMessage(String message) {
+        this.messageImage = null;
+        this.message = message;
+        if (isVisible()) {
+            getParentStatusLineManager().setMessage(message);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IStatusLineManager.
+     */
+    public void setMessage(Image image, String message) {
+        this.messageImage = image;
+        this.message = message;
+        if (isVisible()) {
+            getParentStatusLineManager().setMessage(messageImage, message);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on SubContributionManager.
+     */
+    public override void setVisible(bool visible) {
+        super.setVisible(visible);
+        if (visible) {
+            getParentStatusLineManager().setErrorMessage(errorImage,
+                    errorMessage);
+            getParentStatusLineManager().setMessage(messageImage, message);
+        } else {
+            getParentStatusLineManager().setMessage(null, null);
+            getParentStatusLineManager().setErrorMessage(null, null);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IStatusLineManager.
+     */
+    public void update(bool force) {
+        // This method is not governed by visibility.  The client may
+        // call <code>setVisible</code> and then force an update.  At that
+        // point we need to update the parent.
+        getParentStatusLineManager().update(force);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/SubToolBarManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.action.SubToolBarManager;
+
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.SubContributionManager;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A <code>SubToolBarManager</code> monitors the additional and removal of
+ * items from a parent manager so that visibility of the entire set can be changed as a
+ * unit.
+ */
+public class SubToolBarManager : SubContributionManager,
+        IToolBarManager {
+
+    /**
+     * Constructs a new manager.
+     *
+     * @param mgr the parent manager.  All contributions made to the
+     *      <code>SubToolBarManager</code> are forwarded and appear in the
+     *      parent manager.
+     */
+    public this(IToolBarManager mgr) {
+        super(mgr);
+    }
+
+    /**
+     * @return the parent toolbar manager that this sub-manager contributes to
+     */
+    protected final IToolBarManager getParentToolBarManager() {
+        // Cast is ok because that's the only
+        // thing we accept in the construtor.
+        return cast(IToolBarManager) getParent();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IToolBarManager.
+     */
+    public void update(bool force) {
+        // This method is not governed by visibility.  The client may
+        // call <code>setVisible</code> and then force an update.  At that
+        // point we need to update the parent.
+        getParentToolBarManager().update(force);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/ToolBarContributionItem.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,696 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.ToolBarContributionItem;
+
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.ContributionItem;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.action.ActionContributionItem;
+import org.eclipse.jface.action.SubContributionItem;
+import org.eclipse.jface.action.ICoolBarManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.action.IContributionManager;
+
+// import java.util.Iterator;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.CoolBar;
+import org.eclipse.swt.widgets.CoolItem;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.internal.provisional.action.IToolBarContributionItem;
+import org.eclipse.jface.util.Policy;
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import tango.io.Stdout;
+
+/**
+ * The <code>ToolBarContributionItem</code> class provides a wrapper for tool
+ * bar managers when used in cool bar managers. It extends <code>ContributionItem</code>
+ * but and provides some additional methods to customize the size of the cool
+ * item and to retrieve the underlying tool bar manager.
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.
+ * </p>
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ToolBarContributionItem : ContributionItem, IToolBarContributionItem {
+    alias ContributionItem.fill fill;
+    alias ContributionItem.update update;
+
+    public IContributionManager getParent() {
+        return super.getParent();
+    }
+
+    /**
+     * A constant used by <code>setMinimumItemsToShow</code> and <code>getMinimumItemsToShow</code>
+     * to indicate that all tool items should be shown in the cool item.
+     */
+    public static const int SHOW_ALL_ITEMS = -1;
+
+    /**
+     * The pull down menu used to list all hidden tool items if the current
+     * size is less than the preffered size.
+     */
+    private MenuManager chevronMenuManager = null;
+
+    /**
+     * The widget created for this item; <code>null</code> before creation
+     * and after disposal.
+     */
+    private CoolItem coolItem = null;
+
+    /**
+     * Current height of cool item
+     */
+    private int currentHeight = -1;
+
+    /**
+     * Current width of cool item.
+     */
+    private int currentWidth = -1;
+
+    /**
+     * A flag indicating that this item has been disposed. This prevents future
+     * method invocations from doing things they shouldn't.
+     */
+    private bool disposed = false;
+
+    /**
+     * Mininum number of tool items to show in the cool item widget.
+     */
+    private int minimumItemsToShow = SHOW_ALL_ITEMS;
+
+    /**
+     * The tool bar manager used to manage the tool items contained in the cool
+     * item widget.
+     */
+    private ToolBarManager toolBarManager = null;
+
+    /**
+     * Enable/disable chevron support.
+     */
+    private bool useChevron = true;
+
+    /**
+     * Convenience method equivalent to <code>ToolBarContributionItem(new ToolBarManager(), null)</code>.
+     */
+    public this() {
+        this(new ToolBarManager(), null);
+    }
+
+    /**
+     * Convenience method equivalent to <code>ToolBarContributionItem(toolBarManager, null)</code>.
+     *
+     * @param toolBarManager
+     *            the tool bar manager
+     */
+    public this(IToolBarManager toolBarManager) {
+        this(toolBarManager, null);
+    }
+
+    /**
+     * Creates a tool bar contribution item.
+     *
+     * @param toolBarManager
+     *            the tool bar manager to wrap
+     * @param id
+     *            the contribution item id, or <code>null</code> if none
+     */
+    public this(IToolBarManager toolBarManager, String id) {
+        super(id);
+        Assert.isTrue( null !is cast(ToolBarManager)toolBarManager );
+        this.toolBarManager = cast(ToolBarManager) toolBarManager;
+    }
+
+    /**
+     * Checks whether this contribution item has been disposed. If it has, and
+     * the tracing options are active, then it prints some debugging
+     * information.
+     *
+     * @return <code>true</code> if the item is disposed; <code>false</code>
+     *         otherwise.
+     *
+     */
+    private final bool checkDisposed() {
+        if (disposed) {
+            if (Policy.TRACE_TOOLBAR) {
+                Stdout.formatln("Method invocation on a disposed tool bar contribution item."); //$NON-NLS-1$
+                ExceptionPrintStackTrace( new Exception(null), Stdout );
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.action.IContributionItem#dispose()
+     */
+    public override void dispose() {
+        // Dispose of the ToolBar and all its contributions
+        if (toolBarManager !is null) {
+            toolBarManager.dispose();
+            toolBarManager = null;
+        }
+
+        /*
+         * We need to dispose the cool item or we might be left holding a cool
+         * item with a disposed control.
+         */
+        if ((coolItem !is null) && (!coolItem.isDisposed())) {
+            coolItem.dispose();
+            coolItem = null;
+        }
+
+        // Mark this item as disposed.
+        disposed = true;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.action.IContributionItem#fill(org.eclipse.swt.widgets.CoolBar,
+     *      int)
+     */
+    public override void fill(CoolBar coolBar, int index) {
+        if (checkDisposed()) {
+            return;
+        }
+
+        if (coolItem is null && coolBar !is null) {
+            ToolBar oldToolBar = toolBarManager.getControl();
+            ToolBar toolBar = toolBarManager.createControl(coolBar);
+            if ((oldToolBar !is null) && (oldToolBar.opEquals(toolBar))) {
+                // We are using an old tool bar, so we need to update.
+                toolBarManager.update(true);
+            }
+
+            // Do not create a coolItem if the toolbar is empty
+            if (toolBar.getItemCount() < 1) {
+                return;
+            }
+            int flags = SWT.DROP_DOWN;
+            if (index >= 0) {
+                coolItem = new CoolItem(coolBar, flags, index);
+            } else {
+                coolItem = new CoolItem(coolBar, flags);
+            }
+            // sets the back reference
+            coolItem.setData(this);
+            // Add the toolbar to the CoolItem widget
+            coolItem.setControl(toolBar);
+
+            // Handle Context Menu
+            // ToolBarManager.createControl can actually return a pre-existing control.
+            // Only add the listener if the toolbar was newly created (bug 62097).
+            if (oldToolBar !is toolBar) {
+                toolBar.addListener(SWT.MenuDetect, new class Listener {
+
+                    public void handleEvent(Event event) {
+                        // if the toolbar does not have its own context menu then
+                        // handle the event
+                        if (toolBarManager.getContextMenuManager() is null) {
+                            handleContextMenu(event);
+                        }
+                    }
+                });
+            }
+
+            // Handle for chevron clicking
+            if (getUseChevron()) {
+                // Chevron Support
+                coolItem.addSelectionListener(new class SelectionAdapter {
+
+                    public void widgetSelected(SelectionEvent event) {
+                        if (event.detail is SWT.ARROW) {
+                            handleChevron(event);
+                        }
+                    }
+                });
+            }
+
+            // Handle for disposal
+            coolItem.addDisposeListener(new class DisposeListener {
+
+                public void widgetDisposed(DisposeEvent event) {
+                    handleWidgetDispose(event);
+                }
+            });
+
+            // Sets the size of the coolItem
+            updateSize(true);
+        }
+    }
+
+    /**
+     * Returns a consistent set of wrap indices. The return value will always
+     * include at least one entry and the first entry will always be zero.
+     * CoolBar.getWrapIndices() is inconsistent in whether or not it returns an
+     * index for the first row.
+     */
+    private int[] getAdjustedWrapIndices(int[] wraps) {
+        int[] adjustedWrapIndices;
+        if (wraps.length is 0) {
+            adjustedWrapIndices = [ 0 ];
+        } else {
+            if (wraps[0] !is 0) {
+                adjustedWrapIndices = new int[wraps.length + 1];
+                adjustedWrapIndices[0] = 0;
+                for (int i = 0; i < wraps.length; i++) {
+                    adjustedWrapIndices[i + 1] = wraps[i];
+                }
+            } else {
+                adjustedWrapIndices = wraps;
+            }
+        }
+        return adjustedWrapIndices;
+    }
+
+    /**
+     * Returns the current height of the corresponding cool item.
+     *
+     * @return the current height
+     */
+    public int getCurrentHeight() {
+        if (checkDisposed()) {
+            return -1;
+        }
+        return currentHeight;
+    }
+
+    /**
+     * Returns the current width of the corresponding cool item.
+     *
+     * @return the current size
+     */
+    public int getCurrentWidth() {
+        if (checkDisposed()) {
+            return -1;
+        }
+        return currentWidth;
+    }
+
+    /**
+     * Returns the minimum number of tool items to show in the cool item.
+     *
+     * @return the minimum number of tool items to show, or <code>SHOW_ALL_ITEMS</code>
+     *         if a value was not set
+     * @see #setMinimumItemsToShow(int)
+     */
+    public int getMinimumItemsToShow() {
+        if (checkDisposed()) {
+            return -1;
+        }
+        return minimumItemsToShow;
+    }
+
+    /**
+     * Returns the internal tool bar manager of the contribution item.
+     *
+     * @return the tool bar manager, or <code>null</code> if one is not
+     *         defined.
+     * @see IToolBarManager
+     */
+    public IToolBarManager getToolBarManager() {
+        if (checkDisposed()) {
+            return null;
+        }
+        return toolBarManager;
+    }
+
+    /**
+     * Returns whether chevron support is enabled.
+     *
+     * @return <code>true</code> if chevron support is enabled, <code>false</code>
+     *         otherwise
+     */
+    public bool getUseChevron() {
+        if (checkDisposed()) {
+            return false;
+        }
+        return useChevron;
+    }
+
+    /**
+     * Create and display the chevron menu.
+     */
+    private void handleChevron(SelectionEvent event) {
+        CoolItem item = cast(CoolItem) event.widget;
+        Control control = item.getControl();
+        if (!(cast(ToolBar)control ) ) {
+            return;
+        }
+        CoolBar coolBar = item.getParent();
+        ToolBar toolBar = cast(ToolBar) control;
+        Rectangle toolBarBounds = toolBar.getBounds();
+        ToolItem[] items = toolBar.getItems();
+        ArrayList hidden = new ArrayList();
+        for (int i = 0; i < items.length; ++i) {
+            Rectangle itemBounds = items[i].getBounds();
+            if (!((itemBounds.x + itemBounds.width <= toolBarBounds.width) && (itemBounds.y
+                    + itemBounds.height <= toolBarBounds.height))) {
+                hidden.add(items[i]);
+            }
+        }
+
+        // Create a pop-up menu with items for each of the hidden buttons.
+        if (chevronMenuManager !is null) {
+            chevronMenuManager.dispose();
+        }
+        chevronMenuManager = new MenuManager();
+        for (Iterator i = hidden.iterator(); i.hasNext();) {
+            ToolItem toolItem = cast(ToolItem) i.next();
+            IContributionItem data = cast(IContributionItem) toolItem.getData();
+            if (cast(ActionContributionItem)data ) {
+                ActionContributionItem contribution = new ActionContributionItem(
+                        (cast(ActionContributionItem) data).getAction());
+                chevronMenuManager.add(contribution);
+            } else if (cast(SubContributionItem)data ) {
+                IContributionItem innerData = (cast(SubContributionItem) data)
+                        .getInnerItem();
+                if (cast(ActionContributionItem)innerData ) {
+                    ActionContributionItem contribution = new ActionContributionItem(
+                            (cast(ActionContributionItem) innerData).getAction());
+                    chevronMenuManager.add(contribution);
+                }
+            } else if (data.isSeparator()) {
+                chevronMenuManager.add(new Separator());
+            }
+        }
+        Menu popup = chevronMenuManager.createContextMenu(coolBar);
+        Point chevronPosition = coolBar.toDisplay(event.x, event.y);
+        popup.setLocation(chevronPosition.x, chevronPosition.y);
+        popup.setVisible(true);
+    }
+
+    /**
+     * Handles the event when the toobar item does not have its own context
+     * menu.
+     *
+     * @param event
+     *            the event object
+     */
+    private void handleContextMenu(Event event) {
+        ToolBar toolBar = toolBarManager.getControl();
+        // If parent has a menu then use that one
+        Menu parentMenu = toolBar.getParent().getMenu();
+        if ((parentMenu !is null) && (!parentMenu.isDisposed())) {
+            toolBar.setMenu(parentMenu);
+            // Hook listener to remove menu once it has disapeared
+            parentMenu.addListener(SWT.Hide, new class Listener {
+
+                public void handleEvent(Event innerEvent) {
+                    ToolBar innerToolBar = toolBarManager.getControl();
+                    if (innerToolBar !is null) {
+                        innerToolBar.setMenu(null);
+                        Menu innerParentMenu = innerToolBar.getParent()
+                                .getMenu();
+                        if (innerParentMenu !is null) {
+                            innerParentMenu.removeListener(SWT.Hide, this);
+                        }
+                    }
+                }
+            });
+        }
+    }
+
+    /**
+     * Handles the disposal of the widget.
+     *
+     * @param event
+     *            the event object
+     */
+    private void handleWidgetDispose(DisposeEvent event) {
+        coolItem = null;
+    }
+
+    /**
+     * A contribution item is visible iff its internal state is visible <em>or</em>
+     * the tool bar manager contains something other than group markers and
+     * separators.
+     *
+     * @return <code>true</code> if the tool bar manager contains something
+     *         other than group marks and separators, and the internal state is
+     *         set to be visible.
+     */
+    public override bool isVisible() {
+        if (checkDisposed()) {
+            return false;
+        }
+
+        bool visibleItem = false;
+        if (toolBarManager !is null) {
+            IContributionItem[] contributionItems = toolBarManager.getItems();
+            for (int i = 0; i < contributionItems.length; i++) {
+                IContributionItem contributionItem = contributionItems[i];
+                if ((!contributionItem.isGroupMarker())
+                        && (!contributionItem.isSeparator())) {
+                    visibleItem = true;
+                    break;
+                }
+            }
+        }
+
+        return (visibleItem || super.isVisible());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.action.IContributionItem#saveWidgetState()
+     */
+    public override void saveWidgetState() {
+        if (checkDisposed()) {
+            return;
+        }
+        if (coolItem is null) {
+            return;
+        }
+
+        //1. Save current size
+        CoolBar coolBar = coolItem.getParent();
+        bool isLastOnRow = false;
+        int lastIndex = coolBar.getItemCount() - 1;
+        int coolItemIndex = coolBar.indexOf(coolItem);
+        int[] wrapIndicies = getAdjustedWrapIndices(coolBar.getWrapIndices());
+        // Traverse through all wrap indicies backwards
+        for (int row = wrapIndicies.length - 1; row >= 0; row--) {
+            if (wrapIndicies[row] <= coolItemIndex) {
+
+                int nextRow = row + 1;
+                int nextRowStartIndex;
+                if (nextRow > (wrapIndicies.length - 1)) {
+                    nextRowStartIndex = lastIndex + 1;
+                } else {
+                    nextRowStartIndex = wrapIndicies[nextRow];
+                }
+
+                // Check to see if its the last item on the row
+                if (coolItemIndex is (nextRowStartIndex - 1)) {
+                    isLastOnRow = true;
+                }
+                break;
+            }
+        }
+
+        // Save the preferred size as actual size for the last item on a row
+        int nCurrentWidth;
+        if (isLastOnRow) {
+            nCurrentWidth = coolItem.getPreferredSize().x;
+        } else {
+            nCurrentWidth = coolItem.getSize().x;
+        }
+        setCurrentWidth(nCurrentWidth);
+        setCurrentHeight(coolItem.getSize().y);
+    }
+
+    /**
+     * Sets the current height of the cool item. Update(SIZE) should be called
+     * to adjust the widget.
+     *
+     * @param currentHeight
+     *            the current height to set
+     */
+    public void setCurrentHeight(int currentHeight) {
+        if (checkDisposed()) {
+            return;
+        }
+        this.currentHeight = currentHeight;
+    }
+
+    /**
+     * Sets the current width of the cool item. Update(SIZE) should be called
+     * to adjust the widget.
+     *
+     * @param currentWidth
+     *            the current width to set
+     */
+    public void setCurrentWidth(int currentWidth) {
+        if (checkDisposed()) {
+            return;
+        }
+        this.currentWidth = currentWidth;
+    }
+
+    /**
+     * Sets the minimum number of tool items to show in the cool item. If this
+     * number is less than the total tool items, a chevron will appear and the
+     * hidden tool items appear in a drop down menu. By default, all the tool
+     * items are shown in the cool item.
+     *
+     * @param minimumItemsToShow
+     *            the minimum number of tool items to show.
+     * @see #getMinimumItemsToShow()
+     * @see #setUseChevron(bool)
+     */
+    public void setMinimumItemsToShow(int minimumItemsToShow) {
+        if (checkDisposed()) {
+            return;
+        }
+        this.minimumItemsToShow = minimumItemsToShow;
+    }
+
+    /**
+     * Enables or disables chevron support for the cool item. By default,
+     * chevron support is enabled.
+     *
+     * @param value
+     *            <code>true</code> to enable chevron support, <code>false</code>
+     *            otherwise.
+     */
+    public void setUseChevron(bool value) {
+        if (checkDisposed()) {
+            return;
+        }
+        useChevron = value;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.action.IContributionItem#update(java.lang.String)
+     */
+    public override void update(String propertyName) {
+        if (checkDisposed()) {
+            return;
+        }
+        if (coolItem !is null) {
+            IToolBarManager manager = getToolBarManager();
+            if (manager !is null) {
+                manager.update(true);
+            }
+
+            if ((propertyName is null)
+                    || propertyName.equals(ICoolBarManager.SIZE)) {
+                updateSize(true);
+            }
+        }
+    }
+
+    /**
+     * Updates the cool items' preferred, minimum, and current size. The
+     * preferred size is calculated based on the tool bar size and extra trim.
+     *
+     * @param changeCurrentSize
+     *            <code>true</code> if the current size should be changed to
+     *            the preferred size, <code>false</code> to not change the
+     *            current size
+     */
+    private void updateSize(bool changeCurrentSize) {
+        if (checkDisposed()) {
+            return;
+        }
+        // cannot set size if coolItem is null
+        if (coolItem is null || coolItem.isDisposed()) {
+            return;
+        }
+        bool locked = false;
+        CoolBar coolBar = coolItem.getParent();
+        try {
+            // Fix odd behaviour with locked tool bars
+            if (coolBar !is null) {
+                if (coolBar.getLocked()) {
+                    coolBar.setLocked(false);
+                    locked = true;
+                }
+            }
+            ToolBar toolBar = cast(ToolBar) coolItem.getControl();
+            if ((toolBar is null) || (toolBar.isDisposed())
+                    || (toolBar.getItemCount() <= 0)) {
+                // if the toolbar does not contain any items then dispose of
+                // coolItem
+                coolItem.setData(null);
+                Control control = coolItem.getControl();
+                if ((control !is null) && !control.isDisposed()) {
+                    control.dispose();
+                    coolItem.setControl(null);
+                }
+                if (!coolItem.isDisposed()) {
+                    coolItem.dispose();
+                }
+            } else {
+                // If the toolbar item exists then adjust the size of the cool
+                // item
+                Point toolBarSize = toolBar.computeSize(SWT.DEFAULT,
+                        SWT.DEFAULT);
+                // Set the preffered size to the size of the toolbar plus trim
+                Point preferredSize = coolItem.computeSize(toolBarSize.x,
+                        toolBarSize.y);
+                coolItem.setPreferredSize(preferredSize);
+                // note setMinimumSize must be called before setSize, see PR
+                // 15565
+                // Set minimum size
+                if (getMinimumItemsToShow() !is SHOW_ALL_ITEMS) {
+                    int toolItemWidth = toolBar.getItems()[0].getWidth();
+                    int minimumWidth = toolItemWidth * getMinimumItemsToShow();
+                    coolItem.setMinimumSize(minimumWidth, toolBarSize.y);
+                } else {
+                    coolItem.setMinimumSize(toolBarSize.x, toolBarSize.y);
+                }
+                if (changeCurrentSize) {
+                    // Set current size to preferred size
+                    coolItem.setSize(preferredSize);
+                }
+            }
+        } finally {
+            // If the cool bar was locked, then set it back to locked
+            if ((locked) && (coolBar !is null)) {
+                coolBar.setLocked(true);
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/action/ToolBarManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,451 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.action.ToolBarManager;
+
+import org.eclipse.jface.action.ContributionManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.IContributionItem;
+
+// import java.util.Iterator;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.accessibility.ACC;
+import org.eclipse.swt.accessibility.AccessibleAdapter;
+import org.eclipse.swt.accessibility.AccessibleEvent;
+import org.eclipse.swt.accessibility.AccessibleListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.CoolBar;
+import org.eclipse.swt.widgets.CoolItem;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A tool bar manager is a contribution manager which realizes itself and its
+ * items in a tool bar control.
+ * <p>
+ * This class may be instantiated; it may also be subclassed if a more
+ * sophisticated layout is required.
+ * </p>
+ */
+public class ToolBarManager : ContributionManager,
+        IToolBarManager {
+
+    /**
+     * The tool bar items style; <code>SWT.NONE</code> by default.
+     */
+    private int itemStyle = SWT.NONE;
+
+    /**
+     * The tool bat control; <code>null</code> before creation and after
+     * disposal.
+     */
+    private ToolBar toolBar = null;
+
+    /**
+     * The menu manager to the context menu associated with the toolbar.
+     *
+     * @since 3.0
+     */
+    private MenuManager contextMenuManager = null;
+
+    /**
+     * Creates a new tool bar manager with the default SWT button style. Use the
+     * {@link #createControl(Composite)} method to create the tool bar control.
+     *
+     */
+    public this() {
+        //Do nothing if there are no parameters
+    }
+
+    /**
+     * Creates a tool bar manager with the given SWT button style. Use the
+     * <code>createControl</code> method to create the tool bar control.
+     *
+     * @param style
+     *            the tool bar item style
+     * @see org.eclipse.swt.widgets.ToolBar for valid style bits
+     */
+    public this(int style) {
+        itemStyle = style;
+    }
+
+    /**
+     * Creates a tool bar manager for an existing tool bar control. This manager
+     * becomes responsible for the control, and will dispose of it when the
+     * manager is disposed.
+     * <strong>NOTE</strong> When creating a ToolBarManager from an existing
+     * {@link ToolBar} you will not get the accessible listener provided by
+     * JFace.
+     * @see #ToolBarManager()
+     * @see #ToolBarManager(int)
+     *
+     * @param toolbar
+     *            the tool bar control
+     */
+    public this(ToolBar toolbar) {
+        this();
+        this.toolBar = toolbar;
+    }
+
+    /**
+     * Creates and returns this manager's tool bar control. Does not create a
+     * new control if one already exists. Also create an {@link AccessibleListener}
+     * for the {@link ToolBar}.
+     *
+     * @param parent
+     *            the parent control
+     * @return the tool bar control
+     */
+    public ToolBar createControl(Composite parent) {
+        if (!toolBarExist() && parent !is null) {
+            toolBar = new ToolBar(parent, itemStyle);
+            toolBar.setMenu(getContextMenuControl());
+            update(true);
+
+            toolBar.getAccessible().addAccessibleListener(getAccessibleListener());
+        }
+
+        return toolBar;
+    }
+
+    /**
+     * Get the accessible listener for the tool bar.
+     *
+     * @return AccessibleListener
+     *
+     * @since 3.1
+     */
+    private AccessibleListener getAccessibleListener() {
+        return new class AccessibleAdapter {
+            public void getName(AccessibleEvent e) {
+                if (e.childID !is ACC.CHILDID_SELF) {
+                    ToolItem item = toolBar.getItem(e.childID);
+                    if (item !is null) {
+                        String toolTip = item.getToolTipText();
+                        if (toolTip !is null) {
+                            e.result = toolTip;
+                        }
+                    }
+                }
+            }
+        };
+    }
+
+    /**
+     * Disposes of this tool bar manager and frees all allocated SWT resources.
+     * Notifies all contribution items of the dispose. Note that this method
+     * does not clean up references between this tool bar manager and its
+     * associated contribution items. Use <code>removeAll</code> for that
+     * purpose.
+     */
+    public void dispose() {
+
+        if (toolBarExist()) {
+            toolBar.dispose();
+        }
+        toolBar = null;
+
+        IContributionItem[] items = getItems();
+        for (int i = 0; i < items.length; i++) {
+            items[i].dispose();
+        }
+
+        if (getContextMenuManager() !is null) {
+            getContextMenuManager().dispose();
+            setContextMenuManager(null);
+        }
+    }
+
+    /**
+     * Returns the tool bar control for this manager.
+     *
+     * @return the tool bar control, or <code>null</code> if none (before
+     *         creating or after disposal)
+     */
+    public ToolBar getControl() {
+        return toolBar;
+    }
+
+    /**
+     * Re-lays out the tool bar.
+     * <p>
+     * The default implementation of this framework method re-lays out the
+     * parent when the number of items are different and the new count !is 0
+     *
+     * @param layoutBar
+     *            the tool bar control
+     * @param oldCount
+     *            the old number of items
+     * @param newCount
+     *            the new number of items
+     */
+    protected void relayout(ToolBar layoutBar, int oldCount, int newCount) {
+        if ((oldCount !is newCount) && (newCount!is 0)) {
+            Point beforePack = layoutBar.getSize();
+            layoutBar.pack(true);
+            Point afterPack = layoutBar.getSize();
+
+            // If the TB didn't change size then we're done
+            if (beforePack.opEquals(afterPack))
+                return;
+
+            // OK, we need to re-layout the TB
+            layoutBar.getParent().layout();
+
+            // Now, if we're in a CoolBar then change the CoolItem size as well
+            if (null !is cast(CoolBar)layoutBar.getParent() ) {
+                CoolBar cb = cast(CoolBar) layoutBar.getParent();
+                CoolItem[] items = cb.getItems();
+                for (int i = 0; i < items.length; i++) {
+                    if (items[i].getControl() is layoutBar) {
+                        Point curSize = items[i].getSize();
+                        items[i].setSize(curSize.x+ (afterPack.x - beforePack.x),
+                                    curSize.y+ (afterPack.y - beforePack.y));
+                        return;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns whether the tool bar control is created and not disposed.
+     *
+     * @return <code>true</code> if the control is created and not disposed,
+     *         <code>false</code> otherwise
+     */
+    private bool toolBarExist() {
+        return toolBar !is null && !toolBar.isDisposed();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public void update(bool force) {
+
+        //  long startTime= 0;
+        //  if (DEBUG) {
+        //      dumpStatistics();
+        //      startTime= (new Date()).getTime();
+        //  }
+
+        if (isDirty() || force) {
+
+            if (toolBarExist()) {
+
+                int oldCount = toolBar.getItemCount();
+
+                // clean contains all active items without double separators
+                IContributionItem[] items = getItems();
+                ArrayList clean = new ArrayList(items.length);
+                IContributionItem separator = null;
+                //          long cleanStartTime= 0;
+                //          if (DEBUG) {
+                //              cleanStartTime= (new Date()).getTime();
+                //          }
+                for (int i = 0; i < items.length; ++i) {
+                    IContributionItem ci = items[i];
+                    if (!ci.isVisible()) {
+                        continue;
+                    }
+                    if (ci.isSeparator()) {
+                        // delay creation until necessary
+                        // (handles both adjacent separators, and separator at
+                        // end)
+                        separator = ci;
+                    } else {
+                        if (separator !is null) {
+                            if (clean.size() > 0) {
+                                clean.add(cast(Object)separator);
+                            }
+                            separator = null;
+                        }
+                        clean.add(cast(Object)ci);
+                    }
+                }
+                //          if (DEBUG) {
+                //              System.out.println(" Time needed to build clean vector: " +
+                // ((new Date()).getTime() - cleanStartTime));
+                //          }
+
+                // determine obsolete items (removed or non active)
+                ToolItem[] mi = toolBar.getItems();
+                ArrayList toRemove = new ArrayList(mi.length);
+                for (int i = 0; i < mi.length; i++) {
+                    Object data = mi[i].getData();
+                    if (data is null
+                            || !clean.contains(data)
+                            || (cast(IContributionItem) data && (cast(IContributionItem) data)
+                                    .isDynamic())) {
+                        toRemove.add(mi[i]);
+                    }
+                }
+
+                // Turn redraw off if the number of items to be added
+                // is above a certain threshold, to minimize flicker,
+                // otherwise the toolbar can be seen to redraw after each item.
+                // Do this before any modifications are made.
+                // We assume each contribution item will contribute at least one
+                // toolbar item.
+                bool useRedraw = (clean.size() - (mi.length - toRemove
+                        .size())) >= 3;
+                try {
+                    if (useRedraw) {
+                        toolBar.setRedraw(false);
+                    }
+
+                    // remove obsolete items
+                    for (int i = toRemove.size(); --i >= 0;) {
+                        ToolItem item = cast(ToolItem) toRemove.get(i);
+                        if (!item.isDisposed()) {
+                            Control ctrl = item.getControl();
+                            if (ctrl !is null) {
+                                item.setControl(null);
+                                ctrl.dispose();
+                            }
+                            item.dispose();
+                        }
+                    }
+
+                    // add new items
+                    IContributionItem src, dest;
+                    mi = toolBar.getItems();
+                    int srcIx = 0;
+                    int destIx = 0;
+                    for (Iterator e = clean.iterator(); e.hasNext();) {
+                        src = cast(IContributionItem) e.next();
+
+
+                        // get corresponding item in SWT widget
+                        if (srcIx < mi.length) {
+                            dest = cast(IContributionItem) mi[srcIx].getData();
+                        } else {
+                            dest = null;
+                        }
+
+                        if (dest !is null && (cast(Object)src).opEquals(cast(Object)dest)) {
+                            srcIx++;
+                            destIx++;
+                            continue;
+                        }
+
+                        if (dest !is null && dest.isSeparator()
+                                && src.isSeparator()) {
+                            mi[srcIx].setData(cast(Object)src);
+                            srcIx++;
+                            destIx++;
+                            continue;
+                        }
+
+                        int start = toolBar.getItemCount();
+                        src.fill(toolBar, destIx);
+                        int newItems = toolBar.getItemCount() - start;
+                        for (int i = 0; i < newItems; i++) {
+                            ToolItem item = toolBar.getItem(destIx++);
+                            item.setData(cast(Object)src);
+                        }
+                    }
+
+                    // remove any old tool items not accounted for
+                    for (int i = mi.length; --i >= srcIx;) {
+                        ToolItem item = mi[i];
+                        if (!item.isDisposed()) {
+                            Control ctrl = item.getControl();
+                            if (ctrl !is null) {
+                                item.setControl(null);
+                                ctrl.dispose();
+                            }
+                            item.dispose();
+                        }
+                    }
+
+                    setDirty(false);
+
+                    // turn redraw back on if we turned it off above
+                } finally {
+                    if (useRedraw) {
+                        toolBar.setRedraw(true);
+                    }
+                }
+
+                int newCount = toolBar.getItemCount();
+
+                // If we're forcing a change then ensure that we re-layout everything
+                if (force)
+                    oldCount = newCount+1;
+
+                relayout(toolBar, oldCount, newCount);
+            }
+
+        }
+
+        //  if (DEBUG) {
+        //      System.out.println(" Time needed for update: " + ((new
+        // Date()).getTime() - startTime));
+        //      System.out.println();
+        //  }
+    }
+
+    /**
+     * Returns the control of the Menu Manager. If the menu manager does not
+     * have a control then one is created.
+     *
+     * @return menu widget associated with manager
+     */
+    private Menu getContextMenuControl() {
+        if ((contextMenuManager !is null) && (toolBar !is null)) {
+            Menu menuWidget = contextMenuManager.getMenu();
+            if ((menuWidget is null) || (menuWidget.isDisposed())) {
+                menuWidget = contextMenuManager.createContextMenu(toolBar);
+            }
+            return menuWidget;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the context menu manager for this tool bar manager.
+     *
+     * @return the context menu manager, or <code>null</code> if none
+     * @since 3.0
+     */
+    public MenuManager getContextMenuManager() {
+        return contextMenuManager;
+    }
+
+    /**
+     * Sets the context menu manager for this tool bar manager to the given menu
+     * manager. If the tool bar control exists, it also adds the menu control to
+     * the tool bar.
+     *
+     * @param contextMenuManager
+     *            the context menu manager, or <code>null</code> if none
+     * @since 3.0
+     */
+    public void setContextMenuManager(MenuManager contextMenuManager) {
+        this.contextMenuManager = contextMenuManager;
+        if (toolBar !is null) {
+            toolBar.setMenu(getContextMenuControl());
+        }
+    }
+
+}
Binary file org.eclipse.jface/src/org/eclipse/jface/action/images/stop.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/Binding.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,398 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.bindings.Binding;
+
+import org.eclipse.jface.bindings.TriggerSequence;
+// import java.io.BufferedWriter;
+// import java.io.IOException;
+// import java.io.StringWriter;
+
+import org.eclipse.core.commands.ParameterizedCommand;
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.Set;
+import tango.text.convert.Format;
+
+/**
+ * <p>
+ * A binding is a link between user input and the triggering of a particular
+ * command. The most common example of a binding is a keyboard shortcut, but
+ * there are also mouse and gesture bindings.
+ * </p>
+ * <p>
+ * Bindings are linked to particular conditions within the application. Some of
+ * these conditions change infrequently (e.g., locale, scheme), while some will
+ * tend to change quite frequently (e.g., context). This allows the bindings to
+ * be tailored to particular situations. For example, a set of bindings may be
+ * appropriate only inside a text editor.  Or, perhaps, a set of bindings might
+ * be appropriate only for a given locale, such as bindings that coexist with
+ * the Input Method Editor (IME) on Chinese locales.
+ * </p>
+ * <p>
+ * It is also possible to remove a particular binding. This is typically done as
+ * part of user configuration (e.g., user changing keyboard shortcuts). However,
+ * it can also be helpful when trying to change a binding on a particular locale
+ * or platform. An "unbinding" is really just a binding with no command
+ * identifier. For it to unbind a particular binding, it must match that binding
+ * in its context identifier and scheme identifier. Subclasses (e.g.,
+ * <code>KeyBinding</code>) may require other properties to match (e.g.,
+ * <code>keySequence</code>). If these properties match, then this is an
+ * unbinding. Note: the locale and platform can be different.
+ * </p>
+ * <p>
+ * For example, imagine you have a key binding that looks like this:
+ * </p>
+ * <code><pre>
+ * KeyBinding(command, scheme, context, &quot;Ctrl+Shift+F&quot;)
+ * </pre></code>
+ * <p>
+ * On GTK+, the "Ctrl+Shift+F" interferes with some native behaviour. To change
+ * the binding, we first unbind the "Ctrl+Shift+F" key sequence by
+ * assigning it a null command on the gtk platform.  We then create a new binding
+ * that maps the command to the "Esc Ctrl+F" key sequence.
+ * </p>
+ * <code><pre>
+ *     KeyBinding("Ctrl+Shift+F",null,scheme,context,null,gtk,null,SYSTEM)
+ *     KeyBinding("Esc Ctrl+F",parameterizedCommand,scheme,context,null,gtk,SYSTEM)
+ * </pre></code>
+ * <p>
+ * Bindings are intended to be immutable objects.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class Binding {
+
+    /**
+     * The constant integer hash code value meaning the hash code has not yet
+     * been computed.
+     */
+    private static const int HASH_CODE_NOT_COMPUTED = -1;
+
+    /**
+     * A factor for computing the hash code for all key bindings.
+     */
+    private final const int HASH_FACTOR = 89;
+
+    /**
+     * The type of binding that is defined by the system (i.e., by the
+     * application developer). In the case of an application based on the
+     * Eclipse workbench, this is the registry.
+     */
+    public static const int SYSTEM = 0;
+
+    /**
+     * The type of binding that is defined by the user (i.e., by the end user of
+     * the application). In the case of an application based on the Eclipse
+     * workbench, this is the preference store.
+     */
+    public static const int USER = 1;
+
+    /**
+     * The parameterized command to which this binding applies. This value may
+     * be <code>null</code> if this binding is meant to "unbind" an existing
+     * binding.
+     */
+    private const ParameterizedCommand command;
+
+    /**
+     * The context identifier to which this binding applies. This context must
+     * be active before this key binding becomes active. This value will never
+     * be <code>null</code>.
+     */
+    private const String contextId;
+
+    /**
+     * The hash code for this key binding. This value is computed lazily, and
+     * marked as invalid when one of the values on which it is based changes.
+     */
+    private /+transient+/ int hashCode = HASH_CODE_NOT_COMPUTED;
+
+    /**
+     * The locale in which this binding applies. This value may be
+     * <code>null</code> if this binding is meant to apply to all locales.
+     * This string should be in the same format returned by
+     * <code>Locale.getDefault().toString()</code>.
+     */
+    private const String locale;
+
+    /**
+     * The platform on which this binding applies. This value may be
+     * <code>null</code> if this binding is meant to apply to all platforms.
+     * This string should be in the same format returned by
+     * <code>SWT.getPlatform</code>.
+     */
+    private const String platform;
+
+    /**
+     * The identifier of the scheme in which this binding applies. This value
+     * will never be <code>null</code>.
+     */
+    private const String schemeId;
+
+    /**
+     * The string representation of this binding. This string is for debugging
+     * purposes only, and is not meant to be displayed to the user. This value
+     * is computed lazily.
+     */
+    protected /+transient+/ String string = null;
+
+    /**
+     * The type of binding this represents. This is used to distinguish between
+     * different priority levels for bindings. For example, in our case,
+     * <code>USER</code> bindings override <code>SYSTEM</code> bindings.
+     */
+    private const int type;
+
+    /**
+     * Constructs a new instance of <code>Binding</code>.
+     *
+     * @param command
+     *            The parameterized command to which this binding applies; this
+     *            value may be <code>null</code> if the binding is meant to
+     *            "unbind" a previously defined binding.
+     * @param schemeId
+     *            The scheme to which this binding belongs; this value must not
+     *            be <code>null</code>.
+     * @param contextId
+     *            The context to which this binding applies; this value must not
+     *            be <code>null</code>.
+     * @param locale
+     *            The locale to which this binding applies; this value may be
+     *            <code>null</code> if it applies to all locales.
+     * @param platform
+     *            The platform to which this binding applies; this value may be
+     *            <code>null</code> if it applies to all platforms.
+     * @param windowManager
+     *            The window manager to which this binding applies; this value
+     *            may be <code>null</code> if it applies to all window
+     *            managers. This value is currently ignored.
+     * @param type
+     *            The type of binding. This should be either <code>SYSTEM</code>
+     *            or <code>USER</code>.
+     */
+    protected this(ParameterizedCommand command,
+            String schemeId, String contextId, String locale,
+            String platform, String windowManager, int type) {
+        if (schemeId is null) {
+            throw new NullPointerException("The scheme cannot be null"); //$NON-NLS-1$
+        }
+
+        if (contextId is null) {
+            throw new NullPointerException("The context cannot be null"); //$NON-NLS-1$
+        }
+
+        if ((type !is SYSTEM) && (type !is USER)) {
+            throw new IllegalArgumentException(
+                    "The type must be SYSTEM or USER"); //$NON-NLS-1$
+        }
+
+        this.command = command;
+        this.schemeId = schemeId.intern();
+        this.contextId = contextId.intern();
+        this.locale = (locale is null) ? null : locale.intern();
+        this.platform = (platform is null) ? null : platform.intern();
+        this.type = type;
+    }
+
+    /**
+     * Tests whether this binding is intended to delete another binding. The
+     * receiver must have a <code>null</code> command identifier.
+     *
+     * @param binding
+     *            The binding to test; must not be <code>null</code>.
+     *            This binding must be a <code>SYSTEM</code> binding.
+     * @return <code>true</code> if the receiver deletes the binding defined by
+     *          the argument.
+     */
+    final bool deletes(Binding binding) {
+        bool deletes = true;
+        deletes &= Util.opEquals(getContextId(), binding.getContextId());
+        deletes &= Util.opEquals(getTriggerSequence(), binding
+                .getTriggerSequence());
+        if (getLocale() !is null) {
+            deletes &= !Util.opEquals(getLocale(), binding.getLocale());
+        }
+        if (getPlatform() !is null) {
+            deletes &= !Util.opEquals(getPlatform(), binding.getPlatform());
+        }
+        deletes &= (binding.getType() is SYSTEM);
+        deletes &= Util.opEquals(getParameterizedCommand(), null);
+
+        return deletes;
+    }
+
+    /**
+     * Tests whether this binding is equal to another object. Bindings are only
+     * equal to other bindings with equivalent values.
+     *
+     * @param object
+     *            The object with which to compare; may be <code>null</code>.
+     * @return <code>true</code> if the object is a binding with equivalent
+     *         values for all of its properties; <code>false</code> otherwise.
+     */
+    public final override int opEquals( Object object) {
+        if (this is object) {
+            return true;
+
+        }
+        if (!(cast(Binding)object )) {
+            return false;
+        }
+
+        Binding binding = cast(Binding) object;
+        if (!Util.opEquals(getParameterizedCommand(), binding
+                .getParameterizedCommand())) {
+            return false;
+        }
+        if (!Util.opEquals(getContextId(), binding.getContextId())) {
+            return false;
+        }
+        if (!Util.opEquals(getTriggerSequence(), binding.getTriggerSequence())) {
+            return false;
+        }
+        if (!Util.opEquals(getLocale(), binding.getLocale())) {
+            return false;
+        }
+        if (!Util.opEquals(getPlatform(), binding.getPlatform())) {
+            return false;
+        }
+        if (!Util.opEquals(getSchemeId(), binding.getSchemeId())) {
+            return false;
+        }
+        return (getType() is binding.getType());
+    }
+
+    /**
+     * Returns the parameterized command to which this binding applies. If the
+     * identifier is <code>null</code>, then this binding is "unbinding" an
+     * existing binding.
+     *
+     * @return The fully-parameterized command; may be <code>null</code>.
+     */
+    public final ParameterizedCommand getParameterizedCommand() {
+        return command;
+    }
+
+    /**
+     * Returns the identifier of the context in which this binding applies.
+     *
+     * @return The context identifier; never <code>null</code>.
+     */
+    public final String getContextId() {
+        return contextId;
+    }
+
+    /**
+     * Returns the locale in which this binding applies. If the locale is
+     * <code>null</code>, then this binding applies to all locales. This
+     * string is the same format as returned by
+     * <code>Locale.getDefault().toString()</code>.
+     *
+     * @return The locale; may be <code>null</code>.
+     */
+    public final String getLocale() {
+        return locale;
+    }
+
+    /**
+     * Returns the platform on which this binding applies. If the platform is
+     * <code>null</code>, then this binding applies to all platforms. This
+     * string is the same format as returned by <code>SWT.getPlatform()</code>.
+     *
+     * @return The platform; may be <code>null</code>.
+     */
+    public final String getPlatform() {
+        return platform;
+    }
+
+    /**
+     * Returns the identifier of the scheme in which this binding applies.
+     *
+     * @return The scheme identifier; never <code>null</code>.
+     */
+    public final String getSchemeId() {
+        return schemeId;
+    }
+
+    /**
+     * Returns the sequence of trigger for a given binding. The triggers can be
+     * anything, but above all it must be hashable. This trigger sequence is
+     * used by the binding manager to distinguish between different bindings.
+     *
+     * @return The object representing an input event that will trigger this
+     *         binding; must not be <code>null</code>.
+     */
+    public abstract TriggerSequence getTriggerSequence();
+
+    /**
+     * Returns the type for this binding. As it stands now, this value will
+     * either be <code>SYSTEM</code> or <code>USER</code>. In the future,
+     * more types might be added.
+     *
+     * @return The type for this binding.
+     */
+    public final int getType() {
+        return type;
+    }
+
+    /**
+     * Computes the hash code for this key binding based on all of its
+     * attributes.
+     *
+     * @return The hash code for this key binding.
+     */
+    public final override hash_t toHash() {
+        if (hashCode is HASH_CODE_NOT_COMPUTED) {
+            auto HASH_INITIAL = java.lang.all.toHash(Binding.classinfo.name);
+            hashCode = HASH_INITIAL;
+            hashCode = hashCode * HASH_FACTOR
+                    + Util.toHash(getParameterizedCommand());
+            hashCode = hashCode * HASH_FACTOR + Util.toHash(getContextId());
+            hashCode = hashCode * HASH_FACTOR
+                    + Util.toHash(getTriggerSequence());
+            hashCode = hashCode * HASH_FACTOR + Util.toHash(getLocale());
+            hashCode = hashCode * HASH_FACTOR + Util.toHash(getPlatform());
+            hashCode = hashCode * HASH_FACTOR + Util.toHash(getSchemeId());
+            hashCode = hashCode * HASH_FACTOR + Util.toHash(getType());
+            if (hashCode is HASH_CODE_NOT_COMPUTED) {
+                hashCode++;
+            }
+        }
+
+        return hashCode;
+    }
+
+    /**
+     * The string representation of this binding -- for debugging purposes only.
+     * This string should not be shown to an end user. This should be overridden
+     * by subclasses that add properties.
+     *
+     * @return The string representation; never <code>null</code>.
+     */
+    public override String toString() {
+        if (string is null) {
+            string = Format("Binding({},\n\t{},\n\t{},\n\t{},{},{},{})",
+                getTriggerSequence().toString(),
+                command is null?"":command.toString(),
+                schemeId,
+                contextId,
+                locale is null?"":locale,
+                platform is null?"":platform,
+                (type is SYSTEM) ? "system" : "user");
+        }
+
+        return string;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/BindingManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,2326 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.bindings.BindingManager;
+
+import org.eclipse.jface.bindings.Binding;
+import org.eclipse.jface.bindings.BindingManagerEvent;
+import org.eclipse.jface.bindings.CachedBindingSet;
+import org.eclipse.jface.bindings.IBindingManagerListener;
+import org.eclipse.jface.bindings.ISchemeListener;
+import org.eclipse.jface.bindings.Scheme;
+import org.eclipse.jface.bindings.SchemeEvent;
+import org.eclipse.jface.bindings.Trigger;
+import org.eclipse.jface.bindings.TriggerSequence;
+
+// import java.io.BufferedWriter;
+// import java.io.IOException;
+// import java.io.StringWriter;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.core.commands.CommandManager;
+import org.eclipse.core.commands.ParameterizedCommand;
+import org.eclipse.core.commands.common.HandleObjectManager;
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.commands.contexts.Context;
+import org.eclipse.core.commands.contexts.ContextManager;
+import org.eclipse.core.commands.contexts.ContextManagerEvent;
+import org.eclipse.core.commands.contexts.IContextManagerListener;
+import org.eclipse.core.commands.util.Tracing;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.bindings.keys.IKeyLookup;
+import org.eclipse.jface.bindings.keys.KeyLookupFactory;
+import org.eclipse.jface.bindings.keys.KeyStroke;
+import org.eclipse.jface.contexts.IContextIds;
+import org.eclipse.jface.internal.InternalPolicy;
+import org.eclipse.jface.util.Policy;
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Collection;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+static import tango.text.Text;
+alias tango.text.Text.Text!(char) StringBuffer;
+import tango.text.convert.Format;
+import tango.text.locale.Core;
+static import tango.text.Util;
+
+/**
+ * <p>
+ * A central repository for bindings -- both in the defined and undefined
+ * states. Schemes and bindings can be created and retrieved using this manager.
+ * It is possible to listen to changes in the collection of schemes and bindings
+ * by adding a listener to the manager.
+ * </p>
+ * <p>
+ * The binding manager is very sensitive to performance. Misusing the manager
+ * can render an application unenjoyable to use. As such, each of the public
+ * methods states the current run-time performance. In future releases, it is
+ * guaranteed that the method will run in at least the stated time constraint --
+ * though it might get faster. Where possible, we have also tried to be memory
+ * efficient.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class BindingManager : HandleObjectManager,
+        IContextManagerListener, ISchemeListener {
+
+    /**
+     * This flag can be set to <code>true</code> if the binding manager should
+     * print information to <code>System.out</code> when certain boundary
+     * conditions occur.
+     */
+    public static bool DEBUG = false;
+
+    /**
+     * Returned for optimized lookup.
+     */
+    private static const TriggerSequence[] EMPTY_TRIGGER_SEQUENCE = null;
+
+    /**
+     * The separator character used in locales.
+     */
+    private static const String LOCALE_SEPARATOR = "_"; //$NON-NLS-1$
+
+    /**
+     * </p>
+     * A utility method for adding entries to a map. The map is checked for
+     * entries at the key. If such an entry exists, it is expected to be a
+     * <code>Collection</code>. The value is then appended to the collection.
+     * If no such entry exists, then a collection is created, and the value
+     * added to the collection.
+     * </p>
+     *
+     * @param map
+     *            The map to modify; if this value is <code>null</code>, then
+     *            this method simply returns.
+     * @param key
+     *            The key to look up in the map; may be <code>null</code>.
+     * @param value
+     *            The value to look up in the map; may be <code>null</code>.
+     */
+    private static final void addReverseLookup(Map map, Object key,
+            Object value) {
+        if (map is null) {
+            return;
+        }
+
+        Object currentValue = map.get(key);
+        if (currentValue !is null) {
+            Collection values = cast(Collection) currentValue;
+            values.add(value);
+        } else { // currentValue is null
+            auto values = new ArrayList(1);
+            values.add(value);
+            map.put(key, values);
+        }
+    }
+
+    /**
+     * <p>
+     * Takes a fully-specified string, and converts it into an array of
+     * increasingly less-specific strings. So, for example, "en_GB" would become
+     * ["en_GB", "en", "", null].
+     * </p>
+     * <p>
+     * This method runs in linear time (O(n)) over the length of the string.
+     * </p>
+     *
+     * @param string
+     *            The string to break apart into its less specific components;
+     *            should not be <code>null</code>.
+     * @param separator
+     *            The separator that indicates a separation between a degrees of
+     *            specificity; should not be <code>null</code>.
+     * @return An array of strings from the most specific (i.e.,
+     *         <code>string</code>) to the least specific (i.e.,
+     *         <code>null</code>).
+     */
+    private static final String[] expand(String string, String separator) {
+        // Test for boundary conditions.
+        if (string is null || separator is null) {
+            return new String[0];
+        }
+
+        List strings = new ArrayList();
+        StringBuffer stringBuffer = new StringBuffer();
+        string = string.trim(); // remove whitespace
+        if (string.length > 0) {
+
+            auto tokens = tango.text.Util.delimit(string, separator);
+            foreach( tok; tokens ){
+                if (stringBuffer.length() > 0) {
+                    stringBuffer.append(separator);
+                }
+                stringBuffer.append(tok.trim());
+                strings.add(stringBuffer.toString());
+            }
+        }
+        Collections.reverse(strings);
+        strings.add(Util.ZERO_LENGTH_STRING);
+        strings.add("");
+        return stringcast(strings.toArray());
+    }
+
+    /**
+     * The active bindings. This is a map of triggers (
+     * <code>TriggerSequence</code>) to bindings (<code>Binding</code>).
+     * This value will only be <code>null</code> if the active bindings have
+     * not yet been computed. Otherwise, this value may be empty.
+     */
+    private Map activeBindings = null;
+
+    /**
+     * The active bindings indexed by fully-parameterized commands. This is a
+     * map of fully-parameterized commands (<code>ParameterizedCommand</code>)
+     * to triggers ( <code>TriggerSequence</code>). This value will only be
+     * <code>null</code> if the active bindings have not yet been computed.
+     * Otherwise, this value may be empty.
+     */
+    private Map activeBindingsByParameterizedCommand = null;
+
+    private Set triggerConflicts;
+
+    /**
+     * The scheme that is currently active. An active scheme is the one that is
+     * currently dictating which bindings will actually work. This value may be
+     * <code>null</code> if there is no active scheme. If the active scheme
+     * becomes undefined, then this should automatically revert to
+     * <code>null</code>.
+     */
+    private Scheme activeScheme = null;
+
+    /**
+     * The array of scheme identifiers, starting with the active scheme and
+     * moving up through its parents. This value may be <code>null</code> if
+     * there is no active scheme.
+     */
+    private String[] activeSchemeIds = null;
+
+    /**
+     * The number of bindings in the <code>bindings</code> array.
+     */
+    private int bindingCount = 0;
+
+    /**
+     * A cache of context IDs that weren't defined.
+     */
+    private Set bindingErrors;
+
+    /**
+     * The array of all bindings currently handled by this manager. This array
+     * is the raw list of bindings, as provided to this manager. This value may
+     * be <code>null</code> if there are no bindings. The size of this array
+     * is not necessarily the number of bindings.
+     */
+    private Binding[] bindings = null;
+
+    /**
+     * A cache of the bindings previously computed by this manager. This value
+     * may be empty, but it is never <code>null</code>. This is a map of
+     * <code>CachedBindingSet</code> to <code>CachedBindingSet</code>.
+     */
+    private Map cachedBindings;
+
+    /**
+     * The command manager for this binding manager. This manager is only needed
+     * for the <code>getActiveBindingsFor(String)</code> method. This value is
+     * guaranteed to never be <code>null</code>.
+     */
+    private const CommandManager commandManager;
+
+    /**
+     * The context manager for this binding manager. For a binding manager to
+     * function, it needs to listen for changes to the contexts. This value is
+     * guaranteed to never be <code>null</code>.
+     */
+    private const ContextManager contextManager;
+
+    /**
+     * The locale for this manager. This defaults to the current locale. The
+     * value will never be <code>null</code>.
+     */
+    private String locale;
+
+    /**
+     * The array of locales, starting with the active locale and moving up
+     * through less specific representations of the locale. For example,
+     * ["en_US", "en", "", null]. This value will never be <code>null</code>.
+     */
+    private String[] locales;
+
+    /**
+     * The platform for this manager. This defaults to the current platform. The
+     * value will never be <code>null</code>.
+     */
+    private String platform;
+
+    /**
+     * The array of platforms, starting with the active platform and moving up
+     * through less specific representations of the platform. For example,
+     * ["gtk", "", null]. This value will never be <code>null,/code>.
+     */
+    private String[] platforms;
+
+    /**
+     * A map of prefixes (<code>TriggerSequence</code>) to a map of
+     * available completions (possibly <code>null</code>, which means there
+     * is an exact match). The available completions is a map of trigger (<code>TriggerSequence</code>)
+     * to bindings (<code>Binding</code>). This value may be
+     * <code>null</code> if there is no existing solution.
+     */
+    private Map prefixTable = null;
+
+    /**
+     * <p>
+     * Constructs a new instance of <code>BindingManager</code>.
+     * </p>
+     * <p>
+     * This method completes in amortized constant time (O(1)).
+     * </p>
+     *
+     * @param contextManager
+     *            The context manager that will support this binding manager.
+     *            This value must not be <code>null</code>.
+     * @param commandManager
+     *            The command manager that will support this binding manager.
+     *            This value must not be <code>null</code>.
+     */
+    public this(ContextManager contextManager,
+            CommandManager commandManager) {
+        triggerConflicts = new HashSet();
+        bindingErrors = new HashSet();
+        cachedBindings = new HashMap();
+        locale = tango.text.Util.replace( Culture.current().toString().dup, '-', '_' );
+        locales = expand(locale, LOCALE_SEPARATOR);
+
+        platform = SWT.getPlatform();
+        platforms = expand(platform, Util.ZERO_LENGTH_STRING);
+        if (contextManager is null) {
+            throw new NullPointerException(
+                    "A binding manager requires a context manager"); //$NON-NLS-1$
+        }
+
+        if (commandManager is null) {
+            throw new NullPointerException(
+                    "A binding manager requires a command manager"); //$NON-NLS-1$
+        }
+
+        this.contextManager = contextManager;
+        contextManager.addContextManagerListener(this);
+        this.commandManager = commandManager;
+    }
+
+    /**
+     * <p>
+     * Adds a single new binding to the existing array of bindings. If the array
+     * is currently <code>null</code>, then a new array is created and this
+     * binding is added to it. This method does not detect duplicates.
+     * </p>
+     * <p>
+     * This method completes in amortized <code>O(1)</code>.
+     * </p>
+     *
+     * @param binding
+     *            The binding to be added; must not be <code>null</code>.
+     */
+    public final void addBinding(Binding binding) {
+        if (binding is null) {
+            throw new NullPointerException("Cannot add a null binding"); //$NON-NLS-1$
+        }
+
+        if (bindings is null) {
+            bindings = new Binding[1];
+        } else if (bindingCount >= bindings.length) {
+            Binding[] oldBindings = bindings;
+            bindings = new Binding[oldBindings.length * 2];
+            System.arraycopy(oldBindings, 0, bindings, 0, oldBindings.length);
+        }
+        bindings[bindingCount++] = binding;
+        clearCache();
+    }
+
+    /**
+     * <p>
+     * Adds a listener to this binding manager. The listener will be notified
+     * when the set of defined schemes or bindings changes. This can be used to
+     * track the global appearance and disappearance of bindings.
+     * </p>
+     * <p>
+     * This method completes in amortized constant time (<code>O(1)</code>).
+     * </p>
+     *
+     * @param listener
+     *            The listener to attach; must not be <code>null</code>.
+     */
+    public final void addBindingManagerListener(
+            IBindingManagerListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * <p>
+     * Builds a prefix table look-up for a map of active bindings.
+     * </p>
+     * <p>
+     * This method takes <code>O(mn)</code>, where <code>m</code> is the
+     * length of the trigger sequences and <code>n</code> is the number of
+     * bindings.
+     * </p>
+     *
+     * @param activeBindings
+     *            The map of triggers (<code>TriggerSequence</code>) to
+     *            command ids (<code>String</code>) which are currently
+     *            active. This value may be <code>null</code> if there are no
+     *            active bindings, and it may be empty. It must not be
+     *            <code>null</code>.
+     * @return A map of prefixes (<code>TriggerSequence</code>) to a map of
+     *         available completions (possibly <code>null</code>, which means
+     *         there is an exact match). The available completions is a map of
+     *         trigger (<code>TriggerSequence</code>) to command identifier (<code>String</code>).
+     *         This value will never be <code>null</code>, but may be empty.
+     */
+    private final Map buildPrefixTable(Map activeBindings) {
+        Map prefixTable = new HashMap;
+        Iterator bindingItr = activeBindings.entrySet().iterator();
+        while (bindingItr.hasNext()) {
+            Map.Entry entry = cast(Map.Entry) bindingItr.next();
+            TriggerSequence triggerSequence = cast(TriggerSequence) entry
+                    .getKey();
+
+            // Add the perfect match.
+            if (!prefixTable.containsKey(triggerSequence)) {
+                prefixTable.put(triggerSequence, cast(Object)null);
+            }
+
+            TriggerSequence[] prefixes = triggerSequence.getPrefixes();
+            int prefixesLength = prefixes.length;
+            if (prefixesLength is 0) {
+                continue;
+            }
+
+            // Break apart the trigger sequence.
+            Binding binding = cast(Binding) entry.getValue();
+            for (int i = 0; i < prefixesLength; i++) {
+                TriggerSequence prefix = prefixes[i];
+                Object value = prefixTable.get(prefix);
+                if ((prefixTable.containsKey(prefix)) && (cast(Map)value )) {
+                    (cast(Map) value).put(triggerSequence, binding);
+                } else {
+                    Map map = new HashMap();
+                    prefixTable.put(prefix, cast(Object)map);
+                    map.put(triggerSequence, binding);
+                }
+            }
+        }
+
+        return prefixTable;
+    }
+
+    /**
+     * <p>
+     * Clears the cache, and the existing solution. If debugging is turned on,
+     * then this will also print a message to standard out.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>.
+     * </p>
+     */
+    private final void clearCache() {
+        if (DEBUG) {
+            Tracing.printTrace("BINDINGS", "Clearing cache"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+        cachedBindings.clear();
+        clearSolution();
+    }
+
+    /**
+     * <p>
+     * Clears the existing solution.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>.
+     */
+    private final void clearSolution() {
+        setActiveBindings(null, null, null, null);
+    }
+
+    /**
+     * Compares the identifier of two schemes, and decides which scheme is the
+     * youngest (i.e., the child) of the two. Both schemes should be active
+     * schemes.
+     *
+     * @param schemeId1
+     *            The identifier of the first scheme; must not be
+     *            <code>null</code>.
+     * @param schemeId2
+     *            The identifier of the second scheme; must not be
+     *            <code>null</code>.
+     * @return <code>0</code> if the two schemes are equal of if neither
+     *         scheme is active; <code>1</code> if the second scheme is the
+     *         youngest; and <code>-1</code> if the first scheme is the
+     *         youngest.
+     * @since 3.2
+     */
+    private final int compareSchemes(String schemeId1,
+             String schemeId2) {
+        if (!schemeId2.equals(schemeId1)) {
+            for (int i = 0; i < activeSchemeIds.length; i++) {
+                String schemePointer = activeSchemeIds[i];
+                if (schemeId2.equals(schemePointer)) {
+                    return 1;
+
+                } else if (schemeId1.equals(schemePointer)) {
+                    return -1;
+
+                }
+
+            }
+        }
+
+        return 0;
+    }
+
+    /**
+     * <p>
+     * Computes the bindings given the context tree, and inserts them into the
+     * <code>commandIdsByTrigger</code>. It is assumed that
+     * <code>locales</code>,<code>platforsm</code> and
+     * <code>schemeIds</code> correctly reflect the state of the application.
+     * This method does not deal with caching.
+     * </p>
+     * <p>
+     * This method completes in <code>O(n)</code>, where <code>n</code> is
+     * the number of bindings.
+     * </p>
+     *
+     * @param activeContextTree
+     *            The map representing the tree of active contexts. The map is
+     *            one of child to parent, each being a context id (
+     *            <code>String</code>). The keys are never <code>null</code>,
+     *            but the values may be (i.e., no parent). This map may be
+     *            empty. It may be <code>null</code> if we shouldn't consider
+     *            contexts.
+     * @param bindingsByTrigger
+     *            The empty of map that is intended to be filled with triggers (
+     *            <code>TriggerSequence</code>) to bindings (
+     *            <code>Binding</code>). This value must not be
+     *            <code>null</code> and must be empty.
+     * @param triggersByCommandId
+     *            The empty of map that is intended to be filled with command
+     *            identifiers (<code>String</code>) to triggers (
+     *            <code>TriggerSequence</code>). This value must either be
+     *            <code>null</code> (indicating that these values are not
+     *            needed), or empty (indicating that this map should be
+     *            computed).
+     */
+    private final void computeBindings(Map activeContextTree,
+            Map bindingsByTrigger, Map triggersByCommandId,
+            Map conflictsByTrigger) {
+        /*
+         * FIRST PASS: Remove all of the bindings that are marking deletions.
+         */
+        Binding[] trimmedBindings = removeDeletions(bindings);
+
+        /*
+         * SECOND PASS: Just throw in bindings that match the current state. If
+         * there is more than one match for a binding, then create a list.
+         */
+        Map possibleBindings = new HashMap();
+        int length = trimmedBindings.length;
+        for (int i = 0; i < length; i++) {
+            Binding binding = trimmedBindings[i];
+            bool found;
+
+            // Check the context.
+            String contextId = binding.getContextId();
+            if ((activeContextTree !is null)
+                    && (!activeContextTree.containsKey( stringcast(contextId)))) {
+                continue;
+            }
+
+            // Check the locale.
+            if (!localeMatches(binding)) {
+                continue;
+            }
+
+            // Check the platform.
+            if (!platformMatches(binding)) {
+                continue;
+            }
+
+            // Check the scheme ids.
+            String schemeId = binding.getSchemeId();
+            found = false;
+            if (activeSchemeIds !is null) {
+                for (int j = 0; j < activeSchemeIds.length; j++) {
+                    if (Util.opEquals(schemeId, activeSchemeIds[j])) {
+                        found = true;
+                        break;
+                    }
+                }
+            }
+            if (!found) {
+                continue;
+            }
+
+            // Insert the match into the list of possible matches.
+            TriggerSequence trigger = binding.getTriggerSequence();
+            Object existingMatch = possibleBindings.get(trigger);
+            if (cast(Binding)existingMatch ) {
+                possibleBindings.remove(trigger);
+                Collection matches = new ArrayList;
+                matches.add(existingMatch);
+                matches.add(binding);
+                possibleBindings.put(trigger, cast(Object)matches);
+
+            } else if (cast(Collection)existingMatch ) {
+                auto matches = cast(Collection) existingMatch;
+                matches.add(binding);
+
+            } else {
+                possibleBindings.put(trigger, binding);
+            }
+        }
+
+        MultiStatus conflicts = new MultiStatus("org.eclipse.jface", 0, //$NON-NLS-1$
+                "Keybinding conflicts occurred.  They may interfere with normal accelerator operation.", //$NON-NLS-1$
+                null);
+        /*
+         * THIRD PASS: In this pass, we move any non-conflicting bindings
+         * directly into the map. In the case of conflicts, we apply some
+         * further logic to try to resolve them. If the conflict can't be
+         * resolved, then we log the problem.
+         */
+        Iterator possibleBindingItr = possibleBindings.entrySet()
+                .iterator();
+        while (possibleBindingItr.hasNext()) {
+            Map.Entry entry = cast(Map.Entry) possibleBindingItr.next();
+            TriggerSequence trigger = cast(TriggerSequence) entry.getKey();
+            Object match = entry.getValue();
+            /*
+             * What we do depends slightly on whether we are trying to build a
+             * list of all possible bindings (disregarding context), or a flat
+             * map given the currently active contexts.
+             */
+            if (activeContextTree is null) {
+                // We are building the list of all possible bindings.
+                Collection bindings = new ArrayList;
+                if (cast(Binding)match ) {
+                    bindings.add(match);
+                    bindingsByTrigger.put(trigger, cast(Object)bindings);
+                    addReverseLookup(triggersByCommandId, (cast(Binding) match)
+                            .getParameterizedCommand(), trigger);
+
+                } else if (cast(Collection)match ) {
+                    bindings.addAll( cast(Collection) match);
+                    bindingsByTrigger.put(trigger, cast(Object)bindings);
+
+                    Iterator matchItr = bindings.iterator();
+                    while (matchItr.hasNext()) {
+                        addReverseLookup(triggersByCommandId,
+                                (cast(Binding) matchItr.next())
+                                        .getParameterizedCommand(), trigger);
+                    }
+                }
+
+            } else {
+                // We are building the flat map of trigger to commands.
+                if (cast(Binding)match ) {
+                    Binding binding = cast(Binding) match;
+                    bindingsByTrigger.put(trigger, binding);
+                    addReverseLookup(triggersByCommandId, binding
+                            .getParameterizedCommand(), trigger);
+
+                } else if (cast(Collection)match ) {
+                    Binding winner = resolveConflicts(cast(Collection) match,
+                            activeContextTree);
+                    if (winner is null) {
+                        // warn once ... so as not to flood the logs
+                        conflictsByTrigger.put(trigger, match);
+                        if (!triggerConflicts.add(trigger)) {
+//                             StringWriter sw = new StringWriter();
+//                             BufferedWriter buffer = new BufferedWriter(sw);
+                            StringBuffer sb = new StringBuffer();
+                            try {
+                                sb.append("A conflict occurred for "); //$NON-NLS-1$
+                                sb.append(trigger.toString());
+                                sb.append(':');
+                                Iterator i = (cast(Collection) match).iterator();
+                                while (i.hasNext()) {
+                                    sb.append('\n');
+                                    sb.append( i.next().toString() );
+                                }
+                            } catch (IOException e) {
+                                // we should not get this
+                            }
+                            conflicts.add(new Status(IStatus.WARNING,
+                                    "org.eclipse.jface", //$NON-NLS-1$
+                                    sb.toString()));
+                        }
+                        if (DEBUG) {
+                            Tracing.printTrace("BINDINGS", //$NON-NLS-1$
+                                    "A conflict occurred for " ~ trigger.toString); //$NON-NLS-1$
+                            Tracing.printTrace("BINDINGS", "    " ~ match.toString); //$NON-NLS-1$ //$NON-NLS-2$
+                        }
+                    } else {
+                        bindingsByTrigger.put(trigger, winner);
+                        addReverseLookup(triggersByCommandId, winner
+                                .getParameterizedCommand(), trigger);
+                    }
+                }
+            }
+        }
+        if (conflicts.getSeverity() !is IStatus.OK) {
+            Policy.getLog().log(conflicts);
+        }
+    }
+
+    /**
+     * <p>
+     * Notifies this manager that the context manager has changed. This method
+     * is intended for internal use only.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>.
+     * </p>
+     */
+    public final void contextManagerChanged(
+            ContextManagerEvent contextManagerEvent) {
+        if (contextManagerEvent.isActiveContextsChanged()) {
+// clearSolution();
+            recomputeBindings();
+        }
+    }
+
+    /**
+     * Returns the number of strokes in an array of triggers. It is assumed that
+     * there is one natural key per trigger. The strokes are counted based on
+     * the type of key. Natural keys are worth one; ctrl is worth two; shift is
+     * worth four; and alt is worth eight.
+     *
+     * @param triggers
+     *            The triggers on which to count strokes; must not be
+     *            <code>null</code>.
+     * @return The value of the strokes in the triggers.
+     * @since 3.2
+     */
+    private final int countStrokes(Trigger[] triggers) {
+        int strokeCount = triggers.length;
+        for (int i = 0; i < triggers.length; i++) {
+            Trigger trigger = triggers[i];
+            if (cast(KeyStroke)trigger ) {
+                KeyStroke keyStroke = cast(KeyStroke) trigger;
+                int modifierKeys = keyStroke.getModifierKeys();
+                IKeyLookup lookup = KeyLookupFactory.getDefault();
+                if ((modifierKeys & lookup.getAlt()) !is 0) {
+                    strokeCount += 8;
+                }
+                if ((modifierKeys & lookup.getCtrl()) !is 0) {
+                    strokeCount += 2;
+                }
+                if ((modifierKeys & lookup.getShift()) !is 0) {
+                    strokeCount += 4;
+                }
+                if ((modifierKeys & lookup.getCommand()) !is 0) {
+                    strokeCount += 2;
+                }
+            } else {
+                strokeCount += 99;
+            }
+        }
+
+        return strokeCount;
+    }
+
+    /**
+     * <p>
+     * Creates a tree of context identifiers, representing the hierarchical
+     * structure of the given contexts. The tree is structured as a mapping from
+     * child to parent.
+     * </p>
+     * <p>
+     * This method completes in <code>O(n)</code>, where <code>n</code> is
+     * the height of the context tree.
+     * </p>
+     *
+     * @param contextIds
+     *            The set of context identifiers to be converted into a tree;
+     *            must not be <code>null</code>.
+     * @return The tree of contexts to use; may be empty, but never
+     *         <code>null</code>. The keys and values are both strings.
+     */
+    private final Map createContextTreeFor(Set contextIds) {
+        Map contextTree = new HashMap;
+
+        final Iterator contextIdItr = contextIds.iterator();
+        while (contextIdItr.hasNext()) {
+            Object childContextIdObj = contextIdItr.next();
+            String childContextId = stringcast (childContextIdObj);
+
+            while (childContextId !is null) {
+                // Check if we've already got the part of the tree from here up.
+                if (contextTree.containsKey(childContextIdObj)) {
+                    break;
+                }
+
+                // Retrieve the context.
+                Context childContext = contextManager
+                        .getContext(childContextId);
+
+                // Add the child-parent pair to the tree.
+                try {
+                    String parentContextId = childContext.getParentId();
+                    contextTree.put(childContextIdObj, stringcast(parentContextId));
+                    childContextId = parentContextId;
+                } catch (NotDefinedException e) {
+                    break; // stop ascending
+                }
+            }
+        }
+
+        return contextTree;
+    }
+
+    /**
+     * <p>
+     * Creates a tree of context identifiers, representing the hierarchical
+     * structure of the given contexts. The tree is structured as a mapping from
+     * child to parent. In this tree, the key binding specific filtering of
+     * contexts will have taken place.
+     * </p>
+     * <p>
+     * This method completes in <code>O(n^2)</code>, where <code>n</code>
+     * is the height of the context tree.
+     * </p>
+     *
+     * @param contextIds
+     *            The set of context identifiers to be converted into a tree;
+     *            must not be <code>null</code>.
+     * @return The tree of contexts to use; may be empty, but never
+     *         <code>null</code>. The keys and values are both strings.
+     */
+    private final Map createFilteredContextTreeFor(Set contextIds) {
+        // Check to see whether a dialog or window is active.
+        bool dialog = false;
+        bool window = false;
+        Iterator contextIdItr = contextIds.iterator();
+        while (contextIdItr.hasNext()) {
+            String contextId = stringcast(contextIdItr.next());
+            if (IContextIds.CONTEXT_ID_DIALOG.equals(contextId)) {
+                dialog = true;
+                continue;
+            }
+            if (IContextIds.CONTEXT_ID_WINDOW.equals(contextId)) {
+                window = true;
+                continue;
+            }
+        }
+
+        /*
+         * Remove all context identifiers for contexts whose parents are dialog
+         * or window, and the corresponding dialog or window context is not
+         * active.
+         */
+        contextIdItr = contextIds.iterator();
+        while (contextIdItr.hasNext()) {
+            String contextId = stringcast( contextIdItr.next());
+            Context context = contextManager.getContext(contextId);
+            try {
+                String parentId = context.getParentId();
+                while (parentId !is null) {
+                    if (IContextIds.CONTEXT_ID_DIALOG.equals(parentId)) {
+                        if (!dialog) {
+                            contextIdItr.remove();
+                        }
+                        break;
+                    }
+                    if (IContextIds.CONTEXT_ID_WINDOW.equals(parentId)) {
+                        if (!window) {
+                            contextIdItr.remove();
+                        }
+                        break;
+                    }
+                    if (IContextIds.CONTEXT_ID_DIALOG_AND_WINDOW
+                            .equals(parentId)) {
+                        if ((!window) && (!dialog)) {
+                            contextIdItr.remove();
+                        }
+                        break;
+                    }
+
+                    context = contextManager.getContext(parentId);
+                    parentId = context.getParentId();
+                }
+            } catch (NotDefinedException e) {
+                // since this context was part of an undefined hierarchy,
+                // I'm going to yank it out as a bad bet
+                contextIdItr.remove();
+
+                // This is a logging optimization, only log the error once.
+                if (context is null || !bindingErrors.contains(stringcast(context.getId()))) {
+                    if (context !is null) {
+                        bindingErrors.add(stringcast(context.getId()));
+                    }
+
+                    // now log like you've never logged before!
+                    Policy.getLog().log(new Status( IStatus.ERROR, Policy.JFACE, IStatus.OK,
+                        "Undefined context while filtering dialog/window contexts", //$NON-NLS-1$
+                        e));
+                }
+            }
+        }
+
+        return createContextTreeFor(contextIds);
+    }
+
+    /**
+     * <p>
+     * Notifies all of the listeners to this manager that the defined or active
+     * schemes of bindings have changed.
+     * </p>
+     * <p>
+     * The time this method takes to complete is dependent on external
+     * listeners.
+     * </p>
+     *
+     * @param event
+     *            The event to send to all of the listeners; must not be
+     *            <code>null</code>.
+     */
+    private final void fireBindingManagerChanged(BindingManagerEvent event) {
+        if (event is null) {
+            throw new NullPointerException();
+        }
+
+        Object[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            IBindingManagerListener listener = cast(IBindingManagerListener) listeners[i];
+            listener.bindingManagerChanged(event);
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the active bindings. The caller must not modify the returned map.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>. If the active bindings are
+     * not yet computed, then this completes in <code>O(nn)</code>, where
+     * <code>n</code> is the number of bindings.
+     * </p>
+     *
+     * @return The map of triggers (<code>TriggerSequence</code>) to
+     *         bindings (<code>Binding</code>) which are currently active.
+     *         This value may be <code>null</code> if there are no active
+     *         bindings, and it may be empty.
+     */
+    private final Map getActiveBindings() {
+        if (activeBindings is null) {
+            recomputeBindings();
+        }
+
+        return activeBindings;
+    }
+
+    /**
+     * <p>
+     * Returns the active bindings indexed by command identifier. The caller
+     * must not modify the returned map.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>. If the active bindings are
+     * not yet computed, then this completes in <code>O(nn)</code>, where
+     * <code>n</code> is the number of bindings.
+     * </p>
+     *
+     * @return The map of fully-parameterized commands (<code>ParameterizedCommand</code>)
+     *         to triggers (<code>TriggerSequence</code>) which are
+     *         currently active. This value may be <code>null</code> if there
+     *         are no active bindings, and it may be empty.
+     */
+    private final Map getActiveBindingsByParameterizedCommand() {
+        if (activeBindingsByParameterizedCommand is null) {
+            recomputeBindings();
+        }
+
+        return activeBindingsByParameterizedCommand;
+    }
+
+    /**
+     * <p>
+     * Computes the bindings for the current state of the application, but
+     * disregarding the current contexts. This can be useful when trying to
+     * display all the possible bindings.
+     * </p>
+     * <p>
+     * This method completes in <code>O(n)</code>, where <code>n</code> is
+     * the number of bindings.
+     * </p>
+     *
+     * @return A map of trigger (<code>TriggerSequence</code>) to bindings (
+     *         <code>Collection</code> containing <code>Binding</code>).
+     *         This map may be empty, but it is never <code>null</code>.
+     */
+    public final Map getActiveBindingsDisregardingContext() {
+        if (bindings is null) {
+            // Not yet initialized. This is happening too early. Do nothing.
+            return Collections.EMPTY_MAP;
+        }
+
+        // Build a cached binding set for that state.
+        CachedBindingSet bindingCache = new CachedBindingSet(null,
+                locales, platforms, activeSchemeIds);
+
+        /*
+         * Check if the cached binding set already exists. If so, simply set the
+         * active bindings and return.
+         */
+        CachedBindingSet existingCache = cast(CachedBindingSet) cachedBindings
+                .get(bindingCache);
+        if (existingCache is null) {
+            existingCache = bindingCache;
+            cachedBindings.put(existingCache, existingCache);
+        }
+        Map commandIdsByTrigger = existingCache.getBindingsByTrigger();
+        if (commandIdsByTrigger !is null) {
+            if (DEBUG) {
+                Tracing.printTrace("BINDINGS", "Cache hit"); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+
+            return Collections.unmodifiableMap(commandIdsByTrigger);
+        }
+
+        // There is no cached entry for this.
+        if (DEBUG) {
+            Tracing.printTrace("BINDINGS", "Cache miss"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        // Compute the active bindings.
+        commandIdsByTrigger = new HashMap();
+        Map triggersByParameterizedCommand = new HashMap();
+        Map conflictsByTrigger = new HashMap();
+        computeBindings(null, commandIdsByTrigger,
+                triggersByParameterizedCommand, conflictsByTrigger);
+        existingCache.setBindingsByTrigger(commandIdsByTrigger);
+        existingCache.setTriggersByCommandId(triggersByParameterizedCommand);
+        existingCache.setConflictsByTrigger(conflictsByTrigger);
+        return Collections.unmodifiableMap(commandIdsByTrigger);
+    }
+
+    /**
+     * <p>
+     * Computes the bindings for the current state of the application, but
+     * disregarding the current contexts. This can be useful when trying to
+     * display all the possible bindings.
+     * </p>
+     * <p>
+     * This method completes in <code>O(n)</code>, where <code>n</code> is
+     * the number of bindings.
+     * </p>
+     *
+     * @return A map of trigger (<code>TriggerSequence</code>) to bindings (
+     *         <code>Collection</code> containing <code>Binding</code>).
+     *         This map may be empty, but it is never <code>null</code>.
+     * @since 3.2
+     */
+    private final Map getActiveBindingsDisregardingContextByParameterizedCommand() {
+        if (bindings is null) {
+            // Not yet initialized. This is happening too early. Do nothing.
+            return Collections.EMPTY_MAP;
+        }
+
+        // Build a cached binding set for that state.
+        CachedBindingSet bindingCache = new CachedBindingSet(null,
+                locales, platforms, activeSchemeIds);
+
+        /*
+         * Check if the cached binding set already exists. If so, simply set the
+         * active bindings and return.
+         */
+        CachedBindingSet existingCache = cast(CachedBindingSet) cachedBindings
+                .get(bindingCache);
+        if (existingCache is null) {
+            existingCache = bindingCache;
+            cachedBindings.put(existingCache, existingCache);
+        }
+        Map triggersByParameterizedCommand = existingCache
+                .getTriggersByCommandId();
+        if (triggersByParameterizedCommand !is null) {
+            if (DEBUG) {
+                Tracing.printTrace("BINDINGS", "Cache hit"); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+
+            return /+Collections.unmodifiableMap(+/triggersByParameterizedCommand;
+        }
+
+        // There is no cached entry for this.
+        if (DEBUG) {
+            Tracing.printTrace("BINDINGS", "Cache miss"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        // Compute the active bindings.
+        Map commandIdsByTrigger = new HashMap();
+        Map conflictsByTrigger = new HashMap();
+        triggersByParameterizedCommand = new HashMap();
+        computeBindings(null, commandIdsByTrigger,
+                triggersByParameterizedCommand, conflictsByTrigger);
+        existingCache.setBindingsByTrigger(commandIdsByTrigger);
+        existingCache.setTriggersByCommandId(triggersByParameterizedCommand);
+        existingCache.setConflictsByTrigger(conflictsByTrigger);
+
+        return Collections.unmodifiableMap(triggersByParameterizedCommand);
+    }
+
+    /**
+     * <p>
+     * Computes the bindings for the current state of the application, but
+     * disregarding the current contexts. This can be useful when trying to
+     * display all the possible bindings.
+     * </p>
+     * <p>
+     * This method completes in <code>O(n)</code>, where <code>n</code> is
+     * the number of bindings.
+     * </p>
+     *
+     * @return All of the active bindings (<code>Binding</code>), not sorted
+     *         in any fashion. This collection may be empty, but it is never
+     *         <code>null</code>.
+     */
+    public final Collection getActiveBindingsDisregardingContextFlat() {
+        Collection bindingCollections = getActiveBindingsDisregardingContext()
+                .values();
+        Collection mergedBindings = new ArrayList();
+        Iterator bindingCollectionItr = bindingCollections.iterator();
+        while (bindingCollectionItr.hasNext()) {
+            Collection bindingCollection = cast(Collection) bindingCollectionItr
+                    .next();
+            if ((bindingCollection !is null) && (!bindingCollection.isEmpty())) {
+                mergedBindings.addAll(bindingCollection);
+            }
+        }
+
+        return mergedBindings;
+    }
+
+    /**
+     * <p>
+     * Returns the active bindings for a particular command identifier, but
+     * discounting the current contexts. This method operates in O(n) time over
+     * the number of bindings.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>. If the active bindings are
+     * not yet computed, then this completes in <code>O(nn)</code>, where
+     * <code>n</code> is the number of bindings.
+     * </p>
+     *
+     * @param parameterizedCommand
+     *            The fully-parameterized command whose bindings are requested.
+     *            This argument may be <code>null</code>.
+     * @return The array of active triggers (<code>TriggerSequence</code>)
+     *         for a particular command identifier. This value is guaranteed to
+     *         never be <code>null</code>, but it may be empty.
+     * @since 3.2
+     */
+    public final TriggerSequence[] getActiveBindingsDisregardingContextFor(
+            ParameterizedCommand parameterizedCommand) {
+        Object object = getActiveBindingsDisregardingContextByParameterizedCommand()
+                .get(parameterizedCommand);
+        if (auto collection = cast(Collection)object ) {
+            return arraycast!(TriggerSequence)( collection
+                    .toArray());
+        }
+        return EMPTY_TRIGGER_SEQUENCE;
+    }
+
+    /**
+     * <p>
+     * Returns the active bindings for a particular command identifier. This
+     * method operates in O(n) time over the number of bindings.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>. If the active bindings are
+     * not yet computed, then this completes in <code>O(nn)</code>, where
+     * <code>n</code> is the number of bindings.
+     * </p>
+     *
+     * @param parameterizedCommand
+     *            The fully-parameterized command whose bindings are requested.
+     *            This argument may be <code>null</code>.
+     * @return The array of active triggers (<code>TriggerSequence</code>)
+     *         for a particular command identifier. This value is guaranteed to
+     *         never be <code>null</code>, but it may be empty.
+     */
+    public final TriggerSequence[] getActiveBindingsFor(
+            ParameterizedCommand parameterizedCommand) {
+        Object object = getActiveBindingsByParameterizedCommand().get(
+                parameterizedCommand);
+        if ( auto collection = cast(Collection)object ) {
+            return arraycast!(TriggerSequence)(collection
+                    .toArray(new TriggerSequence[collection.size()]));
+        }
+
+        return EMPTY_TRIGGER_SEQUENCE;
+    }
+
+    /**
+     * <p>
+     * Returns the active bindings for a particular command identifier. This
+     * method operates in O(n) time over the number of bindings.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>. If the active bindings are
+     * not yet computed, then this completes in <code>O(nn)</code>, where
+     * <code>n</code> is the number of bindings.
+     * </p>
+     *
+     * @param commandId
+     *            The identifier of the command whose bindings are requested.
+     *            This argument may be <code>null</code>. It is assumed that
+     *            the command has no parameters.
+     * @return The array of active triggers (<code>TriggerSequence</code>)
+     *         for a particular command identifier. This value is guaranteed not
+     *         to be <code>null</code>, but it may be empty.
+     */
+    public final TriggerSequence[] getActiveBindingsFor(String commandId) {
+        ParameterizedCommand parameterizedCommand = new ParameterizedCommand(
+                commandManager.getCommand(commandId), null);
+        return getActiveBindingsFor(parameterizedCommand);
+    }
+
+    /**
+     * A variation on {@link BindingManager#getActiveBindingsFor(String)} that
+     * returns an array of bindings, rather than trigger sequences. This method
+     * is needed for doing "best" calculations on the active bindings.
+     *
+     * @param commandId
+     *            The identifier of the command for which the active bindings
+     *            should be retrieved; must not be <code>null</code>.
+     * @return The active bindings for the given command; this value may be
+     *         <code>null</code> if there are no active bindings.
+     * @since 3.2
+     */
+    private final Binding[] getActiveBindingsFor1(ParameterizedCommand command) {
+        TriggerSequence[] triggers = getActiveBindingsFor(command);
+        if (triggers.length is 0) {
+            return null;
+        }
+
+        Map activeBindings = getActiveBindings();
+        if (activeBindings !is null) {
+            Binding[] bindings = new Binding[triggers.length];
+            for (int i = 0; i < triggers.length; i++) {
+                TriggerSequence triggerSequence = triggers[i];
+                Object object = activeBindings.get(triggerSequence);
+                Binding binding = cast(Binding) object;
+                bindings[i] = binding;
+            }
+            return bindings;
+        }
+
+        return null;
+    }
+
+    /**
+     * <p>
+     * Gets the currently active scheme.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>.
+     * </p>
+     *
+     * @return The active scheme; may be <code>null</code> if there is no
+     *         active scheme. If a scheme is returned, it is guaranteed to be
+     *         defined.
+     */
+    public final Scheme getActiveScheme() {
+        return activeScheme;
+    }
+
+    /**
+     * Gets the best active binding for a command. The best binding is the one
+     * that would be most appropriate to show in a menu. Bindings which belong
+     * to a child scheme are given preference over those in a parent scheme.
+     * Bindings which belong to a particular locale or platform are given
+     * preference over those that do not. The rest of the calculaton is based
+     * most on various concepts of "length", as well as giving some modifier
+     * keys preference (e.g., <code>Alt</code> is less likely to appear than
+     * <code>Ctrl</code>).
+     *
+     * @param commandId
+     *            The identifier of the command for which the best active
+     *            binding should be retrieved; must not be <code>null</code>.
+     * @return The trigger sequence for the best binding; may be
+     *         <code>null</code> if no bindings are active for the given
+     *         command.
+     * @since 3.2
+     */
+    public final TriggerSequence getBestActiveBindingFor(String commandId) {
+        return getBestActiveBindingFor(new ParameterizedCommand(commandManager.getCommand(commandId), null));
+    }
+
+    /**
+     * @param command
+     * @return
+     *      a trigger sequence, or <code>null</code>
+     * @since 3.4
+     */
+    public final TriggerSequence getBestActiveBindingFor(ParameterizedCommand command) {
+        final Binding[] bindings = getActiveBindingsFor1(command);
+        if ((bindings is null) || (bindings.length is 0)) {
+            return null;
+        }
+
+        Binding bestBinding = bindings[0];
+        int compareTo;
+        for (int i = 1; i < bindings.length; i++) {
+            Binding currentBinding = bindings[i];
+
+            // Bindings in a child scheme are always given preference.
+            String bestSchemeId = bestBinding.getSchemeId();
+            String currentSchemeId = currentBinding.getSchemeId();
+            compareTo = compareSchemes(bestSchemeId, currentSchemeId);
+            if (compareTo > 0) {
+                bestBinding = currentBinding;
+            }
+            if (compareTo !is 0) {
+                continue;
+            }
+
+            /*
+             * Bindings with a locale are given preference over those that do
+             * not.
+             */
+            String bestLocale = bestBinding.getLocale();
+            String currentLocale = currentBinding.getLocale();
+            if ((bestLocale is null) && (currentLocale !is null)) {
+                bestBinding = currentBinding;
+            }
+            if (!(Util.opEquals(bestLocale, currentLocale))) {
+                continue;
+            }
+
+            /*
+             * Bindings with a platform are given preference over those that do
+             * not.
+             */
+            String bestPlatform = bestBinding.getPlatform();
+            String currentPlatform = currentBinding.getPlatform();
+            if ((bestPlatform is null) && (currentPlatform !is null)) {
+                bestBinding = currentBinding;
+            }
+            if (!(Util.opEquals(bestPlatform, currentPlatform))) {
+                continue;
+            }
+
+            /*
+             * Check to see which has the least number of triggers in the
+             * trigger sequence.
+             */
+            TriggerSequence bestTriggerSequence = bestBinding
+                    .getTriggerSequence();
+            TriggerSequence currentTriggerSequence = currentBinding
+                    .getTriggerSequence();
+            Trigger[] bestTriggers = bestTriggerSequence.getTriggers();
+            Trigger[] currentTriggers = currentTriggerSequence
+                    .getTriggers();
+            compareTo = bestTriggers.length - currentTriggers.length;
+            if (compareTo > 0) {
+                bestBinding = currentBinding;
+            }
+            if (compareTo !is 0) {
+                continue;
+            }
+
+            /*
+             * Compare the number of keys pressed in each trigger sequence. Some
+             * types of keys count less than others (i.e., some types of
+             * modifiers keys are less likely to be chosen).
+             */
+            compareTo = countStrokes(bestTriggers)
+                    - countStrokes(currentTriggers);
+            if (compareTo > 0) {
+                bestBinding = currentBinding;
+            }
+            if (compareTo !is 0) {
+                continue;
+            }
+
+            // If this is still a tie, then just chose the shortest text.
+            compareTo = bestTriggerSequence.format().length
+                    - currentTriggerSequence.format().length;
+            if (compareTo > 0) {
+                bestBinding = currentBinding;
+            }
+        }
+
+        return bestBinding.getTriggerSequence();
+    }
+
+    /**
+     * Gets the formatted string representing the best active binding for a
+     * command. The best binding is the one that would be most appropriate to
+     * show in a menu. Bindings which belong to a child scheme are given
+     * preference over those in a parent scheme. The rest of the calculaton is
+     * based most on various concepts of "length", as well as giving some
+     * modifier keys preference (e.g., <code>Alt</code> is less likely to
+     * appear than <code>Ctrl</code>).
+     *
+     * @param commandId
+     *            The identifier of the command for which the best active
+     *            binding should be retrieved; must not be <code>null</code>.
+     * @return The formatted string for the best binding; may be
+     *         <code>null</code> if no bindings are active for the given
+     *         command.
+     * @since 3.2
+     */
+    public final String getBestActiveBindingFormattedFor(String commandId) {
+        TriggerSequence binding = getBestActiveBindingFor(commandId);
+        if (binding !is null) {
+            return binding.format();
+        }
+
+        return null;
+    }
+    /**
+     * <p>
+     * Returns the set of all bindings managed by this class.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>.
+     * </p>
+     *
+     * @return The array of all bindings. This value may be <code>null</code>
+     *         and it may be empty.
+     */
+    public final Binding[] getBindings() {
+        if (bindings is null) {
+            return null;
+        }
+
+        Binding[] returnValue = new Binding[bindingCount];
+        System.arraycopy(bindings, 0, returnValue, 0, bindingCount);
+        return returnValue;
+    }
+
+    /**
+     * <p>
+     * Returns the array of schemes that are defined.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>.
+     * </p>
+     *
+     * @return The array of defined schemes; this value may be empty or
+     *         <code>null</code>.
+     */
+    public final Scheme[] getDefinedSchemes() {
+        return arraycast!(Scheme)(definedHandleObjects
+                .toArray(new Scheme[definedHandleObjects.size()]));
+    }
+
+    /**
+     * <p>
+     * Returns the active locale for this binding manager. The locale is in the
+     * same format as <code>Locale.getDefault().toString()</code>.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>.
+     * </p>
+     *
+     * @return The active locale; never <code>null</code>.
+     */
+    public final String getLocale() {
+        return locale;
+    }
+
+    /**
+     * <p>
+     * Returns all of the possible bindings that start with the given trigger
+     * (but are not equal to the given trigger).
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>. If the bindings aren't
+     * currently computed, then this completes in <code>O(n)</code>, where
+     * <code>n</code> is the number of bindings.
+     * </p>
+     *
+     * @param trigger
+     *            The prefix to look for; must not be <code>null</code>.
+     * @return A map of triggers (<code>TriggerSequence</code>) to bindings (<code>Binding</code>).
+     *         This map may be empty, but it is never <code>null</code>.
+     */
+    public final Map getPartialMatches(TriggerSequence trigger) {
+        Map partialMatches = cast(Map) getPrefixTable().get(trigger);
+        if (partialMatches is null) {
+            return Collections.EMPTY_MAP;
+        }
+
+        return partialMatches;
+    }
+
+    /**
+     * <p>
+     * Returns the command identifier for the active binding matching this
+     * trigger, if any.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>. If the bindings aren't
+     * currently computed, then this completes in <code>O(n)</code>, where
+     * <code>n</code> is the number of bindings.
+     * </p>
+     *
+     * @param trigger
+     *            The trigger to match; may be <code>null</code>.
+     * @return The binding that matches, if any; <code>null</code> otherwise.
+     */
+    public final Binding getPerfectMatch(TriggerSequence trigger) {
+        return cast(Binding) getActiveBindings().get(trigger);
+    }
+
+    /**
+     * <p>
+     * Returns the active platform for this binding manager. The platform is in
+     * the same format as <code>SWT.getPlatform()</code>.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>.
+     * </p>
+     *
+     * @return The active platform; never <code>null</code>.
+     */
+    public final String getPlatform() {
+        return platform;
+    }
+
+    /**
+     * <p>
+     * Returns the prefix table. The caller must not modify the returned map.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>. If the active bindings are
+     * not yet computed, then this completes in <code>O(n)</code>, where
+     * <code>n</code> is the number of bindings.
+     * </p>
+     *
+     * @return A map of prefixes (<code>TriggerSequence</code>) to a map of
+     *         available completions (possibly <code>null</code>, which means
+     *         there is an exact match). The available completions is a map of
+     *         trigger (<code>TriggerSequence</code>) to binding (<code>Binding</code>).
+     *         This value will never be <code>null</code> but may be empty.
+     */
+    private final Map getPrefixTable() {
+        if (prefixTable is null) {
+            recomputeBindings();
+        }
+
+        return prefixTable;
+    }
+
+    /**
+     * <p>
+     * Gets the scheme with the given identifier. If the scheme does not already
+     * exist, then a new (undefined) scheme is created with that identifier.
+     * This guarantees that schemes will remain unique.
+     * </p>
+     * <p>
+     * This method completes in amortized <code>O(1)</code>.
+     * </p>
+     *
+     * @param schemeId
+     *            The identifier for the scheme to retrieve; must not be
+     *            <code>null</code>.
+     * @return A scheme with the given identifier.
+     */
+    public final Scheme getScheme(String schemeId) {
+        checkId(schemeId);
+
+        Scheme scheme = cast(Scheme) handleObjectsById.get(schemeId);
+        if (scheme is null) {
+            scheme = new Scheme(schemeId);
+            handleObjectsById.put(schemeId, scheme);
+            scheme.addSchemeListener(this);
+        }
+
+        return scheme;
+    }
+
+    /**
+     * <p>
+     * Ascends all of the parents of the scheme until no more parents are found.
+     * </p>
+     * <p>
+     * This method completes in <code>O(n)</code>, where <code>n</code> is
+     * the height of the context tree.
+     * </p>
+     *
+     * @param schemeId
+     *            The id of the scheme for which the parents should be found;
+     *            may be <code>null</code>.
+     * @return The array of scheme ids (<code>String</code>) starting with
+     *         <code>schemeId</code> and then ascending through its ancestors.
+     */
+    private final String[] getSchemeIds(String schemeId) {
+        List strings = new ArrayList();
+        while (schemeId !is null) {
+            strings.add( stringcast(schemeId));
+            try {
+                schemeId = getScheme(schemeId).getParentId();
+            } catch (NotDefinedException e) {
+                Policy.getLog().log( new Status(
+                    IStatus.ERROR, Policy.JFACE, IStatus.OK,
+                    "Failed ascending scheme parents", //$NON-NLS-1$
+                    e));
+                return null;
+            }
+        }
+
+        return stringcast(strings.toArray());
+    }
+
+    /**
+     * <p>
+     * Returns whether the given trigger sequence is a partial match for the
+     * given sequence.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>. If the bindings aren't
+     * currently computed, then this completes in <code>O(n)</code>, where
+     * <code>n</code> is the number of bindings.
+     * </p>
+     *
+     * @param trigger
+     *            The sequence which should be the prefix for some binding;
+     *            should not be <code>null</code>.
+     * @return <code>true</code> if the trigger can be found in the active
+     *         bindings; <code>false</code> otherwise.
+     */
+    public final bool isPartialMatch(TriggerSequence trigger) {
+        return (getPrefixTable().get(trigger) !is null);
+    }
+
+    /**
+     * <p>
+     * Returns whether the given trigger sequence is a perfect match for the
+     * given sequence.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>. If the bindings aren't
+     * currently computed, then this completes in <code>O(n)</code>, where
+     * <code>n</code> is the number of bindings.
+     * </p>
+     *
+     * @param trigger
+     *            The sequence which should match exactly; should not be
+     *            <code>null</code>.
+     * @return <code>true</code> if the trigger can be found in the active
+     *         bindings; <code>false</code> otherwise.
+     */
+    public final bool isPerfectMatch(TriggerSequence trigger) {
+        return getActiveBindings().containsKey(trigger);
+    }
+
+    /**
+     * <p>
+     * Tests whether the locale for the binding matches one of the active
+     * locales.
+     * </p>
+     * <p>
+     * This method completes in <code>O(n)</code>, where <code>n</code> is
+     * the number of active locales.
+     * </p>
+     *
+     * @param binding
+     *            The binding with which to test; must not be <code>null</code>.
+     * @return <code>true</code> if the binding's locale matches;
+     *         <code>false</code> otherwise.
+     */
+    private final bool localeMatches(Binding binding) {
+        bool matches = false;
+
+        String locale = binding.getLocale();
+        if (locale is null) {
+            return true; // shortcut a common case
+        }
+
+        for (int i = 0; i < locales.length; i++) {
+            if (Util.opEquals(locales[i], locale)) {
+                matches = true;
+                break;
+            }
+        }
+
+        return matches;
+    }
+
+    /**
+     * <p>
+     * Tests whether the platform for the binding matches one of the active
+     * platforms.
+     * </p>
+     * <p>
+     * This method completes in <code>O(n)</code>, where <code>n</code> is
+     * the number of active platforms.
+     * </p>
+     *
+     * @param binding
+     *            The binding with which to test; must not be <code>null</code>.
+     * @return <code>true</code> if the binding's platform matches;
+     *         <code>false</code> otherwise.
+     */
+    private final bool platformMatches(Binding binding) {
+        bool matches = false;
+
+        String platform = binding.getPlatform();
+        if (platform is null) {
+            return true; // shortcut a common case
+        }
+
+        for (int i = 0; i < platforms.length; i++) {
+            if (Util.opEquals(platforms[i], platform)) {
+                matches = true;
+                break;
+            }
+        }
+
+        return matches;
+    }
+
+    /**
+     * <p>
+     * This recomputes the bindings based on changes to the state of the world.
+     * This computation can be triggered by changes to contexts, the active
+     * scheme, the locale, or the platform. This method tries to use the cache
+     * of pre-computed bindings, if possible. When this method completes,
+     * <code>activeBindings</code> will be set to the current set of bindings
+     * and <code>cachedBindings</code> will contain an instance of
+     * <code>CachedBindingSet</code> representing these bindings.
+     * </p>
+     * <p>
+     * This method completes in <code>O(n+pn)</code>, where <code>n</code>
+     * is the number of bindings, and <code>p</code> is the average number of
+     * triggers in a trigger sequence.
+     * </p>
+     */
+    private final void recomputeBindings() {
+        if (bindings is null) {
+            // Not yet initialized. This is happening too early. Do nothing.
+            setActiveBindings(Collections.EMPTY_MAP, Collections.EMPTY_MAP,
+                    Collections.EMPTY_MAP, Collections.EMPTY_MAP);
+            return;
+        }
+
+        // Figure out the current state.
+        Set activeContextIds = new HashSet(contextManager
+                .getActiveContextIds());
+        Map activeContextTree = createFilteredContextTreeFor(activeContextIds);
+
+        // Build a cached binding set for that state.
+        CachedBindingSet bindingCache = new CachedBindingSet(
+                activeContextTree, locales, platforms, activeSchemeIds);
+
+        /*
+         * Check if the cached binding set already exists. If so, simply set the
+         * active bindings and return.
+         */
+        CachedBindingSet existingCache = cast(CachedBindingSet) cachedBindings
+                .get(bindingCache);
+        if (existingCache is null) {
+            existingCache = bindingCache;
+            cachedBindings.put(existingCache, existingCache);
+        }
+        Map commandIdsByTrigger = existingCache.getBindingsByTrigger();
+        if (commandIdsByTrigger !is null) {
+            if (DEBUG) {
+                Tracing.printTrace("BINDINGS", "Cache hit"); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+            setActiveBindings(commandIdsByTrigger, existingCache
+                    .getTriggersByCommandId(), existingCache.getPrefixTable(),
+                    existingCache.getConflictsByTrigger());
+            return;
+        }
+
+        // There is no cached entry for this.
+        if (DEBUG) {
+            Tracing.printTrace("BINDINGS", "Cache miss"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        // Compute the active bindings.
+        commandIdsByTrigger = new HashMap();
+        Map triggersByParameterizedCommand = new HashMap();
+        Map conflictsByTrigger = new HashMap();
+        computeBindings(activeContextTree, commandIdsByTrigger,
+                triggersByParameterizedCommand, conflictsByTrigger);
+        existingCache.setBindingsByTrigger(commandIdsByTrigger);
+        existingCache.setTriggersByCommandId(triggersByParameterizedCommand);
+        existingCache.setConflictsByTrigger(conflictsByTrigger);
+        setActiveBindings(commandIdsByTrigger, triggersByParameterizedCommand,
+                buildPrefixTable(commandIdsByTrigger),
+                conflictsByTrigger);
+        existingCache.setPrefixTable(prefixTable);
+    }
+
+    /**
+     * <p>
+     * Remove the specific binding by identity. Does nothing if the binding is
+     * not in the manager.
+     * </p>
+     * <p>
+     * This method completes in <code>O(n)</code>, where <code>n</code> is
+     * the number of bindings.
+     * </p>
+     *
+     * @param binding
+     *            The binding to be removed; must not be <code>null</code>.
+     * @since 3.2
+     */
+    public final void removeBinding(Binding binding) {
+        if (bindings is null || bindings.length < 1) {
+            return;
+        }
+
+        Binding[] newBindings = new Binding[bindings.length];
+        bool bindingsChanged = false;
+        int index = 0;
+        for (int i = 0; i < bindingCount; i++) {
+            Binding b = bindings[i];
+            if (b is binding) {
+                bindingsChanged = true;
+            } else {
+                newBindings[index++] = b;
+            }
+        }
+
+        if (bindingsChanged) {
+            this.bindings = newBindings;
+            bindingCount = index;
+            clearCache();
+        }
+    }
+
+    /**
+     * <p>
+     * Removes a listener from this binding manager.
+     * </p>
+     * <p>
+     * This method completes in amortized <code>O(1)</code>.
+     * </p>
+     *
+     * @param listener
+     *            The listener to be removed; must not be <code>null</code>.
+     */
+    public final void removeBindingManagerListener(
+            IBindingManagerListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * <p>
+     * Removes any binding that matches the given values -- regardless of
+     * command identifier.
+     * </p>
+     * <p>
+     * This method completes in <code>O(n)</code>, where <code>n</code> is
+     * the number of bindings.
+     * </p>
+     *
+     * @param sequence
+     *            The sequence to match; may be <code>null</code>.
+     * @param schemeId
+     *            The scheme id to match; may be <code>null</code>.
+     * @param contextId
+     *            The context id to match; may be <code>null</code>.
+     * @param locale
+     *            The locale to match; may be <code>null</code>.
+     * @param platform
+     *            The platform to match; may be <code>null</code>.
+     * @param windowManager
+     *            The window manager to match; may be <code>null</code>. TODO
+     *            Currently ignored.
+     * @param type
+     *            The type to look for.
+     *
+     */
+    public final void removeBindings(TriggerSequence sequence,
+            String schemeId, String contextId, String locale,
+            String platform, String windowManager, int type) {
+        if ((bindings is null) || (bindingCount < 1)) {
+            return;
+        }
+
+        Binding[] newBindings = new Binding[bindings.length];
+        bool bindingsChanged = false;
+        int index = 0;
+        for (int i = 0; i < bindingCount; i++) {
+            Binding binding = bindings[i];
+            bool equals = true;
+            equals &= Util.opEquals(sequence, binding.getTriggerSequence());
+            equals &= Util.opEquals(schemeId, binding.getSchemeId());
+            equals &= Util.opEquals(contextId, binding.getContextId());
+            equals &= Util.opEquals(locale, binding.getLocale());
+            equals &= Util.opEquals(platform, binding.getPlatform());
+            equals &= (type is binding.getType());
+            if (equals) {
+                bindingsChanged = true;
+            } else {
+                newBindings[index++] = binding;
+            }
+        }
+
+        if (bindingsChanged) {
+            this.bindings = newBindings;
+            bindingCount = index;
+            clearCache();
+        }
+    }
+
+    /**
+     * <p>
+     * Attempts to remove deletion markers from the collection of bindings.
+     * </p>
+     * <p>
+     * This method completes in <code>O(n)</code>, where <code>n</code> is
+     * the number of bindings.
+     * </p>
+     *
+     * @param bindings
+     *            The bindings from which the deleted items should be removed.
+     *            This array should not be <code>null</code>, but may be
+     *            empty.
+     * @return The array of bindings with the deletions removed; never
+     *         <code>null</code>, but may be empty. Contains only instances
+     *         of <code>Binding</code>.
+     */
+    private final Binding[] removeDeletions(Binding[] bindings) {
+        auto deletions = new HashMap;
+        Binding[] bindingsCopy = new Binding[bindingCount];
+        System.arraycopy(bindings, 0, bindingsCopy, 0, bindingCount);
+        int deletedCount = 0;
+
+        // Extract the deletions.
+        for (int i = 0; i < bindingCount; i++) {
+            Binding binding = bindingsCopy[i];
+            if ((binding.getParameterizedCommand() is null)
+                    && (localeMatches(binding)) && (platformMatches(binding))) {
+                TriggerSequence sequence = binding.getTriggerSequence();
+                Object currentValue = deletions.get(sequence);
+                if (cast(Binding)currentValue ) {
+                    Collection collection = new ArrayList;
+                    collection.add(currentValue);
+                    collection.add(binding);
+                    deletions.put(sequence, cast(Object)collection);
+                } else if ( auto collection = cast(Collection)currentValue ) {
+                    collection.add(binding);
+                } else {
+                    deletions.put(sequence, binding);
+                }
+                bindingsCopy[i] = null;
+                deletedCount++;
+            }
+        }
+
+        if (DEBUG) {
+            Tracing.printTrace("BINDINGS", Format("There are {} deletion markers", deletions.size()) //$NON-NLS-1$ //$NON-NLS-2$
+                    ); //$NON-NLS-1$
+        }
+
+        // Remove the deleted items.
+        for (int i = 0; i < bindingCount; i++) {
+            Binding binding = bindingsCopy[i];
+            if (binding !is null) {
+                Object deletion = deletions.get(binding
+                        .getTriggerSequence());
+                if (cast(Binding)deletion ) {
+                    if ((cast(Binding) deletion).deletes(binding)) {
+                        bindingsCopy[i] = null;
+                        deletedCount++;
+                    }
+
+                } else if (cast(Collection)deletion ) {
+                    Collection collection = cast(Collection) deletion;
+                    Iterator iterator = collection.iterator();
+                    while (iterator.hasNext()) {
+                        Object deletionBinding = iterator.next();
+                        if (cast(Binding)deletionBinding ) {
+                            if ((cast(Binding) deletionBinding).deletes(binding)) {
+                                bindingsCopy[i] = null;
+                                deletedCount++;
+                                break;
+                            }
+                        }
+                    }
+
+                }
+            }
+        }
+
+        // Compact the array.
+        Binding[] returnValue = new Binding[bindingCount - deletedCount];
+        int index = 0;
+        for (int i = 0; i < bindingCount; i++) {
+            Binding binding = bindingsCopy[i];
+            if (binding !is null) {
+                returnValue[index++] = binding;
+            }
+        }
+
+        return returnValue;
+    }
+
+    /**
+     * <p>
+     * Attempts to resolve the conflicts for the given bindings.
+     * </p>
+     * <p>
+     * This method completes in <code>O(n)</code>, where <code>n</code> is
+     * the number of bindings.
+     * </p>
+     *
+     * @param bindings
+     *            The bindings which all match the same trigger sequence; must
+     *            not be <code>null</code>, and should contain at least two
+     *            items. This collection should only contain instances of
+     *            <code>Binding</code> (i.e., no <code>null</code> values).
+     * @param activeContextTree
+     *            The tree of contexts to be used for all of the comparison. All
+     *            of the keys should be active context identifiers (i.e., never
+     *            <code>null</code>). The values will be their parents (i.e.,
+     *            possibly <code>null</code>). Both keys and values are
+     *            context identifiers (<code>String</code>). This map should
+     *            never be empty, and must never be <code>null</code>.
+     * @return The binding which best matches the current state. If there is a
+     *         tie, then return <code>null</code>.
+     */
+    private final Binding resolveConflicts(Collection bindings,
+            Map activeContextTree) {
+        /*
+         * This flag is used to indicate when the bestMatch binding conflicts
+         * with another binding. We keep the best match binding so that we know
+         * if we find a better binding. However, if we don't find a better
+         * binding, then we known to return null.
+         */
+        bool conflict = false;
+
+        Iterator bindingItr = bindings.iterator();
+        Binding bestMatch = cast(Binding) bindingItr.next();
+
+        /*
+         * Iterate over each binding and compare it with the best match. If a
+         * better match is found, then replace the best match and set the
+         * conflict flag to false. If a conflict is found, then leave the best
+         * match and set the conflict flag. Otherwise, just continue.
+         */
+        while (bindingItr.hasNext()) {
+            Binding current = cast(Binding) bindingItr.next();
+
+            /*
+             * SCHEME: Test whether the current is in a child scheme. Bindings
+             * defined in a child scheme will always take priority over bindings
+             * defined in a parent scheme.
+             */
+            String currentSchemeId = current.getSchemeId();
+            String bestSchemeId = bestMatch.getSchemeId();
+            int compareTo = compareSchemes(bestSchemeId, currentSchemeId);
+            if (compareTo > 0) {
+                bestMatch = current;
+                conflict = false;
+            }
+            if (compareTo !is 0) {
+                continue;
+            }
+
+            /*
+             * CONTEXTS: Check for context superiority. Bindings defined in a
+             * child context will take priority over bindings defined in a
+             * parent context -- assuming that the schemes lead to a conflict.
+             */
+            String currentContext = current.getContextId();
+            String bestContext = bestMatch.getContextId();
+            if (!currentContext.equals(bestContext)) {
+                bool goToNextBinding = false;
+
+                // Ascend the current's context tree.
+                String contextPointer = currentContext;
+                while (contextPointer !is null) {
+                    if (contextPointer.equals(bestContext)) {
+                        // the current wins
+                        bestMatch = current;
+                        conflict = false;
+                        goToNextBinding = true;
+                        break;
+                    }
+                    contextPointer = stringcast(activeContextTree
+                            .get(stringcast(contextPointer)));
+                }
+
+                // Ascend the best match's context tree.
+                contextPointer = bestContext;
+                while (contextPointer !is null) {
+                    if (contextPointer.equals(currentContext)) {
+                        // the best wins
+                        goToNextBinding = true;
+                        break;
+                    }
+                    contextPointer = stringcast( activeContextTree
+                            .get(stringcast(contextPointer)));
+                }
+
+                if (goToNextBinding) {
+                    continue;
+                }
+            }
+
+            /*
+             * TYPE: Test for type superiority.
+             */
+            if (current.getType() > bestMatch.getType()) {
+                bestMatch = current;
+                conflict = false;
+                continue;
+            } else if (bestMatch.getType() > current.getType()) {
+                continue;
+            }
+
+            // We could not resolve the conflict between these two.
+            conflict = true;
+        }
+
+        // If the best match represents a conflict, then return null.
+        if (conflict) {
+            return null;
+        }
+
+        // Otherwise, we have a winner....
+        return bestMatch;
+    }
+
+    /**
+     * <p>
+     * Notifies this manager that a scheme has changed. This method is intended
+     * for internal use only.
+     * </p>
+     * <p>
+     * This method calls out to listeners, and so the time it takes to complete
+     * is dependent on third-party code.
+     * </p>
+     *
+     * @param schemeEvent
+     *            An event describing the change in the scheme.
+     */
+    public final void schemeChanged(SchemeEvent schemeEvent) {
+        if (schemeEvent.isDefinedChanged()) {
+            Scheme scheme = schemeEvent.getScheme();
+            bool schemeIdAdded = scheme.isDefined();
+            bool activeSchemeChanged = false;
+            if (schemeIdAdded) {
+                definedHandleObjects.add(scheme);
+            } else {
+                definedHandleObjects.remove(scheme);
+
+                if (activeScheme is scheme) {
+                    activeScheme = null;
+                    activeSchemeIds = null;
+                    activeSchemeChanged = true;
+
+                    // Clear the binding solution.
+                    clearSolution();
+                }
+            }
+
+            if (isListenerAttached()) {
+                fireBindingManagerChanged(new BindingManagerEvent(this, false,
+                        null, activeSchemeChanged, scheme, schemeIdAdded,
+                        false, false));
+            }
+        }
+    }
+
+    /**
+     * Sets the active bindings and the prefix table. This ensures that the two
+     * values change at the same time, and that any listeners are notified
+     * appropriately.
+     *
+     * @param activeBindings
+     *            This is a map of triggers ( <code>TriggerSequence</code>)
+     *            to bindings (<code>Binding</code>). This value will only
+     *            be <code>null</code> if the active bindings have not yet
+     *            been computed. Otherwise, this value may be empty.
+     * @param activeBindingsByCommandId
+     *            This is a map of fully-parameterized commands (<code>ParameterizedCommand</code>)
+     *            to triggers ( <code>TriggerSequence</code>). This value
+     *            will only be <code>null</code> if the active bindings have
+     *            not yet been computed. Otherwise, this value may be empty.
+     * @param prefixTable
+     *            A map of prefixes (<code>TriggerSequence</code>) to a map
+     *            of available completions (possibly <code>null</code>, which
+     *            means there is an exact match). The available completions is a
+     *            map of trigger (<code>TriggerSequence</code>) to binding (<code>Binding</code>).
+     *            This value may be <code>null</code> if there is no existing
+     *            solution.
+     */
+    private final void setActiveBindings(Map activeBindings,
+            Map activeBindingsByCommandId, Map prefixTable,
+            Map conflicts) {
+        this.activeBindings = activeBindings;
+        Map previousBindingsByParameterizedCommand = this.activeBindingsByParameterizedCommand;
+        this.activeBindingsByParameterizedCommand = activeBindingsByCommandId;
+        this.prefixTable = prefixTable;
+        InternalPolicy.currentConflicts = conflicts;
+
+        fireBindingManagerChanged(new BindingManagerEvent(this, true,
+                previousBindingsByParameterizedCommand, false, null, false,
+                false, false));
+    }
+
+    /**
+     * <p>
+     * Selects one of the schemes as the active scheme. This scheme must be
+     * defined.
+     * </p>
+     * <p>
+     * This method completes in <code>O(n)</code>, where <code>n</code> is
+     * the height of the context tree.
+     * </p>
+     *
+     * @param scheme
+     *            The scheme to become active; must not be <code>null</code>.
+     * @throws NotDefinedException
+     *             If the given scheme is currently undefined.
+     */
+    public final void setActiveScheme(Scheme scheme) {
+        if (scheme is null) {
+            throw new NullPointerException("Cannot activate a null scheme"); //$NON-NLS-1$
+        }
+
+        if ((scheme is null) || (!scheme.isDefined())) {
+            throw new NotDefinedException(
+                    "Cannot activate an undefined scheme. " //$NON-NLS-1$
+                            ~ scheme.getId());
+        }
+
+        if (Util.opEquals(activeScheme, scheme)) {
+            return;
+        }
+
+        activeScheme = scheme;
+        activeSchemeIds = getSchemeIds(activeScheme.getId());
+        clearSolution();
+        fireBindingManagerChanged(new BindingManagerEvent(this, false, null,
+                true, null, false, false, false));
+    }
+
+    /**
+     * <p>
+     * Changes the set of bindings for this binding manager. Changing the set of
+     * bindings all at once ensures that: (1) duplicates are removed; and (2)
+     * avoids unnecessary intermediate computations. This method clears the
+     * existing bindings, but does not trigger a recomputation (other method
+     * calls are required to do that).
+     * </p>
+     * <p>
+     * This method completes in <code>O(n)</code>, where <code>n</code> is
+     * the number of bindings.
+     * </p>
+     *
+     * @param bindings
+     *            The new array of bindings; may be <code>null</code>. This
+     *            set is copied into a local data structure.
+     */
+    public final void setBindings(Binding[] bindings) {
+        if (Arrays.equals(this.bindings, bindings)) {
+            return; // nothing has changed
+        }
+
+        if ((bindings is null) || (bindings.length is 0)) {
+            this.bindings = null;
+            bindingCount = 0;
+        } else {
+            int bindingsLength = bindings.length;
+            this.bindings = new Binding[bindingsLength];
+            System.arraycopy(bindings, 0, this.bindings, 0, bindingsLength);
+            bindingCount = bindingsLength;
+        }
+        clearCache();
+    }
+
+    /**
+     * <p>
+     * Changes the locale for this binding manager. The locale can be used to
+     * provide locale-specific bindings. If the locale is different than the
+     * current locale, this will force a recomputation of the bindings. The
+     * locale is in the same format as
+     * <code>Locale.getDefault().toString()</code>.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>.
+     * </p>
+     *
+     * @param locale
+     *            The new locale; must not be <code>null</code>.
+     * @see Locale#getDefault()
+     */
+    public final void setLocale(String locale) {
+        if (locale is null) {
+            throw new NullPointerException("The locale cannot be null"); //$NON-NLS-1$
+        }
+
+        if (!Util.opEquals(this.locale, locale)) {
+            this.locale = locale;
+            this.locales = expand(locale, LOCALE_SEPARATOR);
+            clearSolution();
+            fireBindingManagerChanged(new BindingManagerEvent(this, false,
+                    null, false, null, false, true, false));
+        }
+    }
+
+    /**
+     * <p>
+     * Changes the platform for this binding manager. The platform can be used
+     * to provide platform-specific bindings. If the platform is different than
+     * the current platform, then this will force a recomputation of the
+     * bindings. The locale is in the same format as
+     * <code>SWT.getPlatform()</code>.
+     * </p>
+     * <p>
+     * This method completes in <code>O(1)</code>.
+     * </p>
+     *
+     * @param platform
+     *            The new platform; must not be <code>null</code>.
+     * @see org.eclipse.swt.SWT#getPlatform()
+     */
+    public final void setPlatform(String platform) {
+        if (platform is null) {
+            throw new NullPointerException("The platform cannot be null"); //$NON-NLS-1$
+        }
+
+        if (!Util.opEquals(this.platform, platform)) {
+            this.platform = platform;
+            this.platforms = expand(platform, Util.ZERO_LENGTH_STRING);
+            clearSolution();
+            fireBindingManagerChanged(new BindingManagerEvent(this, false,
+                    null, false, null, false, false, true));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/BindingManagerEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.bindings.BindingManagerEvent;
+
+import org.eclipse.jface.bindings.BindingManager;
+import org.eclipse.jface.bindings.Scheme;
+import org.eclipse.jface.bindings.TriggerSequence;
+
+
+import org.eclipse.core.commands.ParameterizedCommand;
+import org.eclipse.core.commands.common.AbstractBitSetEvent;
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An instance of this class describes changes to an instance of
+ * <code>BindingManager</code>.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see IBindingManagerListener#bindingManagerChanged(BindingManagerEvent)
+ */
+public final class BindingManagerEvent : AbstractBitSetEvent {
+
+    /**
+     * The bit used to represent whether the map of active bindings has changed.
+     */
+    private static const int CHANGED_ACTIVE_BINDINGS = 1;
+
+    /**
+     * The bit used to represent whether the active scheme has changed.
+     */
+    private static const int CHANGED_ACTIVE_SCHEME = 1 << 1;
+
+    /**
+     * The bit used to represent whether the active locale has changed.
+     */
+    private static const int CHANGED_LOCALE = 1 << 2;
+
+    /**
+     * The bit used to represent whether the active platform has changed.
+     */
+    private static const int CHANGED_PLATFORM = 1 << 3;
+
+    /**
+     * The bit used to represent whether the scheme's defined state has changed.
+     */
+    private static const int CHANGED_SCHEME_DEFINED = 1 << 4;
+
+    /**
+     * The binding manager that has changed; this value is never
+     * <code>null</code>.
+     */
+    private const BindingManager manager;
+
+    /**
+     * The map of triggers (<code>Collection</code> of
+     * <code>TriggerSequence</code>) by parameterized command (<code>ParameterizedCommand</code>)
+     * before the change occurred. This map may be empty and it may be
+     * <code>null</code>.
+     */
+    private const Map previousTriggersByParameterizedCommand;
+
+    /**
+     * The scheme that became defined or undefined. This value may be
+     * <code>null</code> if no scheme changed its defined state.
+     */
+    private const Scheme scheme;
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param manager
+     *            the instance of the binding manager that changed; must not be
+     *            <code>null</code>.
+     * @param activeBindingsChanged
+     *            Whether the active bindings have changed.
+     * @param previousTriggersByParameterizedCommand
+     *            The map of triggers (<code>TriggerSequence</code>) by
+     *            fully-parameterized command (<code>ParameterizedCommand</code>)
+     *            before the change occured. This map may be <code>null</code>
+     *            or empty.
+     * @param activeSchemeChanged
+     *            true, iff the active scheme changed.
+     * @param scheme
+     *            The scheme that became defined or undefined; <code>null</code>
+     *            if no scheme changed state.
+     * @param schemeDefined
+     *            <code>true</code> if the given scheme became defined;
+     *            <code>false</code> otherwise.
+     * @param localeChanged
+     *            <code>true</code> iff the active locale changed
+     * @param platformChanged
+     *            <code>true</code> iff the active platform changed
+     */
+    public this(BindingManager manager,
+            bool activeBindingsChanged,
+            Map previousTriggersByParameterizedCommand,
+            bool activeSchemeChanged, Scheme scheme,
+            bool schemeDefined, bool localeChanged,
+            bool platformChanged) {
+        if (manager is null) {
+            throw new NullPointerException(
+                    "A binding manager event needs a binding manager"); //$NON-NLS-1$
+        }
+        this.manager = manager;
+
+        if (schemeDefined && (scheme is null)) {
+            throw new NullPointerException(
+                    "If a scheme changed defined state, then there should be a scheme identifier"); //$NON-NLS-1$
+        }
+        this.scheme = scheme;
+
+        this.previousTriggersByParameterizedCommand = previousTriggersByParameterizedCommand;
+
+        if (activeBindingsChanged) {
+            changedValues |= CHANGED_ACTIVE_BINDINGS;
+        }
+        if (activeSchemeChanged) {
+            changedValues |= CHANGED_ACTIVE_SCHEME;
+        }
+        if (localeChanged) {
+            changedValues |= CHANGED_LOCALE;
+        }
+        if (platformChanged) {
+            changedValues |= CHANGED_PLATFORM;
+        }
+        if (schemeDefined) {
+            changedValues |= CHANGED_SCHEME_DEFINED;
+        }
+    }
+
+    /**
+     * Returns the instance of the manager that changed.
+     *
+     * @return the instance of the manager that changed. Guaranteed not to be
+     *         <code>null</code>.
+     */
+    public final BindingManager getManager() {
+        return manager;
+    }
+
+    /**
+     * Returns the scheme that changed.
+     *
+     * @return The changed scheme
+     */
+    public final Scheme getScheme() {
+        return scheme;
+    }
+
+    /**
+     * Returns whether the active bindings have changed.
+     *
+     * @return <code>true</code> if the active bindings have changed;
+     *         <code>false</code> otherwise.
+     */
+    public final bool isActiveBindingsChanged() {
+        return ((changedValues & CHANGED_ACTIVE_BINDINGS) !is 0);
+    }
+
+    /**
+     * Computes whether the active bindings have changed for a given command
+     * identifier.
+     *
+     * @param parameterizedCommand
+     *            The fully-parameterized command whose bindings might have
+     *            changed; must not be <code>null</code>.
+     * @return <code>true</code> if the active bindings have changed for the
+     *         given command identifier; <code>false</code> otherwise.
+     */
+    public final bool isActiveBindingsChangedFor(
+            ParameterizedCommand parameterizedCommand) {
+        TriggerSequence[] currentBindings = manager
+                .getActiveBindingsFor(parameterizedCommand);
+        TriggerSequence[] previousBindings;
+        if (previousTriggersByParameterizedCommand !is null) {
+            Collection previousBindingCollection = cast(Collection) previousTriggersByParameterizedCommand
+                    .get(parameterizedCommand);
+            if (previousBindingCollection is null) {
+                previousBindings = null;
+            } else {
+                previousBindings = cast(TriggerSequence[])previousBindingCollection.toArray();
+            }
+        } else {
+            previousBindings = null;
+        }
+
+        return !Util.opEquals(currentBindings, previousBindings);
+    }
+
+    /**
+     * Returns whether or not the active scheme changed.
+     *
+     * @return true, iff the active scheme property changed.
+     */
+    public bool isActiveSchemeChanged() {
+        return ((changedValues & CHANGED_ACTIVE_SCHEME) !is 0);
+    }
+
+    /**
+     * Returns whether the locale has changed
+     *
+     * @return <code>true</code> if the locale changed; <code>false</code>
+     *         otherwise.
+     */
+    public bool isLocaleChanged() {
+        return ((changedValues & CHANGED_LOCALE) !is 0);
+    }
+
+    /**
+     * Returns whether the platform has changed
+     *
+     * @return <code>true</code> if the platform changed; <code>false</code>
+     *         otherwise.
+     */
+    public bool isPlatformChanged() {
+        return ((changedValues & CHANGED_PLATFORM) !is 0);
+    }
+
+    /**
+     * Returns whether the list of defined scheme identifiers has changed.
+     *
+     * @return <code>true</code> if the list of scheme identifiers has
+     *         changed; <code>false</code> otherwise.
+     */
+    public final bool isSchemeChanged() {
+        return (scheme !is null);
+    }
+
+    /**
+     * Returns whether or not the scheme became defined
+     *
+     * @return <code>true</code> if the scheme became defined.
+     */
+    public final bool isSchemeDefined() {
+        return (((changedValues & CHANGED_SCHEME_DEFINED) !is 0) && (scheme !is null));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/CachedBindingSet.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,374 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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 org.eclipse.jface.bindings.CachedBindingSet;
+
+import org.eclipse.jface.bindings.TriggerSequence;
+import org.eclipse.jface.bindings.Binding;
+
+
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * <p>
+ * A resolution of bindings for a given state. To see if we already have a
+ * cached binding set, just create one of these binding sets and then look it up
+ * in a map. If it is not already there, then add it and set the cached binding
+ * resolution.
+ * </p>
+ *
+ * @since 3.1
+ */
+final class CachedBindingSet {
+
+    /**
+     * A factor for computing the hash code for all cached binding sets.
+     */
+    private const static int HASH_FACTOR = 89;
+
+    /**
+     * <p>
+     * A representation of the tree of active contexts at the time this cached
+     * binding set was computed. It is a map of context id (<code>String</code>)
+     * to context id (<code>String</code>). Each key represents one of the
+     * active contexts or one of its ancestors, while each value represents its
+     * parent. This is a way of perserving information about what the hierarchy
+     * looked like.
+     * </p>
+     * <p>
+     * This value will be <code>null</code> if the contexts were disregarded
+     * in the computation. It may also be empty. All of the keys are guaranteed
+     * to be non- <code>null</code>, but the values can be <code>null</code>
+     * (i.e., no parent).
+     * </p>
+     */
+    private const Map activeContextTree;
+
+    /**
+     * The map representing the resolved state of the bindings. This is a map of
+     * a trigger (<code>TriggerSequence</code>) to binding (<code>Binding</code>).
+     * This value may be <code>null</code> if it has not yet been initialized.
+     */
+    private Map bindingsByTrigger = null;
+
+    /**
+     * A map of triggers to collections of bindings. If this binding set
+     * contains conflicts, they are logged here.
+     *
+     * @since 3.3
+     */
+    private Map conflictsByTrigger = null;
+
+    /**
+     * The hash code for this object. This value is computed lazily, and marked
+     * as invalid when one of the values on which it is based changes.
+     */
+    private /+transient+/ int hashCode;
+
+    /**
+     * Whether <code>hashCode</code> still contains a valid value.
+     */
+    private /+transient+/ bool hashCodeComputed = false;
+
+    /**
+     * <p>
+     * The list of locales that were active at the time this binding set was
+     * computed. This list starts with the most specific representation of the
+     * locale, and moves to more general representations. For example, this
+     * array might look like ["en_US", "en", "", null].
+     * </p>
+     * <p>
+     * This value will never be <code>null</code>, and it will never be
+     * empty. It must contain at least one element, but its elements can be
+     * <code>null</code>.
+     * </p>
+     */
+    private const String[] locales;
+
+    /**
+     * <p>
+     * The list of platforms that were active at the time this binding set was
+     * computed. This list starts with the most specific representation of the
+     * platform, and moves to more general representations. For example, this
+     * array might look like ["gtk", "", null].
+     * </p>
+     * <p>
+     * This value will never be <code>null</code>, and it will never be
+     * empty. It must contain at least one element, but its elements can be
+     * <code>null</code>.
+     * </p>
+     */
+    private const String[] platforms;
+
+    /**
+     * A map of prefixes (<code>TriggerSequence</code>) to a map of
+     * available completions (possibly <code>null</code>, which means there
+     * is an exact match). The available completions is a map of trigger (<code>TriggerSequence</code>)
+     * to command identifier (<code>String</code>). This value is
+     * <code>null</code> if it has not yet been initialized.
+     */
+    private Map prefixTable = null;
+
+    /**
+     * <p>
+     * The list of schemes that were active at the time this binding set was
+     * computed. This list starts with the active scheme, and then continues
+     * with all of its ancestors -- in order. For example, this might look like
+     * ["emacs", "default"].
+     * </p>
+     * <p>
+     * This value will never be <code>null</code>, and it will never be
+     * empty. It must contain at least one element. Its elements cannot be
+     * <code>null</code>.
+     * </p>
+     */
+    private const String[] schemeIds;
+
+    /**
+     * The map representing the resolved state of the bindings. This is a map of
+     * a command id (<code>String</code>) to triggers (<code>Collection</code>
+     * of <code>TriggerSequence</code>). This value may be <code>null</code>
+     * if it has not yet been initialized.
+     */
+    private Map triggersByCommandId = null;
+
+    /**
+     * Constructs a new instance of <code>CachedBindingSet</code>.
+     *
+     * @param activeContextTree
+     *            The set of context identifiers that were active when this
+     *            binding set was calculated; may be empty. If it is
+     *            <code>null</code>, then the contexts were disregarded in
+     *            the computation. This is a map of context id (
+     *            <code>String</code>) to parent context id (
+     *            <code>String</code>). This is a way of caching the look of
+     *            the context tree at the time the binding set was computed.
+     * @param locales
+     *            The locales that were active when this binding set was
+     *            calculated. The first element is the currently active locale,
+     *            and it is followed by increasingly more general locales. This
+     *            must not be <code>null</code> and must contain at least one
+     *            element. The elements can be <code>null</code>, though.
+     * @param platforms
+     *            The platform that were active when this binding set was
+     *            calculated. The first element is the currently active
+     *            platform, and it is followed by increasingly more general
+     *            platforms. This must not be <code>null</code> and must
+     *            contain at least one element. The elements can be
+     *            <code>null</code>, though.
+     * @param schemeIds
+     *            The scheme that was active when this binding set was
+     *            calculated, followed by its ancestors. This may be
+     *            <code>null</code or empty. The
+     *            elements cannot be <code>null</code>.
+     */
+    this(Map activeContextTree, String[] locales,
+            String[] platforms, String[] schemeIds) {
+        if (locales is null) {
+            throw new NullPointerException("The locales cannot be null."); //$NON-NLS-1$
+        }
+
+        if (locales.length is 0) {
+            throw new NullPointerException("The locales cannot be empty."); //$NON-NLS-1$
+        }
+
+        if (platforms is null) {
+            throw new NullPointerException("The platforms cannot be null."); //$NON-NLS-1$
+        }
+
+        if (platforms.length is 0) {
+            throw new NullPointerException("The platforms cannot be empty."); //$NON-NLS-1$
+        }
+
+        this.activeContextTree = activeContextTree;
+        this.locales = locales;
+        this.platforms = platforms;
+        this.schemeIds = schemeIds;
+    }
+
+    /**
+     * Compares this binding set with another object. The objects will be equal
+     * if they are both instance of <code>CachedBindingSet</code> and have
+     * equivalent values for all of their properties.
+     *
+     * @param object
+     *            The object with which to compare; may be <code>null</code>.
+     * @return <code>true</code> if they are both instances of
+     *         <code>CachedBindingSet</code> and have the same values for all
+     *         of their properties; <code>false</code> otherwise.
+     */
+    public final override int opEquals(Object object) {
+        if (!(cast(CachedBindingSet)object )) {
+            return false;
+        }
+
+        CachedBindingSet other = cast(CachedBindingSet) object;
+
+        if (!Util.opEquals(cast(Object)activeContextTree, cast(Object)other.activeContextTree)) {
+            return false;
+        }
+        if (!Util.opEquals(locales, other.locales)) {
+            return false;
+        }
+        if (!Util.opEquals(platforms, other.platforms)) {
+            return false;
+        }
+        return Util.opEquals(schemeIds, other.schemeIds);
+    }
+
+    /**
+     * Returns the map of command identifiers indexed by trigger sequence.
+     *
+     * @return A map of triggers (<code>TriggerSequence</code>) to bindings (<code>Binding</code>).
+     *         This value may be <code>null</code> if this was not yet
+     *         initialized.
+     */
+    final Map getBindingsByTrigger() {
+        return bindingsByTrigger;
+    }
+
+    /**
+     * Returns a map of conflicts for this set of contexts.
+     *
+     * @return A map of trigger to a collection of Bindings. May be
+     *         <code>null</code>.
+     * @since 3.3
+     */
+    final Map getConflictsByTrigger() {
+        return conflictsByTrigger;
+    }
+
+    /**
+     * Returns the map of prefixes to a map of trigger sequence to command
+     * identifiers.
+     *
+     * @return A map of prefixes (<code>TriggerSequence</code>) to a map of
+     *         available completions (possibly <code>null</code>, which means
+     *         there is an exact match). The available completions is a map of
+     *         trigger (<code>TriggerSequence</code>) to command identifier (<code>String</code>).
+     *         This value may be <code>null</code> if it has not yet been
+     *         initialized.
+     */
+    final Map getPrefixTable() {
+        return prefixTable;
+    }
+
+    /**
+     * Returns the map of triggers indexed by command identifiers.
+     *
+     * @return A map of command identifiers (<code>String</code>) to
+     *         triggers (<code>Collection</code> of
+     *         <code>TriggerSequence</code>). This value may be
+     *         <code>null</code> if this was not yet initialized.
+     */
+    final Map getTriggersByCommandId() {
+        return triggersByCommandId;
+    }
+
+    /**
+     * Computes the hash code for this cached binding set. The hash code is
+     * based only on the immutable values. This allows the set to be created and
+     * checked for in a hashed collection <em>before</em> doing any
+     * computation.
+     *
+     * @return The hash code for this cached binding set.
+     */
+    public final override hash_t toHash() {
+        if (!hashCodeComputed) {
+
+            auto HASH_INITIAL = java.lang.all.toHash(CachedBindingSet.classinfo.name );
+            hashCode = HASH_INITIAL;
+            hashCode = hashCode * HASH_FACTOR
+                    + Util.toHash(cast(Object)activeContextTree);
+            hashCode = hashCode * HASH_FACTOR + Util.toHash(locales);
+            hashCode = hashCode * HASH_FACTOR + Util.toHash(platforms);
+            hashCode = hashCode * HASH_FACTOR + Util.toHash(schemeIds);
+            hashCodeComputed = true;
+        }
+
+        return hashCode;
+    }
+
+    /**
+     * Sets the map of command identifiers indexed by trigger.
+     *
+     * @param commandIdsByTrigger
+     *            The map to set; must not be <code>null</code>. This is a
+     *            map of triggers (<code>TriggerSequence</code>) to binding (<code>Binding</code>).
+     */
+    final void setBindingsByTrigger(Map commandIdsByTrigger) {
+        if (commandIdsByTrigger is null) {
+            throw new NullPointerException(
+                    "Cannot set a null binding resolution"); //$NON-NLS-1$
+        }
+
+        this.bindingsByTrigger = commandIdsByTrigger;
+    }
+
+    /**
+     * Sets the map of conflicting bindings by trigger.
+     *
+     * @param conflicts
+     *            The map to set; must not be <code>null</code>.
+     * @since 3.3
+     */
+    final void setConflictsByTrigger(Map conflicts) {
+        if (conflicts is null) {
+            throw new NullPointerException(
+                    "Cannot set a null binding conflicts"); //$NON-NLS-1$
+        }
+        conflictsByTrigger = conflicts;
+    }
+
+    /**
+     * Sets the map of prefixes to a map of trigger sequence to command
+     * identifiers.
+     *
+     * @param prefixTable
+     *            A map of prefixes (<code>TriggerSequence</code>) to a map
+     *            of available completions (possibly <code>null</code>, which
+     *            means there is an exact match). The available completions is a
+     *            map of trigger (<code>TriggerSequence</code>) to command
+     *            identifier (<code>String</code>). Must not be
+     *            <code>null</code>.
+     */
+    final void setPrefixTable(Map prefixTable) {
+        if (prefixTable is null) {
+            throw new NullPointerException("Cannot set a null prefix table"); //$NON-NLS-1$
+        }
+
+        this.prefixTable = prefixTable;
+    }
+
+    /**
+     * Sets the map of triggers indexed by command identifiers.
+     *
+     * @param triggersByCommandId
+     *            The map to set; must not be <code>null</code>. This is a
+     *            map of command identifiers (<code>String</code>) to
+     *            triggers (<code>Collection</code> of
+     *            <code>TriggerSequence</code>).
+     */
+    final void setTriggersByCommandId(Map triggersByCommandId) {
+        if (triggersByCommandId is null) {
+            throw new NullPointerException(
+                    "Cannot set a null binding resolution"); //$NON-NLS-1$
+        }
+
+        this.triggersByCommandId = triggersByCommandId;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/IBindingManagerListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.bindings.IBindingManagerListener;
+
+import org.eclipse.jface.bindings.BindingManagerEvent;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * <p>
+ * An instance of <code>BindingManagerListener</code> can be used by clients to
+ * receive notification of changes to an instance of
+ * <code>BindingManager</code>.
+ * </p>
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see BindingManager#addBindingManagerListener(IBindingManagerListener)
+ * @see org.eclipse.jface.bindings.BindingManager#addBindingManagerListener(IBindingManagerListener)
+ * @see BindingManagerEvent
+ */
+public interface IBindingManagerListener {
+
+    /**
+     * Notifies that attributes inside an instance of <code>BindingManager</code> have changed.
+     * Specific details are described in the <code>BindingManagerEvent</code>.  Changes in the
+     * binding manager can cause the set of defined or active schemes or bindings to change.
+     *
+     * @param event
+     *            the binding manager event. Guaranteed not to be <code>null</code>.
+     */
+    void bindingManagerChanged(BindingManagerEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/ISchemeListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.bindings.ISchemeListener;
+
+import org.eclipse.jface.bindings.SchemeEvent;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * An instance of <code>ISchemeListener</code> can be used by clients to
+ * receive notification of changes to one or more instances of
+ * <code>IScheme</code>.
+ * </p>
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see Scheme#addSchemeListener(ISchemeListener)
+ * @see Scheme#removeSchemeListener(ISchemeListener)
+ * @see SchemeEvent
+ */
+public interface ISchemeListener {
+
+    /**
+     * Notifies that one or more attributes of an instance of
+     * <code>IScheme</code> have changed. Specific details are described in
+     * the <code>SchemeEvent</code>.
+     *
+     * @param schemeEvent
+     *            the scheme event. Guaranteed not to be <code>null</code>.
+     */
+    void schemeChanged(SchemeEvent schemeEvent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/Scheme.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,286 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.bindings.Scheme;
+
+import org.eclipse.jface.bindings.ISchemeListener;
+import org.eclipse.jface.bindings.SchemeEvent;
+
+
+import org.eclipse.core.commands.common.NamedHandleObject;
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.HashSet;
+import tango.text.convert.Format;
+
+/**
+ * <p>
+ * An instance of <code>IScheme</code> is a handle representing a binding
+ * scheme as defined by the extension point <code>org.eclipse.ui.bindings</code>.
+ * The identifier of the handle is the identifier of the scheme being represented.
+ * </p>
+ * <p>
+ * An instance of <code>IScheme</code> can be obtained from an instance of
+ * <code>ICommandManager</code> for any identifier, whether or not a scheme
+ * with that identifier is defined in the plugin registry.
+ * </p>
+ * <p>
+ * The handle-based nature of this API allows it to work well with runtime
+ * plugin activation and deactivation. If a scheme is defined, that means that
+ * its corresponding plug-in is active. If the plug-in is then deactivated, the
+ * scheme will still exist but it will be undefined. An attempt to use an
+ * undefined scheme will result in a <code>NotDefinedException</code>
+ * being thrown.
+ * </p>
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see ISchemeListener
+ * @see org.eclipse.core.commands.CommandManager
+ */
+public final class Scheme : NamedHandleObject, Comparable {
+
+    /**
+     * The collection of all objects listening to changes on this scheme. This
+     * value is <code>null</code> if there are no listeners.
+     */
+    private Set listeners = null;
+
+    /**
+     * The parent identifier for this scheme. This is the identifier of the
+     * scheme from which this scheme inherits some of its bindings. This value
+     * can be <code>null</code> if the scheme has no parent.
+     */
+    private String parentId = null;
+
+    /**
+     * Constructs a new instance of <code>Scheme</code> with an identifier.
+     *
+     * @param id
+     *            The identifier to create; must not be <code>null</code>.
+     */
+    this(String id) {
+        super(id);
+    }
+
+    /**
+     * Registers an instance of <code>ISchemeListener</code> to listen for
+     * changes to attributes of this instance.
+     *
+     * @param schemeListener
+     *            the instance of <code>ISchemeListener</code> to register.
+     *            Must not be <code>null</code>. If an attempt is made to
+     *            register an instance of <code>ISchemeListener</code> which
+     *            is already registered with this instance, no operation is
+     *            performed.
+     */
+    public final void addSchemeListener(ISchemeListener schemeListener) {
+        if (schemeListener is null) {
+            throw new NullPointerException("Can't add a null scheme listener."); //$NON-NLS-1$
+        }
+
+        if (listeners is null) {
+            listeners = new HashSet();
+        }
+
+        listeners.add(cast(Object)schemeListener);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Comparable#compareTo(java.lang.Object)
+     */
+    public final int compareTo(Object object) {
+        Scheme scheme = cast(Scheme) object;
+        int compareTo = Util.compare(this.id, scheme.id);
+        if (compareTo is 0) {
+            compareTo = Util.compare(this.name, scheme.name);
+            if (compareTo is 0) {
+                compareTo = Util.compare(this.parentId, scheme.parentId);
+                if (compareTo is 0) {
+                    compareTo = Util.compare(this.description,
+                            scheme.description);
+                    if (compareTo is 0) {
+                        compareTo = Util.compare(this.defined, scheme.defined);
+                    }
+                }
+            }
+        }
+
+        return compareTo;
+    }
+
+    /**
+     * <p>
+     * Defines this scheme by giving it a name, and possibly a description and a
+     * parent identifier as well. The defined property for the scheme automatically
+     * becomes <code>true</code>.
+     * </p>
+     * <p>
+     * Notification is sent to all listeners that something has changed.
+     * </p>
+     *
+     * @param name
+     *            The name of this scheme; must not be <code>null</code>.
+     * @param description
+     *            The description for this scheme; may be <code>null</code>.
+     * @param parentId
+     *            The parent identifier for this scheme; may be
+     *            <code>null</code>.
+     */
+    public final void define(String name, String description,
+            String parentId) {
+        if (name is null) {
+            throw new NullPointerException(
+                    "The name of a scheme cannot be null"); //$NON-NLS-1$
+        }
+
+        bool definedChanged = !this.defined;
+        this.defined = true;
+
+        bool nameChanged = !Util.opEquals(this.name, name);
+        this.name = name;
+
+        bool descriptionChanged = !Util.opEquals(this.description,
+                description);
+        this.description = description;
+
+        bool parentIdChanged = !Util.opEquals(this.parentId, parentId);
+        this.parentId = parentId;
+
+        fireSchemeChanged(new SchemeEvent(this, definedChanged, nameChanged,
+                descriptionChanged, parentIdChanged));
+    }
+
+    /**
+     * Notifies all listeners that this scheme has changed. This sends the given
+     * event to all of the listeners, if any.
+     *
+     * @param event
+     *            The event to send to the listeners; must not be
+     *            <code>null</code>.
+     */
+    private final void fireSchemeChanged(SchemeEvent event) {
+        if (event is null) {
+            throw new NullPointerException(
+                    "Cannot send a null event to listeners."); //$NON-NLS-1$
+        }
+
+        if (listeners is null) {
+            return;
+        }
+
+        Iterator listenerItr = listeners.iterator();
+        while (listenerItr.hasNext()) {
+            final ISchemeListener listener = cast(ISchemeListener) listenerItr
+                    .next();
+            listener.schemeChanged(event);
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the identifier of the parent of the scheme represented by this
+     * handle.
+     * </p>
+     * <p>
+     * Notification is sent to all registered listeners if this attribute
+     * changes.
+     * </p>
+     *
+     * @return the identifier of the parent of the scheme represented by this
+     *         handle. May be <code>null</code>.
+     * @throws NotDefinedException
+     *             if the scheme represented by this handle is not defined.
+     */
+    public final String getParentId() {
+        if (!defined) {
+            throw new NotDefinedException(
+                    "Cannot get the parent identifier from an undefined scheme. "  //$NON-NLS-1$
+                    ~ id);
+        }
+
+        return parentId;
+    }
+
+    /**
+     * Unregisters an instance of <code>ISchemeListener</code> listening for
+     * changes to attributes of this instance.
+     *
+     * @param schemeListener
+     *            the instance of <code>ISchemeListener</code> to unregister.
+     *            Must not be <code>null</code>. If an attempt is made to
+     *            unregister an instance of <code>ISchemeListener</code> which
+     *            is not already registered with this instance, no operation is
+     *            performed.
+     */
+    public final void removeSchemeListener(ISchemeListener schemeListener) {
+        if (schemeListener is null) {
+            throw new NullPointerException("Cannot remove a null listener."); //$NON-NLS-1$
+        }
+
+        if (listeners is null) {
+            return;
+        }
+
+        listeners.remove(cast(Object)schemeListener);
+
+        if (listeners.isEmpty()) {
+            listeners = null;
+        }
+    }
+
+    /**
+     * The string representation of this command -- for debugging purposes only.
+     * This string should not be shown to an end user.
+     *
+     * @return The string representation; never <code>null</code>.
+     */
+    public override final String toString() {
+        if (string is null) {
+            string = Format("Scheme({},{},{},{},{})",id,name,description,parentId,defined);
+        }
+        return string;
+    }
+
+    /**
+     * Makes this scheme become undefined. This has the side effect of changing
+     * the name, description and parent identifier to <code>null</code>.
+     * Notification is sent to all listeners.
+     */
+    public override final void undefine() {
+        string = null;
+
+        bool definedChanged = defined;
+        defined = false;
+
+        bool nameChanged = name !is null;
+        name = null;
+
+        bool descriptionChanged = description !is null;
+        description = null;
+
+        bool parentIdChanged = parentId !is null;
+        parentId = null;
+
+        fireSchemeChanged(new SchemeEvent(this, definedChanged, nameChanged,
+                descriptionChanged, parentIdChanged));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/SchemeEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.bindings.SchemeEvent;
+
+import org.eclipse.jface.bindings.Scheme;
+
+import org.eclipse.core.commands.common.AbstractNamedHandleEvent;
+
+import java.lang.all;
+
+/**
+ * An instance of this class describes changes to an instance of
+ * <code>IScheme</code>.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see ISchemeListener#schemeChanged(SchemeEvent)
+ */
+public final class SchemeEvent : AbstractNamedHandleEvent {
+
+    /**
+     * The bit used to represent whether the scheme has changed its parent.
+     */
+    private static const int CHANGED_PARENT_ID = LAST_USED_BIT << 1;
+
+    /**
+     * The scheme that has changed; this value is never <code>null</code>.
+     */
+    private const Scheme scheme;
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param scheme
+     *            the instance of the interface that changed; must not be
+     *            <code>null</code>.
+     * @param definedChanged
+     *            true, iff the defined property changed.
+     * @param nameChanged
+     *            true, iff the name property changed.
+     * @param descriptionChanged
+     *            <code>true</code> if the description property changed;
+     *            <code>false</code> otherwise.
+     * @param parentIdChanged
+     *            true, iff the parentId property changed.
+     */
+    public this(Scheme scheme, bool definedChanged,
+            bool nameChanged, bool descriptionChanged,
+            bool parentIdChanged) {
+        super(definedChanged, descriptionChanged, nameChanged);
+
+        if (scheme is null) {
+            throw new NullPointerException();
+        }
+        this.scheme = scheme;
+
+        if (parentIdChanged) {
+            changedValues |= CHANGED_PARENT_ID;
+        }
+    }
+
+    /**
+     * Returns the instance of the scheme that changed.
+     *
+     * @return the instance of the scheme that changed. Guaranteed not to be
+     *         <code>null</code>.
+     */
+    public final Scheme getScheme() {
+        return scheme;
+    }
+
+    /**
+     * Returns whether or not the parentId property changed.
+     *
+     * @return true, iff the parentId property changed.
+     */
+    public final bool isParentIdChanged() {
+        return ((changedValues & CHANGED_PARENT_ID) !is 0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/Trigger.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.bindings.Trigger;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * The abstract class for any object that can be used as a trigger for a binding.
+ * This ensures that trigger conform to certain minimum requirements. Namely, triggers
+ * need to be hashable.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class Trigger : Comparable {
+
+    /**
+     * Tests whether this object is equal to another object. A handle object is
+     * only equal to another trigger with the same properties.
+     *
+     * @param object
+     *            The object with which to compare; may be <code>null</code>.
+     * @return <code>true</code> if the objects are equal; <code>false</code>
+     *         otherwise.
+     */
+    public override abstract int opEquals(Object object);
+
+    /**
+     * Computes the hash code for this object.
+     *
+     * @return The hash code for this object.
+     */
+    public override abstract hash_t toHash();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/TriggerSequence.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,206 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.bindings.TriggerSequence;
+
+import org.eclipse.jface.bindings.Trigger;
+
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * A sequence of one or more triggers. None of these triggers may be
+ * <code>null</code>.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class TriggerSequence {
+
+    /**
+     * The value to see that hash code to if the hash code is not yet computed.
+     */
+    private static const int HASH_CODE_NOT_COMPUTED = -1;
+
+    /**
+     * A factor for computing the hash code for all trigger sequences.
+     */
+    private static const int HASH_FACTOR = 89;
+
+    /**
+     * The hash code for this object. This value is computed lazily, and marked
+     * as invalid when one of the values on which it is based changes.  This
+     * values is <code>HASH_CODE_NOT_COMPUTED</code> iff the hash code has not
+     * yet been computed.
+     */
+    protected /+transient+/ int hashCode = HASH_CODE_NOT_COMPUTED;
+
+    /**
+     * The list of trigger in this sequence. This value is never
+     * <code>null</code>, and never contains <code>null</code> elements.
+     */
+    protected const Trigger[] triggers;
+
+    /**
+     * Constructs a new instance of <code>TriggerSequence</code>.
+     *
+     * @param triggers
+     *            The triggers contained within this sequence; must not be
+     *            <code>null</code> or contain <code>null</code> elements.
+     *            May be empty.
+     */
+    public this(Trigger[] triggers) {
+        /+
+        if (triggers is null) {
+            throw new NullPointerException("The triggers cannot be null"); //$NON-NLS-1$
+        }
+        +/
+
+        for (int i = 0; i < triggers.length; i++) {
+            if (triggers[i] is null) {
+                throw new IllegalArgumentException(
+                        "All triggers in a trigger sequence must be an instance of Trigger"); //$NON-NLS-1$
+            }
+        }
+
+        int triggerLength = triggers.length;
+        this.triggers = new Trigger[triggerLength];
+        System.arraycopy(triggers, 0, this.triggers, 0, triggerLength);
+    }
+
+    /**
+     * Returns whether or not this key sequence ends with the given key
+     * sequence.
+     *
+     * @param triggerSequence
+     *            a trigger sequence. Must not be <code>null</code>.
+     * @param equals
+     *            whether or not an identical trigger sequence should be
+     *            considered as a possible match.
+     * @return <code>true</code>, iff the given trigger sequence ends with
+     *         this trigger sequence.
+     */
+    public final bool endsWith(TriggerSequence triggerSequence,
+            bool equals) {
+        if (triggerSequence is null) {
+            throw new NullPointerException(
+                    "Cannot end with a null trigger sequence"); //$NON-NLS-1$
+        }
+
+        return Util.endsWith(triggers, triggerSequence.triggers, equals);
+    }
+
+    public final override int opEquals(Object object) {
+        // Check if they're the same.
+        if (object is this) {
+            return true;
+        }
+
+        // Check if they're the same type.
+        if (!(cast(TriggerSequence)object )) {
+            return false;
+        }
+
+        TriggerSequence triggerSequence = cast(TriggerSequence) object;
+        return Util.opEquals(triggers, triggerSequence.triggers);
+    }
+
+    /**
+     * Formats this trigger sequence into the current default look.
+     *
+     * @return A string representation for this trigger sequence using the
+     *         default look; never <code>null</code>.
+     */
+    public abstract String format();
+
+    /**
+     * <p>
+     * Returns a list of prefixes for the current sequence. A prefix is any
+     * leading subsequence in a <code>TriggerSequence</code>. A prefix is
+     * also an instance of <code>TriggerSequence</code>.
+     * </p>
+     * <p>
+     * For example, consider a trigger sequence that consists of four triggers:
+     * A, B, C and D. The prefixes would be "", "A", "A B", and "A B C". The
+     * list of prefixes must always be the same as the size of the trigger list.
+     * </p>
+     *
+     * @return The array of possible prefixes for this sequence. This array must
+     *         not be <code>null</code>, but may be empty. It must only
+     *         contains instances of <code>TriggerSequence</code>.
+     */
+    public abstract TriggerSequence[] getPrefixes();
+
+    /**
+     * Returns the list of triggers.
+     *
+     * @return The triggers; never <code>null</code> and guaranteed to only
+     *         contain instances of <code>Trigger</code>.
+     */
+    public final Trigger[] getTriggers() {
+        int triggerLength = triggers.length;
+        Trigger[] triggerCopy = new Trigger[triggerLength];
+        System.arraycopy(triggers, 0, triggerCopy, 0, triggerLength);
+        return triggerCopy;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#hashCode()
+     */
+    public final override hash_t toHash() {
+        if (hashCode is HASH_CODE_NOT_COMPUTED) {
+            auto HASH_INITIAL = java.lang.all.toHash( TriggerSequence.classinfo.name );
+            hashCode = HASH_INITIAL;
+            hashCode = hashCode * HASH_FACTOR + Util.toHash(triggers);
+            if (hashCode is HASH_CODE_NOT_COMPUTED) {
+                hashCode++;
+            }
+        }
+
+        return hashCode;
+    }
+
+    /**
+     * Returns whether or not this trigger sequence is empty.
+     *
+     * @return <code>true</code>, iff the trigger sequence is empty.
+     */
+    public final bool isEmpty() {
+        return (triggers.length is 0);
+    }
+
+    /**
+     * Returns whether or not this trigger sequence starts with the given
+     * trigger sequence.
+     *
+     * @param triggerSequence
+     *            a trigger sequence. Must not be <code>null</code>.
+     * @param equals
+     *            whether or not an identical trigger sequence should be
+     *            considered as a possible match.
+     * @return <code>true</code>, iff the given trigger sequence starts with
+     *         this key sequence.
+     */
+    public final bool startsWith(TriggerSequence triggerSequence,
+            bool equals) {
+        if (triggerSequence is null) {
+            throw new NullPointerException(
+                    "A trigger sequence cannot start with null"); //$NON-NLS-1$
+        }
+
+        return Util.startsWith(triggers, triggerSequence.triggers, equals);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/keys/IKeyLookup.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,461 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.bindings.keys.IKeyLookup;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * A facilitiy for converting the formal representation for key strokes
+ * (i.e., used in persistence) into real key stroke instances.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IKeyLookup {
+    /**
+     * The formal name of the 'Alt' key.
+     */
+    public static const String ALT_NAME = "ALT"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Arrow Down' key.
+     */
+    public static const String ARROW_DOWN_NAME = "ARROW_DOWN"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Arrow Left' key.
+     */
+    public static const String ARROW_LEFT_NAME = "ARROW_LEFT"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Arrow Right' key.
+     */
+    public static const String ARROW_RIGHT_NAME = "ARROW_RIGHT"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Arrow Up' key.
+     */
+    public static const String ARROW_UP_NAME = "ARROW_UP"; //$NON-NLS-1$
+
+    /**
+     * An alternate name for the backspace key.
+     */
+    public static const String BACKSPACE_NAME = "BACKSPACE"; //$NON-NLS-1$
+
+    /**
+     * The formal name for the 'Break' key.
+     */
+    public static const String BREAK_NAME = "BREAK"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the backspace key.
+     */
+    public static const String BS_NAME = "BS"; //$NON-NLS-1$
+
+    /**
+     * The formal name for the 'Caps Lock' key.
+     */
+    public static const String CAPS_LOCK_NAME = "CAPS_LOCK"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Command' key.
+     */
+    public static const String COMMAND_NAME = "COMMAND"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the carriage return (U+000D)
+     */
+    public static const String CR_NAME = "CR"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Ctrl' key.
+     */
+    public static const String CTRL_NAME = "CTRL"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the delete (U+007F) key
+     */
+    public static const String DEL_NAME = "DEL"; //$NON-NLS-1$
+
+    /**
+     * An alternative name for the delete key.
+     */
+    public static const String DELETE_NAME = "DELETE"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'End' key.
+     */
+    public static const String END_NAME = "END"; //$NON-NLS-1$
+
+    /**
+     * An alternative name for the enter key.
+     */
+    public static const String ENTER_NAME = "ENTER"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the escape (U+001B) key.
+     */
+    public static const String ESC_NAME = "ESC"; //$NON-NLS-1$
+
+    /**
+     * An alternative name for the escape key.
+     */
+    public static const String ESCAPE_NAME = "ESCAPE"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'F1' key.
+     */
+    public static const String F1_NAME = "F1"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'F10' key.
+     */
+    public static const String F10_NAME = "F10"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'F11' key.
+     */
+    public static const String F11_NAME = "F11"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'F12' key.
+     */
+    public static const String F12_NAME = "F12"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'F13' key.
+     */
+    public static const String F13_NAME = "F13"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'F14' key.
+     */
+    public static const String F14_NAME = "F14"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'F15' key.
+     */
+    public static const String F15_NAME = "F15"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'F2' key.
+     */
+    public static const String F2_NAME = "F2"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'F3' key.
+     */
+    public static const String F3_NAME = "F3"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'F4' key.
+     */
+    public static const String F4_NAME = "F4"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'F5' key.
+     */
+    public static const String F5_NAME = "F5"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'F6' key.
+     */
+    public static const String F6_NAME = "F6"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'F7' key.
+     */
+    public static const String F7_NAME = "F7"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'F8' key.
+     */
+    public static const String F8_NAME = "F8"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'F9' key.
+     */
+    public static const String F9_NAME = "F9"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the form feed (U+000C) key.
+     */
+    public static const String FF_NAME = "FF"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Home' key.
+     */
+    public static const String HOME_NAME = "HOME"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Insert' key.
+     */
+    public static const String INSERT_NAME = "INSERT"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the line feed (U+000A) key.
+     */
+    public static const String LF_NAME = "LF"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'M1' key.
+     */
+    public static const String M1_NAME = "M1"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'M2' key.
+     */
+    public static const String M2_NAME = "M2"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'M3' key.
+     */
+    public static const String M3_NAME = "M3"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'M4' key.
+     */
+    public static const String M4_NAME = "M4"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the null (U+0000) key.
+     */
+    public static const String NUL_NAME = "NUL"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'NumLock' key.
+     */
+    public static const String NUM_LOCK_NAME = "NUM_LOCK"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the '0' key on the numpad.
+     */
+    public static const String NUMPAD_0_NAME = "NUMPAD_0"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the '1' key on the numpad.
+     */
+    public static const String NUMPAD_1_NAME = "NUMPAD_1"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the '2' key on the numpad.
+     */
+    public static const String NUMPAD_2_NAME = "NUMPAD_2"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the '3' key on the numpad.
+     */
+    public static const String NUMPAD_3_NAME = "NUMPAD_3"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the '4' key on the numpad.
+     */
+    public static const String NUMPAD_4_NAME = "NUMPAD_4"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the '5' key on the numpad.
+     */
+    public static const String NUMPAD_5_NAME = "NUMPAD_5"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the '6' key on the numpad.
+     */
+    public static const String NUMPAD_6_NAME = "NUMPAD_6"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the '7' key on the numpad.
+     */
+    public static const String NUMPAD_7_NAME = "NUMPAD_7"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the '8' key on the numpad.
+     */
+    public static const String NUMPAD_8_NAME = "NUMPAD_8"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the '9' key on the numpad.
+     */
+    public static const String NUMPAD_9_NAME = "NUMPAD_9"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Add' key on the numpad.
+     */
+    public static const String NUMPAD_ADD_NAME = "NUMPAD_ADD"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Decimal' key on the numpad.
+     */
+    public static const String NUMPAD_DECIMAL_NAME = "NUMPAD_DECIMAL"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Divide' key on the numpad.
+     */
+    public static const String NUMPAD_DIVIDE_NAME = "NUMPAD_DIVIDE"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Enter' key on the numpad.
+     */
+    public static const String NUMPAD_ENTER_NAME = "NUMPAD_ENTER"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the '=' key on the numpad.
+     */
+    public static const String NUMPAD_EQUAL_NAME = "NUMPAD_EQUAL"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Multiply' key on the numpad.
+     */
+    public static const String NUMPAD_MULTIPLY_NAME = "NUMPAD_MULTIPLY"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Subtract' key on the numpad.
+     */
+    public static const String NUMPAD_SUBTRACT_NAME = "NUMPAD_SUBTRACT"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Page Down' key.
+     */
+    public static const String PAGE_DOWN_NAME = "PAGE_DOWN"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Page Up' key.
+     */
+    public static const String PAGE_UP_NAME = "PAGE_UP"; //$NON-NLS-1$
+
+    /**
+     * The formal name for the 'Pause' key.
+     */
+    public static const String PAUSE_NAME = "PAUSE"; //$NON-NLS-1$
+
+    /**
+     * The formal name for the 'Print Screen' key.
+     */
+    public static const String PRINT_SCREEN_NAME = "PRINT_SCREEN"; //$NON-NLS-1$
+
+    /**
+     * An alternative name for the enter key.
+     */
+    public static const String RETURN_NAME = "RETURN"; //$NON-NLS-1$
+
+    /**
+     * The formal name for the 'Scroll Lock' key.
+     */
+    public static const String SCROLL_LOCK_NAME = "SCROLL_LOCK"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the 'Shift' key.
+     */
+    public static const String SHIFT_NAME = "SHIFT"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the space (U+0020) key.
+     */
+    public static const String SPACE_NAME = "SPACE"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the tab (U+0009) key.
+     */
+    public static const String TAB_NAME = "TAB"; //$NON-NLS-1$
+
+    /**
+     * The formal name of the vertical tab (U+000B) key.
+     */
+    public static const String VT_NAME = "VT"; //$NON-NLS-1$
+
+    /**
+     * Looks up a single natural key by its formal name, and returns the integer
+     * representation for this natural key
+     *
+     * @param name
+     *            The formal name of the natural key to look-up; must not be
+     *            <code>null</code>.
+     * @return The integer representation of this key. If the natural key cannot
+     *         be found, then this method returns <code>0</code>.
+     */
+    public int formalKeyLookup(String name);
+
+    /**
+     * Looks up a single natural key by its formal name, and returns the integer
+     * representation for this natural key
+     *
+     * @param name
+     *            The formal name of the natural key to look-up; must not be
+     *            <code>null</code>.
+     * @return The integer representation of this key. If the natural key cannot
+     *         be found, then this method returns <code>0</code>.
+     */
+    public Integer formalKeyLookupInteger(String name);
+
+    /**
+     * Looks up a single modifier key by its formal name, and returns the integer
+     * representation for this modifier key
+     *
+     * @param name
+     *            The formal name of the modifier key to look-up; must not be
+     *            <code>null</code>.
+     * @return The integer representation of this key. If the modifier key
+     *         cannot be found, then this method returns <code>0</code>.
+     */
+    public int formalModifierLookup(String name);
+
+    /**
+     * Looks up a key value, and returns the formal string representation for
+     * that key
+     *
+     * @param key
+     *            The key to look-up.
+     * @return The formal string representation of this key. If this key cannot
+     *         be found, then it is simply the character corresponding to that
+     *         integer value.
+     */
+    public String formalNameLookup(int key);
+
+    /**
+     * Returns the integer representation of the ALT key.
+     *
+     * @return The ALT key
+     */
+    public int getAlt();
+
+    /**
+     * Returns the integer representation of the COMMAND key.
+     *
+     * @return The COMMAND key
+     */
+    public int getCommand();
+
+    /**
+     * Returns the integer representation of the CTRL key.
+     *
+     * @return The CTRL key
+     */
+    public int getCtrl();
+
+    /**
+     * Returns the integer representation of the SHIFT key.
+     *
+     * @return The SHIFT key
+     */
+    public int getShift();
+
+    /**
+     * Returns whether the given key is a modifier key.
+     *
+     * @param key
+     *            The integer value of the key to check.
+     * @return <code>true</code> if the key is one of the modifier keys;
+     *         <code>false</code> otherwise.
+     */
+    public bool isModifierKey(int key);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/keys/KeyBinding.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.bindings.keys.KeyBinding;
+
+import org.eclipse.jface.bindings.keys.KeySequence;
+
+import org.eclipse.core.commands.ParameterizedCommand;
+import org.eclipse.jface.bindings.Binding;
+import org.eclipse.jface.bindings.TriggerSequence;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * A keyboard shortcut. This is a binding between some keyboard input and the
+ * triggering of a command. This object is immutable.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class KeyBinding : Binding {
+
+    /**
+     * The key sequence which triggers this binding. This sequence is never
+     * <code>null</code>.
+     */
+    private const KeySequence keySequence;
+
+    /**
+     * Constructs a new instance of <code>KeyBinding</code>.
+     *
+     * @param keySequence
+     *            The key sequence which should trigger this binding. This value
+     *            must not be <code>null</code>. It also must be a complete,
+     *            non-empty key sequence.
+     * @param command
+     *            The parameterized command to which this binding applies; this
+     *            value may be <code>null</code> if the binding is meant to
+     *            "unbind" a previously defined binding.
+     * @param schemeId
+     *            The scheme to which this binding belongs; this value must not
+     *            be <code>null</code>.
+     * @param contextId
+     *            The context to which this binding applies; this value must not
+     *            be <code>null</code>.
+     * @param locale
+     *            The locale to which this binding applies; this value may be
+     *            <code>null</code> if it applies to all locales.
+     * @param platform
+     *            The platform to which this binding applies; this value may be
+     *            <code>null</code> if it applies to all platforms.
+     * @param windowManager
+     *            The window manager to which this binding applies; this value
+     *            may be <code>null</code> if it applies to all window
+     *            managers. This value is currently ignored.
+     * @param type
+     *            The type of binding. This should be either <code>SYSTEM</code>
+     *            or <code>USER</code>.
+     */
+    public this(KeySequence keySequence,
+            ParameterizedCommand command, String schemeId,
+            String contextId, String locale, String platform,
+            String windowManager, int type) {
+        super(command, schemeId, contextId, locale, platform, windowManager,
+                type);
+
+        if (keySequence is null) {
+            throw new NullPointerException("The key sequence cannot be null"); //$NON-NLS-1$
+        }
+
+        if (!keySequence.isComplete()) {
+            throw new IllegalArgumentException(
+                    "Cannot bind to an incomplete key sequence"); //$NON-NLS-1$
+        }
+
+        if (keySequence.isEmpty()) {
+            throw new IllegalArgumentException(
+                    "Cannot bind to an empty key sequence"); //$NON-NLS-1$
+        }
+
+        this.keySequence = keySequence;
+    }
+
+    /**
+     * Returns the key sequence which triggers this binding. The key sequence
+     * will not be <code>null</code>, empty or incomplete.
+     *
+     * @return The key sequence; never <code>null</code>.
+     */
+    public final KeySequence getKeySequence() {
+        return keySequence;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.Binding#getTriggerSequence()
+     */
+    public override TriggerSequence getTriggerSequence() {
+        return getKeySequence();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/keys/KeyLookupFactory.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.bindings.keys.KeyLookupFactory;
+
+import org.eclipse.jface.bindings.keys.SWTKeyLookup;
+import org.eclipse.jface.bindings.keys.IKeyLookup;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * <p>
+ * A factory class for <code>ILookup</code> instances. This factory can be
+ * used to retrieve instances of look-ups defined by this package. It also
+ * allows you to define your own look-up for use in the classes.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class KeyLookupFactory {
+
+    /**
+     * The SWT key look-up defined by this package.
+     */
+    private static SWTKeyLookup SWT_KEY_LOOKUP;
+
+    /**
+     * The instance that should be used by <code>KeyStroke</code> in
+     * converting string representations to instances.
+     */
+    private static IKeyLookup defaultLookup;
+
+    private static void check_staticthis(){
+        if( SWT_KEY_LOOKUP is null ){
+            synchronized{
+                if( SWT_KEY_LOOKUP is null ){
+                    SWT_KEY_LOOKUP = new SWTKeyLookup();
+                    defaultLookup = SWT_KEY_LOOKUP;
+                }
+            }
+        }
+    }
+
+    /**
+     * Provides an instance of <code>SWTKeyLookup</code>.
+     *
+     * @return The SWT look-up table for key stroke format information; never
+     *         <code>null</code>.
+     */
+    public static final IKeyLookup getSWTKeyLookup() {
+        check_staticthis();
+        return SWT_KEY_LOOKUP;
+    }
+
+    /**
+     * An accessor for the current default look-up.
+     *
+     * @return The default look-up; never <code>null</code>.
+     */
+    public static final IKeyLookup getDefault() {
+        check_staticthis();
+        return defaultLookup;
+    }
+
+    /**
+     * Sets the default look-up.
+     *
+     * @param defaultLookup
+     *            the default look-up. Must not be <code>null</code>.
+     */
+    public static final void setDefault(IKeyLookup defaultLookup) {
+        check_staticthis();
+        if (defaultLookup is null) {
+            throw new NullPointerException("The look-up must not be null"); //$NON-NLS-1$
+        }
+
+        KeyLookupFactory.defaultLookup = defaultLookup;
+    }
+
+    /**
+     * This class should not be instantiated.
+     */
+    private this() {
+        // Not to be constructred.
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/keys/KeySequence.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,292 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.bindings.keys.KeySequence;
+
+import org.eclipse.jface.bindings.keys.KeyStroke;
+
+
+import org.eclipse.jface.bindings.TriggerSequence;
+import org.eclipse.jface.bindings.keys.formatting.KeyFormatterFactory;
+
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.List;
+import java.util.Set;
+import java.text.ParseException;
+static import tango.text.Util;
+/**
+ * <p>
+ * A <code>KeySequence</code> is defined as a list of zero or more
+ * <code>KeyStrokes</code>, with the stipulation that all
+ * <code>KeyStroke</code> objects must be complete, save for the last one,
+ * whose completeness is optional. A <code>KeySequence</code> is said to be
+ * complete if all of its <code>KeyStroke</code> objects are complete.
+ * </p>
+ * <p>
+ * All <code>KeySequence</code> objects have a formal string representation
+ * available via the <code>toString()</code> method. There are a number of
+ * methods to get instances of <code>KeySequence</code> objects, including one
+ * which can parse this formal string representation.
+ * </p>
+ * <p>
+ * All <code>KeySequence</code> objects, via the <code>format()</code>
+ * method, provide a version of their formal string representation translated by
+ * platform and locale, suitable for display to a user.
+ * </p>
+ * <p>
+ * <code>KeySequence</code> objects are immutable. Clients are not permitted
+ * to extend this class.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class KeySequence : TriggerSequence, Comparable {
+
+    /**
+     * An empty key sequence instance for use by everyone.
+     */
+    private const static KeySequence EMPTY_KEY_SEQUENCE;
+
+    static this(){
+        EMPTY_KEY_SEQUENCE = new KeySequence(null);
+    }
+
+    /**
+     * The delimiter between multiple key strokes in a single key sequence --
+     * expressed in the formal key stroke grammar. This is not to be displayed
+     * to the user. It is only intended as an internal representation.
+     */
+    public const static String KEY_STROKE_DELIMITER = "\u0020"; //$NON-NLS-1$
+
+    /**
+     * The set of delimiters for <code>KeyStroke</code> objects allowed during
+     * parsing of the formal string representation.
+     */
+    public const static String KEY_STROKE_DELIMITERS = KEY_STROKE_DELIMITER
+            ~ "\b\r\u007F\u001B\f\n\0\t\u000B"; //$NON-NLS-1$
+
+    /**
+     * Gets an instance of <code>KeySequence</code>.
+     *
+     * @return a key sequence. This key sequence will have no key strokes.
+     *         Guaranteed not to be <code>null</code>.
+     */
+    public static final KeySequence getInstance() {
+        return EMPTY_KEY_SEQUENCE;
+    }
+
+    /**
+     * Creates an instance of <code>KeySequence</code> given a key sequence
+     * and a key stroke.
+     *
+     * @param keySequence
+     *            a key sequence. Must not be <code>null</code>.
+     * @param keyStroke
+     *            a key stroke. Must not be <code>null</code>.
+     * @return a key sequence that is equal to the given key sequence with the
+     *         given key stroke appended to the end. Guaranteed not to be
+     *         <code>null</code>.
+     */
+    public static final KeySequence getInstance(KeySequence keySequence,
+            KeyStroke keyStroke) {
+        if (keySequence is null || keyStroke is null) {
+            throw new NullPointerException();
+        }
+
+        KeyStroke[] oldKeyStrokes = keySequence.getKeyStrokes();
+        int oldKeyStrokeLength = oldKeyStrokes.length;
+        KeyStroke[] newKeyStrokes = new KeyStroke[oldKeyStrokeLength + 1];
+        System.arraycopy(oldKeyStrokes, 0, newKeyStrokes, 0,
+                        oldKeyStrokeLength);
+        newKeyStrokes[oldKeyStrokeLength] = keyStroke;
+        return new KeySequence(newKeyStrokes);
+    }
+
+    /**
+     * Creates an instance of <code>KeySequence</code> given a single key
+     * stroke.
+     *
+     * @param keyStroke
+     *            a single key stroke. Must not be <code>null</code>.
+     * @return a key sequence. Guaranteed not to be <code>null</code>.
+     */
+    public static final KeySequence getInstance(KeyStroke keyStroke) {
+        return new KeySequence([ keyStroke ]);
+    }
+
+    /**
+     * Creates an instance of <code>KeySequence</code> given an array of key
+     * strokes.
+     *
+     * @param keyStrokes
+     *            the array of key strokes. This array may be empty, but it must
+     *            not be <code>null</code>. This array must not contain
+     *            <code>null</code> elements.
+     * @return a key sequence. Guaranteed not to be <code>null</code>.
+     */
+    public static final KeySequence getInstance(KeyStroke[] keyStrokes) {
+        return new KeySequence(keyStrokes);
+    }
+
+    /**
+     * Creates an instance of <code>KeySequence</code> given a list of key
+     * strokes.
+     *
+     * @param keyStrokes
+     *            the list of key strokes. This list may be empty, but it must
+     *            not be <code>null</code>. If this list is not empty, it
+     *            must only contain instances of <code>KeyStroke</code>.
+     * @return a key sequence. Guaranteed not to be <code>null</code>.
+     */
+    public static final KeySequence getInstance(List keyStrokes) {
+        return new KeySequence(arraycast!(KeyStroke)(keyStrokes.toArray()));
+    }
+
+    /**
+     * Creates an instance of <code>KeySequence</code> by parsing a given
+     * formal string representation.
+     *
+     * @param string
+     *            the formal string representation to parse.
+     * @return a key sequence. Guaranteed not to be <code>null</code>.
+     * @throws ParseException
+     *             if the given formal string representation could not be parsed
+     *             to a valid key sequence.
+     */
+    public static final KeySequence getInstance(String string) {
+        if (string is null) {
+            throw new NullPointerException();
+        }
+
+        try {
+            auto tokens = tango.text.Util.delimit( string, KEY_STROKE_DELIMITERS );
+            KeyStroke[] keyStrokeArray = new KeyStroke[ tokens.length ];
+            foreach( idx, tok; tokens ){
+                keyStrokeArray[idx] = KeyStroke.getInstance(tok);
+            }
+            return new KeySequence(keyStrokeArray);
+        } catch (IllegalArgumentException e) {
+            throw new ParseException(
+                    "Could not construct key sequence with these key strokes: " //$NON-NLS-1$
+                            /+~ keyStrokes+/);
+        } catch (NullPointerException e) {
+            throw new ParseException(
+                    "Could not construct key sequence with these key strokes: " //$NON-NLS-1$
+                            /+~ keyStrokes+/);
+        }
+    }
+
+    /**
+     * Constructs an instance of <code>KeySequence</code> given a list of key
+     * strokes.
+     *
+     * @param keyStrokes
+     *            the list of key strokes. This list may be empty, but it must
+     *            not be <code>null</code>. If this list is not empty, it
+     *            must only contain instances of <code>KeyStroke</code>.
+     */
+    protected this(KeyStroke[] keyStrokes) {
+        super(keyStrokes);
+
+        for (int i = 0; i < cast(int)triggers.length - 1; i++) {
+            KeyStroke keyStroke = cast(KeyStroke) triggers[i];
+
+            if (!keyStroke.isComplete()) {
+                throw new IllegalArgumentException(null);
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#compareTo(java.lang.Object)
+     */
+    public final int compareTo(Object object) {
+        KeySequence castedObject = cast(KeySequence) object;
+        return Util.compare( arraycast!(Comparable)(triggers), arraycast!(Comparable)(castedObject.triggers));
+    }
+
+    /**
+     * Formats this key sequence into the current default look.
+     *
+     * @return A string representation for this key sequence using the default
+     *         look; never <code>null</code>.
+     */
+    public override final String format() {
+        return KeyFormatterFactory.getDefault().format(this);
+    }
+
+    /**
+     * Returns the list of key strokes for this key sequence.
+     *
+     * @return the list of key strokes keys. This list may be empty, but is
+     *         guaranteed not to be <code>null</code>. If this list is not
+     *         empty, it is guaranteed to only contain instances of
+     *         <code>KeyStroke</code>.
+     */
+    public final KeyStroke[] getKeyStrokes() {
+        int triggerLength = triggers.length;
+        KeyStroke[] keyStrokes = new KeyStroke[triggerLength];
+        System.arraycopy(triggers, 0, keyStrokes, 0, triggerLength);
+        return keyStrokes;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.TriggerSequence#getPrefixes()
+     */
+    public override final TriggerSequence[] getPrefixes() {
+        int numberOfPrefixes = triggers.length;
+        TriggerSequence[] prefixes = new TriggerSequence[numberOfPrefixes];
+        prefixes[0] = KeySequence.getInstance();
+        for (int i = 0; i < numberOfPrefixes - 1; i++) {
+            final KeyStroke[] prefixKeyStrokes = new KeyStroke[i + 1];
+            System.arraycopy(triggers, 0, prefixKeyStrokes, 0, i + 1);
+            prefixes[i + 1] = KeySequence.getInstance(prefixKeyStrokes);
+        }
+
+        return prefixes;
+    }
+
+    /**
+     * Returns whether or not this key sequence is complete. Key sequences are
+     * complete iff all of their key strokes are complete.
+     *
+     * @return <code>true</code>, iff the key sequence is complete.
+     */
+    public final bool isComplete() {
+        int triggersLength = triggers.length;
+        for (int i = 0; i < triggersLength; i++) {
+            if (!(cast(KeyStroke) triggers[i]).isComplete()) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns the formal string representation for this key sequence.
+     *
+     * @return The formal string representation for this key sequence.
+     *         Guaranteed not to be <code>null</code>.
+     * @see java.lang.Object#toString()
+     */
+    public override final String toString() {
+        return KeyFormatterFactory.getFormalKeyFormatter().format(this);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/keys/KeySequenceText.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,988 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.bindings.keys.KeySequenceText;
+
+import org.eclipse.jface.bindings.keys.KeyStroke;
+import org.eclipse.jface.bindings.keys.SWTKeySupport;
+import org.eclipse.jface.bindings.keys.KeySequence;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import java.lang.all;
+import java.util.Collections;
+import java.util.Collection;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TreeSet;
+import java.text.ParseException;
+
+/**
+ * <p>
+ * A wrapper around the SWT text widget that traps literal key presses and
+ * converts them into key sequences for display. There are two types of key
+ * strokes that are displayed: complete and incomplete. A complete key stroke is
+ * one with a natural key, while an incomplete one has no natural key.
+ * Incomplete key strokes are only displayed until they are made complete or
+ * their component key presses are released.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class KeySequenceText {
+
+    /**
+     * A key listener that traps incoming events and displays them in the
+     * wrapped text field. It has no effect on traversal operations.
+     */
+    private class KeyTrapListener : Listener {
+        /**
+         * The index at which insertion should occur. This is used if there is a
+         * replacement occurring in the middle of the stroke, and the first key
+         * stroke was incomplete.
+         */
+        private int insertionIndex = -1;
+
+        /**
+         * Resets the insertion index to point nowhere. In other words, it is
+         * set to <code>-1</code>.
+         */
+        void clearInsertionIndex() {
+            insertionIndex = -1;
+        }
+
+        /**
+         * Deletes the current selection. If there is no selection, then it
+         * deletes the last key stroke.
+         *
+         * @param keyStrokes
+         *            The key strokes from which to delete. This list must not
+         *            be <code>null</code>, and must represent a valid key
+         *            sequence.
+         * @return An array of keystrokes minus the keystrokes that were
+         *         deleted.
+         */
+        private final KeyStroke[] deleteKeyStroke(KeyStroke[] keyStrokes) {
+            clearInsertionIndex();
+
+            if (hasSelection()) {
+                /*
+                 * Delete the current selection -- disallowing incomplete
+                 * strokes in the middle of the sequence.
+                 */
+                KeyStroke[][] deletedKeyStrokes = new KeyStroke[][](1);
+                deleteSelection(keyStrokes, false, deletedKeyStrokes);
+                return deletedKeyStrokes[0];
+            }
+
+            // Remove the last key stroke.
+            if (keyStrokes.length > 0) {
+                int newKeyStrokesLength = keyStrokes.length - 1;
+                KeyStroke[] newKeyStrokes = new KeyStroke[newKeyStrokesLength];
+                System.arraycopy(keyStrokes, 0, newKeyStrokes, 0,
+                        newKeyStrokesLength);
+                return newKeyStrokes;
+            }
+
+            return keyStrokes;
+        }
+
+        /**
+         * Handles the key pressed and released events on the wrapped text
+         * widget. This makes sure to either add the pressed key to the
+         * temporary key stroke, or complete the current temporary key stroke
+         * and prompt for the next. In the case of a key release, this makes
+         * sure that the temporary stroke is correctly displayed --
+         * corresponding with modifier keys that may have been released.
+         *
+         * @param event
+         *            The triggering event; must not be <code>null</code>.
+         */
+        public void handleEvent(Event event) {
+            KeyStroke[] keyStrokes = getKeySequence().getKeyStrokes();
+
+            // Dispatch the event to the correct handler.
+            if (event.type is SWT.KeyDown) {
+                keyStrokes = handleKeyDown(event, keyStrokes);
+            } else if (event.type is SWT.KeyUp) {
+                keyStrokes = handleKeyUp(event, keyStrokes);
+            }
+
+            // Update the underlying widget.
+            setKeySequence(KeySequence.getInstance(keyStrokes));
+
+            // Prevent the event from reaching the widget.
+            event.doit = false;
+        }
+
+        /**
+         * Handles the case where the key event is an <code>SWT.KeyDown</code>
+         * event. This either causes a deletion (if it is an unmodified
+         * backspace key stroke), or an insertion (if it is any other key).
+         *
+         * @param event
+         *            The trigger key down event; must not be <code>null</code>.
+         * @param keyStrokes
+         *            The current list of key strokes. This valud must not be
+         *            <code>null</code>, and it must represent a valid key
+         *            sequence.
+         */
+        private KeyStroke[] handleKeyDown(Event event, KeyStroke[] keyStrokes) {
+            // Is it an unmodified backspace character?
+            if ((event.character is SWT.BS || event.character is SWT.DEL) && (event.stateMask is 0)) {
+                return deleteKeyStroke(keyStrokes);
+            }
+
+            return insertKeyStroke(event, keyStrokes);
+        }
+
+        /**
+         * Handles the case where the key event is an <code>SWT.KeyUp</code>
+         * event. This resets the insertion index. If there is an incomplete
+         * stroke, then that incomplete stroke is modified to match the keys
+         * that are still held. If no keys are held, then the incomplete stroke
+         * is removed.
+         *
+         * @param event
+         *            The triggering event; must not be <code>null</code>
+         * @param keyStrokes
+         *            The key strokes that are part of the current key sequence;
+         *            these key strokes are guaranteed to represent a valid key
+         *            sequence. This value must not be <code>null</code>.
+         */
+        private final KeyStroke[] handleKeyUp(Event event,
+                KeyStroke[] keyStrokes) {
+            if (hasIncompleteStroke()) {
+                /*
+                 * Figure out the SWT integer representation of the remaining
+                 * values.
+                 */
+                Event mockEvent = new Event();
+                if ((event.keyCode & SWT.MODIFIER_MASK) !is 0) {
+                    // This key up is a modifier key being released.
+                    mockEvent.stateMask = event.stateMask - event.keyCode;
+                } else {
+                    /*
+                     * This key up is the other end of a key down that was
+                     * trapped by the operating system or window manager.
+                     */
+                    mockEvent.stateMask = event.stateMask;
+                }
+
+                /*
+                 * Get a reasonable facsimile of the stroke that is still
+                 * pressed.
+                 */
+                int key = SWTKeySupport
+                        .convertEventToUnmodifiedAccelerator(mockEvent);
+                KeyStroke remainingStroke = SWTKeySupport
+                        .convertAcceleratorToKeyStroke(key);
+                int keyStrokesLength = keyStrokes.length;
+                KeyStroke[] newKeyStrokes;
+                if ((keyStrokesLength > 0)
+                        && (remainingStroke.getModifierKeys() !is 0)) {
+                    newKeyStrokes = new KeyStroke[keyStrokesLength];
+                    System.arraycopy(keyStrokes, 0, newKeyStrokes, 0,
+                            keyStrokesLength - 1);
+                    newKeyStrokes[keyStrokesLength - 1] = remainingStroke;
+
+                } else if (keyStrokesLength > 0) {
+                    newKeyStrokes = new KeyStroke[keyStrokesLength - 1];
+                    System.arraycopy(keyStrokes, 0, newKeyStrokes, 0,
+                            keyStrokesLength - 1);
+
+                } else if (remainingStroke.getModifierKeys() !is 0) {
+                    newKeyStrokes = new KeyStroke[keyStrokesLength + 1];
+                    System.arraycopy(keyStrokes, 0, newKeyStrokes, 0,
+                            keyStrokesLength);
+                    newKeyStrokes[keyStrokesLength] = remainingStroke;
+
+                } else {
+                    newKeyStrokes = keyStrokes;
+
+                }
+
+                return newKeyStrokes;
+            }
+
+            return keyStrokes;
+        }
+
+        /**
+         * <p>
+         * Handles the case where a key down event is leading to a key stroke
+         * being inserted. The current selection is deleted, and an invalid
+         * remanents of the stroke are also removed. The insertion is carried
+         * out at the cursor position.
+         * </p>
+         * <p>
+         * If only a natural key is selected (as part of a larger key stroke),
+         * then it is possible for the user to press a natural key to replace
+         * the old natural key. In this situation, pressing any modifier keys
+         * will replace the whole thing.
+         * </p>
+         * <p>
+         * If the insertion point is not at the end of the sequence, then
+         * incomplete strokes will not be immediately inserted. Only when the
+         * sequence is completed is the stroke inserted. This is a requirement
+         * as the widget must always represent a valid key sequence. The
+         * insertion point is tracked using <code>insertionIndex</code>,
+         * which is an index into the key stroke array.
+         * </p>
+         *
+         * @param event
+         *            The triggering key down event; must not be
+         *            <code>null</code>.
+         * @param keyStrokes
+         *            The key strokes into which the current stroke should be
+         *            inserted. This value must not be <code>null</code>, and
+         *            must represent a valid key sequence.
+         */
+        private final KeyStroke[] insertKeyStroke(Event event,
+                KeyStroke[] keyStrokes) {
+            // Compute the key stroke to insert.
+            int key = SWTKeySupport.convertEventToUnmodifiedAccelerator(event);
+            KeyStroke stroke = SWTKeySupport.convertAcceleratorToKeyStroke(key);
+
+            /*
+             * Only insert the stroke if it is *not ScrollLock. Let's not get
+             * silly
+             */
+            if ((SWT.NUM_LOCK is stroke.getNaturalKey())
+                    || (SWT.CAPS_LOCK is stroke.getNaturalKey())
+                    || (SWT.SCROLL_LOCK is stroke.getNaturalKey())) {
+                return keyStrokes;
+            }
+
+            if (insertionIndex !is -1) {
+                // There is a previous replacement still going on.
+                if (stroke.isComplete()) {
+                    keyStrokes = insertStrokeAt(keyStrokes, stroke,
+                            insertionIndex);
+                    clearInsertionIndex();
+                }
+
+            } else if (hasSelection()) {
+                // There is a selection that needs to be replaced.
+                KeyStroke[][] deletedKeyStrokes = new KeyStroke[][](1);
+                insertionIndex = deleteSelection(keyStrokes, stroke
+                        .isComplete(), deletedKeyStrokes);
+                keyStrokes = deletedKeyStrokes[0];
+                if ((stroke.isComplete())
+                        || (insertionIndex >= keyStrokes.length)) {
+                    keyStrokes = insertStrokeAt(keyStrokes, stroke,
+                            insertionIndex);
+                    clearInsertionIndex();
+                }
+
+            } else {
+                // No selection, so remove the incomplete stroke, if any
+                if ((hasIncompleteStroke()) && (keyStrokes.length > 0)) {
+                    KeyStroke[] newKeyStrokes = new KeyStroke[keyStrokes.length - 1];
+                    System.arraycopy(keyStrokes, 0, newKeyStrokes, 0,
+                            keyStrokes.length - 1);
+                    keyStrokes = newKeyStrokes;
+                }
+
+                // And then add the new stroke.
+                if ((keyStrokes.length is 0)
+                        || (insertionIndex >= keyStrokes.length)
+                        || (isCursorInLastPosition())) {
+                    keyStrokes = insertStrokeAt(keyStrokes, stroke,
+                            keyStrokes.length);
+                    clearInsertionIndex();
+                } else {
+                    /*
+                     * I'm just getting the insertionIndex here. No actual
+                     * deletion should occur.
+                     */
+                    KeyStroke[][] deletedKeyStrokes = new KeyStroke[][](1);
+                    insertionIndex = deleteSelection(keyStrokes, stroke
+                            .isComplete(), deletedKeyStrokes);
+                    keyStrokes = deletedKeyStrokes[0];
+                    if (stroke.isComplete()) {
+                        keyStrokes = insertStrokeAt(keyStrokes, stroke,
+                                insertionIndex);
+                        clearInsertionIndex();
+                    }
+                }
+
+            }
+
+            return keyStrokes;
+        }
+    }
+
+    /**
+     * A traversal listener that blocks all traversal except for tabs and arrow
+     * keys.
+     */
+    private class TraversalFilter : Listener {
+        /**
+         * Handles the traverse event on the text field wrapped by this class.
+         * It swallows all traverse events example for tab and arrow key
+         * navigation. The other forms of navigation can be reached by tabbing
+         * off of the control.
+         *
+         * @param event
+         *            The trigger event; must not be <code>null</code>.
+         */
+        public void handleEvent(Event event) {
+            switch (event.detail) {
+            case SWT.TRAVERSE_ESCAPE:
+            case SWT.TRAVERSE_MNEMONIC:
+            case SWT.TRAVERSE_NONE:
+            case SWT.TRAVERSE_PAGE_NEXT:
+            case SWT.TRAVERSE_PAGE_PREVIOUS:
+            case SWT.TRAVERSE_RETURN:
+                event.type = SWT.None;
+                event.doit = false;
+                break;
+
+            case SWT.TRAVERSE_TAB_NEXT:
+            case SWT.TRAVERSE_TAB_PREVIOUS:
+                // Check if modifiers other than just 'Shift' were
+                // down.
+                if ((event.stateMask & (SWT.MODIFIER_MASK ^ SWT.SHIFT)) !is 0) {
+                    // Modifiers other than shift were down.
+                    event.type = SWT.None;
+                    event.doit = false;
+                    break;
+                }
+
+                // fall through -- either no modifiers, or just shift.
+
+            case SWT.TRAVERSE_ARROW_NEXT:
+            case SWT.TRAVERSE_ARROW_PREVIOUS:
+            default:
+                // Let the traversal happen, but clear the incomplete
+                // stroke
+                if (hasIncompleteStroke()) {
+                    KeyStroke[] oldKeyStrokes = getKeySequence()
+                            .getKeyStrokes();
+                    int newKeyStrokesLength = oldKeyStrokes.length - 1;
+                    if (newKeyStrokesLength >= 1) {
+                        KeyStroke[] newKeyStrokes = new KeyStroke[newKeyStrokesLength];
+                        System.arraycopy(oldKeyStrokes, 0, newKeyStrokes, 0,
+                                newKeyStrokesLength);
+                        setKeySequence(KeySequence.getInstance(newKeyStrokes));
+                    } else {
+                        setKeySequence(KeySequence.getInstance());
+                    }
+                }
+            }
+
+        }
+    }
+
+    /**
+     * The manager resposible for installing and removing the traversal filter
+     * when the key sequence entry widget gains and loses focus.
+     */
+    private class TraversalFilterManager : FocusListener {
+        /** The managed filter. We only need one instance. */
+        private TraversalFilter filter;
+
+        private bool filtering = false;
+
+        this(){
+            filter = new TraversalFilter();
+        }
+
+        /**
+         * Attaches the global traversal filter.
+         *
+         * @param event
+         *            Ignored.
+         */
+        public void focusGained(FocusEvent event) {
+            Display.getCurrent().addFilter(SWT.Traverse, filter);
+            filtering = true;
+        }
+
+        /**
+         * Detaches the global traversal filter.
+         *
+         * @param event
+         *            Ignored.
+         */
+        public void focusLost(FocusEvent event) {
+            Display.getCurrent().removeFilter(SWT.Traverse, filter);
+            filtering = false;
+        }
+
+        /**
+         * Remove the traverse filter if we close without focusOut.
+         */
+        public void dispose() {
+            if (filtering) {
+                Display.getCurrent().removeFilter(SWT.Traverse, filter);
+            }
+        }
+    }
+
+    /**
+     * A modification listener that makes sure that external events to this
+     * class (i.e., direct modification of the underlying text) do not break
+     * this class' view of the world.
+     */
+    private class UpdateSequenceListener : ModifyListener {
+        /**
+         * Handles the modify event on the underlying text widget.
+         *
+         * @param event
+         *            The triggering event; ignored.
+         */
+        public void modifyText(ModifyEvent event) {
+            try {
+                // The original sequence.
+                KeySequence originalSequence = getKeySequence();
+
+                // The new sequence drawn from the text.
+                String contents = getText();
+                KeySequence newSequence = KeySequence.getInstance(contents);
+
+                // Check to see if they're the same.
+                if (!originalSequence.opEquals(newSequence)) {
+                    setKeySequence(newSequence);
+                }
+
+            } catch (ParseException e) {
+                // Abort any cut/paste-driven modifications
+                setKeySequence(getKeySequence());
+            }
+        }
+    }
+
+    static this(){
+        TreeSet trappedKeys = new TreeSet();
+        trappedKeys.add(SWTKeySupport.convertAcceleratorToKeyStroke(SWT.TAB));
+        trappedKeys.add(SWTKeySupport.convertAcceleratorToKeyStroke(SWT.TAB
+                | SWT.SHIFT));
+        trappedKeys.add(SWTKeySupport.convertAcceleratorToKeyStroke(SWT.BS));
+        List trappedKeyList = new ArrayList(trappedKeys);
+        TRAPPED_KEYS = Collections.unmodifiableList(trappedKeyList);
+    }
+
+    /** An empty string instance for use in clearing text values. */
+    private static const String EMPTY_STRING = ""; //$NON-NLS-1$
+
+    /**
+     * The special integer value for the maximum number of strokes indicating
+     * that an infinite number should be allowed.
+     */
+    public static const int INFINITE = -1;
+
+    /**
+     * The name of the property representing the current key sequence in this
+     * key sequence widget.
+     *
+     * @since 3.2
+     */
+    public static const String P_KEY_SEQUENCE = "org.eclipse.jface.bindings.keys.KeySequenceText.KeySequence"; //$NON-NLS-1$
+
+    /**
+     * The keys trapped by this widget. This list is guaranteed to be roughly
+     * accurate. Perfection is not possible, as SWT does not export traversal
+     * keys as constants.
+     */
+    public static const List TRAPPED_KEYS;
+
+    /**
+     * The key filter attached to the underlying widget that traps key events.
+     */
+    private const KeyTrapListener keyFilter;
+
+    /**
+     * The text of the key sequence -- containing only the complete key strokes.
+     */
+    private KeySequence keySequence;
+
+    /**
+     * Those listening to changes to the key sequence in this widget. This value
+     * may be <code>null</code> if there are no listeners.
+     */
+    private Collection listeners = null;
+
+    /** The maximum number of key strokes permitted in the sequence. */
+    private int maxStrokes = INFINITE;
+
+    /** The text widget that is wrapped for this class. */
+    private const Text text;
+
+    /**
+     * The listener that makes sure that the text widget remains up-to-date with
+     * regards to external modification of the text (e.g., cut & pasting).
+     */
+    private const UpdateSequenceListener updateSequenceListener;
+
+    /**
+     * Constructs an instance of <code>KeySequenceTextField</code> with the
+     * text field to use. If the platform is carbon (MacOS X), then the font is
+     * set to be the same font used to display accelerators in the menus.
+     *
+     * @param wrappedText
+     *            The text widget to wrap; must not be <code>null</code>.
+     */
+    public this(Text wrappedText) {
+        keyFilter = new KeyTrapListener();
+        keySequence = KeySequence.getInstance();
+        updateSequenceListener = new UpdateSequenceListener();
+
+        text = wrappedText;
+
+        // Set the font if the platform is carbon.
+        if ("carbon".equals(SWT.getPlatform())) { //$NON-NLS-1$
+            // Don't worry about this font name here; it is the official menu
+            // font and point size on the Mac.
+            Font font = new Font(cast(Device)text.getDisplay(),
+                    "Lucida Grande", 13, SWT.NORMAL); //$NON-NLS-1$
+            text.setFont(font);
+            text.addDisposeListener(new class(font) DisposeListener {
+                Font font_;
+                this(Font a){
+                    font_=a;
+                }
+                public void widgetDisposed(DisposeEvent e) {
+                    font_.dispose();
+                }
+            });
+        }
+
+        // Add the key listener.
+        text.addListener(SWT.KeyUp, keyFilter);
+        text.addListener(SWT.KeyDown, keyFilter);
+
+        TraversalFilterManager traversalFilterManager = new TraversalFilterManager();
+        text.addFocusListener(traversalFilterManager);
+        text.addDisposeListener(new class(traversalFilterManager) DisposeListener {
+            TraversalFilterManager traversalFilterManager_;
+            this(TraversalFilterManager a){
+                traversalFilterManager_=a;
+            }
+            public void widgetDisposed(DisposeEvent e) {
+                traversalFilterManager_.dispose();
+            }
+        });
+
+        // Add an internal modify listener.
+        text.addModifyListener(updateSequenceListener);
+    }
+
+    /**
+     * Adds a property change listener to this key sequence widget. It will be
+     * notified when the key sequence changes.
+     *
+     * @param listener
+     *            The listener to be notified when changes occur; must not be
+     *            <code>null</code>.
+     * @since 3.2
+     */
+    public final void addPropertyChangeListener(
+            IPropertyChangeListener listener) {
+        if (listener is null) {
+            return;
+        }
+
+        if (listeners is null) {
+            listeners = new ArrayList(1);
+        }
+
+        listeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Clears the text field and resets all the internal values.
+     */
+    public void clear() {
+        KeySequence oldKeySequence = keySequence;
+        keySequence = KeySequence.getInstance();
+        text.setText(EMPTY_STRING);
+        firePropertyChangeEvent(oldKeySequence);
+    }
+
+    /**
+     * Removes the key strokes from the list corresponding the selection. If
+     * <code>allowIncomplete</code>, then invalid key sequences will be
+     * allowed (i.e., those with incomplete strokes in the non-terminal
+     * position). Otherwise, incomplete strokes will be removed. This modifies
+     * <code>keyStrokes</code> in place, and has no effect on the text widget
+     * this class wraps.
+     *
+     * @param keyStrokes
+     *            The list of key strokes from which the selection should be
+     *            removed; must not be <code>null</code>.
+     * @param allowIncomplete
+     *            Whether incomplete strokes should be allowed to exist in the
+     *            list after the deletion.
+     * @param deletedKeyStrokes
+     *            The list of keystrokes that were deleted by this operation.
+     *            Declared as final since it will hold a reference to the new
+     *            keyStroke array that has deleted the selected keystrokes.
+     * @return The index at which a subsequent insert should occur. This index
+     *         only has meaning to the <code>insertStrokeAt</code> method.
+     */
+    private final int deleteSelection(KeyStroke[] keyStrokes,
+            bool allowIncomplete, KeyStroke[][] deletedKeyStrokes) {
+        // Get the current selection.
+        Point selection = text.getSelection();
+        int start = selection.x;
+        int end = selection.y;
+
+        /*
+         * Using the key sequence format method, discover the point at which
+         * adding key strokes passes or equals the start of the selection. In
+         * other words, find the first stroke that is part of the selection.
+         * Keep track of the text range under which the stroke appears (i.e.,
+         * startTextIndex->string.length() is the first selected stroke).
+         */
+        String string;
+        auto currentStrokes = new ArrayList();
+        int startTextIndex = 0; // keeps track of the start of the stroke
+        int keyStrokesLength = keyStrokes.length;
+        int i;
+        for (i = 0; (i < keyStrokesLength) && (string.length < start); i++) {
+            startTextIndex = string.length;
+            currentStrokes.add(keyStrokes[i]);
+            string = KeySequence.getInstance(currentStrokes).format();
+        }
+
+        /*
+         * If string.length() is start, then the cursor is positioned between
+         * strokes (i.e., selection is outside of a stroke).
+         */
+        int startStrokeIndex;
+        if (string.length is start) {
+            startStrokeIndex = currentStrokes.size();
+        } else {
+            startStrokeIndex = currentStrokes.size() - 1;
+        }
+
+        /*
+         * Check to see if the cursor is only positioned, rather than actually
+         * selecting something. We only need to compute the end if there is a
+         * selection.
+         */
+        int endStrokeIndex;
+        if (start is end) {
+            // return the current keystrokes, nothing has to be deleted
+            deletedKeyStrokes[0] = keyStrokes;
+            return startStrokeIndex;
+        }
+
+        for (; (i < keyStrokesLength) && (string.length < end); i++) {
+            currentStrokes.add(keyStrokes[i]);
+            string = KeySequence.getInstance(currentStrokes).format();
+        }
+        endStrokeIndex = currentStrokes.size() - 1;
+        if (endStrokeIndex < 0) {
+            endStrokeIndex = 0;
+        }
+
+        /*
+         * Remove the strokes that are touched by the selection. Keep track of
+         * the first stroke removed.
+         */
+        int newLength = keyStrokesLength
+                - (endStrokeIndex - startStrokeIndex + 1);
+        deletedKeyStrokes[0] = new KeyStroke[newLength];
+        KeyStroke startStroke = keyStrokes[startStrokeIndex];
+        KeyStroke keyStrokeResult[] = new KeyStroke[newLength];
+        System.arraycopy(keyStrokes, 0, keyStrokeResult, 0, startStrokeIndex);
+        System.arraycopy(keyStrokes, endStrokeIndex + 1, keyStrokeResult,
+                startStrokeIndex, keyStrokesLength - endStrokeIndex - 1);
+        System.arraycopy(keyStrokeResult, 0, deletedKeyStrokes[0], 0, newLength);
+
+        /*
+         * Allow the first stroke removed to be replaced by an incomplete
+         * stroke.
+         */
+        if (allowIncomplete) {
+            int modifierKeys = startStroke.getModifierKeys();
+            KeyStroke incompleteStroke = KeyStroke.getInstance(modifierKeys,
+                    KeyStroke.NO_KEY);
+            int incompleteStrokeLength = incompleteStroke.format().length;
+            if ((startTextIndex + incompleteStrokeLength) <= start) {
+                KeyStroke[] added = new KeyStroke[newLength + 1];
+                System.arraycopy(deletedKeyStrokes[0], 0, added, 0,
+                        startStrokeIndex);
+                added[startStrokeIndex] = incompleteStroke;
+                System.arraycopy(deletedKeyStrokes[0], startStrokeIndex, added,
+                        startStrokeIndex + 1, newLength - startStrokeIndex);
+                deletedKeyStrokes[0] = added;
+            }
+        }
+
+        return startStrokeIndex;
+    }
+
+    /**
+     * Fires a property change event to all of the listeners.
+     *
+     * @param oldKeySequence
+     *            The old key sequence; must not be <code>null</code>.
+     * @since 3.2
+     */
+    protected final void firePropertyChangeEvent(
+            KeySequence oldKeySequence) {
+        if (listeners !is null) {
+            Iterator listenerItr = listeners.iterator();
+            PropertyChangeEvent event = new PropertyChangeEvent(this,
+                    P_KEY_SEQUENCE, oldKeySequence, getKeySequence());
+             while (listenerItr.hasNext()) {
+                IPropertyChangeListener listener = cast(IPropertyChangeListener) listenerItr
+                        .next();
+                listener.propertyChange(event);
+            }
+        }
+    }
+
+    /**
+     * An accessor for the <code>KeySequence</code> that corresponds to the
+     * current state of the text field. This includes incomplete strokes.
+     *
+     * @return The key sequence representation; never <code>null</code>.
+     */
+    public KeySequence getKeySequence() {
+        return keySequence;
+    }
+
+    /**
+     * An accessor for the underlying text widget's contents.
+     *
+     * @return The text contents of this entry; never <code>null</code>.
+     */
+    private String getText() {
+        return text.getText();
+    }
+
+    /**
+     * Tests whether the current key sequence has a stroke with no natural key.
+     *
+     * @return <code>true</code> is there is an incomplete stroke;
+     *         <code>false</code> otherwise.
+     */
+    private bool hasIncompleteStroke() {
+        return !keySequence.isComplete();
+    }
+
+    /**
+     * Tests whether the current text widget has some text selection.
+     *
+     * @return <code>true</code> if the number of selected characters it
+     *         greater than zero; <code>false</code> otherwise.
+     */
+    private bool hasSelection() {
+        return (text.getSelectionCount() > 0);
+    }
+
+    /**
+     * Inserts the key stroke at the current insertion point. This does a
+     * regular delete and insert, as if the key had been pressed.
+     *
+     * @param stroke
+     *            The key stroke to insert; must not be <code>null</code>.
+     */
+    public void insert(KeyStroke stroke) {
+        if (!stroke.isComplete()) {
+            return;
+        }
+
+        // Copy the key strokes in the current key sequence.
+        KeySequence keySequence = getKeySequence();
+        KeyStroke[] oldKeyStrokes = keySequence.getKeyStrokes();
+        KeyStroke[] newKeyStrokes;
+        if ((hasIncompleteStroke()) && (!keySequence.isEmpty())) {
+            int newKeyStrokesLength = oldKeyStrokes.length - 1;
+            newKeyStrokes = new KeyStroke[newKeyStrokesLength];
+            System.arraycopy(oldKeyStrokes, 0, newKeyStrokes, 0,
+                    newKeyStrokesLength);
+        } else {
+            newKeyStrokes = oldKeyStrokes;
+        }
+
+        KeyStroke[][] deletedKeyStrokes = new KeyStroke[][](1);
+        int index = deleteSelection(newKeyStrokes, false, deletedKeyStrokes);
+        if (index is -1) {
+            index = 0;
+        }
+
+        KeyStroke[] keyStrokes = insertStrokeAt(newKeyStrokes, stroke, index);
+        keyFilter.clearInsertionIndex();
+        setKeySequence(KeySequence.getInstance(keyStrokes));
+    }
+
+    /**
+     * Inserts the stroke at the given index in the list of strokes. If the
+     * stroke currently at that index is incomplete, then it tries to merge the
+     * two strokes. If merging is a complete failure (unlikely), then it will
+     * simply overwrite the incomplete stroke. If the stroke at the index is
+     * complete, then it simply inserts the stroke independently.
+     *
+     * @param keyStrokes
+     *            The list of key strokes in which the key stroke should be
+     *            appended; must not be <code>null</code>.
+     * @param stroke
+     *            The stroke to insert; should not be <code>null</code>.
+     * @param index
+     *            The index at which to insert; must be a valid index into the
+     *            list of key strokes.
+     */
+    private final KeyStroke[] insertStrokeAt(KeyStroke[] keyStrokes,
+            KeyStroke stroke, int index) {
+        int keyStrokesLength = keyStrokes.length;
+        KeyStroke currentStroke = (index >= keyStrokesLength) ? null
+                : keyStrokes[index];
+        if ((currentStroke !is null) && (!currentStroke.isComplete())) {
+            int modifierKeys = currentStroke.getModifierKeys();
+            int naturalKey = stroke.getNaturalKey();
+            modifierKeys |= stroke.getModifierKeys();
+            keyStrokes[index] = KeyStroke.getInstance(modifierKeys, naturalKey);
+            return keyStrokes;
+        }
+
+        KeyStroke[] newKeyStrokes = new KeyStroke[keyStrokesLength + 1];
+        System.arraycopy(keyStrokes, 0, newKeyStrokes, 0, index);
+        newKeyStrokes[index] = stroke;
+        if (index < keyStrokesLength) {
+            System.arraycopy(keyStrokes, index, newKeyStrokes, index + 1,
+                    keyStrokesLength-index);
+        }
+        return newKeyStrokes;
+    }
+
+    /**
+     * Tests whether the cursor is in the last position. This means that the
+     * selection extends to the last position.
+     *
+     * @return <code>true</code> if the selection extends to the last
+     *         position; <code>false</code> otherwise.
+     */
+    private bool isCursorInLastPosition() {
+        return (text.getSelection().y >= getText().length);
+    }
+
+    /**
+     * Removes the given listener from this key sequence widget.
+     *
+     * @param listener
+     *            The listener to be removed; must not be <code>null</code>.
+     * @since 3.2
+     */
+    public final void removePropertyChangeListener(
+            IPropertyChangeListener listener) {
+        if ((listener is null) || (listeners is null)) {
+            return;
+        }
+
+        listeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * <p>
+     * A mutator for the key sequence stored within this widget. The text and
+     * caret position are updated.
+     * </p>
+     * <p>
+     * All sequences are limited to maxStrokes number of strokes in length. If
+     * there are already that number of strokes, then it does not show
+     * incomplete strokes, and does not keep track of them.
+     * </p>
+     *
+     * @param newKeySequence
+     *            The new key sequence for this widget; may be <code>null</code>
+     *            if none.
+     */
+    public void setKeySequence(KeySequence newKeySequence) {
+        KeySequence oldKeySequence = keySequence;
+
+        if (newKeySequence is null) {
+            text.setText(""); //$NON-NLS-1$
+        } else {
+            keySequence = newKeySequence;
+        }
+
+        // Trim any extra strokes.
+        if (maxStrokes !is INFINITE) {
+            KeyStroke[] oldKeyStrokes = keySequence.getKeyStrokes();
+            if (maxStrokes < oldKeyStrokes.length) {
+                KeyStroke[] newKeyStrokes = new KeyStroke[maxStrokes];
+                System
+                        .arraycopy(oldKeyStrokes, 0, newKeyStrokes, 0,
+                                maxStrokes);
+                keySequence = KeySequence.getInstance(newKeyStrokes);
+            }
+        }
+
+        // Check to see if the text has changed.
+        String currentString = getText();
+        String newString = keySequence.format();
+        if (!currentString.equals(newString)) {
+            // We need to update the text
+            text.removeModifyListener(updateSequenceListener);
+            text.setText(keySequence.format());
+            text.addModifyListener(updateSequenceListener);
+            text.setSelection(getText().length);
+        }
+
+        firePropertyChangeEvent(oldKeySequence);
+    }
+
+    /**
+     * Returns the maximum number of strokes that are permitted in this widget
+     * at one time.
+     *
+     * @return The maximum number of strokes; will be a positive integer or
+     *         <code>INFINITE</code>.
+     */
+    public int getKeyStrokeLimit() {
+        return maxStrokes;
+    }
+
+    /**
+     * A mutator for the maximum number of strokes that are permitted in this
+     * widget at one time.
+     *
+     * @param keyStrokeLimit
+     *            The maximum number of strokes; must be a positive integer or
+     *            <code>INFINITE</code>.
+     */
+    public void setKeyStrokeLimit(int keyStrokeLimit) {
+        if (keyStrokeLimit > 0 || keyStrokeLimit is INFINITE) {
+            this.maxStrokes = keyStrokeLimit;
+        } else {
+            throw new IllegalArgumentException(null);
+        }
+
+        // Make sure we are obeying the new limit.
+        setKeySequence(getKeySequence());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/keys/KeyStroke.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,285 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.bindings.keys.KeyStroke;
+
+import org.eclipse.jface.bindings.keys.IKeyLookup;
+import org.eclipse.jface.bindings.keys.KeyLookupFactory;
+
+import org.eclipse.jface.bindings.Trigger;
+import org.eclipse.jface.bindings.keys.formatting.KeyFormatterFactory;
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.Set;
+import java.text.ParseException;
+static import tango.text.Util;
+/**
+ * <p>
+ * A <code>KeyStroke</code> is defined as an optional set of modifier keys
+ * followed optionally by a natural key. A <code>KeyStroke</code> is said to
+ * be complete if it contains a natural key. A natural key is any Unicode
+ * character (e.g., "backspace", etc.), any character belonging to a natural
+ * language (e.g., "A", "1", "[", etc.), or any special control character
+ * specific to computers (e.g., "F10", "PageUp", etc.).
+ * </p>
+ * <p>
+ * All <code>KeyStroke</code> objects have a formal string representation
+ * available via the <code>toString()</code> method. There are a number of
+ * methods to get instances of <code>KeyStroke</code> objects, including one
+ * which can parse this formal string representation.
+ * </p>
+ * <p>
+ * All <code>KeyStroke</code> objects, via the <code>format()</code> method,
+ * provide a version of their formal string representation translated by
+ * platform and locale, suitable for display to a user.
+ * </p>
+ * <p>
+ * <code>KeyStroke</code> objects are immutable. Clients are not permitted to
+ * extend this class.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class KeyStroke : Trigger, Comparable {
+
+    /**
+     * The delimiter between multiple keys in a single key strokes -- expressed
+     * in the formal key stroke grammar. This is not to be displayed to the
+     * user. It is only intended as an internal representation.
+     */
+    public static const String KEY_DELIMITER = "\u002B"; //$NON-NLS-1$
+
+    /**
+     * The set of delimiters for <code>Key</code> objects allowed during
+     * parsing of the formal string representation.
+     */
+    public static const String KEY_DELIMITERS = KEY_DELIMITER;
+
+    /**
+     * The representation for no key.
+     */
+    public static const int NO_KEY = 0;
+
+    /**
+     * Creates an instance of <code>KeyStroke</code> given a natural key.
+     *
+     * @param naturalKey
+     *            the natural key. The format of this integer is defined by
+     *            whichever widget toolkit you are using; <code>NO_KEY</code>
+     *            always means no natural key.
+     * @return a key stroke. This key stroke will have no modifier keys.
+     *         Guaranteed not to be <code>null</code>.
+     * @see SWTKeySupport
+     */
+    public static final KeyStroke getInstance(int naturalKey) {
+        return new KeyStroke(NO_KEY, naturalKey);
+    }
+
+    /**
+     * Creates an instance of <code>KeyStroke</code> given a set of modifier keys
+     * and a natural key.
+     *
+     * @param modifierKeys
+     *            the modifier keys. The format of this integer is defined by
+     *            whichever widget toolkit you are using; <code>NO_KEY</code>
+     *            always means no modifier keys.
+     * @param naturalKey
+     *            the natural key. The format of this integer is defined by
+     *            whichever widget toolkit you are using; <code>NO_KEY</code>
+     *            always means no natural key.
+     * @return a key stroke. Guaranteed not to be <code>null</code>.
+     * @see SWTKeySupport
+     */
+    public static final KeyStroke getInstance(int modifierKeys,
+            int naturalKey) {
+        return new KeyStroke(modifierKeys, naturalKey);
+    }
+
+    /**
+     * Creates an instance of <code>KeyStroke</code> by parsing a given a formal
+     * string representation.
+     *
+     * @param string
+     *            the formal string representation to parse.
+     * @return a key stroke. Guaranteed not to be <code>null</code>.
+     * @throws ParseException
+     *             if the given formal string representation could not be parsed
+     *             to a valid key stroke.
+     */
+    public static final KeyStroke getInstance(String string) {
+        if (string is null) {
+            throw new NullPointerException("Cannot parse a null string"); //$NON-NLS-1$
+        }
+
+        IKeyLookup lookup = KeyLookupFactory.getDefault();
+        int modifierKeys = NO_KEY;
+        int naturalKey = NO_KEY;
+        int i = 0;
+
+        auto tokens = tango.text.Util.delimit( string, KEY_DELIMITERS );
+        foreach( idx, token; tokens ){
+
+            if (i % 2 is 0) {
+                if ( idx+1 < tokens.length ) {
+                    token = token.toUpperCase();
+                    int modifierKey = lookup.formalModifierLookup(token);
+                    if (modifierKey is NO_KEY) {
+                        throw new ParseException(
+                                "Cannot create key stroke with duplicate or non-existent modifier key: " //$NON-NLS-1$
+                                        ~ token);
+                    }
+
+                    modifierKeys |= modifierKey;
+
+                } else if (token.length is 1) {
+                    naturalKey = token.charAt(0);
+
+                } else {
+                    token = token.toUpperCase();
+                    naturalKey = lookup.formalKeyLookup(token);
+                }
+            }
+
+            i++;
+        }
+
+        return new KeyStroke(modifierKeys, naturalKey);
+    }
+
+    /**
+     * An integer representation of the modifier keys; <code>NO_KEY</code>
+     * means that there is no modifier key.
+     */
+    private const int modifierKeys;
+
+    /**
+     * The natural key for this key stroke. This value is <code>NO_KEY</code>
+     * if the key stroke is incomplete (i.e., has no natural key).
+     */
+    private const int naturalKey;
+
+    /**
+     * Constructs an instance of <code>KeyStroke</code> given a set of
+     * modifier keys and a natural key.
+     *
+     * @param modifierKeys
+     *            the modifier keys. The format of this integer is defined by
+     *            whichever widget toolkit you are using; <code>NO_KEY</code>
+     *            always means no modifier keys.
+     * @param naturalKey
+     *            the natural key. The format of this integer is defined by
+     *            whichever widget toolkit you are using; <code>NO_KEY</code>
+     *            always means no natural key.
+     * @see SWTKeySupport
+     */
+    private this(int modifierKeys, int naturalKey) {
+        this.modifierKeys = modifierKeys;
+        this.naturalKey = naturalKey;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Comparable#compareTo(java.lang.Object)
+     */
+    public final int compareTo(Object object) {
+        KeyStroke keyStroke = cast(KeyStroke) object;
+        int compareTo = Util.compare(modifierKeys, keyStroke.modifierKeys);
+
+        if (compareTo is 0) {
+            compareTo = Util.compare(naturalKey, keyStroke.naturalKey);
+        }
+
+        return compareTo;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public final override int opEquals(Object object) {
+        if (!(cast(KeyStroke)object )) {
+            return false;
+        }
+
+        KeyStroke keyStroke = cast(KeyStroke) object;
+        if (modifierKeys !is keyStroke.modifierKeys) {
+            return false;
+        }
+
+        return (naturalKey is keyStroke.naturalKey);
+    }
+
+    /**
+     * Formats this key stroke into the current default look.
+     *
+     * @return A string representation for this key stroke using the default
+     *         look; never <code>null</code>.
+     */
+    public final String format() {
+        return KeyFormatterFactory.getDefault().format(this);
+    }
+
+    /**
+     * Returns the modifier keys for this key stroke.
+     *
+     * @return the bit mask of modifier keys; <code>NO_KEY</code> means that
+     *         there is no modifier key.
+     */
+    public final int getModifierKeys() {
+        return modifierKeys;
+    }
+
+    /**
+     * Returns the natural key for this key stroke.
+     *
+     * @return The natural key for this key stroke. This value is
+     *         <code>NO_KEY</code> if the key stroke is incomplete (i.e., has
+     *         no natural key).
+     */
+    public final int getNaturalKey() {
+        return naturalKey;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#hashCode()
+     */
+    public final override hash_t toHash() {
+        return modifierKeys << 4 + naturalKey;
+    }
+
+    /**
+     * Returns whether or not this key stroke is complete. Key strokes are
+     * complete iff they have a natural key which is not <code>NO_KEY</code>.
+     *
+     * @return <code>true</code>, iff the key stroke is complete.
+     */
+    public final bool isComplete() {
+        return (naturalKey !is NO_KEY);
+    }
+
+    /**
+     * Returns the formal string representation for this key stroke.
+     *
+     * @return The formal string representation for this key stroke. Guaranteed
+     *         not to be <code>null</code>.
+     * @see java.lang.Object#toString()
+     */
+    public final override String toString() {
+        return KeyFormatterFactory.getFormalKeyFormatter().format(this);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/keys/ParseException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.bindings.keys.ParseException;
+
+import java.lang.all;
+import java.text.ParseException;
+
+/**
+ * <p>
+ * An exception indicating problems while parsing formal string representations
+ * of either <code>KeyStroke</code> or <code>KeySequence</code> objects.
+ * </p>
+ * <p>
+ * <code>ParseException</code> objects are immutable. Clients are not
+ * permitted to extend this class.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class ParseException : Exception {
+
+    /**
+     * Generated serial version UID for this class.
+     *
+     * @since 3.1
+     */
+    private static const long serialVersionUID = 3257009864814376241L;
+
+    /**
+     * Constructs a <code>ParseException</code> with the specified detail
+     * message.
+     *
+     * @param s
+     *            the detail message.
+     */
+    public this(String s) {
+        super(s);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/keys/SWTKeyLookup.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,378 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.bindings.keys.SWTKeyLookup;
+
+import org.eclipse.jface.bindings.keys.IKeyLookup;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * <p>
+ * A look-up table for the formal grammar for keys, and the integer values they
+ * represent. This look-up table is hard-coded to use SWT representations. By
+ * replacing this class (and
+ * {@link org.eclipse.jface.bindings.keys.SWTKeySupport}), you can remove the
+ * dependency on SWT.
+ * </p>
+ *
+ * @since 3.1
+ * @see org.eclipse.jface.bindings.keys.KeyLookupFactory
+ */
+public final class SWTKeyLookup : IKeyLookup {
+
+    /**
+     * The look-up table for modifier keys. This is a map of formal name (<code>String</code>)
+     * to integer value (<code>Integer</code>).
+     */
+    private const Map modifierKeyTable;
+
+    /**
+     * The look-up table for formal names. This is a map of integer value (<code>Integer</code>)
+     * to formal name (<code>String</code>).
+     */
+    private const Map nameTable;
+
+    /**
+     * The look-up table for natural keys. This is a map of formal name (<code>String</code>)
+     * to integer value (<code>Integer</code>).
+     */
+    private const Map naturalKeyTable;
+
+    /**
+     * Constructs a new look-up class. This should only be done by the look-up
+     * factory.
+     *
+     * @see KeyLookupFactory
+     */
+    this() {
+        modifierKeyTable = new HashMap();
+        nameTable = new HashMap();
+        naturalKeyTable = new HashMap();
+        Integer alt = new Integer(SWT.ALT);
+        Integer command = new Integer(SWT.COMMAND);
+        Integer ctrl = new Integer(SWT.CTRL);
+        Integer shift = new Integer(SWT.SHIFT);
+        modifierKeyTable.put(ALT_NAME, alt);
+        nameTable.put(alt, ALT_NAME);
+        modifierKeyTable.put(COMMAND_NAME, command);
+        nameTable.put(command, COMMAND_NAME);
+        modifierKeyTable.put(CTRL_NAME, ctrl);
+        nameTable.put(ctrl, CTRL_NAME);
+        modifierKeyTable.put(SHIFT_NAME, shift);
+        nameTable.put(shift, SHIFT_NAME);
+        modifierKeyTable.put(M1_NAME,
+                "carbon".equals(SWT.getPlatform()) ? command : ctrl); //$NON-NLS-1$
+        modifierKeyTable.put(M2_NAME, shift);
+        modifierKeyTable.put(M3_NAME, alt);
+        modifierKeyTable.put(M4_NAME, "carbon".equals(SWT.getPlatform()) ? ctrl //$NON-NLS-1$
+                : command);
+
+        Integer arrowDown = new Integer(SWT.ARROW_DOWN);
+        naturalKeyTable.put(ARROW_DOWN_NAME, arrowDown);
+        nameTable.put(arrowDown, ARROW_DOWN_NAME);
+        Integer arrowLeft = new Integer(SWT.ARROW_LEFT);
+        naturalKeyTable.put(ARROW_LEFT_NAME, arrowLeft);
+        nameTable.put(arrowLeft, ARROW_LEFT_NAME);
+        Integer arrowRight = new Integer(SWT.ARROW_RIGHT);
+        naturalKeyTable.put(ARROW_RIGHT_NAME, arrowRight);
+        nameTable.put(arrowRight, ARROW_RIGHT_NAME);
+        Integer arrowUp = new Integer(SWT.ARROW_UP);
+        naturalKeyTable.put(ARROW_UP_NAME, arrowUp);
+        nameTable.put(arrowUp, ARROW_UP_NAME);
+        Integer breakKey = new Integer(SWT.BREAK);
+        naturalKeyTable.put(BREAK_NAME, breakKey);
+        nameTable.put(breakKey, BREAK_NAME);
+        Integer bs = new Integer(SWT.BS);
+        naturalKeyTable.put(BS_NAME, bs);
+        nameTable.put(bs, BS_NAME);
+        naturalKeyTable.put(BACKSPACE_NAME, bs);
+        Integer capsLock = new Integer(SWT.CAPS_LOCK);
+        naturalKeyTable.put(CAPS_LOCK_NAME, capsLock);
+        nameTable.put(capsLock, CAPS_LOCK_NAME);
+        Integer cr = new Integer(SWT.CR);
+        naturalKeyTable.put(CR_NAME, cr);
+        nameTable.put(cr, CR_NAME);
+        naturalKeyTable.put(ENTER_NAME, cr);
+        naturalKeyTable.put(RETURN_NAME, cr);
+        Integer del = new Integer(SWT.DEL);
+        naturalKeyTable.put(DEL_NAME, del);
+        nameTable.put(del, DEL_NAME);
+        naturalKeyTable.put(DELETE_NAME, del);
+        Integer end = new Integer(SWT.END);
+        naturalKeyTable.put(END_NAME, end);
+        nameTable.put(end, END_NAME);
+        Integer esc = new Integer(SWT.ESC);
+        naturalKeyTable.put(ESC_NAME, esc);
+        nameTable.put(esc, ESC_NAME);
+        naturalKeyTable.put(ESCAPE_NAME, esc);
+        Integer f1 = new Integer(SWT.F1);
+        naturalKeyTable.put(F1_NAME, f1);
+        nameTable.put(f1, F1_NAME);
+        Integer f2 = new Integer(SWT.F2);
+        naturalKeyTable.put(F2_NAME, new Integer(SWT.F2));
+        nameTable.put(f2, F2_NAME);
+        Integer f3 = new Integer(SWT.F3);
+        naturalKeyTable.put(F3_NAME, new Integer(SWT.F3));
+        nameTable.put(f3, F3_NAME);
+        Integer f4 = new Integer(SWT.F4);
+        naturalKeyTable.put(F4_NAME, new Integer(SWT.F4));
+        nameTable.put(f4, F4_NAME);
+        Integer f5 = new Integer(SWT.F5);
+        naturalKeyTable.put(F5_NAME, new Integer(SWT.F5));
+        nameTable.put(f5, F5_NAME);
+        Integer f6 = new Integer(SWT.F6);
+        naturalKeyTable.put(F6_NAME, new Integer(SWT.F6));
+        nameTable.put(f6, F6_NAME);
+        Integer f7 = new Integer(SWT.F7);
+        naturalKeyTable.put(F7_NAME, new Integer(SWT.F7));
+        nameTable.put(f7, F7_NAME);
+        Integer f8 = new Integer(SWT.F8);
+        naturalKeyTable.put(F8_NAME, new Integer(SWT.F8));
+        nameTable.put(f8, F8_NAME);
+        Integer f9 = new Integer(SWT.F9);
+        naturalKeyTable.put(F9_NAME, new Integer(SWT.F9));
+        nameTable.put(f9, F9_NAME);
+        Integer f10 = new Integer(SWT.F10);
+        naturalKeyTable.put(F10_NAME, new Integer(SWT.F10));
+        nameTable.put(f10, F10_NAME);
+        Integer f11 = new Integer(SWT.F11);
+        naturalKeyTable.put(F11_NAME, new Integer(SWT.F11));
+        nameTable.put(f11, F11_NAME);
+        Integer f12 = new Integer(SWT.F12);
+        naturalKeyTable.put(F12_NAME, new Integer(SWT.F12));
+        nameTable.put(f12, F12_NAME);
+        Integer f13 = new Integer(SWT.F13);
+        naturalKeyTable.put(F13_NAME, new Integer(SWT.F13));
+        nameTable.put(f13, F13_NAME);
+        Integer f14 = new Integer(SWT.F14);
+        naturalKeyTable.put(F14_NAME, new Integer(SWT.F14));
+        nameTable.put(f14, F14_NAME);
+        Integer f15 = new Integer(SWT.F15);
+        naturalKeyTable.put(F15_NAME, new Integer(SWT.F15));
+        nameTable.put(f15, F15_NAME);
+        Integer ff = new Integer(12); // ASCII 0x0C
+        naturalKeyTable.put(FF_NAME, ff);
+        nameTable.put(ff, FF_NAME);
+        Integer home = new Integer(SWT.HOME);
+        naturalKeyTable.put(HOME_NAME, home);
+        nameTable.put(home, HOME_NAME);
+        Integer insert = new Integer(SWT.INSERT);
+        naturalKeyTable.put(INSERT_NAME, insert);
+        nameTable.put(insert, INSERT_NAME);
+        Integer lf = new Integer(SWT.LF);
+        naturalKeyTable.put(LF_NAME, lf);
+        nameTable.put(lf, LF_NAME);
+        Integer nul = new Integer(SWT.NULL);
+        naturalKeyTable.put(NUL_NAME, nul);
+        nameTable.put(nul, NUL_NAME);
+        Integer numLock = new Integer(SWT.NUM_LOCK);
+        naturalKeyTable.put(NUM_LOCK_NAME, numLock);
+        nameTable.put(numLock, NUM_LOCK_NAME);
+        Integer keypad0 = new Integer(SWT.KEYPAD_0);
+        naturalKeyTable.put(NUMPAD_0_NAME, keypad0);
+        nameTable.put(keypad0, NUMPAD_0_NAME);
+        Integer keypad1 = new Integer(SWT.KEYPAD_1);
+        naturalKeyTable.put(NUMPAD_1_NAME, keypad1);
+        nameTable.put(keypad1, NUMPAD_1_NAME);
+        Integer keypad2 = new Integer(SWT.KEYPAD_2);
+        naturalKeyTable.put(NUMPAD_2_NAME, keypad2);
+        nameTable.put(keypad2, NUMPAD_2_NAME);
+        Integer keypad3 = new Integer(SWT.KEYPAD_3);
+        naturalKeyTable.put(NUMPAD_3_NAME, keypad3);
+        nameTable.put(keypad3, NUMPAD_3_NAME);
+        Integer keypad4 = new Integer(SWT.KEYPAD_4);
+        naturalKeyTable.put(NUMPAD_4_NAME, keypad4);
+        nameTable.put(keypad4, NUMPAD_4_NAME);
+        Integer keypad5 = new Integer(SWT.KEYPAD_5);
+        naturalKeyTable.put(NUMPAD_5_NAME, keypad5);
+        nameTable.put(keypad5, NUMPAD_5_NAME);
+        Integer keypad6 = new Integer(SWT.KEYPAD_6);
+        naturalKeyTable.put(NUMPAD_6_NAME, keypad6);
+        nameTable.put(keypad6, NUMPAD_6_NAME);
+        Integer keypad7 = new Integer(SWT.KEYPAD_7);
+        naturalKeyTable.put(NUMPAD_7_NAME, keypad7);
+        nameTable.put(keypad7, NUMPAD_7_NAME);
+        Integer keypad8 = new Integer(SWT.KEYPAD_8);
+        naturalKeyTable.put(NUMPAD_8_NAME, keypad8);
+        nameTable.put(keypad8, NUMPAD_8_NAME);
+        Integer keypad9 = new Integer(SWT.KEYPAD_9);
+        naturalKeyTable.put(NUMPAD_9_NAME, keypad9);
+        nameTable.put(keypad9, NUMPAD_9_NAME);
+        Integer keypadAdd = new Integer(SWT.KEYPAD_ADD);
+        naturalKeyTable.put(NUMPAD_ADD_NAME, keypadAdd);
+        nameTable.put(keypadAdd, NUMPAD_ADD_NAME);
+        Integer keypadDecimal = new Integer(SWT.KEYPAD_DECIMAL);
+        naturalKeyTable.put(NUMPAD_DECIMAL_NAME, keypadDecimal);
+        nameTable.put(keypadDecimal, NUMPAD_DECIMAL_NAME);
+        Integer keypadDivide = new Integer(SWT.KEYPAD_DIVIDE);
+        naturalKeyTable.put(NUMPAD_DIVIDE_NAME, keypadDivide);
+        nameTable.put(keypadDivide, NUMPAD_DIVIDE_NAME);
+        Integer keypadCr = new Integer(SWT.KEYPAD_CR);
+        naturalKeyTable.put(NUMPAD_ENTER_NAME, keypadCr);
+        nameTable.put(keypadCr, NUMPAD_ENTER_NAME);
+        Integer keypadEqual = new Integer(SWT.KEYPAD_EQUAL);
+        naturalKeyTable.put(NUMPAD_EQUAL_NAME, keypadEqual);
+        nameTable.put(keypadEqual, NUMPAD_EQUAL_NAME);
+        Integer keypadMultiply = new Integer(SWT.KEYPAD_MULTIPLY);
+        naturalKeyTable.put(NUMPAD_MULTIPLY_NAME, keypadMultiply);
+        nameTable.put(keypadMultiply, NUMPAD_MULTIPLY_NAME);
+        Integer keypadSubtract = new Integer(SWT.KEYPAD_SUBTRACT);
+        naturalKeyTable.put(NUMPAD_SUBTRACT_NAME, keypadSubtract);
+        nameTable.put(keypadSubtract, NUMPAD_SUBTRACT_NAME);
+        Integer pageDown = new Integer(SWT.PAGE_DOWN);
+        naturalKeyTable.put(PAGE_DOWN_NAME, pageDown);
+        nameTable.put(pageDown, PAGE_DOWN_NAME);
+        Integer pageUp = new Integer(SWT.PAGE_UP);
+        naturalKeyTable.put(PAGE_UP_NAME, pageUp);
+        nameTable.put(pageUp, PAGE_UP_NAME);
+        Integer pause = new Integer(SWT.PAUSE);
+        naturalKeyTable.put(PAUSE_NAME, pause);
+        nameTable.put(pause, PAUSE_NAME);
+        Integer printScreen = new Integer(SWT.PRINT_SCREEN);
+        naturalKeyTable.put(PRINT_SCREEN_NAME, printScreen);
+        nameTable.put(printScreen, PRINT_SCREEN_NAME);
+        Integer scrollLock = new Integer(SWT.SCROLL_LOCK);
+        naturalKeyTable.put(SCROLL_LOCK_NAME, scrollLock);
+        nameTable.put(scrollLock, SCROLL_LOCK_NAME);
+        Integer space = new Integer(' ');
+        naturalKeyTable.put(SPACE_NAME, space);
+        nameTable.put(space, SPACE_NAME);
+        Integer tab = new Integer(SWT.TAB);
+        naturalKeyTable.put(TAB_NAME, tab);
+        nameTable.put(tab, TAB_NAME);
+        Integer vt = new Integer(11); // ASCII 0x0B
+        naturalKeyTable.put(VT_NAME, vt);
+        nameTable.put(vt, VT_NAME);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.IKeyLookup#formalKeyLookup(java.lang.String)
+     *
+     */
+    public final int formalKeyLookup(String name) {
+        Object value = naturalKeyTable.get(name);
+        if (cast(Integer)value ) {
+            return (cast(Integer) value).intValue();
+        }
+
+        if (name.length > 0) {
+            throw new IllegalArgumentException("Unrecognized formal key name: " //$NON-NLS-1$
+                    ~ name);
+        }
+
+        return name.charAt(0);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.IKeyLookup#formalKeyLookupInteger(java.lang.String)
+     *
+     */
+    public final Integer formalKeyLookupInteger(String name) {
+        Object value = naturalKeyTable.get(name);
+        if (cast(Integer)value ) {
+            return cast(Integer) value;
+        }
+
+        return new Integer(name.charAt(0));
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.IKeyLookup#formalModifierLookup(java.lang.String)
+     *
+     */
+    public final int formalModifierLookup(String name) {
+        Object value = modifierKeyTable.get(name);
+        if (cast(Integer)value ) {
+            return (cast(Integer) value).intValue();
+        }
+
+        return 0;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.IKeyLookup#formalNameLookup(int)
+     *
+     */
+    public final String formalNameLookup(int key) {
+        Integer keyObject = new Integer(key);
+        if (nameTable.containsKey(keyObject) ) {
+            return stringcast(nameTable.get(keyObject));
+        }
+
+        return dcharToString( cast(dchar) key );
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.IKeyLookup#getAlt()
+     *
+     */
+    public final int getAlt() {
+        return SWT.ALT;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.IKeyLookup#getCommand()
+     *
+     */
+    public final int getCommand() {
+        return SWT.COMMAND;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.IKeyLookup#getCtrl()
+     *
+     */
+    public final int getCtrl() {
+        return SWT.CTRL;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.IKeyLookup#getShift()
+     *
+     */
+    public final int getShift() {
+        return SWT.SHIFT;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.IKeyLookup#isModifierKey(int)
+     *
+     */
+    public final bool isModifierKey(int key) {
+        return ((key & SWT.MODIFIER_MASK) !is 0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/keys/SWTKeySupport.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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 org.eclipse.jface.bindings.keys.SWTKeySupport;
+
+import org.eclipse.jface.bindings.keys.KeyStroke;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.jface.bindings.keys.formatting.IKeyFormatter;
+import org.eclipse.jface.bindings.keys.formatting.NativeKeyFormatter;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * A utility class for converting SWT events into key strokes.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class SWTKeySupport {
+
+    /**
+     * A formatter that displays key sequences in a style native to the
+     * platform.
+     */
+    private static const IKeyFormatter NATIVE_FORMATTER;
+
+    static this(){
+        NATIVE_FORMATTER = new NativeKeyFormatter();
+    }
+
+    /**
+     * Given an SWT accelerator value, provide the corresponding key stroke.
+     *
+     * @param accelerator
+     *            The accelerator to convert; should be a valid SWT accelerator
+     *            value.
+     * @return The equivalent key stroke; never <code>null</code>.
+     */
+    public static final KeyStroke convertAcceleratorToKeyStroke(int accelerator) {
+        int modifierKeys = accelerator & SWT.MODIFIER_MASK;
+        int naturalKey;
+        if (accelerator is modifierKeys) {
+            naturalKey = KeyStroke.NO_KEY;
+        } else {
+            naturalKey = accelerator - modifierKeys;
+        }
+
+        return KeyStroke.getInstance(modifierKeys, naturalKey);
+    }
+
+    /**
+     * <p>
+     * Converts the given event into an SWT accelerator value -- considering the
+     * modified character with the shift modifier. This is the third accelerator
+     * value that should be checked when processing incoming key events.
+     * </p>
+     * <p>
+     * For example, on a standard US keyboard, "Ctrl+Shift+5" would be viewed as
+     * "Ctrl+Shift+%".
+     * </p>
+     *
+     * @param event
+     *            The event to be converted; must not be <code>null</code>.
+     * @return The combination of the state mask and the unmodified character.
+     */
+    public static final int convertEventToModifiedAccelerator(Event event) {
+        int modifiers = event.stateMask & SWT.MODIFIER_MASK;
+        char character = topKey(event);
+        return modifiers + toUpperCase(character);
+    }
+
+    /**
+     * <p>
+     * Converts the given event into an SWT accelerator value -- considering the
+     * unmodified character with all modifier keys. This is the first
+     * accelerator value that should be checked when processing incoming key
+     * events. However, all alphabetic characters are considered as their
+     * uppercase equivalents.
+     * </p>
+     * <p>
+     * For example, on a standard US keyboard, "Ctrl+Shift+5" would be viewed as
+     * "Ctrl+Shift+5".
+     * </p>
+     *
+     * @param event
+     *            The event to be converted; must not be <code>null</code>.
+     * @return The combination of the state mask and the unmodified character.
+     */
+    public static final int convertEventToUnmodifiedAccelerator(
+            Event event) {
+        return convertEventToUnmodifiedAccelerator(event.stateMask,
+                event.keyCode);
+    }
+
+    /**
+     * <p>
+     * Converts the given state mask and key code into an SWT accelerator value --
+     * considering the unmodified character with all modifier keys. All
+     * alphabetic characters are considered as their uppercase equivalents.
+     * </p>
+     * <p>
+     * For example, on a standard US keyboard, "Ctrl+Shift+5" would be viewed as
+     * "Ctrl+Shift+5".
+     * </p>
+     *
+     * @param stateMask
+     *            The integer mask of modifiers keys depressed when this was
+     *            pressed.
+     * @param keyCode
+     *            The key that was pressed, before being modified.
+     * @return The combination of the state mask and the unmodified character.
+     */
+    private static final int convertEventToUnmodifiedAccelerator(
+            int stateMask, int keyCode) {
+        int modifiers = stateMask & SWT.MODIFIER_MASK;
+        int character = keyCode;
+        return modifiers + toUpperCase(character);
+    }
+
+    /**
+     * <p>
+     * Converts the given event into an SWT accelerator value -- considering the
+     * unmodified character with all modifier keys. This is the first
+     * accelerator value that should be checked. However, all alphabetic
+     * characters are considered as their uppercase equivalents.
+     * </p>
+     * <p>
+     * For example, on a standard US keyboard, "Ctrl+Shift+5" would be viewed as
+     * "Ctrl+%".
+     * </p>
+     *
+     * @param event
+     *            The event to be converted; must not be <code>null</code>.
+     * @return The combination of the state mask and the unmodified character.
+     */
+    public static final int convertEventToUnmodifiedAccelerator(
+            KeyEvent event) {
+        return convertEventToUnmodifiedAccelerator(event.stateMask,
+                event.keyCode);
+    }
+
+    /**
+     * Converts the given event into an SWT accelerator value -- considering the
+     * modified character without the shift modifier. This is the second
+     * accelerator value that should be checked when processing incoming key
+     * events. Key strokes with alphabetic natural keys are run through
+     * <code>convertEventToUnmodifiedAccelerator</code>.
+     *
+     * @param event
+     *            The event to be converted; must not be <code>null</code>.
+     * @return The combination of the state mask without shift, and the modified
+     *         character.
+     */
+    public static final int convertEventToUnshiftedModifiedAccelerator(
+            Event event) {
+        // Disregard alphabetic key strokes.
+        if (CharacterIsLetter(cast(dchar) event.keyCode)) {
+            return convertEventToUnmodifiedAccelerator(event);
+        }
+
+        int modifiers = event.stateMask & (SWT.MODIFIER_MASK ^ SWT.SHIFT);
+        char character = topKey(event);
+        return modifiers + toUpperCase(character);
+    }
+
+    /**
+     * Given a key stroke, this method provides the equivalent SWT accelerator
+     * value. The functional inverse of
+     * <code>convertAcceleratorToKeyStroke</code>.
+     *
+     * @param keyStroke
+     *            The key stroke to convert; must not be <code>null</code>.
+     * @return The SWT accelerator value
+     */
+    public static final int convertKeyStrokeToAccelerator(
+            KeyStroke keyStroke) {
+        return keyStroke.getModifierKeys() + keyStroke.getNaturalKey();
+    }
+
+    /**
+     * Provides an instance of <code>IKeyFormatter</code> appropriate for the
+     * current instance.
+     *
+     * @return an instance of <code>IKeyFormatter</code> appropriate for the
+     *         current instance; never <code>null</code>.
+     */
+    public static IKeyFormatter getKeyFormatterForPlatform() {
+        return NATIVE_FORMATTER;
+    }
+
+    /**
+     * Makes sure that a fully-modified character is converted to the normal
+     * form. This means that "Ctrl+" key strokes must reverse the modification
+     * caused by control-escaping. Also, all lower case letters are converted to
+     * uppercase.
+     *
+     * @param event
+     *            The event from which the fully-modified character should be
+     *            pulled.
+     * @return The modified character, uppercase and without control-escaping.
+     */
+    private static final char topKey(Event event) {
+        char character = event.character;
+        bool ctrlDown = (event.stateMask & SWT.CTRL) !is 0;
+
+        if (ctrlDown && event.character !is event.keyCode
+                && event.character < 0x20
+                && (event.keyCode & SWT.KEYCODE_BIT) is 0) {
+            character += 0x40;
+        }
+
+        return character;
+    }
+
+    /**
+     * Makes the given character uppercase if it is a letter.
+     *
+     * @param keyCode
+     *            The character to convert.
+     * @return The uppercase equivalent, if any; otherwise, the character
+     *         itself.
+     */
+    private static final int toUpperCase(int keyCode) {
+        // Will this key code be truncated?
+        if (keyCode > 0xFFFF) {
+            return keyCode;
+        }
+
+        // Downcast in safety. Only make characters uppercase.
+        char character = cast(char) keyCode;
+        return CharacterIsLetter(character) ? CharacterToUpper(character)
+                : keyCode;
+    }
+
+    /**
+     * This class should never be instantiated.
+     */
+    protected this() {
+        // This class should never be instantiated.
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/keys/formatting/AbstractKeyFormatter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,183 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.bindings.keys.formatting.AbstractKeyFormatter;
+
+import org.eclipse.jface.bindings.keys.formatting.IKeyFormatter;
+
+
+import org.eclipse.jface.bindings.keys.IKeyLookup;
+import org.eclipse.jface.bindings.keys.KeyLookupFactory;
+import org.eclipse.jface.bindings.keys.KeySequence;
+import org.eclipse.jface.bindings.keys.KeyStroke;
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.ResourceBundle;
+
+/**
+ * <p>
+ * An abstract implementation of a key formatter that provides a lot of common
+ * key formatting functionality. It is recommended that implementations of
+ * <code>IKeyFormatter</code> subclass from here, rather than implementing
+ * <code>IKeyFormatter</code> directly.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class AbstractKeyFormatter : IKeyFormatter {
+
+    /**
+     * The key for the delimiter between keys. This is used in the
+     * internationalization bundles.
+     */
+    protected static const String KEY_DELIMITER_KEY = "KEY_DELIMITER"; //$NON-NLS-1$
+
+    /**
+     * The key for the delimiter between key strokes. This is used in the
+     * internationalization bundles.
+     */
+    protected static const String KEY_STROKE_DELIMITER_KEY = "KEY_STROKE_DELIMITER"; //$NON-NLS-1$
+
+    /**
+     * An empty integer array that can be used in
+     * <code>sortModifierKeys(int)</code>.
+     */
+    protected static const int[] NO_MODIFIER_KEYS = null;
+
+    /**
+     * The bundle in which to look up the internationalized text for all of the
+     * individual keys in the system. This is the platform-agnostic version of
+     * the internationalized strings. Some platforms (namely Carbon) provide
+     * special Unicode characters and glyphs for some keys.
+     */
+    private static const ResourceBundle RESOURCE_BUNDLE;
+
+    /**
+     * The keys in the resource bundle. This is used to avoid missing resource
+     * exceptions when they aren't necessary.
+     */
+    private static const Set resourceBundleKeys;
+
+    static this() {
+        RESOURCE_BUNDLE = ResourceBundle.getBundle(
+            getImportData!("org.eclipse.jface.bindings.keys.formatting.AbstractKeyFormatter.properties"));
+        resourceBundleKeys = new HashSet();
+        foreach( element; RESOURCE_BUNDLE.getKeys()){
+            resourceBundleKeys.add(stringcast(element));
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keysKeyFormatter#format(org.eclipse.jface.bindings.keys.KeySequence)
+     */
+    public String format(int key) {
+        IKeyLookup lookup = KeyLookupFactory.getDefault();
+        String name = lookup.formalNameLookup(key);
+
+        if (resourceBundleKeys.contains(name)) {
+            return Util.translateString(RESOURCE_BUNDLE, name, name);
+        }
+
+        return name;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.KeyFormatter#format(org.eclipse.jface.bindings.keys.KeySequence)
+     */
+    public String format(KeySequence keySequence) {
+        StringBuffer stringBuffer = new StringBuffer();
+
+        KeyStroke[] keyStrokes = keySequence.getKeyStrokes();
+        int keyStrokesLength = keyStrokes.length;
+        for (int i = 0; i < keyStrokesLength; i++) {
+            stringBuffer.append(format(keyStrokes[i]));
+
+            if (i + 1 < keyStrokesLength) {
+                stringBuffer.append(getKeyStrokeDelimiter());
+            }
+        }
+
+        return stringBuffer.toString();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.KeyFormatter#formatKeyStroke(org.eclipse.jface.bindings.keys.KeyStroke)
+     */
+    public String format(KeyStroke keyStroke) {
+        String keyDelimiter = getKeyDelimiter();
+
+        // Format the modifier keys, in sorted order.
+        int modifierKeys = keyStroke.getModifierKeys();
+        int[] sortedModifierKeys = sortModifierKeys(modifierKeys);
+        StringBuffer stringBuffer = new StringBuffer();
+        if (sortedModifierKeys !is null) {
+            for (int i = 0; i < sortedModifierKeys.length; i++) {
+                int modifierKey = sortedModifierKeys[i];
+                if (modifierKey !is KeyStroke.NO_KEY) {
+                    stringBuffer.append(format(modifierKey));
+                    stringBuffer.append(keyDelimiter);
+                }
+            }
+        }
+
+        // Format the natural key, if any.
+        int naturalKey = keyStroke.getNaturalKey();
+        if (naturalKey !is 0) {
+            stringBuffer.append(format(naturalKey));
+        }
+
+        return stringBuffer.toString();
+
+    }
+
+    /**
+     * An accessor for the delimiter you wish to use between keys. This is used
+     * by the default format implementations to determine the key delimiter.
+     *
+     * @return The delimiter to use between keys; should not be
+     *         <code>null</code>.
+     */
+    protected abstract String getKeyDelimiter();
+
+    /**
+     * An accessor for the delimiter you wish to use between key strokes. This
+     * used by the default format implementations to determine the key stroke
+     * delimiter.
+     *
+     * @return The delimiter to use between key strokes; should not be
+     *         <code>null</code>.
+     */
+    protected abstract String getKeyStrokeDelimiter();
+
+    /**
+     * Separates the modifier keys from each other, and then places them in an
+     * array in some sorted order. The sort order is dependent on the type of
+     * formatter.
+     *
+     * @param modifierKeys
+     *            The modifier keys from the key stroke.
+     * @return An array of modifier key values -- separated and sorted in some
+     *         order. Any values in this array that are
+     *         <code>KeyStroke.NO_KEY</code> should be ignored.
+     */
+    protected abstract int[] sortModifierKeys(int modifierKeys);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/keys/formatting/EmacsKeyFormatter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.bindings.keys.formatting.EmacsKeyFormatter;
+
+import org.eclipse.jface.bindings.keys.formatting.AbstractKeyFormatter;
+
+import org.eclipse.jface.bindings.keys.IKeyLookup;
+import org.eclipse.jface.bindings.keys.KeyLookupFactory;
+import org.eclipse.jface.bindings.keys.KeySequence;
+import org.eclipse.jface.bindings.keys.KeyStroke;
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.ResourceBundle;
+
+/**
+ * <p>
+ * A key formatter providing the Emacs-style accelerators using single letters
+ * to represent the modifier keys.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class EmacsKeyFormatter : AbstractKeyFormatter {
+    alias AbstractKeyFormatter.format format;
+
+    /**
+     * The resource bundle used by <code>format()</code> to translate formal
+     * string representations by locale.
+     */
+    private const static ResourceBundle RESOURCE_BUNDLE;
+
+    static this(){
+        RESOURCE_BUNDLE = ResourceBundle.getBundle(
+            getImportData!("org.eclipse.jface.bindings.keys.formatting.EmacsKeyFormatter.properties"));
+    }
+    /**
+     * Formats an individual key into a human readable format. This converts the
+     * key into a format similar to Xemacs.
+     *
+     * @param key
+     *            The key to format; must not be <code>null</code>.
+     * @return The key formatted as a string; should not be <code>null</code>.
+     */
+    public override String format(int key) {
+        IKeyLookup lookup = KeyLookupFactory.getDefault();
+        if (lookup.isModifierKey(key)) {
+            String formattedName = Util.translateString(RESOURCE_BUNDLE, lookup
+                    .formalNameLookup(key), null);
+            if (formattedName !is null) {
+                return formattedName;
+            }
+        }
+
+        return super.format(key).toLowerCase();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.AbstractKeyFormatter#getKeyDelimiter()
+     */
+    protected override String getKeyDelimiter() {
+        return Util.translateString(RESOURCE_BUNDLE, KEY_DELIMITER_KEY,
+                KeyStroke.KEY_DELIMITER);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.AbstractKeyFormatter#getKeyStrokeDelimiter()
+     */
+    protected override String getKeyStrokeDelimiter() {
+        return Util.translateString(RESOURCE_BUNDLE, KEY_STROKE_DELIMITER_KEY,
+                KeySequence.KEY_STROKE_DELIMITER);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.AbstractKeyFormatter#sortModifierKeys(int)
+     */
+    protected override int[] sortModifierKeys(int modifierKeys) {
+        IKeyLookup lookup = KeyLookupFactory.getDefault();
+        int[] sortedKeys = new int[4];
+        int index = 0;
+
+        if ((modifierKeys & lookup.getAlt()) !is 0) {
+            sortedKeys[index++] = lookup.getAlt();
+        }
+        if ((modifierKeys & lookup.getCommand()) !is 0) {
+            sortedKeys[index++] = lookup.getCommand();
+        }
+        if ((modifierKeys & lookup.getCtrl()) !is 0) {
+            sortedKeys[index++] = lookup.getCtrl();
+        }
+        if ((modifierKeys & lookup.getShift()) !is 0) {
+            sortedKeys[index++] = lookup.getShift();
+        }
+
+        return sortedKeys;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/keys/formatting/FormalKeyFormatter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.bindings.keys.formatting.FormalKeyFormatter;
+
+import org.eclipse.jface.bindings.keys.formatting.AbstractKeyFormatter;
+
+import org.eclipse.jface.bindings.keys.IKeyLookup;
+import org.eclipse.jface.bindings.keys.KeyLookupFactory;
+import org.eclipse.jface.bindings.keys.KeySequence;
+import org.eclipse.jface.bindings.keys.KeyStroke;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * Formats the keys in the internal key sequence grammar. This is used for
+ * persistence, and is not really intended for display to the user.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class FormalKeyFormatter : AbstractKeyFormatter {
+    alias AbstractKeyFormatter.format format;
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.KeyFormatter#format(org.eclipse.ui.keys.KeySequence)
+     */
+    public override String format(int key) {
+        IKeyLookup lookup = KeyLookupFactory.getDefault();
+        return lookup.formalNameLookup(key);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.AbstractKeyFormatter#getKeyDelimiter()
+     */
+    protected override String getKeyDelimiter() {
+        return KeyStroke.KEY_DELIMITER;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.AbstractKeyFormatter#getKeyStrokeDelimiter()
+     */
+    protected override String getKeyStrokeDelimiter() {
+        return KeySequence.KEY_STROKE_DELIMITER;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.AbstractKeyFormatter#sortModifierKeys(int)
+     */
+    protected override int[] sortModifierKeys(int modifierKeys) {
+        IKeyLookup lookup = KeyLookupFactory.getDefault();
+        int[] sortedKeys = new int[4];
+        int index = 0;
+
+        if ((modifierKeys & lookup.getAlt()) !is 0) {
+            sortedKeys[index++] = lookup.getAlt();
+        }
+        if ((modifierKeys & lookup.getCommand()) !is 0) {
+            sortedKeys[index++] = lookup.getCommand();
+        }
+        if ((modifierKeys & lookup.getCtrl()) !is 0) {
+            sortedKeys[index++] = lookup.getCtrl();
+        }
+        if ((modifierKeys & lookup.getShift()) !is 0) {
+            sortedKeys[index++] = lookup.getShift();
+        }
+
+        return sortedKeys;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/keys/formatting/IKeyFormatter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.bindings.keys.formatting.IKeyFormatter;
+
+import org.eclipse.jface.bindings.keys.KeySequence;
+import org.eclipse.jface.bindings.keys.KeyStroke;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * Any formatter capable of taking a key sequence or a key stroke and converting
+ * it into a string. These formatters are used to produce the strings that the
+ * user sees in the keys preference page and the menus, as well as the strings
+ * that are used for persistent storage.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IKeyFormatter {
+
+    /**
+     * Formats an individual key into a human readable format. This uses an
+     * internationalization resource bundle to look up the key. This does not do
+     * any platform-specific formatting (e.g., Carbon's command character).
+     *
+     * @param key
+     *            The key to format.
+     * @return The key formatted as a string; should not be <code>null</code>.
+     */
+    String format(int key);
+
+    /**
+     * Format the given key sequence into a string. The manner of the conversion
+     * is dependent on the formatter. It is required that unequal key sequences
+     * return unequal strings.
+     *
+     * @param keySequence
+     *            The key sequence to convert; must not be <code>null</code>.
+     * @return A string representation of the key sequence; must not be
+     *         <code>null</code>.
+     */
+    String format(KeySequence keySequence);
+
+    /**
+     * Format the given key strokes into a string. The manner of the conversion
+     * is dependent on the formatter. It is required that unequal key strokes
+     * return unequal strings.
+     *
+     * @param keyStroke
+     *            The key stroke to convert; must not be <Code>null</code>.
+     * @return A string representation of the key stroke; must not be <code>
+     *         null</code>
+     */
+    String format(KeyStroke keyStroke);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/keys/formatting/KeyFormatterFactory.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.bindings.keys.formatting.KeyFormatterFactory;
+
+import org.eclipse.jface.bindings.keys.formatting.FormalKeyFormatter;
+import org.eclipse.jface.bindings.keys.formatting.EmacsKeyFormatter;
+import org.eclipse.jface.bindings.keys.formatting.IKeyFormatter;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * <p>
+ * A cache for formatters. It keeps a few instances of pre-defined instances of
+ * <code>IKeyFormatter</code> available for use. It also allows the default
+ * formatter to be changed.
+ * </p>
+ *
+ * @since 3.1
+ * @see org.eclipse.jface.bindings.keys.formatting.IKeyFormatter
+ */
+public final class KeyFormatterFactory {
+
+    /**
+     * The formatter that renders key bindings in a platform-dependent manner.
+     */
+    private static /+const+/ IKeyFormatter FORMAL_KEY_FORMATTER;
+
+    /**
+     * The formatter that renders key bindings in a form similar to XEmacs
+     */
+    private static /+const+/ IKeyFormatter EMACS_KEY_FORMATTER;
+
+    /**
+     * The default formatter. This is normally the formal key formatter, but can
+     * be changed by users of this API.
+     */
+    private static IKeyFormatter defaultKeyFormatter;
+
+    private static void check_static_init(){
+        if( FORMAL_KEY_FORMATTER is null ){
+            synchronized if( FORMAL_KEY_FORMATTER is null ){
+                FORMAL_KEY_FORMATTER = new FormalKeyFormatter();
+                EMACS_KEY_FORMATTER = new EmacsKeyFormatter();
+                defaultKeyFormatter = FORMAL_KEY_FORMATTER;
+            }
+        }
+    }
+
+    /**
+     * An accessor for the current default key formatter.
+     *
+     * @return The default formatter; never <code>null</code>.
+     */
+    public static final IKeyFormatter getDefault() {
+        check_static_init();
+        return defaultKeyFormatter;
+    }
+
+    /**
+     * Provides an instance of <code>EmacsKeyFormatter</code>.
+     *
+     * @return The Xemacs formatter; never <code>null</code>.
+     */
+    public static final IKeyFormatter getEmacsKeyFormatter() {
+        check_static_init();
+        return EMACS_KEY_FORMATTER;
+    }
+
+    /**
+     * Provides an instance of <code>FormalKeyFormatter</code>.
+     *
+     * @return The formal formatter; never <code>null</code>.
+     */
+    public static final IKeyFormatter getFormalKeyFormatter() {
+        check_static_init();
+        return FORMAL_KEY_FORMATTER;
+    }
+
+    /**
+     * Sets the default key formatter.
+     *
+     * @param defaultKeyFormatter
+     *            the default key formatter. Must not be <code>null</code>.
+     */
+    public static final void setDefault(IKeyFormatter defaultKeyFormatter) {
+        check_static_init();
+        if (defaultKeyFormatter is null) {
+            throw new NullPointerException();
+        }
+
+        KeyFormatterFactory.defaultKeyFormatter = defaultKeyFormatter;
+    }
+
+    /**
+     * This class should not be instantiated.
+     */
+    private this() {
+        // Not to be constructred.
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/bindings/keys/formatting/NativeKeyFormatter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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 org.eclipse.jface.bindings.keys.formatting.NativeKeyFormatter;
+
+import org.eclipse.jface.bindings.keys.formatting.AbstractKeyFormatter;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.jface.bindings.keys.IKeyLookup;
+import org.eclipse.jface.bindings.keys.KeyLookupFactory;
+import org.eclipse.jface.bindings.keys.KeySequence;
+import org.eclipse.jface.bindings.keys.KeyStroke;
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.HashMap;
+import java.util.ResourceBundle;
+
+/**
+ * <p>
+ * Formats the key sequences and key strokes into the native human-readable
+ * format. This is typically what you would see on the menus for the given
+ * platform and locale.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class NativeKeyFormatter : AbstractKeyFormatter {
+    alias AbstractKeyFormatter.format format;
+
+    /**
+     * The key into the internationalization resource bundle for the delimiter
+     * to use between keys (on the Carbon platform).
+     */
+    private const static String CARBON_KEY_DELIMITER_KEY = "CARBON_KEY_DELIMITER"; //$NON-NLS-1$
+
+    /**
+     * A look-up table for the string representations of various carbon keys.
+     */
+    private const static HashMap CARBON_KEY_LOOK_UP;
+
+    /**
+     * The resource bundle used by <code>format()</code> to translate formal
+     * string representations by locale.
+     */
+    private const static ResourceBundle RESOURCE_BUNDLE;
+
+    /**
+     * The key into the internationalization resource bundle for the delimiter
+     * to use between key strokes (on the Win32 platform).
+     */
+    private const static String WIN32_KEY_STROKE_DELIMITER_KEY = "WIN32_KEY_STROKE_DELIMITER"; //$NON-NLS-1$
+
+    static this() {
+        CARBON_KEY_LOOK_UP = new HashMap();
+        RESOURCE_BUNDLE = ResourceBundle.getBundle(
+            getImportData!("org.eclipse.jface.bindings.keys.formatting.NativeKeyFormatter.properties"));
+
+        Object carbonBackspace = stringcast("\u232B"); //$NON-NLS-1$
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.BS_NAME), carbonBackspace);
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.BACKSPACE_NAME), carbonBackspace);
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.CR_NAME), stringcast("\u21A9")); //$NON-NLS-1$
+        Object carbonDelete = stringcast("\u2326"); //$NON-NLS-1$
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.DEL_NAME), carbonDelete);
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.DELETE_NAME), carbonDelete);
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.SPACE_NAME), stringcast("\u2423")); //$NON-NLS-1$
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.ALT_NAME), stringcast("\u2325")); //$NON-NLS-1$
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.COMMAND_NAME), stringcast("\u2318")); //$NON-NLS-1$
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.CTRL_NAME), stringcast("\u2303")); //$NON-NLS-1$
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.SHIFT_NAME), stringcast("\u21E7")); //$NON-NLS-1$
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.ARROW_DOWN_NAME), stringcast("\u2193")); //$NON-NLS-1$
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.ARROW_LEFT_NAME), stringcast("\u2190")); //$NON-NLS-1$
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.ARROW_RIGHT_NAME), stringcast("\u2192")); //$NON-NLS-1$
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.ARROW_UP_NAME), stringcast("\u2191")); //$NON-NLS-1$
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.END_NAME), stringcast("\u2198")); //$NON-NLS-1$
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.NUMPAD_ENTER_NAME), stringcast("\u2324")); //$NON-NLS-1$
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.HOME_NAME), stringcast("\u2196")); //$NON-NLS-1$
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.PAGE_DOWN_NAME), stringcast("\u21DF")); //$NON-NLS-1$
+        CARBON_KEY_LOOK_UP.put(stringcast(IKeyLookup.PAGE_UP_NAME), stringcast("\u21DE")); //$NON-NLS-1$
+    }
+
+    /**
+     * Formats an individual key into a human readable format. This uses an
+     * internationalization resource bundle to look up the key. This does the
+     * platform-specific formatting for Carbon.
+     *
+     * @param key
+     *            The key to format.
+     * @return The key formatted as a string; should not be <code>null</code>.
+     */
+    public override final String format(int key) {
+        IKeyLookup lookup = KeyLookupFactory.getDefault();
+        String name = lookup.formalNameLookup(key);
+
+        // TODO consider platform-specific resource bundles
+        if ("carbon".equals(SWT.getPlatform())) { //$NON-NLS-1$
+            String formattedName = stringcast( CARBON_KEY_LOOK_UP.get(name));
+            if (formattedName !is null) {
+                return formattedName;
+            }
+        }
+
+        return super.format(key);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.AbstractKeyFormatter#getKeyDelimiter()
+     */
+    protected override String getKeyDelimiter() {
+        // We must do the look up every time, as our locale might change.
+        if ("carbon".equals(SWT.getPlatform())) { //$NON-NLS-1$
+            return Util.translateString(RESOURCE_BUNDLE,
+                    CARBON_KEY_DELIMITER_KEY, Util.ZERO_LENGTH_STRING);
+        }
+
+        return Util.translateString(RESOURCE_BUNDLE, KEY_DELIMITER_KEY,
+                KeyStroke.KEY_DELIMITER);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.AbstractKeyFormatter#getKeyStrokeDelimiter()
+     */
+    protected override String getKeyStrokeDelimiter() {
+        // We must do the look up every time, as our locale might change.
+        if ("win32".equals(SWT.getPlatform())) { //$NON-NLS-1$
+            return Util.translateString(RESOURCE_BUNDLE,
+                    WIN32_KEY_STROKE_DELIMITER_KEY,
+                    KeySequence.KEY_STROKE_DELIMITER);
+        }
+
+        return Util.translateString(RESOURCE_BUNDLE, KEY_STROKE_DELIMITER_KEY,
+                KeySequence.KEY_STROKE_DELIMITER);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.bindings.keys.AbstractKeyFormatter#sortModifierKeys(int)
+     */
+    protected override int[] sortModifierKeys(int modifierKeys) {
+        IKeyLookup lookup = KeyLookupFactory.getDefault();
+        String platform = SWT.getPlatform();
+        int[] sortedKeys = new int[4];
+        int index = 0;
+
+        if ("win32".equals(platform) || "wpf".equals(platform)) { //$NON-NLS-1$ //$NON-NLS-2$
+            if ((modifierKeys & lookup.getCtrl()) !is 0) {
+                sortedKeys[index++] = lookup.getCtrl();
+            }
+            if ((modifierKeys & lookup.getAlt()) !is 0) {
+                sortedKeys[index++] = lookup.getAlt();
+            }
+            if ((modifierKeys & lookup.getShift()) !is 0) {
+                sortedKeys[index++] = lookup.getShift();
+            }
+
+        } else if ("gtk".equals(platform) || "motif".equals(platform)) { //$NON-NLS-1$ //$NON-NLS-2$
+            if ((modifierKeys & lookup.getShift()) !is 0) {
+                sortedKeys[index++] = lookup.getShift();
+            }
+            if ((modifierKeys & lookup.getCtrl()) !is 0) {
+                sortedKeys[index++] = lookup.getCtrl();
+            }
+            if ((modifierKeys & lookup.getAlt()) !is 0) {
+                sortedKeys[index++] = lookup.getAlt();
+            }
+
+        } else if ("carbon".equals(platform)) { //$NON-NLS-1$
+            if ((modifierKeys & lookup.getShift()) !is 0) {
+                sortedKeys[index++] = lookup.getShift();
+            }
+            if ((modifierKeys & lookup.getCtrl()) !is 0) {
+                sortedKeys[index++] = lookup.getCtrl();
+            }
+            if ((modifierKeys & lookup.getAlt()) !is 0) {
+                sortedKeys[index++] = lookup.getAlt();
+            }
+            if ((modifierKeys & lookup.getCommand()) !is 0) {
+                sortedKeys[index++] = lookup.getCommand();
+            }
+
+        }
+
+        return sortedKeys;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/commands/ActionHandler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.commands.ActionHandler;
+
+
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.HandlerEvent;
+import org.eclipse.core.commands.IHandlerListener;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * <p>
+ * This class adapts instances of <code>IAction</code> to
+ * <code>IHandler</code>.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class ActionHandler : AbstractHandler {
+
+    /**
+     * The wrapped action. This value is never <code>null</code>.
+     */
+    private const IAction action;
+
+    /**
+     * The property change listener hooked on to the action. This is initialized
+     * when the first listener is attached to this handler, and is removed when
+     * the handler is disposed or the last listener is removed.
+     */
+    private IPropertyChangeListener propertyChangeListener;
+
+    /**
+     * Creates a new instance of this class given an instance of
+     * <code>IAction</code>.
+     *
+     * @param action
+     *            the action. Must not be <code>null</code>.
+     */
+    public this(IAction action) {
+        if (action is null) {
+            throw new NullPointerException();
+        }
+
+        this.action = action;
+    }
+
+    public override final void addHandlerListener(IHandlerListener handlerListener) {
+        if (!hasListeners()) {
+            attachListener();
+        }
+
+        super.addHandlerListener(handlerListener);
+    }
+
+    /**
+     * When a listener is attached to this handler, then this registers a
+     * listener with the underlying action.
+     *
+     * @since 3.1
+     */
+    private final void attachListener() {
+        if (propertyChangeListener is null) {
+            propertyChangeListener = new class() IPropertyChangeListener {
+                public final void propertyChange(
+                        PropertyChangeEvent propertyChangeEvent) {
+                    String property = propertyChangeEvent.getProperty();
+                    fireHandlerChanged(new HandlerEvent(this.outer,
+                            IAction.ENABLED.equals(property), IAction.HANDLED
+                                    .equals(property)));
+                }
+            };
+        }
+
+        this.action.addPropertyChangeListener(propertyChangeListener);
+    }
+
+    /**
+     * When no more listeners are registered, then this is used to removed the
+     * property change listener from the underlying action.
+     */
+    private final void detachListener() {
+        this.action.removePropertyChangeListener(propertyChangeListener);
+        propertyChangeListener = null;
+    }
+
+    /**
+     * Removes the property change listener from the action.
+     *
+     * @see org.eclipse.core.commands.IHandler#dispose()
+     */
+    public override final void dispose() {
+        if (hasListeners()) {
+            action.removePropertyChangeListener(propertyChangeListener);
+        }
+    }
+
+    public final Object execute(ExecutionEvent event) {
+        if ((action.getStyle() is IAction.AS_CHECK_BOX)
+                || (action.getStyle() is IAction.AS_RADIO_BUTTON)) {
+            action.setChecked(!action.isChecked());
+        }
+        Object trigger = event.getTrigger();
+        try {
+            if (auto ev = cast(Event)trigger) {
+                action.runWithEvent(ev);
+            } else {
+                action.runWithEvent(new Event());
+            }
+        } catch (Exception e) {
+            throw new ExecutionException(
+                    "While executing the action, an exception occurred", e); //$NON-NLS-1$
+        }
+        return null;
+    }
+
+    /**
+     * Returns the action associated with this handler
+     *
+     * @return the action associated with this handler (not null)
+     * @since 3.1
+     */
+    public final IAction getAction() {
+        return action;
+    }
+
+    public override final bool isEnabled() {
+        return action.isEnabled();
+    }
+
+    public override final bool isHandled() {
+        return action.isHandled();
+    }
+
+    public override final void removeHandlerListener(
+            IHandlerListener handlerListener) {
+        super.removeHandlerListener(handlerListener);
+
+        if (!hasListeners()) {
+            detachListener();
+        }
+    }
+
+    public override final String toString() {
+        return "ActionHandler(" ~ (cast(Object)action).toString ~ ")";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/commands/PersistentState.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.commands.PersistentState;
+
+import org.eclipse.core.commands.State;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * <p>
+ * This is a state that can be made persistent. A state is persisted to a
+ * preference store.
+ * </p>
+ * <p>
+ * Clients may extend this class.
+ * </p>
+ *
+ * @since 3.2
+ */
+public abstract class PersistentState : State {
+
+    /**
+     * Whether this state should be persisted.
+     */
+    private bool persisted;
+
+    /**
+     * Loads this state from the preference store, given the location at which
+     * to look. This method must be symmetric with a call to
+     * {@link #save(IPreferenceStore, String)}.
+     *
+     * @param store
+     *            The store from which to read; must not be <code>null</code>.
+     * @param preferenceKey
+     *            The key at which the state is stored; must not be
+     *            <code>null</code>.
+     */
+    public abstract void load(IPreferenceStore store,
+            String preferenceKey);
+
+    /**
+     * Saves this state to the preference store, given the location at which to
+     * write. This method must be symmetric with a call to
+     * {@link #load(IPreferenceStore, String)}.
+     *
+     * @param store
+     *            The store to which the state should be written; must not be
+     *            <code>null</code>.
+     * @param preferenceKey
+     *            The key at which the state should be stored; must not be
+     *            <code>null</code>.
+     */
+    public abstract void save(IPreferenceStore store,
+            String preferenceKey);
+
+    /**
+     * Sets whether this state should be persisted.
+     *
+     * @param persisted
+     *            Whether this state should be persisted.
+     */
+    public void setShouldPersist(bool persisted) {
+        this.persisted = persisted;
+    }
+
+    /**
+     * Whether this state should be persisted. Subclasses should check this
+     * method before loading or saving.
+     *
+     * @return <code>true</code> if this state should be persisted;
+     *         <code>false</code> otherwise.
+     */
+    public bool shouldPersist() {
+        return persisted;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/commands/RadioState.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,272 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.jface.commands.RadioState;
+
+
+import org.eclipse.core.commands.IStateListener;
+import org.eclipse.core.commands.State;
+import org.eclipse.jface.menus.IMenuStateIds;
+
+import org.eclipse.jface.commands.ToggleState;
+
+import java.lang.all;
+import java.util.Collection;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * <p>
+ * A piece of bool state grouped with other bool states. Of these states,
+ * only one may have a value of {@link Boolean#TRUE} at any given point in time.
+ * The values of all other states must be {@link Boolean#FALSE}.
+ * </p>
+ * <p>
+ * If this state is registered using {@link IMenuStateIds#STYLE}, then it will
+ * control the presentation of the command if displayed in the menus, tool bars
+ * or status line.
+ * </p>
+ * <p>
+ * Clients may instantiate or extend this interface.
+ * </p>
+ *
+ * @since 3.2
+ */
+public class RadioState : ToggleState {
+
+    /**
+     * The manager of radio groups within the application. This ensures that
+     * only one member of a radio group is active at any one time, and tracks
+     * group memberships.
+     */
+    private static final class RadioStateManager {
+
+        /**
+         * A group of radio states with the same identifier.
+         */
+        private static final class RadioGroup : IStateListener {
+
+            /**
+             * The active state. If there is no active state, then this value is
+             * <code>null</code>.
+             */
+            private RadioState active = null;
+
+            /**
+             * The current members in this group. If there are no members, then
+             * this value is <code>nlistenerull</code>.
+             */
+            private Set members = null;
+
+            /**
+             * Activates a memeber. This checks to see if there are any other
+             * active members. If there are, they are deactivated.
+             *
+             * @param state
+             *            The state that should become active; must not be
+             *            <code>null</code>.
+             */
+            private final void activateMember(RadioState state) {
+                if (active !is null && active !is state) {
+                    active.setValue(Boolean.FALSE);
+                }
+                active = state;
+            }
+
+            /**
+             * Adds a member to this radio group. If the state being added is
+             * active, then it replaces the currently active group memeber as
+             * the active state.
+             *
+             * @param state
+             *            The state to add; must not be <code>null</code>.
+             */
+            private final void addMember(RadioState state) {
+                if (members is null) {
+                    members = new HashSet();
+                }
+
+                members.add(state);
+                state.addListener(this);
+
+                Object value = state.getValue();
+                if ( auto v = cast(Boolean)value ) {
+                    if (v.booleanValue()) {
+                        activateMember(state);
+                    }
+                }
+            }
+
+            public final void handleStateChange(State state,
+                    Object oldValue) {
+                Object newValue = state.getValue();
+                if ( auto v = cast(Boolean)newValue) {
+                    if (v.booleanValue()) {
+                        activateMember(cast(RadioState) state);
+                    }
+                }
+            }
+
+            /**
+             * Removes a member from this radio group. If the state was the
+             * active state, then there will be no active state.
+             *
+             * @param state
+             *            The state to remove; must not be <code>null</code>.
+             */
+            private final void removeMember(RadioState state) {
+                state.removeListener(this);
+                if (active is state) {
+                    active = null;
+                }
+
+                if (members is null) {
+                    return;
+                }
+                members.remove(state);
+            }
+        }
+
+        /**
+         * The map of radio states indexed by identifier (<code>String</code>).
+         * The radio states is either a single <code>RadioState</code>
+         * instance or a <code>Collection</code> of <code>RadioState</code>
+         * instances.
+         */
+        private static Map radioStatesById = null;
+
+        /**
+         * Activates a particular state within a given group.
+         *
+         * @param identifier
+         *            The identifier of the group to which the state belongs;
+         *            must not be <code>null</code>.
+         * @param state
+         *            The state to activate; must not be <code>null</code>.
+         */
+        private static final void activateGroup(String identifier,
+                RadioState state) {
+            if (radioStatesById is null) {
+                return;
+            }
+
+            auto currentValue = radioStatesById.get(identifier);
+            if ( auto grp = cast(RadioGroup)currentValue) {
+                RadioGroup radioGroup = grp;
+                radioGroup.activateMember(state);
+            }
+        }
+
+        /**
+         * Registers a piece of state with the radio manager.
+         *
+         * @param identifier
+         *            The identifier of the radio group; must not be
+         *            <code>null</code>.
+         * @param state
+         *            The state to register; must not be <code>null</code>.
+         */
+        private static final void registerState(String identifier,
+                RadioState state) {
+            if (radioStatesById is null) {
+                radioStatesById = new HashMap();
+            }
+
+            auto currentValue = radioStatesById.get(identifier);
+            RadioGroup radioGroup;
+            if ( auto grp = cast(RadioGroup)currentValue) {
+                radioGroup = grp;
+            } else {
+                radioGroup = new RadioGroup();
+            }
+            radioGroup.addMember(state);
+        }
+
+        /**
+         * Unregisters a piece of state from the radio manager.
+         *
+         * @param identifier
+         *            The identifier of the radio group; must not be
+         *            <code>null</code>.
+         * @param state
+         *            The state to unregister; must not be <code>null</code>.
+         */
+        private static final void unregisterState(String identifier,
+                RadioState state) {
+            if (radioStatesById is null) {
+                return;
+            }
+
+            auto currentValue = radioStatesById.get(identifier);
+            if ( auto grp = cast(RadioGroup)currentValue ) {
+                final RadioGroup radioGroup = grp;
+                radioGroup.removeMember(state);
+            }
+        }
+    }
+
+    /**
+     * The identifier of the radio group to which this state belongs. This value
+     * may be <code>null</code> if this state doesn't really belong to a group
+     * (yet).
+     */
+    private String radioGroupIdentifier = null;
+
+    /**
+     * Unregisters this state from the manager, which detaches the listeners.
+     */
+    public override void dispose() {
+        setRadioGroupIdentifier(null);
+    }
+
+    /**
+     * Sets the identifier of the radio group for this piece of state. If the
+     * identifier is cleared, then the state is unregistered.
+     *
+     * @param identifier
+     *            The identifier of the radio group for this state; may be
+     *            <code>null</code> if the identifier is being cleared.
+     *
+     */
+    public final void setRadioGroupIdentifier(String identifier) {
+        if (identifier is null) {
+            RadioStateManager.unregisterState(radioGroupIdentifier, this);
+            radioGroupIdentifier = null;
+        } else {
+            radioGroupIdentifier = identifier;
+            RadioStateManager.registerState(identifier, this);
+        }
+    }
+
+    /**
+     * Sets the value for this object. This notifies the radio state manager of
+     * the change.
+     *
+     * @param value
+     *            The new value; should be a <code>Boolean</code>.
+     */
+    public override void setValue(Object value) {
+        if (!( cast(Boolean)value )) {
+            throw new IllegalArgumentException(
+                    "RadioState takes a Boolean as a value"); //$NON-NLS-1$
+        }
+
+        if (( cast(Boolean)value ).booleanValue() && (radioGroupIdentifier !is null)) {
+            RadioStateManager.activateGroup(radioGroupIdentifier, this);
+        }
+
+        super.setValue(value);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/commands/ToggleState.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.commands.ToggleState;
+
+import org.eclipse.jface.menus.IMenuStateIds;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import org.eclipse.jface.commands.PersistentState;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * <p>
+ * A piece of state storing a {@link Boolean}.
+ * </p>
+ * <p>
+ * If this state is registered using {@link IMenuStateIds#STYLE}, then it will
+ * control the presentation of the command if displayed in the menus, tool bars
+ * or status line.
+ * </p>
+ * <p>
+ * Clients may instantiate this class, but must not extend.
+ * </p>
+ *
+ * @since 3.2
+ */
+public class ToggleState : PersistentState {
+
+    /**
+     * Constructs a new <code>ToggleState</code>. By default, the toggle is
+     * off (e.g., <code>false</code>).
+     */
+    public this() {
+        setValue(Boolean.FALSE);
+    }
+
+    public override final void load(IPreferenceStore store,
+            String preferenceKey) {
+        bool currentValue = (cast(Boolean) getValue()).booleanValue();
+        store.setDefault(preferenceKey, currentValue);
+        if (shouldPersist() && (store.contains(preferenceKey))) {
+            bool value = store.getBoolean(preferenceKey);
+            setValue(value ? Boolean.TRUE : Boolean.FALSE);
+        }
+    }
+
+    public override final void save(IPreferenceStore store,
+            String preferenceKey) {
+        if (shouldPersist()) {
+            Object value = getValue();
+            if ( auto v = cast(Boolean)value ) {
+                store.setValue(preferenceKey, v.booleanValue());
+            }
+        }
+    }
+
+    public override void setValue(Object value) {
+        if (!(cast(Boolean)value)) {
+            throw new IllegalArgumentException(
+                    "ToggleState takes a Boolean as a value"); //$NON-NLS-1$
+        }
+
+        super.setValue(value);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/contexts/IContextIds.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.contexts.IContextIds;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * A list of well-known context identifiers. The context identifiers use the
+ * prefix "org.eclipse.ui" for historical reasons. These contexts exist as part
+ * of JFace.
+ * </p>
+ * <p>
+ * This interface should not be implemented or extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IContextIds {
+
+    /**
+     * The identifier for the context that is active when a shell registered as
+     * a dialog.
+     */
+    public static const String CONTEXT_ID_DIALOG = "org.eclipse.ui.contexts.dialog"; //$NON-NLS-1$
+
+    /**
+     * The identifier for the context that is active when a shell is registered
+     * as either a window or a dialog.
+     */
+    public static const String CONTEXT_ID_DIALOG_AND_WINDOW = "org.eclipse.ui.contexts.dialogAndWindow"; //$NON-NLS-1$
+
+    /**
+     * The identifier for the context that is active when a shell is registered
+     * as a window.
+     */
+    public static const String CONTEXT_ID_WINDOW = "org.eclipse.ui.contexts.window"; //$NON-NLS-1$
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/AnimatorFactory.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.dialogs.AnimatorFactory;
+
+import java.lang.all;
+
+import org.eclipse.jface.dialogs.ControlAnimator;
+
+import org.eclipse.swt.widgets.Control;
+
+
+/**
+ * Factory for control animators used by JFace to animate the display of an SWT
+ * Control. Through the use of the method
+ * {@link org.eclipse.jface.util.Policy#setAnimatorFactory(AnimatorFactory)}
+ * a new type of animator factory can be plugged into JFace.
+ *
+ * @since 3.2
+ * @deprecated as of 3.3, this class is no longer used.
+ */
+public class AnimatorFactory {
+    /**
+     * Creates a new ControlAnimator for use by JFace in animating
+     * the display of an SWT Control. <p>
+     * Subclasses should override this method.
+     *
+     * @param control the SWT Control to de displayed
+     * @return the ControlAnimator.
+     * @since 3.2
+     */
+    public ControlAnimator createAnimator(Control control) {
+        return new ControlAnimator(control);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/ControlAnimator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.dialogs.ControlAnimator;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * ControlAnimator provides a simple implementation to display or hide a control
+ * at the bottom of the parent composite. Other animations will be written as
+ * subclasses of this class. <p>
+ * Instances of this class can be created using an AnimatorFactory.
+ *
+ * @since 3.2
+ */
+
+
+public class ControlAnimator {
+    /** the control that will be displayed or hidden */
+    protected Control control;
+
+    /**
+     * Constructs a new ControlAnimator instance and passes along the
+     * control that will be displayed or hidden.
+     *
+     * @param control the control that will be displayed or hidden.
+     */
+    public this(Control control) {
+        this.control = control;
+    }
+
+    /**
+     * Displays or hides a control at the bottom of the parent composite
+     * and makes use of the control's SWT visible flag.<p>
+     * Subclasses should override this method.</p>
+     *
+     * @param visible <code>true</code> if the control should be shown,
+     *        and <code>false</code> otherwise.
+     */
+    public void setVisible(bool visible){
+        // Using the SWT visible flag to determine if the control has
+        // already been displayed or hidden. Return if already displayed
+        // and visible is true, or if already hidden and visible is false.
+        if (!(control.isVisible() ^ visible))
+            return;
+        control.setVisible(visible);
+        Rectangle parentBounds = control.getParent().getBounds();
+        int bottom = parentBounds.height;
+        int endY = visible ? bottom - control.getBounds().height
+                : bottom;
+        Point loc = control.getLocation();
+        control.setLocation(loc.x,endY);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/ControlEnableState.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.ControlEnableState;
+
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * Helper class to save the enable/disable state of a control including all its
+ * descendent controls.
+ */
+public class ControlEnableState {
+    /**
+     * List of exception controls (element type: <code>Control</code>);
+     * <code>null</code> if none.
+     */
+    private List exceptions = null;
+
+    /**
+     * List of saved states (element type: <code>ItemState</code>).
+     */
+    private List states;
+
+    /**
+     * Internal class for recording the enable/disable state of a single
+     * control.
+     */
+    private class ItemState {
+        /** the control */
+        protected Control item;
+
+        /** the state */
+        protected bool state;
+
+        /**
+         * Create a new instance of the receiver.
+         *
+         * @param item
+         * @param state
+         */
+        public this(Control item, bool state) {
+            this.item = item;
+            this.state = state;
+        }
+
+        /**
+         * Restore the enabled state to the original value.
+         *
+         */
+        public void restore() {
+            if (item is null || item.isDisposed()) {
+                return;
+            }
+            item.setEnabled(state);
+        }
+    }
+
+    /**
+     * Creates a new object and saves in it the current enable/disable state of
+     * the given control and its descendents; the controls that are saved are
+     * also disabled.
+     *
+     * @param w
+     *            the control
+     */
+    protected this(Control w) {
+        this(w, null);
+    }
+
+    /**
+     * Creates a new object and saves in it the current enable/disable state of
+     * the given control and its descendents except for the given list of
+     * exception cases; the controls that are saved are also disabled.
+     *
+     * @param w
+     *            the control
+     * @param exceptions
+     *            the list of controls to not disable (element type:
+     *            <code>Control</code>), or <code>null</code> if none
+     */
+    protected this(Control w, List exceptions) {
+//         super();
+        states = new ArrayList();
+        this.exceptions = exceptions;
+        readStateForAndDisable(w);
+    }
+
+    /**
+     * Saves the current enable/disable state of the given control and its
+     * descendents in the returned object; the controls are all disabled.
+     *
+     * @param w
+     *            the control
+     * @return an object capturing the enable/disable state
+     */
+    public static ControlEnableState disable(Control w) {
+        return new ControlEnableState(w);
+    }
+
+    /**
+     * Saves the current enable/disable state of the given control and its
+     * descendents in the returned object except for the given list of exception
+     * cases; the controls that are saved are also disabled.
+     *
+     * @param w
+     *            the control
+     * @param exceptions
+     *            the list of controls to not disable (element type:
+     *            <code>Control</code>)
+     * @return an object capturing the enable/disable state
+     */
+    public static ControlEnableState disable(Control w, List exceptions) {
+        return new ControlEnableState(w, exceptions);
+    }
+
+    /**
+     * Recursively reads the enable/disable state for the given window and
+     * disables all controls.
+     * @param control Control
+     */
+    private void readStateForAndDisable(Control control) {
+        if ((exceptions !is null && exceptions.contains(control))) {
+            return;
+        }
+        if (auto c = cast(Composite)control ) {
+            Control[] children = c.getChildren();
+            for (int i = 0; i < children.length; i++) {
+                readStateForAndDisable(children[i]);
+            }
+        }
+        // XXX: Workaround for 1G2Q8SS: ITPUI:Linux - Combo box is not enabled
+        // in "File->New->Solution"
+        states.add(new ItemState(control, control.getEnabled()));
+        control.setEnabled(false);
+    }
+
+    /**
+     * Restores the window enable state saved in this object.
+     */
+    public void restore() {
+        int size = states.size();
+        for (int i = 0; i < size; i++) {
+            (cast(ItemState) states.get(i)).restore();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/Dialog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1340 @@
+/*******************************************************************************
+ * 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
+ *     Remy Chi Jian Suen <remy.suen@gmail.com> - Bug 218553 [JFace] mis-spelling of their in applyDialogFont(...)
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.Dialog;
+
+import org.eclipse.jface.dialogs.IDialogBlockedHandler;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.IDialogConstants;
+// import tango.util.Arrays;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.Policy;
+import org.eclipse.jface.window.IShellProvider;
+import org.eclipse.jface.window.SameShellProvider;
+import org.eclipse.jface.window.Window;
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * A dialog is a specialized window used for narrow-focused communication with
+ * the user.
+ * <p>
+ * Dialogs are usually modal. Consequently, it is generally bad practice to open
+ * a dialog without a parent. A modal dialog without a parent is not prevented
+ * from disappearing behind the application's other windows, making it very
+ * confusing for the user.
+ * </p>
+ * <p>
+ * If there is more than one modal dialog is open the second one should be
+ * parented off of the shell of the first one otherwise it is possible that the
+ * OS will give focus to the first dialog potentially blocking the UI.
+ * </p>
+ */
+public abstract class Dialog : Window {
+    /**
+     * Image registry key for error image (value
+     * <code>"dialog_error_image"</code>).
+     *
+     * @deprecated use
+     *             org.eclipse.swt.widgets.Display.getSystemImage(SWT.ICON_ERROR)
+     */
+    public static const String DLG_IMG_ERROR = "dialog_error_image"; //$NON-NLS-1$
+
+    /**
+     * Image registry key for info image (value <code>"dialog_info_image"</code>).
+     *
+     * @deprecated use
+     *             org.eclipse.swt.widgets.Display.getSystemImage(SWT.ICON_INFORMATION)
+     */
+    public static const String DLG_IMG_INFO = "dialog_info_imageg"; //$NON-NLS-1$
+
+    /**
+     * Image registry key for question image (value
+     * <code>"dialog_question_image"</code>).
+     *
+     * @deprecated org.eclipse.swt.widgets.Display.getSystemImage(SWT.ICON_QUESTION)
+     */
+    public static const String DLG_IMG_QUESTION = "dialog_question_image"; //$NON-NLS-1$
+
+    /**
+     * Image registry key for warning image (value
+     * <code>"dialog_warning_image"</code>).
+     *
+     * @deprecated use
+     *             org.eclipse.swt.widgets.Display.getSystemImage(SWT.ICON_WARNING)
+     */
+    public static const String DLG_IMG_WARNING = "dialog_warning_image"; //$NON-NLS-1$
+
+    /**
+     * Image registry key for info message image (value
+     * <code>"dialog_messasge_info_image"</code>).
+     *
+     * @since 2.0
+     */
+    public static const String DLG_IMG_MESSAGE_INFO = "dialog_messasge_info_image"; //$NON-NLS-1$
+
+    /**
+     * Image registry key for info message image (value
+     * <code>"dialog_messasge_warning_image"</code>).
+     *
+     * @since 2.0
+     */
+    public static const String DLG_IMG_MESSAGE_WARNING = "dialog_messasge_warning_image"; //$NON-NLS-1$
+
+    /**
+     * Image registry key for info message image (value
+     * <code>"dialog_message_error_image"</code>).
+     *
+     * @since 2.0
+     */
+    public static const String DLG_IMG_MESSAGE_ERROR = "dialog_message_error_image"; //$NON-NLS-1$
+
+    /**
+     * Image registry key for help image (value
+     * <code>"dialog_help_image"</code>).
+     *
+     * @since 3.2
+     */
+    public static const String DLG_IMG_HELP = "dialog_help_image"; //$NON-NLS-1$
+
+    /**
+     * The ellipsis is the string that is used to represent shortened text.
+     *
+     * @since 3.0
+     */
+    public static const String ELLIPSIS = "..."; //$NON-NLS-1$
+
+    /**
+     * The dialog settings key name for stored dialog x location.
+     *
+     * @since 3.2
+     */
+    private static const String DIALOG_ORIGIN_X = "DIALOG_X_ORIGIN"; //$NON-NLS-1$
+
+    /**
+     * The dialog settings key name for stored dialog y location.
+     *
+     * @since 3.2
+     */
+    private static const String DIALOG_ORIGIN_Y = "DIALOG_Y_ORIGIN"; //$NON-NLS-1$
+
+    /**
+     * The dialog settings key name for stored dialog width.
+     *
+     * @since 3.2
+     */
+    private static const String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$
+
+    /**
+     * The dialog settings key name for stored dialog height.
+     *
+     * @since 3.2
+     */
+    private static const String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$
+
+    /**
+     * The dialog settings key name for the font used when the dialog
+     * height and width was stored.
+     *
+     *@since 3.2
+     */
+    private static const String DIALOG_FONT_DATA = "DIALOG_FONT_NAME"; //$NON-NLS-1$
+
+    /**
+     * A value that can be used for stored dialog width or height that
+     * indicates that the default bounds should be used.
+     *
+     * @since 3.2
+     */
+    public static const int DIALOG_DEFAULT_BOUNDS = -1;
+
+    /**
+     * Constants that can be used for specifying the strategy for persisting
+     * dialog bounds.  These constants represent bit masks that can be used
+     * together.
+     *
+     *@since 3.2
+     */
+
+    /**
+     * Persist the last location of the dialog.
+     * @since 3.2
+     */
+    public static const int DIALOG_PERSISTLOCATION = 0x0001;
+    /**
+     * Persist the last known size of the dialog.
+     * @since 3.2
+     */
+    public static const int DIALOG_PERSISTSIZE = 0x0002;
+
+    /**
+     * The dialog area; <code>null</code> until dialog is layed out.
+     */
+    protected Control dialogArea;
+
+    /**
+     * The button bar; <code>null</code> until dialog is layed out.
+     */
+    public Control buttonBar;
+
+    /**
+     * Collection of buttons created by the <code>createButton</code> method.
+     */
+    private Button[int] buttons;
+
+    /**
+     * Font metrics to use for determining pixel sizes.
+     */
+    private FontMetrics fontMetrics;
+
+    /**
+     * Number of horizontal dialog units per character, value <code>4</code>.
+     */
+    private static const int HORIZONTAL_DIALOG_UNIT_PER_CHAR = 4;
+
+    /**
+     * Number of vertical dialog units per character, value <code>8</code>.
+     */
+    private static const int VERTICAL_DIALOG_UNITS_PER_CHAR = 8;
+
+    /**
+     * Returns the number of pixels corresponding to the height of the given
+     * number of characters.
+     * <p>
+     * The required <code>FontMetrics</code> parameter may be created in the
+     * following way: <code>
+     *  GC gc = new GC(control);
+     *  gc.setFont(control.getFont());
+     *  fontMetrics = gc.getFontMetrics();
+     *  gc.dispose();
+     * </code>
+     * </p>
+     *
+     * @param fontMetrics
+     *            used in performing the conversion
+     * @param chars
+     *            the number of characters
+     * @return the number of pixels
+     * @since 2.0
+     */
+    public static int convertHeightInCharsToPixels(FontMetrics fontMetrics,
+            int chars) {
+        return fontMetrics.getHeight() * chars;
+    }
+
+    /**
+     * Returns the number of pixels corresponding to the given number of
+     * horizontal dialog units.
+     * <p>
+     * The required <code>FontMetrics</code> parameter may be created in the
+     * following way: <code>
+     *  GC gc = new GC(control);
+     *  gc.setFont(control.getFont());
+     *  fontMetrics = gc.getFontMetrics();
+     *  gc.dispose();
+     * </code>
+     * </p>
+     *
+     * @param fontMetrics
+     *            used in performing the conversion
+     * @param dlus
+     *            the number of horizontal dialog units
+     * @return the number of pixels
+     * @since 2.0
+     */
+    public static int convertHorizontalDLUsToPixels(FontMetrics fontMetrics,
+            int dlus) {
+        // round to the nearest pixel
+        return (fontMetrics.getAverageCharWidth() * dlus + HORIZONTAL_DIALOG_UNIT_PER_CHAR / 2)
+                / HORIZONTAL_DIALOG_UNIT_PER_CHAR;
+    }
+
+    /**
+     * Returns the number of pixels corresponding to the given number of
+     * vertical dialog units.
+     * <p>
+     * The required <code>FontMetrics</code> parameter may be created in the
+     * following way: <code>
+     *  GC gc = new GC(control);
+     *  gc.setFont(control.getFont());
+     *  fontMetrics = gc.getFontMetrics();
+     *  gc.dispose();
+     * </code>
+     * </p>
+     *
+     * @param fontMetrics
+     *            used in performing the conversion
+     * @param dlus
+     *            the number of vertical dialog units
+     * @return the number of pixels
+     * @since 2.0
+     */
+    public static int convertVerticalDLUsToPixels(FontMetrics fontMetrics,
+            int dlus) {
+        // round to the nearest pixel
+        return (fontMetrics.getHeight() * dlus + VERTICAL_DIALOG_UNITS_PER_CHAR / 2)
+                / VERTICAL_DIALOG_UNITS_PER_CHAR;
+    }
+
+    /**
+     * Returns the number of pixels corresponding to the width of the given
+     * number of characters.
+     * <p>
+     * The required <code>FontMetrics</code> parameter may be created in the
+     * following way: <code>
+     *  GC gc = new GC(control);
+     *  gc.setFont(control.getFont());
+     *  fontMetrics = gc.getFontMetrics();
+     *  gc.dispose();
+     * </code>
+     * </p>
+     *
+     * @param fontMetrics
+     *            used in performing the conversion
+     * @param chars
+     *            the number of characters
+     * @return the number of pixels
+     * @since 2.0
+     */
+    public static int convertWidthInCharsToPixels(FontMetrics fontMetrics,
+            int chars) {
+        return fontMetrics.getAverageCharWidth() * chars;
+    }
+
+    /**
+     * Shortens the given text <code>textValue</code> so that its width in
+     * pixels does not exceed the width of the given control. Overrides
+     * characters in the center of the original string with an ellipsis ("...")
+     * if necessary. If a <code>null</code> value is given, <code>null</code>
+     * is returned.
+     *
+     * @param textValue
+     *            the original string or <code>null</code>
+     * @param control
+     *            the control the string will be displayed on
+     * @return the string to display, or <code>null</code> if null was passed
+     *         in
+     *
+     * @since 3.0
+     */
+    public static String shortenText(String textValue, Control control) {
+        if (textValue is null) {
+            return null;
+        }
+        GC gc = new GC(control);
+        int maxWidth = control.getBounds().width - 5;
+        int maxExtent = gc.textExtent(textValue).x;
+        if (maxExtent < maxWidth) {
+            gc.dispose();
+            return textValue;
+        }
+        int length = textValue.length;
+        int charsToClip = cast(int) Math.round(0.95f*length * (1 - (cast(float)maxWidth/maxExtent)));
+        int pivot = length / 2;
+        int start = pivot - (charsToClip/2);
+        int end = pivot + (charsToClip/2) + 1;
+        while (start >= 0 && end < length) {
+            String s1 = textValue.substring(0, start);
+            String s2 = textValue.substring(end, length);
+            String s = s1 ~ ELLIPSIS ~ s2;
+            int l = gc.textExtent(s).x;
+            if (l < maxWidth) {
+                gc.dispose();
+                return s;
+            }
+            start--;
+            end++;
+        }
+        gc.dispose();
+        return textValue;
+    }
+
+    /**
+     * Create a default instance of the blocked handler which does not do
+     * anything.
+     */
+    private static IDialogBlockedHandler blockedHandler_;
+    public static IDialogBlockedHandler blockedHandler(){
+        static_this_blockedhandler();
+        return blockedHandler_;
+    }
+    public static IDialogBlockedHandler blockedHandler( IDialogBlockedHandler b ){
+        static_this_blockedhandler();
+        return ( blockedHandler_ = b );
+    }
+    private static bool static_this_blockedhandler_completed = false;
+    private static void static_this_blockedhandler(){
+        if( static_this_blockedhandler_completed ){
+            return;
+        }
+        synchronized{
+            if( static_this_blockedhandler_completed ){
+                return;
+            }
+            blockedHandler_ = new class IDialogBlockedHandler {
+                /*
+                * (non-Javadoc)
+                *
+                * @see org.eclipse.jface.dialogs.IDialogBlockedHandler#clearBlocked()
+                */
+                public void clearBlocked() {
+                    // No default behaviour
+                }
+
+                /*
+                * (non-Javadoc)
+                *
+                * @see org.eclipse.jface.dialogs.IDialogBlockedHandler#showBlocked(org.eclipse.core.runtime.IProgressMonitor,
+                *      org.eclipse.core.runtime.IStatus, java.lang.String)
+                */
+                public void showBlocked(IProgressMonitor blocking,
+                        IStatus blockingStatus, String blockedName) {
+                    // No default behaviour
+                }
+
+                /*
+                * (non-Javadoc)
+                *
+                * @see org.eclipse.jface.dialogs.IDialogBlockedHandler#showBlocked(org.eclipse.swt.widgets.Shell,
+                *      org.eclipse.core.runtime.IProgressMonitor,
+                *      org.eclipse.core.runtime.IStatus, java.lang.String)
+                */
+                public void showBlocked(Shell parentShell, IProgressMonitor blocking,
+                        IStatus blockingStatus, String blockedName) {
+                    // No default behaviour
+                }
+            };
+            static_this_blockedhandler_completed = true;
+        }
+    }
+
+    /**
+     * Creates a dialog instance. Note that the window will have no visual
+     * representation (no widgets) until it is told to open. By default,
+     * <code>open</code> blocks for dialogs.
+     *
+     * @param parentShell
+     *            the parent shell, or <code>null</code> to create a top-level
+     *            shell
+     */
+    protected this(Shell parentShell) {
+        this(new SameShellProvider(parentShell));
+        if (parentShell is null && Policy.DEBUG_DIALOG_NO_PARENT) {
+            Policy.getLog().log(
+                    new Status(IStatus.INFO, Policy.JFACE, IStatus.INFO, this
+                            .classinfo.name
+                            ~ " created with no shell",//$NON-NLS-1$
+                            new Exception( null, null )));
+        }
+    }
+
+    /**
+     * Creates a dialog with the given parent.
+     *
+     * @param parentShell
+     *            object that returns the current parent shell
+     *
+     * @since 3.1
+     */
+    protected this(IShellProvider parentShell) {
+        super(parentShell);
+        if (isResizable()) {
+            setShellStyle(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.MAX | SWT.RESIZE
+                    | getDefaultOrientation());
+        } else {
+            setShellStyle(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL
+                    | getDefaultOrientation());
+        }
+        setBlockOnOpen(true);
+    }
+
+    /**
+     * Notifies that this dialog's button with the given id has been pressed.
+     * <p>
+     * The <code>Dialog</code> implementation of this framework method calls
+     * <code>okPressed</code> if the ok button is the pressed, and
+     * <code>cancelPressed</code> if the cancel button is the pressed. All
+     * other button presses are ignored. Subclasses may override to handle other
+     * buttons, but should call <code>super.buttonPressed</code> if the
+     * default handling of the ok and cancel buttons is desired.
+     * </p>
+     *
+     * @param buttonId
+     *            the id of the button that was pressed (see
+     *            <code>IDialogConstants.*_ID</code> constants)
+     */
+    protected void buttonPressed(int buttonId) {
+        if (IDialogConstants.OK_ID is buttonId) {
+            okPressed();
+        } else if (IDialogConstants.CANCEL_ID is buttonId) {
+            cancelPressed();
+        }
+    }
+
+    /**
+     * Notifies that the cancel button of this dialog has been pressed.
+     * <p>
+     * The <code>Dialog</code> implementation of this framework method sets
+     * this dialog's return code to <code>Window.CANCEL</code> and closes the
+     * dialog. Subclasses may override if desired.
+     * </p>
+     */
+    protected void cancelPressed() {
+        setReturnCode(CANCEL);
+        close();
+    }
+
+    /**
+     * Returns the number of pixels corresponding to the height of the given
+     * number of characters.
+     * <p>
+     * This method may only be called after <code>initializeDialogUnits</code>
+     * has been called.
+     * </p>
+     * <p>
+     * Clients may call this framework method, but should not override it.
+     * </p>
+     *
+     * @param chars
+     *            the number of characters
+     * @return the number of pixels
+     */
+    protected int convertHeightInCharsToPixels(int chars) {
+        // test for failure to initialize for backward compatibility
+        if (fontMetrics is null) {
+            return 0;
+        }
+        return convertHeightInCharsToPixels(fontMetrics, chars);
+    }
+
+    /**
+     * Returns the number of pixels corresponding to the given number of
+     * horizontal dialog units.
+     * <p>
+     * This method may only be called after <code>initializeDialogUnits</code>
+     * has been called.
+     * </p>
+     * <p>
+     * Clients may call this framework method, but should not override it.
+     * </p>
+     *
+     * @param dlus
+     *            the number of horizontal dialog units
+     * @return the number of pixels
+     */
+    protected int convertHorizontalDLUsToPixels(int dlus) {
+        // test for failure to initialize for backward compatibility
+        if (fontMetrics is null) {
+            return 0;
+        }
+        return convertHorizontalDLUsToPixels(fontMetrics, dlus);
+    }
+
+    /**
+     * Returns the number of pixels corresponding to the given number of
+     * vertical dialog units.
+     * <p>
+     * This method may only be called after <code>initializeDialogUnits</code>
+     * has been called.
+     * </p>
+     * <p>
+     * Clients may call this framework method, but should not override it.
+     * </p>
+     *
+     * @param dlus
+     *            the number of vertical dialog units
+     * @return the number of pixels
+     */
+    protected int convertVerticalDLUsToPixels(int dlus) {
+        // test for failure to initialize for backward compatibility
+        if (fontMetrics is null) {
+            return 0;
+        }
+        return convertVerticalDLUsToPixels(fontMetrics, dlus);
+    }
+
+    /**
+     * Returns the number of pixels corresponding to the width of the given
+     * number of characters.
+     * <p>
+     * This method may only be called after <code>initializeDialogUnits</code>
+     * has been called.
+     * </p>
+     * <p>
+     * Clients may call this framework method, but should not override it.
+     * </p>
+     *
+     * @param chars
+     *            the number of characters
+     * @return the number of pixels
+     */
+    protected int convertWidthInCharsToPixels(int chars) {
+        // test for failure to initialize for backward compatibility
+        if (fontMetrics is null) {
+            return 0;
+        }
+        return convertWidthInCharsToPixels(fontMetrics, chars);
+    }
+
+    /**
+     * Creates a new button with the given id.
+     * <p>
+     * The <code>Dialog</code> implementation of this framework method creates
+     * a standard push button, registers it for selection events including
+     * button presses, and registers default buttons with its shell. The button
+     * id is stored as the button's client data. If the button id is
+     * <code>IDialogConstants.CANCEL_ID</code>, the new button will be
+     * accessible from <code>getCancelButton()</code>. If the button id is
+     * <code>IDialogConstants.OK_ID</code>, the new button will be accesible
+     * from <code>getOKButton()</code>. Note that the parent's layout is
+     * assumed to be a <code>GridLayout</code> and the number of columns in
+     * this layout is incremented. Subclasses may override.
+     * </p>
+     *
+     * @param parent
+     *            the parent composite
+     * @param id
+     *            the id of the button (see <code>IDialogConstants.*_ID</code>
+     *            constants for standard dialog button ids)
+     * @param label
+     *            the label from the button
+     * @param defaultButton
+     *            <code>true</code> if the button is to be the default button,
+     *            and <code>false</code> otherwise
+     *
+     * @return the new button
+     *
+     * @see #getCancelButton
+     * @see #getOKButton()
+     */
+    protected Button createButton(Composite parent, int id, String label,
+            bool defaultButton) {
+        // increment the number of columns in the button bar
+        (cast(GridLayout) parent.getLayout()).numColumns++;
+        Button button = new Button(parent, SWT.PUSH);
+        button.setText(label);
+        button.setFont(JFaceResources.getDialogFont());
+        button.setData(new ValueWrapperInt(id));
+        button.addSelectionListener(new class SelectionAdapter {
+            public void widgetSelected(SelectionEvent event) {
+                buttonPressed((cast(ValueWrapperInt) event.widget.getData()).value);
+            }
+        });
+        if (defaultButton) {
+            Shell shell = parent.getShell();
+            if (shell !is null) {
+                shell.setDefaultButton(button);
+            }
+        }
+        buttons[id] = button;
+        setButtonLayoutData(button);
+        return button;
+    }
+
+    /**
+     * Creates and returns the contents of this dialog's button bar.
+     * <p>
+     * The <code>Dialog</code> implementation of this framework method lays
+     * out a button bar and calls the <code>createButtonsForButtonBar</code>
+     * framework method to populate it. Subclasses may override.
+     * </p>
+     * <p>
+     * The returned control's layout data must be an instance of
+     * <code>GridData</code>.
+     * </p>
+     *
+     * @param parent
+     *            the parent composite to contain the button bar
+     * @return the button bar control
+     */
+    protected Control createButtonBar(Composite parent) {
+        Composite composite = new Composite(parent, SWT.NONE);
+        // create a layout with spacing and margins appropriate for the font
+        // size.
+        GridLayout layout = new GridLayout();
+        layout.numColumns = 0; // this is incremented by createButton
+        layout.makeColumnsEqualWidth = true;
+        layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
+        layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
+        layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
+        layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
+        composite.setLayout(layout);
+        GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END
+                | GridData.VERTICAL_ALIGN_CENTER);
+        composite.setLayoutData(data);
+        composite.setFont(parent.getFont());
+
+        // Add the buttons to the button bar.
+        createButtonsForButtonBar(composite);
+        return composite;
+    }
+
+    /**
+     * Adds buttons to this dialog's button bar.
+     * <p>
+     * The <code>Dialog</code> implementation of this framework method adds
+     * standard ok and cancel buttons using the <code>createButton</code>
+     * framework method. These standard buttons will be accessible from
+     * <code>getCancelButton</code>, and <code>getOKButton</code>.
+     * Subclasses may override.
+     * </p>
+     *
+     * @param parent
+     *            the button bar composite
+     */
+    protected void createButtonsForButtonBar(Composite parent) {
+        // create OK and Cancel buttons by default
+        createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
+                true);
+        createButton(parent, IDialogConstants.CANCEL_ID,
+                IDialogConstants.CANCEL_LABEL, false);
+    }
+
+    /*
+     * @see Window.initializeBounds()
+     */
+    protected override void initializeBounds() {
+        String platform = SWT.getPlatform();
+        if ("carbon".equals(platform)) { //$NON-NLS-1$
+            // On Mac OS X the default button must be the right-most button
+            Shell shell = getShell();
+            if (shell !is null) {
+                Button defaultButton = shell.getDefaultButton();
+                if (defaultButton !is null
+                        && isContained(buttonBar, defaultButton)) {
+                    defaultButton.moveBelow(null);
+                }
+            }
+        }
+
+        super.initializeBounds();
+    }
+
+    /**
+     * Returns true if the given Control is a direct or indirect child of
+     * container.
+     *
+     * @param container
+     *            the potential parent
+     * @param control
+     * @return bool <code>true</code> if control is a child of container
+     */
+    private bool isContained(Control container, Control control) {
+        Composite parent;
+        while ((parent = control.getParent()) !is null) {
+            if (parent is container) {
+                return true;
+            }
+            control = parent;
+        }
+        return false;
+    }
+
+    /**
+     * The <code>Dialog</code> implementation of this <code>Window</code>
+     * method creates and lays out the top level composite for the dialog, and
+     * determines the appropriate horizontal and vertical dialog units based on
+     * the font size. It then calls the <code>createDialogArea</code> and
+     * <code>createButtonBar</code> methods to create the dialog area and
+     * button bar, respectively. Overriding <code>createDialogArea</code> and
+     * <code>createButtonBar</code> are recommended rather than overriding
+     * this method.
+     */
+    protected override Control createContents(Composite parent) {
+        // create the top level composite for the dialog
+        Composite composite = new Composite(parent, 0);
+        GridLayout layout = new GridLayout();
+        layout.marginHeight = 0;
+        layout.marginWidth = 0;
+        layout.verticalSpacing = 0;
+        composite.setLayout(layout);
+        composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+        applyDialogFont(composite);
+        // initialize the dialog units
+        initializeDialogUnits(composite);
+        // create the dialog area and button bar
+        dialogArea = createDialogArea(composite);
+        buttonBar = createButtonBar(composite);
+
+        return composite;
+    }
+
+    /**
+     * Creates and returns the contents of the upper part of this dialog (above
+     * the button bar).
+     * <p>
+     * The <code>Dialog</code> implementation of this framework method creates
+     * and returns a new <code>Composite</code> with standard margins and
+     * spacing.
+     * </p>
+     * <p>
+     * The returned control's layout data must be an instance of
+     * <code>GridData</code>. This method must not modify the parent's
+     * layout.
+     * </p>
+     * <p>
+     * Subclasses must override this method but may call <code>super</code> as
+     * in the following example:
+     * </p>
+     *
+     * <pre>
+     * Composite composite = (Composite) super.createDialogArea(parent);
+     * //add controls to composite as necessary
+     * return composite;
+     * </pre>
+     *
+     * @param parent
+     *            the parent composite to contain the dialog area
+     * @return the dialog area control
+     */
+    protected Control createDialogArea(Composite parent) {
+        // create a composite with standard margins and spacing
+        Composite composite = new Composite(parent, SWT.NONE);
+        GridLayout layout = new GridLayout();
+        layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
+        layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
+        layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
+        layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
+        composite.setLayout(layout);
+        composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+        applyDialogFont(composite);
+        return composite;
+    }
+
+    /**
+     * Returns the button created by the method <code>createButton</code> for
+     * the specified ID as defined on <code>IDialogConstants</code>. If
+     * <code>createButton</code> was never called with this ID, or if
+     * <code>createButton</code> is overridden, this method will return
+     * <code>null</code>.
+     *
+     * @param id
+     *            the id of the button to look for
+     *
+     * @return the button for the ID or <code>null</code>
+     *
+     * @see #createButton(Composite, int, String, bool)
+     * @since 2.0
+     */
+    protected Button getButton(int id) {
+        if( auto btn = id in buttons ){
+            return *btn;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the button bar control.
+     * <p>
+     * Clients may call this framework method, but should not override it.
+     * </p>
+     *
+     * @return the button bar, or <code>null</code> if the button bar has not
+     *         been created yet
+     */
+    protected Control getButtonBar() {
+        return buttonBar;
+    }
+
+    /**
+     * Returns the button created when <code>createButton</code> is called
+     * with an ID of <code>IDialogConstants.CANCEL_ID</code>. If
+     * <code>createButton</code> was never called with this parameter, or if
+     * <code>createButton</code> is overridden, <code>getCancelButton</code>
+     * will return <code>null</code>.
+     *
+     * @return the cancel button or <code>null</code>
+     *
+     * @see #createButton(Composite, int, String, bool)
+     * @since 2.0
+     * @deprecated Use <code>getButton(IDialogConstants.CANCEL_ID)</code>
+     *             instead. This method will be removed soon.
+     */
+    protected Button getCancelButton() {
+        return getButton(IDialogConstants.CANCEL_ID);
+    }
+
+    /**
+     * Returns the dialog area control.
+     * <p>
+     * Clients may call this framework method, but should not override it.
+     * </p>
+     *
+     * @return the dialog area, or <code>null</code> if the dialog area has
+     *         not been created yet
+     */
+    protected Control getDialogArea() {
+        return dialogArea;
+    }
+
+    /**
+     * Returns the standard dialog image with the given key. Note that these
+     * images are managed by the dialog framework, and must not be disposed by
+     * another party.
+     *
+     * @param key
+     *            one of the <code>Dialog.DLG_IMG_* </code> constants
+     * @return the standard dialog image
+     *
+     * NOTE: Dialog does not use the following images in the registry
+     * DLG_IMG_ERROR DLG_IMG_INFO DLG_IMG_QUESTION DLG_IMG_WARNING
+     *
+     * They are now coming directly from SWT, see ImageRegistry. For backwards
+     * compatibility they are still supported, however new code should use SWT
+     * for these.
+     *
+     * @see Display#getSystemImage(int)
+     */
+    public static Image getImage(String key) {
+        return JFaceResources.getImageRegistry().get(key);
+    }
+
+    /**
+     * Returns the button created when <code>createButton</code> is called
+     * with an ID of <code>IDialogConstants.OK_ID</code>. If
+     * <code>createButton</code> was never called with this parameter, or if
+     * <code>createButton</code> is overridden, <code>getOKButton</code>
+     * will return <code>null</code>.
+     *
+     * @return the OK button or <code>null</code>
+     *
+     * @see #createButton(Composite, int, String, bool)
+     * @since 2.0
+     * @deprecated Use <code>getButton(IDialogConstants.OK_ID)</code> instead.
+     *             This method will be removed soon.
+     */
+    protected Button getOKButton() {
+        return getButton(IDialogConstants.OK_ID);
+    }
+
+    /**
+     * Initializes the computation of horizontal and vertical dialog units based
+     * on the size of current font.
+     * <p>
+     * This method must be called before any of the dialog unit based conversion
+     * methods are called.
+     * </p>
+     *
+     * @param control
+     *            a control from which to obtain the current font
+     */
+    protected void initializeDialogUnits(Control control) {
+        // Compute and store a font metric
+        GC gc = new GC(control);
+        gc.setFont(JFaceResources.getDialogFont());
+        fontMetrics = gc.getFontMetrics();
+        gc.dispose();
+    }
+
+    /**
+     * Notifies that the ok button of this dialog has been pressed.
+     * <p>
+     * The <code>Dialog</code> implementation of this framework method sets
+     * this dialog's return code to <code>Window.OK</code> and closes the
+     * dialog. Subclasses may override.
+     * </p>
+     */
+    protected void okPressed() {
+        setReturnCode(OK);
+        close();
+    }
+
+    /**
+     * Set the layout data of the button to a GridData with appropriate heights
+     * and widths.
+     *
+     * @param button
+     */
+    protected void setButtonLayoutData(Button button) {
+        GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+        int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
+        Point minSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+        data.widthHint = Math.max(widthHint, minSize.x);
+        button.setLayoutData(data);
+    }
+
+    /**
+     * Set the layout data of the button to a FormData with appropriate heights
+     * and widths.
+     *
+     * @param button
+     */
+    protected void setButtonLayoutFormData(Button button) {
+        FormData data = new FormData();
+        int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
+        Point minSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+        data.width = Math.max(widthHint, minSize.x);
+        button.setLayoutData(data);
+    }
+
+    /**
+     * @see org.eclipse.jface.window.Window#close()
+     */
+    public override bool close() {
+        if (getShell() !is null && !getShell().isDisposed()) {
+            saveDialogBounds(getShell());
+        }
+
+        bool returnValue = super.close();
+        if (returnValue) {
+            buttons = null;
+            buttonBar = null;
+            dialogArea = null;
+        }
+        return returnValue;
+    }
+
+    /**
+     * Applies the dialog font to all controls that currently have the default
+     * font.
+     *
+     * @param control
+     *            the control to apply the font to. Font will also be applied to
+     *            its children. If the control is <code>null</code> nothing
+     *            happens.
+     */
+    public static void applyDialogFont(Control control) {
+        if (control is null || dialogFontIsDefault()) {
+            return;
+        }
+        Font dialogFont = JFaceResources.getDialogFont();
+        applyDialogFont(control, dialogFont);
+    }
+
+    /**
+     * Sets the dialog font on the control and any of its children if their font
+     * is not otherwise set.
+     *
+     * @param control
+     *            the control to apply the font to. Font will also be applied to
+     *            its children.
+     * @param dialogFont
+     *            the dialog font to set
+     */
+    private static void applyDialogFont(Control control, Font dialogFont) {
+        if (hasDefaultFont(control)) {
+            control.setFont(dialogFont);
+        }
+        if ( auto comp = cast(Composite)control ) {
+            Control[] children = comp.getChildren();
+            for (int i = 0; i < children.length; i++) {
+                applyDialogFont(children[i], dialogFont);
+            }
+        }
+    }
+
+    /**
+     * Return whether or not this control has the same font as it's default.
+     *
+     * @param control
+     *            Control
+     * @return bool
+     */
+    private static bool hasDefaultFont(Control control) {
+        FontData[] controlFontData = control.getFont().getFontData();
+        FontData[] defaultFontData = getDefaultFont(control).getFontData();
+        if (controlFontData.length is defaultFontData.length) {
+            for (int i = 0; i < controlFontData.length; i++) {
+                if (controlFontData[i].opEquals(defaultFontData[i])) {
+                    continue;
+                }
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Get the default font for this type of control.
+     *
+     * @param control
+     * @return the default font
+     */
+    private static Font getDefaultFont(Control control) {
+        String fontName = "DEFAULT_FONT_" ~ control.classinfo.name; //$NON-NLS-1$
+        if (JFaceResources.getFontRegistry().hasValueFor(fontName)) {
+            return JFaceResources.getFontRegistry().get(fontName);
+        }
+        Font cached = control.getFont();
+        control.setFont(null);
+        Font defaultFont = control.getFont();
+        control.setFont(cached);
+        JFaceResources.getFontRegistry().put(fontName,
+                defaultFont.getFontData());
+        return defaultFont;
+    }
+
+    /**
+     * Return whether or not the dialog font is currently the same as the
+     * default font.
+     *
+     * @return bool if the two are the same
+     */
+    protected static bool dialogFontIsDefault() {
+        FontData[] dialogFontData = JFaceResources.getFontRegistry()
+                .getFontData(JFaceResources.DIALOG_FONT);
+        FontData[] defaultFontData = JFaceResources.getFontRegistry()
+                .getFontData(JFaceResources.DEFAULT_FONT);
+        return ArrayEquals(dialogFontData, defaultFontData);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.window.Window#create()
+     */
+    public override void create() {
+        super.create();
+        applyDialogFont(buttonBar);
+    }
+
+    /**
+     * Get the IDialogBlockedHandler to be used by WizardDialogs and
+     * ModalContexts.
+     *
+     * @return Returns the blockedHandler.
+     */
+    public static IDialogBlockedHandler getBlockedHandler() {
+        return blockedHandler;
+    }
+
+    /**
+     * Set the IDialogBlockedHandler to be used by WizardDialogs and
+     * ModalContexts.
+     *
+     * @param blockedHandler
+     *            The blockedHandler for the dialogs.
+     */
+    public static void setBlockedHandler(IDialogBlockedHandler blockedHandler) {
+        Dialog.blockedHandler = blockedHandler;
+    }
+
+    /**
+     * Gets the dialog settings that should be used for remembering the bounds of
+     * of the dialog, according to the dialog bounds strategy.
+     *
+     * @return settings the dialog settings used to store the dialog's location
+     *         and/or size, or <code>null</code> if the dialog's bounds should
+     *         never be stored.
+     *
+     * @since 3.2
+     * @see Dialog#getDialogBoundsStrategy()
+     */
+    protected IDialogSettings getDialogBoundsSettings() {
+        return null;
+    }
+
+    /**
+     * Get the integer constant that describes the strategy for persisting the
+     * dialog bounds. This strategy is ignored if the implementer does not also
+     * specify the dialog settings for storing the bounds in
+     * Dialog.getDialogBoundsSettings().
+     *
+     * @return the constant describing the strategy for persisting the dialog
+     *         bounds.
+     *
+     * @since 3.2
+     * @see Dialog#DIALOG_PERSISTLOCATION
+     * @see Dialog#DIALOG_PERSISTSIZE
+     * @see Dialog#getDialogBoundsSettings()
+     */
+    protected int getDialogBoundsStrategy() {
+        return DIALOG_PERSISTLOCATION | DIALOG_PERSISTSIZE;
+    }
+
+    /**
+     * Saves the bounds of the shell in the appropriate dialog settings. The
+     * bounds are recorded relative to the parent shell, if there is one, or
+     * display coordinates if there is no parent shell.
+     *
+     * @param shell
+     *            The shell whose bounds are to be stored
+     *
+     * @since 3.2
+     */
+    private void saveDialogBounds(Shell shell) {
+        IDialogSettings settings = getDialogBoundsSettings();
+        if (settings !is null) {
+            Point shellLocation = shell.getLocation();
+            Point shellSize = shell.getSize();
+            Shell parent = getParentShell();
+            if (parent !is null) {
+                Point parentLocation = parent.getLocation();
+                shellLocation.x -= parentLocation.x;
+                shellLocation.y -= parentLocation.y;
+            }
+            int strategy = getDialogBoundsStrategy();
+            if ((strategy & DIALOG_PERSISTLOCATION) !is 0) {
+                settings.put(DIALOG_ORIGIN_X, shellLocation.x);
+                settings.put(DIALOG_ORIGIN_Y, shellLocation.y);
+            }
+            if ((strategy & DIALOG_PERSISTSIZE) !is 0) {
+                settings.put(DIALOG_WIDTH, shellSize.x);
+                settings.put(DIALOG_HEIGHT, shellSize.y);
+                FontData [] fontDatas = JFaceResources.getDialogFont().getFontData();
+                if (fontDatas.length > 0) {
+                    settings.put(DIALOG_FONT_DATA, fontDatas[0].toString());
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the initial size to use for the shell. Overridden
+     * to check whether a size has been stored in dialog settings.
+     * If a size has been stored, it is returned.
+     *
+     * @return the initial size of the shell
+     *
+     * @since 3.2
+     * @see #getDialogBoundsSettings()
+     * @see #getDialogBoundsStrategy()
+     */
+    protected override Point getInitialSize() {
+        Point result = super.getInitialSize();
+
+        // Check the dialog settings for a stored size.
+        if ((getDialogBoundsStrategy() & DIALOG_PERSISTSIZE)!is 0) {
+            IDialogSettings settings = getDialogBoundsSettings();
+            if (settings !is null) {
+                // Check that the dialog font matches the font used
+                // when the bounds was stored.  If the font has changed,
+                // we do not honor the stored settings.
+                // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=132821
+                bool useStoredBounds = true;
+                String previousDialogFontData = settings.get(DIALOG_FONT_DATA);
+                // There is a previously stored font, so we will check it.
+                // Note that if we haven't stored the font before, then we will
+                // use the stored bounds.  This allows restoring of dialog bounds
+                // that were stored before we started storing the fontdata.
+                if (previousDialogFontData !is null && previousDialogFontData.length > 0) {
+                    FontData [] fontDatas = JFaceResources.getDialogFont().getFontData();
+                    if (fontDatas.length > 0) {
+                        String currentDialogFontData = fontDatas[0].toString();
+                        useStoredBounds = currentDialogFontData.equalsIgnoreCase(previousDialogFontData);
+                    }
+                }
+                if (useStoredBounds) {
+                    try {
+                        // Get the stored width and height.
+                        int width = settings.getInt(DIALOG_WIDTH);
+                        if (width !is DIALOG_DEFAULT_BOUNDS) {
+                            result.x = width;
+                        }
+                        int height = settings.getInt(DIALOG_HEIGHT);
+                        if (height !is DIALOG_DEFAULT_BOUNDS) {
+                            result.y = height;
+                        }
+
+                    } catch (NumberFormatException e) {
+                    }
+                }
+            }
+        }
+        // No attempt is made to constrain the bounds. The default
+        // constraining behavior in Window will be used.
+        return result;
+    }
+
+    /**
+     * Returns the initial location to use for the shell. Overridden
+     * to check whether the bounds of the dialog have been stored in
+     * dialog settings.  If a location has been stored, it is returned.
+     *
+     * @param initialSize
+     *            the initial size of the shell, as returned by
+     *            <code>getInitialSize</code>.
+     * @return the initial location of the shell
+     *
+     * @since 3.2
+     * @see #getDialogBoundsSettings()
+     * @see #getDialogBoundsStrategy()
+     */
+    protected override Point getInitialLocation(Point initialSize) {
+        Point result = super.getInitialLocation(initialSize);
+        if ((getDialogBoundsStrategy() & DIALOG_PERSISTLOCATION)!is 0) {
+            IDialogSettings settings = getDialogBoundsSettings();
+            if (settings !is null) {
+                try {
+                    int x = settings.getInt(DIALOG_ORIGIN_X);
+                    int y = settings.getInt(DIALOG_ORIGIN_Y);
+                    result = new Point(x, y);
+                    // The coordinates were stored relative to the parent shell.
+                    // Convert to display coordinates.
+                    Shell parent = getParentShell();
+                    if (parent !is null) {
+                        Point parentLocation = parent.getLocation();
+                        result.x += parentLocation.x;
+                        result.y += parentLocation.y;
+                    }
+                } catch (NumberFormatException e) {
+                }
+            }
+        }
+        // No attempt is made to constrain the bounds. The default
+        // constraining behavior in Window will be used.
+        return result;
+    }
+    
+    /**
+     * Returns a bool indicating whether the dialog should be
+     * considered resizable when the shell style is initially
+     * set.  
+     * 
+     * This method is used to ensure that all style 
+     * bits appropriate for resizable dialogs are added to the 
+     * shell style.  Individual dialogs may always set the shell 
+     * style to ensure that a dialog is resizable, but using this
+     * method ensures that resizable dialogs will be created with
+     * the same set of style bits.
+     * 
+     * Style bits will never be removed based on the return value 
+     * of this method.  For example, if a dialog returns 
+     * <code>false</code>, but also sets a style bit for a 
+     * SWT.RESIZE border, the style bit will be honored.
+     * 
+     * @return a bool indicating whether the dialog is 
+     * resizable and should have the default style bits for
+     * resizable dialogs
+     * 
+     * @since 3.4
+     */
+    protected bool isResizable() {
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/DialogMessageArea.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.DialogMessageArea;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IMessageProvider;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The DialogMessageArea is a resusable component for adding an accessible
+ * message area to a dialog.
+ *
+ * When the message is normal a CLabel is used but an errors replaces the
+ * message area with a non editable text that can take focus for use by screen
+ * readers.
+ *
+ * @since 3.0
+ */
+public class DialogMessageArea : Object {
+    private Text messageText;
+
+    private Label messageImageLabel;
+
+    private Composite messageComposite;
+
+    private String lastMessageText;
+
+    private int lastMessageType;
+
+    private CLabel titleLabel;
+
+    /**
+     * Create a new instance of the receiver.
+     */
+    public this() {
+        //No initial behaviour
+    }
+
+    /**
+     * Create the contents for the receiver.
+     *
+     * @param parent
+     *            the Composite that the children will be created in
+     */
+    public void createContents(Composite parent) {
+
+        // Message label
+        titleLabel = new CLabel(parent, SWT.NONE);
+        titleLabel.setFont(JFaceResources.getBannerFont());
+        messageComposite = new Composite(parent, SWT.NONE);
+        GridLayout messageLayout = new GridLayout();
+        messageLayout.numColumns = 2;
+        messageLayout.marginWidth = 0;
+        messageLayout.marginHeight = 0;
+        messageLayout.makeColumnsEqualWidth = false;
+        messageComposite.setLayout(messageLayout);
+        messageImageLabel = new Label(messageComposite, SWT.NONE);
+        messageImageLabel.setImage(JFaceResources
+                .getImage(Dialog.DLG_IMG_MESSAGE_INFO));
+        messageImageLabel.setLayoutData(new GridData(
+                GridData.VERTICAL_ALIGN_CENTER));
+
+        messageText = new Text(messageComposite, SWT.NONE);
+        messageText.setEditable(false);
+
+        GridData textData = new GridData(GridData.GRAB_HORIZONTAL
+                | GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_CENTER);
+        messageText.setLayoutData(textData);
+
+    }
+
+    /**
+     * Set the layoutData for the title area. In most cases this will be a copy
+     * of the layoutData used in setMessageLayoutData.
+     *
+     * @param layoutData
+     *            the layoutData for the title
+     * @see #setMessageLayoutData(Object)
+     */
+    public void setTitleLayoutData(Object layoutData) {
+        titleLabel.setLayoutData(layoutData);
+    }
+
+    /**
+     * Set the layoutData for the messageArea. In most cases this will be a copy
+     * of the layoutData used in setTitleLayoutData.
+     *
+     * @param layoutData
+     *            the layoutData for the message area composite.
+     * @see #setTitleLayoutData(Object)
+     */
+    public void setMessageLayoutData(Object layoutData) {
+        messageComposite.setLayoutData(layoutData);
+    }
+
+    /**
+     * Show the title.
+     *
+     * @param titleMessage
+     *            String for the titke
+     * @param titleImage
+     *            Image or <code>null</code>
+     */
+    public void showTitle(String titleMessage, Image titleImage) {
+        titleLabel.setImage(titleImage);
+        titleLabel.setText(titleMessage);
+        restoreTitle();
+        return;
+    }
+
+    /**
+     * Enable the title and disable the message text and image.
+     */
+    public void restoreTitle() {
+        titleLabel.setVisible(true);
+        messageComposite.setVisible(false);
+        lastMessageText = null;
+        lastMessageType = IMessageProvider.NONE;
+    }
+
+    /**
+     * Show the new message in the message text and update the image. Base the
+     * background color on whether or not there are errors.
+     *
+     * @param newMessage
+     *            The new value for the message
+     * @param newType
+     *            One of the IMessageProvider constants. If newType is
+     *            IMessageProvider.NONE show the title.
+     * @see IMessageProvider
+     */
+    public void updateText(String newMessage, int newType) {
+        Image newImage = null;
+        switch (newType) {
+        case IMessageProvider.NONE:
+            if (newMessage is null) {
+                restoreTitle();
+            } else {
+                showTitle(newMessage, null);
+            }
+            return;
+        case IMessageProvider.INFORMATION:
+            newImage = JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_INFO);
+            break;
+        case IMessageProvider.WARNING:
+            newImage = JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_WARNING);
+            break;
+        case IMessageProvider.ERROR:
+            newImage = JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_ERROR);
+
+            break;
+        default:
+        }
+        messageComposite.setVisible(true);
+        titleLabel.setVisible(false);
+        // Any more updates required?
+        // If the message text equals the tooltip (i.e. non-shortened text is the same)
+        // and shortened text is the same (i.e. not a resize)
+        // and the image is the same then nothing to do
+        String shortText = Dialog.shortenText(newMessage,messageText);
+        if (newMessage.equals(messageText.getToolTipText())
+                && newImage is messageImageLabel.getImage()
+                    && shortText.equals(messageText.getText())) {
+            return;
+        }
+        messageImageLabel.setImage(newImage);
+        messageText.setText(Dialog.shortenText(newMessage,messageText));
+        messageText.setToolTipText(newMessage);
+        lastMessageText = newMessage;
+
+    }
+
+
+    /**
+     * Clear the error message. Restore the previously displayed message if
+     * there is one, if not restore the title label.
+     *
+     */
+    public void clearErrorMessage() {
+        if (lastMessageText is null) {
+            restoreTitle();
+        } else {
+            updateText(lastMessageText, lastMessageType);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/DialogPage.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,480 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.DialogPage;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IDialogPage;
+import org.eclipse.jface.dialogs.IMessageProvider;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Abstract base implementation of a dialog page. All dialog pages are
+ * subclasses of this one.
+ */
+public abstract class DialogPage : IDialogPage, IMessageProvider {
+    /**
+     * The control for this dialog page.
+     */
+    private Control control;
+
+    /**
+     * Optional title; <code>null</code> if none.
+     *
+     * @see #setTitle
+     */
+    private String title = null;
+
+    /**
+     * Optional description; <code>null</code> if none.
+     *
+     * @see #setDescription
+     */
+    private String description = null;
+
+    /**
+     * Cached image; <code>null</code> if none.
+     *
+     * @see #setImageDescriptor(ImageDescriptor)
+     */
+    private Image image = null;
+
+    /**
+     * Optional image; <code>null</code> if none.
+     *
+     * @see #setImageDescriptor(ImageDescriptor)
+     */
+    private ImageDescriptor imageDescriptor = null;
+
+    /**
+     * The current message; <code>null</code> if none.
+     */
+    private String message = null;
+
+    /**
+     * The current message type; default value <code>NONE</code>.
+     */
+    private int messageType = NONE;
+
+    /**
+     * The current error message; <code>null</code> if none.
+     */
+    private String errorMessage = null;
+
+    /**
+     * Font metrics to use for determining pixel sizes.
+     */
+    private FontMetrics fontMetrics;
+
+    /**
+     * Creates a new empty dialog page.
+     */
+    protected this() {
+        //No initial behaviour
+    }
+
+    /**
+     * Creates a new dialog page with the given title.
+     *
+     * @param title
+     *            the title of this dialog page, or <code>null</code> if none
+     */
+    protected this(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Creates a new dialog page with the given title and image.
+     *
+     * @param title
+     *            the title of this dialog page, or <code>null</code> if none
+     * @param image
+     *            the image for this dialog page, or <code>null</code> if none
+     */
+    protected this(String title, ImageDescriptor image) {
+        this(title);
+        imageDescriptor = image;
+    }
+
+    /**
+     * Returns the number of pixels corresponding to the height of the given
+     * number of characters.
+     * <p>
+     * This method may only be called after <code>initializeDialogUnits</code>
+     * has been called.
+     * </p>
+     * <p>
+     * Clients may call this framework method, but should not override it.
+     * </p>
+     *
+     * @param chars
+     *            the number of characters
+     * @return the number of pixels
+     */
+    protected int convertHeightInCharsToPixels(int chars) {
+        // test for failure to initialize for backward compatibility
+        if (fontMetrics is null) {
+            return 0;
+        }
+        return Dialog.convertHeightInCharsToPixels(fontMetrics, chars);
+    }
+
+    /**
+     * Returns the number of pixels corresponding to the given number of
+     * horizontal dialog units.
+     * <p>
+     * This method may only be called after <code>initializeDialogUnits</code>
+     * has been called.
+     * </p>
+     * <p>
+     * Clients may call this framework method, but should not override it.
+     * </p>
+     *
+     * @param dlus
+     *            the number of horizontal dialog units
+     * @return the number of pixels
+     */
+    protected int convertHorizontalDLUsToPixels(int dlus) {
+        // test for failure to initialize for backward compatibility
+        if (fontMetrics is null) {
+            return 0;
+        }
+        return Dialog.convertHorizontalDLUsToPixels(fontMetrics, dlus);
+    }
+
+    /**
+     * Returns the number of pixels corresponding to the given number of
+     * vertical dialog units.
+     * <p>
+     * This method may only be called after <code>initializeDialogUnits</code>
+     * has been called.
+     * </p>
+     * <p>
+     * Clients may call this framework method, but should not override it.
+     * </p>
+     *
+     * @param dlus
+     *            the number of vertical dialog units
+     * @return the number of pixels
+     */
+    protected int convertVerticalDLUsToPixels(int dlus) {
+        // test for failure to initialize for backward compatibility
+        if (fontMetrics is null) {
+            return 0;
+        }
+        return Dialog.convertVerticalDLUsToPixels(fontMetrics, dlus);
+    }
+
+    /**
+     * Returns the number of pixels corresponding to the width of the given
+     * number of characters.
+     * <p>
+     * This method may only be called after <code>initializeDialogUnits</code>
+     * has been called.
+     * </p>
+     * <p>
+     * Clients may call this framework method, but should not override it.
+     * </p>
+     *
+     * @param chars
+     *            the number of characters
+     * @return the number of pixels
+     */
+    protected int convertWidthInCharsToPixels(int chars) {
+        // test for failure to initialize for backward compatibility
+        if (fontMetrics is null) {
+            return 0;
+        }
+        return Dialog.convertWidthInCharsToPixels(fontMetrics, chars);
+    }
+
+    /**
+     * The <code>DialogPage</code> implementation of an
+     * <code>IDialogPage</code> method does nothing. Subclasses may extend.
+     */
+    public void dispose() {
+        // deallocate SWT resources
+        if (image !is null) {
+            image.dispose();
+            image = null;
+        }
+    }
+
+    /**
+     * Returns the top level control for this dialog page.
+     *
+     * @return the top level control
+     */
+    public Control getControl() {
+        return control;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IDialogPage.
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Returns the symbolic font name used by dialog pages.
+     *
+     * @return the symbolic font name
+     */
+    protected String getDialogFontName() {
+        return JFaceResources.DIALOG_FONT;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IDialogPage.
+     */
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+
+    /**
+     * Returns the default font to use for this dialog page.
+     *
+     * @return the font
+     */
+    protected Font getFont() {
+        return JFaceResources.getFontRegistry().get(getDialogFontName());
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IDialogPage.
+     */
+    public Image getImage() {
+        if (image is null) {
+            if (imageDescriptor !is null) {
+                image = imageDescriptor.createImage();
+            }
+        }
+        return image;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IDialogPage.
+     */
+    public String getMessage() {
+        return message;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IMessageProvider.
+     */
+    public int getMessageType() {
+        return messageType;
+    }
+
+    /**
+     * Returns this dialog page's shell. Convenience method for
+     * <code>getControl().getShell()</code>. This method may only be called
+     * after the page's control has been created.
+     *
+     * @return the shell
+     */
+    public Shell getShell() {
+        return getControl().getShell();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IDialogPage.
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Returns the tool tip text for the widget with the given id.
+     * <p>
+     * The default implementation of this framework method does nothing and
+     * returns <code>null</code>. Subclasses may override.
+     * </p>
+     *
+     * @param widgetId
+     *            the id of the widget for which hover help is requested
+     * @return the tool tip text, or <code>null</code> if none
+     * @deprecated
+     */
+    protected final String getToolTipText(int widgetId) {
+        // return nothing by default
+        return null;
+    }
+
+    /**
+     * Initializes the computation of horizontal and vertical dialog units based
+     * on the size of current font.
+     * <p>
+     * This method must be called before any of the dialog unit based conversion
+     * methods are called.
+     * </p>
+     *
+     * @param testControl
+     *            a control from which to obtain the current font
+     */
+    protected void initializeDialogUnits(Control testControl) {
+        // Compute and store a font metric
+        GC gc = new GC(testControl);
+        gc.setFont(JFaceResources.getDialogFont());
+        fontMetrics = gc.getFontMetrics();
+        gc.dispose();
+    }
+
+    /**
+     * Sets the <code>GridData</code> on the specified button to be one that
+     * is spaced for the current dialog page units. The method
+     * <code>initializeDialogUnits</code> must be called once before calling
+     * this method for the first time.
+     *
+     * @param button
+     *            the button to set the <code>GridData</code>
+     * @return the <code>GridData</code> set on the specified button
+     */
+    protected GridData setButtonLayoutData(Button button) {
+        GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+        int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
+        Point minSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+        data.widthHint = Math.max(widthHint, minSize.x);
+        button.setLayoutData(data);
+        return data;
+    }
+
+    /**
+     * Tests whether this page's UI content has already been created.
+     *
+     * @return <code>true</code> if the control has been created, and
+     *         <code>false</code> if not
+     */
+    protected bool isControlCreated() {
+        return control !is null;
+    }
+
+    /**
+     * This default implementation of an <code>IDialogPage</code> method does
+     * nothing. Subclasses should override to take some action in response to a
+     * help request.
+     */
+    public void performHelp() {
+        //No default help
+    }
+
+    /**
+     * Set the control for the receiver.
+     * @param newControl
+     */
+    protected void setControl(Control newControl) {
+        control = newControl;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IDialogPage.
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * Sets or clears the error message for this page.
+     *
+     * @param newMessage
+     *            the message, or <code>null</code> to clear the error message
+     */
+    public void setErrorMessage(String newMessage) {
+        errorMessage = newMessage;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IDialogPage.
+     */
+    public void setImageDescriptor(ImageDescriptor desc) {
+        imageDescriptor = desc;
+        if (image !is null) {
+            image.dispose();
+            image = null;
+        }
+    }
+
+    /**
+     * Sets or clears the message for this page.
+     * <p>
+     * This is a shortcut for <code>setMessage(newMesasge, NONE)</code>
+     * </p>
+     *
+     * @param newMessage
+     *            the message, or <code>null</code> to clear the message
+     */
+    public void setMessage(String newMessage) {
+        setMessage(newMessage, NONE);
+    }
+
+    /**
+     * Sets the message for this page with an indication of what type of message
+     * it is.
+     * <p>
+     * The valid message types are one of <code>NONE</code>,
+     * <code>INFORMATION</code>,<code>WARNING</code>, or
+     * <code>ERROR</code>.
+     * </p>
+     * <p>
+     * Note that for backward compatibility, a message of type
+     * <code>ERROR</code> is different than an error message (set using
+     * <code>setErrorMessage</code>). An error message overrides the current
+     * message until the error message is cleared. This method replaces the
+     * current message and does not affect the error message.
+     * </p>
+     *
+     * @param newMessage
+     *            the message, or <code>null</code> to clear the message
+     * @param newType
+     *            the message type
+     * @since 2.0
+     */
+    public void setMessage(String newMessage, int newType) {
+        message = newMessage;
+        messageType = newType;
+    }
+
+    /**
+     * The <code>DialogPage</code> implementation of this
+     * <code>IDialogPage</code> method remembers the title in an internal
+     * state variable. Subclasses may extend.
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * The <code>DialogPage</code> implementation of this
+     * <code>IDialogPage</code> method sets the control to the given
+     * visibility state. Subclasses may extend.
+     */
+    public void setVisible(bool visible) {
+        control.setVisible(visible);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/DialogSettings.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,503 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.dialogs.DialogSettings;
+
+import org.eclipse.jface.dialogs.IDialogSettings;
+
+
+static import tango.text.xml.Document;
+static import tango.text.xml.SaxParser;
+static import tango.text.xml.PullParser;
+
+import java.lang.all;
+import java.util.Enumeration;
+import java.util.Collections;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+static import java.io.OutputStream;
+static import tango.text.convert.Integer;
+static import tango.text.convert.Float;
+static import tango.text.Text;
+static import tango.io.stream.Format;
+static import tango.io.model.IConduit;
+import tango.io.device.File;
+import tango.core.Exception;
+import java.nonstandard.XmlTranscode;
+
+/**
+ * Concrete implementation of a dialog settings (<code>IDialogSettings</code>)
+ * using a hash table and XML. The dialog store can be read
+ * from and saved to a stream. All keys and values must be strings or array of
+ * strings. Primitive types are converted to strings.
+ * <p>
+ * This class was not designed to be subclassed.
+ *
+ * Here is an example of using a DialogSettings:
+ * </p>
+ * <pre>
+ * <code>
+ * DialogSettings settings = new DialogSettings("root");
+ * settings.put("Boolean1",true);
+ * settings.put("Long1",100);
+ * settings.put("Array1",new String[]{"aaaa1","bbbb1","cccc1"});
+ * DialogSettings section = new DialogSettings("sectionName");
+ * settings.addSection(section);
+ * section.put("Int2",200);
+ * section.put("Float2",1.1);
+ * section.put("Array2",new String[]{"aaaa2","bbbb2","cccc2"});
+ * settings.save("c:\\temp\\test\\dialog.xml");
+ * </code>
+ * </pre>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+
+public class DialogSettings : IDialogSettings {
+    alias tango.text.xml.Document.Document!(char) Document;
+    alias tango.text.xml.Document.Document!(char).Node Element;
+    // The name of the DialogSettings.
+    private String name;
+
+    /* A Map of DialogSettings representing each sections in a DialogSettings.
+     It maps the DialogSettings' name to the DialogSettings */
+    private Map sections;
+
+    /* A Map with all the keys and values of this sections.
+     Either the keys an values are restricted to strings. */
+    private Map items;
+
+    // A Map with all the keys mapped to array of strings.
+    private Map arrayItems;
+
+    private static const String TAG_SECTION = "section";//$NON-NLS-1$
+
+    private static const String TAG_NAME = "name";//$NON-NLS-1$
+
+    private static const String TAG_KEY = "key";//$NON-NLS-1$
+
+    private static const String TAG_VALUE = "value";//$NON-NLS-1$
+
+    private static const String TAG_LIST = "list";//$NON-NLS-1$
+
+    private static const String TAG_ITEM = "item";//$NON-NLS-1$
+
+    /**
+     * Create an empty dialog settings which loads and saves its
+     * content to a file.
+     * Use the methods <code>load(String)</code> and <code>store(String)</code>
+     * to load and store this dialog settings.
+     *
+     * @param sectionName the name of the section in the settings.
+     */
+    public this(String sectionName) {
+        name = sectionName;
+        items = new HashMap();
+        arrayItems = new HashMap();
+        sections = new HashMap();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public IDialogSettings addNewSection(String sectionName) {
+        DialogSettings section = new DialogSettings(sectionName);
+        addSection(section);
+        return section;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public void addSection(IDialogSettings section) {
+        sections.put(stringcast(section.getName()), cast(Object)section);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public String get(String key) {
+        return stringcast(items.get(stringcast(key)));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public String[] getArray(String key) {
+        return stringArrayFromObject(arrayItems.get(stringcast(key)));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public bool getBoolean(String key) {
+        return stringcast(items.get(stringcast(key))) == "true";
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public double getDouble(String key) {
+        String setting = stringcast(items.get(stringcast(key)));
+        if (setting is null) {
+            throw new NumberFormatException(
+                    "There is no setting associated with the key \"" ~ key ~ "\"");//$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        return tango.text.convert.Float.toFloat(setting);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public float getFloat(String key) {
+        String setting = stringcast(items.get(stringcast(key)));
+        if (setting is null) {
+            throw new NumberFormatException(
+                    "There is no setting associated with the key \"" ~ key ~ "\"");//$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        return tango.text.convert.Float.toFloat(setting);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public int getInt(String key) {
+        String setting = stringcast(items.get(stringcast(key)));
+        if (setting is null) {
+            //new Integer(null) will throw a NumberFormatException and meet our spec, but this message
+            //is clearer.
+            throw new NumberFormatException(
+                    "There is no setting associated with the key \"" ~ key ~ "\"");//$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        return tango.text.convert.Integer.toInt(setting);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public long getLong(String key) {
+        String setting = stringcast(items.get(stringcast(key)));
+        if (setting is null) {
+            //new Long(null) will throw a NumberFormatException and meet our spec, but this message
+            //is clearer.
+            throw new NumberFormatException(
+                    "There is no setting associated with the key \"" ~ key ~ "\"");//$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        return tango.text.convert.Integer.toLong(setting);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public IDialogSettings getSection(String sectionName) {
+        return cast(IDialogSettings) sections.get(stringcast(sectionName));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public IDialogSettings[] getSections() {
+        Collection values = sections.values();
+        IDialogSettings[] result = arraycast!(IDialogSettings)( values.toArray() );
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public void load( tango.io.model.IConduit.InputStream input) {
+        Document document = new Document();
+        try {
+            char[] content;
+            char[1024] readbuf;
+            int chunksize = 0;
+            while( (chunksize=input.read(readbuf)) !is tango.io.model.IConduit.InputStream.Eof ){
+                content ~=  readbuf[ 0 .. chunksize ];
+            }
+            document.parse( content );
+
+            //Strip out any comments first
+            foreach( n; document.query[].filter( delegate bool(Element n) {
+                    return n.type is tango.text.xml.PullParser.XmlNodeType.Comment ;
+                })){
+                //TODO: remove() was added after tango 0.99.5
+                //n.remove();
+            }
+            load(document, document.tree.child );
+        } catch (IOException e) {
+            // ignore
+        } catch (TextException e) {
+            // ignore
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    //TODO: solve overload load(char[])
+    public void load(String fileName) {
+        scope file = new File( fileName );
+        load( file.input );
+        file.close;
+    }
+
+    /* (non-Javadoc)
+     * Load the setting from the <code>document</code>
+     */
+    private void load(Document document, Element root) {
+
+        name = root.attributes.name(null,TAG_NAME).value();
+
+        foreach( n; root.query[TAG_ITEM] ){
+            if( root is n.parent() ){
+                String key = n.attributes.name(null,TAG_KEY).value().dup;
+                String value = n.attributes.name(null,TAG_VALUE).value().dup;
+                items.put(stringcast(key), stringcast(value));
+            }
+        }
+        foreach( n; root.query[TAG_LIST].dup ){
+            if( root is n.parent() ){
+                auto child = n;
+                String key = child.attributes.name(null,TAG_KEY).value().dup;
+                char[][] valueList;
+                foreach( node; root.query[TAG_ITEM].dup ){
+                    if (child is node.parent()) {
+                        valueList ~= node.attributes.name(null,TAG_VALUE).value().dup;
+                    }
+                }
+                arrayItems.put(stringcast(key), new ArrayWrapperString2(valueList) );
+            }
+        }
+        foreach( n; root.query[TAG_SECTION].dup ){
+            if( root is n.parent() ){
+                DialogSettings s = new DialogSettings("NoName");//$NON-NLS-1$
+                s.load(document, n);
+                addSection(s);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public void put(String key, String[] value) {
+        arrayItems.put(stringcast(key), new ArrayWrapperString2(value));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public void put(String key, double value) {
+        put(key, tango.text.convert.Float.toString(value));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public void put(String key, float value) {
+        put(key, tango.text.convert.Float.toString(value));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public void put(String key, int value) {
+        put(key, tango.text.convert.Integer.toString(value));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public void put(String key, long value) {
+        put(key, tango.text.convert.Integer.toString(value));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public void put(String key, String value) {
+        items.put(stringcast(key), stringcast(value));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public void put(String key, bool value) {
+        put(key, value ? "true" : "false" );
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public void save(tango.io.model.IConduit.OutputStream writer) {
+        save(new XMLWriter(writer));
+    }
+
+
+    /* (non-Javadoc)
+     * Method declared on IDialogSettings.
+     */
+    public void save(String fileName) {
+        auto stream = new tango.io.device.File.File(
+                fileName,
+                tango.io.device.File.File.WriteCreate);
+        XMLWriter writer = new XMLWriter(stream.output);
+        save(writer);
+        writer.close();
+    }
+
+    /* (non-Javadoc)
+     * Save the settings in the <code>document</code>.
+     */
+    private void save(XMLWriter out_) {
+        HashMap attributes = new HashMap(2);
+        attributes.put(stringcast(TAG_NAME), stringcast(name is null ? "" : name)); //$NON-NLS-1$
+        out_.startTag(TAG_SECTION, attributes);
+        attributes.clear();
+
+        Object EMPTY_STR = new ArrayWrapperString("");
+        foreach( key,value; items ){
+            attributes.put(stringcast(TAG_KEY), key is null ? EMPTY_STR : key); //$NON-NLS-1$
+            String string = stringcast(value);//cast(String) items.get(stringcast(key));
+            attributes.put(stringcast(TAG_VALUE), stringcast(string is null ? "" : string)); //$NON-NLS-1$
+            out_.printTag(TAG_ITEM, attributes, true);
+        }
+
+        attributes.clear();
+        foreach( key,value; arrayItems ){
+            attributes.put(stringcast(TAG_KEY), key is null ? EMPTY_STR : key); //$NON-NLS-1$
+            out_.startTag(TAG_LIST, attributes);
+            attributes.clear();
+            String[] strValues = stringArrayFromObject(value);
+            if (value !is null) {
+                for (int index = 0; index < strValues.length; index++) {
+                    String string = strValues[index];
+                    attributes.put(stringcast(TAG_VALUE), stringcast(string is null ? "" : string)); //$NON-NLS-1$
+                    out_.printTag(TAG_ITEM, attributes, true);
+                }
+            }
+            out_.endTag(TAG_LIST);
+            attributes.clear();
+        }
+        for (Iterator i = sections.values().iterator(); i.hasNext();) {
+            (cast(DialogSettings) i.next()).save(out_);
+        }
+        out_.endTag(TAG_SECTION);
+    }
+
+
+    /**
+     * A simple XML writer.  Using this instead of the javax.xml.transform classes allows
+     * compilation against JCL Foundation (bug 80059).
+     */
+    private static class XMLWriter : tango.io.stream.Format.FormatOutput!(char) {
+        /** current number of tabs to use for ident */
+        protected int tab;
+
+        /** the xml header */
+        protected static const String XML_VERSION = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; //$NON-NLS-1$
+
+        /**
+         * Create a new XMLWriter
+         * @param output the write to used when writing to
+         */
+        public this(tango.io.model.IConduit.OutputStream output) {
+            super( tango.text.convert.Format.Format, output);
+            tab = 0;
+            print(XML_VERSION);
+            newline;
+        }
+
+        /**
+         * write the intended end tag
+         * @param name the name of the tag to end
+         */
+        public void endTag(String name) {
+            tab--;
+            printTag("/" ~ name, null, false); //$NON-NLS-1$
+        }
+
+        private void printTabulation() {
+            for (int i = 0; i < tab; i++) {
+                super.print('\t');
+            }
+        }
+
+        /**
+         * write the tag to the stream and format it by itending it and add new line after the tag
+         * @param name the name of the tag
+         * @param parameters map of parameters
+         * @param close should the tag be ended automatically (=> empty tag)
+         */
+        public void printTag(String name, HashMap parameters, bool close) {
+            printTag(name, parameters, true, true, close);
+        }
+
+        private void printTag(String name, HashMap parameters, bool shouldTab, bool newLine, bool close) {
+            StringBuffer sb = new StringBuffer();
+            sb.append('<');
+            sb.append(name);
+            if (parameters !is null) {
+                for (Enumeration e = Collections.enumeration(parameters.keySet()); e.hasMoreElements();) {
+                    sb.append(" "); //$NON-NLS-1$
+                    String key = stringcast( e.nextElement());
+                    sb.append(key);
+                    sb.append("=\""); //$NON-NLS-1$
+                    //sb.append(getEscaped(String.valueOf(parameters.get(key))));
+                    sb.append(xmlEscape(stringcast(parameters.get(stringcast(key)))));
+                    sb.append("\""); //$NON-NLS-1$
+                }
+            }
+            if (close) {
+                sb.append('/');
+            }
+            sb.append('>');
+            if (shouldTab) {
+                printTabulation();
+            }
+            if (newLine) {
+                print(sb.toString());
+                newline;
+            } else {
+                print(sb.toString());
+            }
+        }
+
+        /**
+         * start the tag
+         * @param name the name of the tag
+         * @param parameters map of parameters
+         */
+        public void startTag(String name, HashMap parameters) {
+            startTag(name, parameters, true);
+            tab++;
+        }
+
+        private void startTag(String name, HashMap parameters, bool newLine) {
+            printTag(name, parameters, true, newLine, false);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/DialogTray.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.dialogs.DialogTray;
+
+import java.lang.all;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * <p>
+ * This class is the abstract superclass of all dialog trays. A tray can be opened
+ * in any <code>TrayDialog</code>.
+ * </p>
+ *
+ * @see org.eclipse.jface.dialogs.TrayDialog
+ * @since 3.2
+ */
+public abstract class DialogTray {
+
+    /**
+     * Creates the contents (widgets) that will be contained in the tray.
+     * <p>
+     * Tray implementions must not set a layout on the parent composite, or assume
+     * a particular layout on the parent. The tray dialog will allocate space
+     * according to the natural size of the tray, and will fill the tray area with the
+     * tray's contents.
+     * </p>
+     *
+     * @param parent the composite that will contain the tray
+     * @return the contents of the tray, as a <code>Control</code>
+     */
+    protected abstract Control createContents(Composite parent);
+    package Control createContents_package(Composite parent){
+        return createContents(parent);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/ErrorDialog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,698 @@
+/*******************************************************************************
+ * 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
+ *      Sebastian Davids <sdavids@gmx.de> - Fix for bug 19346 - Dialog font should
+ *          be activated and used by other components.
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.ErrorDialog;
+
+import org.eclipse.jface.dialogs.IconAndMessageDialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.ErrorSupportProvider;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.Policy;
+
+import java.lang.all;
+import java.util.Set;
+/**
+ * A dialog to display one or more errors to the user, as contained in an
+ * <code>IStatus</code> object. If an error contains additional detailed
+ * information then a Details button is automatically supplied, which shows or
+ * hides an error details viewer when pressed by the user.
+ *
+ * @see org.eclipse.core.runtime.IStatus
+ */
+public class ErrorDialog : IconAndMessageDialog {
+    /**
+     * Static to prevent opening of error dialogs for automated testing.
+     */
+    public static bool AUTOMATED_MODE = false;
+
+    /**
+     * Reserve room for this many list items.
+     */
+    private static const int LIST_ITEM_COUNT = 7;
+
+    /**
+     * The nesting indent.
+     */
+    private static const String NESTING_INDENT = "  "; //$NON-NLS-1$
+
+    /**
+     * The Details button.
+     */
+    private Button detailsButton;
+
+    /**
+     * The title of the dialog.
+     */
+    private String title;
+
+    /**
+     * The SWT list control that displays the error details.
+     */
+    private List list;
+
+    /**
+     * Indicates whether the error details viewer is currently created.
+     */
+    private bool listCreated = false;
+
+    /**
+     * Filter mask for determining which status items to display.
+     */
+    private int displayMask = 0xFFFF;
+
+    /**
+     * The main status object.
+     */
+    private IStatus status;
+
+    /**
+     * The current clipboard. To be disposed when closing the dialog.
+     */
+    private Clipboard clipboard;
+
+    private bool shouldIncludeTopLevelErrorInDetails = false;
+
+
+    /**
+     * Creates an error dialog. Note that the dialog will have no visual
+     * representation (no widgets) until it is told to open.
+     * <p>
+     * Normally one should use <code>openError</code> to create and open one
+     * of these. This constructor is useful only if the error object being
+     * displayed contains child items <it>and </it> you need to specify a mask
+     * which will be used to filter the displaying of these children. The error
+     * dialog will only be displayed if there is at least one child status
+     * matching the mask.
+     * </p>
+     *
+     * @param parentShell
+     *            the shell under which to create this dialog
+     * @param dialogTitle
+     *            the title to use for this dialog, or <code>null</code> to
+     *            indicate that the default title should be used
+     * @param message
+     *            the message to show in this dialog, or <code>null</code> to
+     *            indicate that the error's message should be shown as the
+     *            primary message
+     * @param status
+     *            the error to show to the user
+     * @param displayMask
+     *            the mask to use to filter the displaying of child items, as
+     *            per <code>IStatus.matches</code>
+     * @see org.eclipse.core.runtime.IStatus#matches(int)
+     */
+    public this(Shell parentShell, String dialogTitle, String message,
+            IStatus status, int displayMask) {
+        super(parentShell);
+        this.title = dialogTitle is null ? JFaceResources
+                .getString("Problem_Occurred") : //$NON-NLS-1$
+                dialogTitle;
+        this.message = message is null ? status.getMessage()
+                : JFaceResources
+                        .format(
+                                "Reason", message, status.getMessage() ); //$NON-NLS-1$
+        this.status = status;
+        this.displayMask = displayMask;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Dialog. Handles the pressing of the Ok
+     * or Details button in this dialog. If the Ok button was pressed then close
+     * this dialog. If the Details button was pressed then toggle the displaying
+     * of the error details area. Note that the Details button will only be
+     * visible if the error being displayed specifies child details.
+     */
+    protected override void buttonPressed(int id) {
+        if (id is IDialogConstants.DETAILS_ID) {
+            // was the details button pressed?
+            toggleDetailsArea();
+        } else {
+            super.buttonPressed(id);
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared in Window.
+     */
+    protected override void configureShell(Shell shell) {
+        super.configureShell(shell);
+        shell.setText(title);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
+     */
+    protected override void createButtonsForButtonBar(Composite parent) {
+        // create OK and Details buttons
+        createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
+                true);
+        createDetailsButton(parent);
+    }
+
+    /**
+     * Create the area for extra error support information.
+     *
+     * @param parent
+     */
+    private void createSupportArea(Composite parent) {
+
+        ErrorSupportProvider provider = Policy.getErrorSupportProvider();
+
+        if (provider is null)
+            return;
+
+        Composite supportArea = new Composite(parent, SWT.NONE);
+        provider.createSupportArea(supportArea, status);
+
+        GridData supportData = new GridData(SWT.FILL, SWT.FILL, true, true);
+        supportData.verticalSpan = 4;
+        supportArea.setLayoutData(supportData);
+        if (supportArea.getLayout() is null){
+            GridLayout layout = new GridLayout();
+            layout.marginWidth = 0;
+            layout.marginHeight = 0;
+            supportArea.setLayout(layout); // Give it a default layout if one isn't set
+        }
+
+
+    }
+
+    /**
+     * Create the details button if it should be included.
+     *
+     * @param parent
+     *            the parent composite
+     * @since 3.2
+     */
+    protected void createDetailsButton(Composite parent) {
+        if (shouldShowDetailsButton()) {
+            detailsButton = createButton(parent, IDialogConstants.DETAILS_ID,
+                    IDialogConstants.SHOW_DETAILS_LABEL, false);
+        }
+    }
+
+    /**
+     * This implementation of the <code>Dialog</code> framework method creates
+     * and lays out a composite. Subclasses that require a different dialog area
+     * may either override this method, or call the <code>super</code>
+     * implementation and add controls to the created composite.
+     *
+     * Note:  Since 3.4, the created composite no longer grabs excess vertical space.
+     * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=72489.
+     * If the old behavior is desired by subclasses, get the returned composite's
+     * layout data and set grabExcessVerticalSpace to true.
+     */
+    protected override Control createDialogArea(Composite parent) {
+        createMessageArea(parent);
+        createSupportArea(parent);
+        // create a composite with standard margins and spacing
+        Composite composite = new Composite(parent, SWT.NONE);
+        GridLayout layout = new GridLayout();
+        layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
+        layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
+        layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
+        layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
+        layout.numColumns = 2;
+        composite.setLayout(layout);
+        GridData childData = new GridData(GridData.FILL_BOTH);
+        childData.horizontalSpan = 2;
+        childData.grabExcessVerticalSpace = false;
+        composite.setLayoutData(childData);
+        composite.setFont(parent.getFont());
+
+        return composite;
+    }
+
+    /*
+     * @see IconAndMessageDialog#createDialogAndButtonArea(Composite)
+     */
+    protected override void createDialogAndButtonArea(Composite parent) {
+        super.createDialogAndButtonArea(parent);
+        if ( auto dialogComposite = cast(Composite)this.dialogArea ) {
+            // Create a label if there are no children to force a smaller layout
+            if (dialogComposite.getChildren().length is 0) {
+                new Label(dialogComposite, SWT.NULL);
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.IconAndMessageDialog#getImage()
+     */
+    protected override Image getImage() {
+        if (status !is null) {
+            if (status.getSeverity() is IStatus.WARNING) {
+                return getWarningImage();
+            }
+            if (status.getSeverity() is IStatus.INFO) {
+                return getInfoImage();
+            }
+        }
+        // If it was not a warning or an error then return the error image
+        return getErrorImage();
+    }
+
+    /**
+     * Create this dialog's drop-down list component.
+     *
+     * @param parent
+     *            the parent composite
+     * @return the drop-down list component
+     */
+    protected List createDropDownList(Composite parent) {
+        // create the list
+        list = new List(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL
+                | SWT.MULTI);
+        // fill the list
+        populateList(list);
+        GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL
+                | GridData.GRAB_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL
+                | GridData.GRAB_VERTICAL);
+        data.heightHint = list.getItemHeight() * LIST_ITEM_COUNT;
+        data.horizontalSpan = 2;
+        list.setLayoutData(data);
+        list.setFont(parent.getFont());
+        Menu copyMenu = new Menu(list);
+        MenuItem copyItem = new MenuItem(copyMenu, SWT.NONE);
+        copyItem.addSelectionListener(new class SelectionListener {
+            /*
+             * @see SelectionListener.widgetSelected (SelectionEvent)
+             */
+            public void widgetSelected(SelectionEvent e) {
+                copyToClipboard();
+            }
+
+            /*
+             * @see SelectionListener.widgetDefaultSelected(SelectionEvent)
+             */
+            public void widgetDefaultSelected(SelectionEvent e) {
+                copyToClipboard();
+            }
+        });
+        copyItem.setText(JFaceResources.getString("copy")); //$NON-NLS-1$
+        list.setMenu(copyMenu);
+        listCreated = true;
+        return list;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Window.
+     */
+    /**
+     * Extends <code>Window.open()</code>. Opens an error dialog to display
+     * the error. If you specified a mask to filter the displaying of these
+     * children, the error dialog will only be displayed if there is at least
+     * one child status matching the mask.
+     */
+    public override int open() {
+        if (!AUTOMATED_MODE && shouldDisplay(status, displayMask)) {
+            return super.open();
+        }
+        setReturnCode(OK);
+        return OK;
+    }
+
+    /**
+     * Opens an error dialog to display the given error. Use this method if the
+     * error object being displayed does not contain child items, or if you wish
+     * to display all such items without filtering.
+     *
+     * @param parent
+     *            the parent shell of the dialog, or <code>null</code> if none
+     * @param dialogTitle
+     *            the title to use for this dialog, or <code>null</code> to
+     *            indicate that the default title should be used
+     * @param message
+     *            the message to show in this dialog, or <code>null</code> to
+     *            indicate that the error's message should be shown as the
+     *            primary message
+     * @param status
+     *            the error to show to the user
+     * @return the code of the button that was pressed that resulted in this
+     *         dialog closing. This will be <code>Dialog.OK</code> if the OK
+     *         button was pressed, or <code>Dialog.CANCEL</code> if this
+     *         dialog's close window decoration or the ESC key was used.
+     */
+    public static int openError(Shell parent, String dialogTitle,
+            String message, IStatus status) {
+        return openError(parent, dialogTitle, message, status, IStatus.OK
+                | IStatus.INFO | IStatus.WARNING | IStatus.ERROR);
+    }
+
+    /**
+     * Opens an error dialog to display the given error. Use this method if the
+     * error object being displayed contains child items <it>and </it> you wish
+     * to specify a mask which will be used to filter the displaying of these
+     * children. The error dialog will only be displayed if there is at least
+     * one child status matching the mask.
+     *
+     * @param parentShell
+     *            the parent shell of the dialog, or <code>null</code> if none
+     * @param title
+     *            the title to use for this dialog, or <code>null</code> to
+     *            indicate that the default title should be used
+     * @param message
+     *            the message to show in this dialog, or <code>null</code> to
+     *            indicate that the error's message should be shown as the
+     *            primary message
+     * @param status
+     *            the error to show to the user
+     * @param displayMask
+     *            the mask to use to filter the displaying of child items, as
+     *            per <code>IStatus.matches</code>
+     * @return the code of the button that was pressed that resulted in this
+     *         dialog closing. This will be <code>Dialog.OK</code> if the OK
+     *         button was pressed, or <code>Dialog.CANCEL</code> if this
+     *         dialog's close window decoration or the ESC key was used.
+     * @see org.eclipse.core.runtime.IStatus#matches(int)
+     */
+    public static int openError(Shell parentShell, String title,
+            String message, IStatus status, int displayMask) {
+        ErrorDialog dialog = new ErrorDialog(parentShell, title, message,
+                status, displayMask);
+        return dialog.open();
+    }
+
+    /**
+     * Populates the list using this error dialog's status object. This walks
+     * the child static of the status object and displays them in a list. The
+     * format for each entry is status_path : status_message If the status's
+     * path was null then it (and the colon) are omitted.
+     *
+     * @param listToPopulate
+     *            The list to fill.
+     */
+    private void populateList(List listToPopulate) {
+        populateList(listToPopulate, status, 0,
+                shouldIncludeTopLevelErrorInDetails);
+    }
+
+    /**
+     * Populate the list with the messages from the given status. Traverse the
+     * children of the status deeply and also traverse CoreExceptions that
+     * appear in the status.
+     *
+     * @param listToPopulate
+     *            the list to populate
+     * @param buildingStatus
+     *            the status being displayed
+     * @param nesting
+     *            the nesting level (increases one level for each level of
+     *            children)
+     * @param includeStatus
+     *            whether to include the buildingStatus in the display or just
+     *            its children
+     */
+    private void populateList(List listToPopulate, IStatus buildingStatus,
+            int nesting, bool includeStatus) {
+
+        if (!buildingStatus.matches(displayMask)) {
+            return;
+        }
+
+        Exception t = buildingStatus.getException();
+        bool isCoreException = null !is cast(CoreException)t;
+        bool incrementNesting = false;
+
+        if (includeStatus) {
+            StringBuffer sb = new StringBuffer();
+            for (int i = 0; i < nesting; i++) {
+                sb.append(NESTING_INDENT);
+            }
+            String message = buildingStatus.getMessage();
+            sb.append(message);
+            listToPopulate.add(sb.toString());
+            incrementNesting = true;
+        }
+
+        if (!isCoreException && t !is null) {
+            // Include low-level exception message
+            StringBuffer sb = new StringBuffer();
+            for (int i = 0; i < nesting; i++) {
+                sb.append(NESTING_INDENT);
+            }
+            //TODO how about getLocalizedMessage?
+            //String message = t.getLocalizedMessage();
+            String message = t.msg;
+            if (message is null) {
+                message = t.toString();
+            }
+
+            sb.append(message);
+            listToPopulate.add(sb.toString());
+            incrementNesting = true;
+        }
+
+        if (incrementNesting) {
+            nesting++;
+        }
+
+        // Look for a nested core exception
+        if (isCoreException) {
+            CoreException ce = cast(CoreException) t;
+            IStatus eStatus = ce.getStatus();
+            // Only print the exception message if it is not contained in the
+            // parent message
+            if (message is null || message.indexOf(eStatus.getMessage()) is -1) {
+                populateList(listToPopulate, eStatus, nesting, true);
+            }
+        }
+
+        // Look for child status
+        IStatus[] children = buildingStatus.getChildren();
+        for (int i = 0; i < children.length; i++) {
+            populateList(listToPopulate, children[i], nesting, true);
+        }
+    }
+
+    /**
+     * Returns whether the given status object should be displayed.
+     *
+     * @param status
+     *            a status object
+     * @param mask
+     *            a mask as per <code>IStatus.matches</code>
+     * @return <code>true</code> if the given status should be displayed, and
+     *         <code>false</code> otherwise
+     * @see org.eclipse.core.runtime.IStatus#matches(int)
+     */
+    protected static bool shouldDisplay(IStatus status, int mask) {
+        IStatus[] children = status.getChildren();
+        if (children is null || children.length is 0) {
+            return status.matches(mask);
+        }
+        for (int i = 0; i < children.length; i++) {
+            if (children[i].matches(mask)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Toggles the unfolding of the details area. This is triggered by the user
+     * pressing the details button.
+     */
+    private void toggleDetailsArea() {
+        Point windowSize = getShell().getSize();
+        Point oldSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
+        if (listCreated) {
+            list.dispose();
+            listCreated = false;
+            detailsButton.setText(IDialogConstants.SHOW_DETAILS_LABEL);
+        } else {
+            list = createDropDownList(cast(Composite) getContents());
+            detailsButton.setText(IDialogConstants.HIDE_DETAILS_LABEL);
+            getContents().getShell().layout();
+        }
+        Point newSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
+        getShell()
+                .setSize(
+                        new Point(windowSize.x, windowSize.y
+                                + (newSize.y - oldSize.y)));
+    }
+
+    /**
+     * Put the details of the status of the error onto the stream.
+     *
+     * @param buildingStatus
+     * @param buffer
+     * @param nesting
+     */
+    private void populateCopyBuffer(IStatus buildingStatus,
+            StringBuffer buffer, int nesting) {
+        if (!buildingStatus.matches(displayMask)) {
+            return;
+        }
+        for (int i = 0; i < nesting; i++) {
+            buffer.append(NESTING_INDENT);
+        }
+        buffer.append(buildingStatus.getMessage());
+        buffer.append("\n"); //$NON-NLS-1$
+
+        // Look for a nested core exception
+        Exception t = buildingStatus.getException();
+        if ( auto ce = cast(CoreException)t ) {
+            populateCopyBuffer(ce.getStatus(), buffer, nesting + 1);
+        } else if (t !is null) {
+            // Include low-level exception message
+            for (int i = 0; i < nesting; i++) {
+                buffer.append(NESTING_INDENT);
+            }
+            //TODO: getLocalizedMessage?
+            String message = t.msg;
+            if (message is null) {
+                message = t.toString();
+            }
+            buffer.append(message);
+            buffer.append("\n"); //$NON-NLS-1$
+        }
+
+        IStatus[] children = buildingStatus.getChildren();
+        for (int i = 0; i < children.length; i++) {
+            populateCopyBuffer(children[i], buffer, nesting + 1);
+        }
+    }
+
+    /**
+     * Copy the contents of the statuses to the clipboard.
+     */
+    private void copyToClipboard() {
+        if (clipboard !is null) {
+            clipboard.dispose();
+        }
+        StringBuffer statusBuffer = new StringBuffer();
+        populateCopyBuffer(status, statusBuffer, 0);
+        clipboard = new Clipboard(list.getDisplay());
+        clipboard.setContents([ new ArrayWrapperString(statusBuffer.toString()) ],
+                [ TextTransfer.getInstance() ]);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.window.Window#close()
+     */
+    public override bool close() {
+        if (clipboard !is null) {
+            clipboard.dispose();
+        }
+        return super.close();
+    }
+
+    /**
+     * Show the details portion of the dialog if it is not already visible. This
+     * method will only work when it is invoked after the control of the dialog
+     * has been set. In other words, after the <code>createContents</code>
+     * method has been invoked and has returned the control for the content area
+     * of the dialog. Invoking the method before the content area has been set
+     * or after the dialog has been disposed will have no effect.
+     *
+     * @since 3.1
+     */
+    protected final void showDetailsArea() {
+        if (!listCreated) {
+            Control control = getContents();
+            if (control !is null && !control.isDisposed()) {
+                toggleDetailsArea();
+            }
+        }
+    }
+
+    /**
+     * Return whether the Details button should be included. This method is
+     * invoked once when the dialog is built. By default, the Details button is
+     * only included if the status used when creating the dialog was a
+     * multi-status or if the status contains an exception. Subclasses may
+     * override.
+     *
+     * @return whether the Details button should be included
+     * @since 3.1
+     */
+    protected bool shouldShowDetailsButton() {
+        return status.isMultiStatus() || status.getException() !is null;
+    }
+
+    /**
+     * Set the status displayed by this error dialog to the given status. This
+     * only affects the status displayed by the Details list. The message, image
+     * and title should be updated by the subclass, if desired.
+     *
+     * @param status
+     *            the status to be displayed in the details list
+     * @since 3.1
+     */
+    protected final void setStatus(IStatus status) {
+        if (this.status !is status) {
+            this.status = status;
+        }
+        shouldIncludeTopLevelErrorInDetails = true;
+        if (listCreated) {
+            repopulateList();
+        }
+    }
+
+    /**
+     * Repopulate the supplied list widget.
+     */
+    private void repopulateList() {
+        if (list !is null && !list.isDisposed()) {
+            list.removeAll();
+            populateList(list);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.dialogs.IconAndMessageDialog#getColumnCount()
+     */
+    override int getColumnCount() {
+        if (Policy.getErrorSupportProvider() is null)
+            return 2;
+        return 3;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.eclipse.jface.dialogs.Dialog#isResizable()
+     */
+    protected bool isResizable() {
+        return true;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/ErrorSupportProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.dialogs.ErrorSupportProvider;
+
+import java.lang.all;
+
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * A ErrorSupportProvider defines the area to be shown in an error dialog for extra support information.
+ * @since 3.3
+ *
+ */
+public abstract class ErrorSupportProvider {
+
+
+        /**
+         * Create an area for adding support components as a child of parent.
+         * @param parent The parent {@link Composite}
+         * @param status The {@link IStatus} that is being displayed.
+         * @return Control
+         */
+        public abstract Control createSupportArea(Composite parent, IStatus status);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/IDialogBlockedHandler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.IDialogBlockedHandler;
+
+
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.wizard.WizardDialog;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The IDialogBlockedHandler is the handler used by
+ * JFace to provide extra information when a
+ * blocking has occured. There is one static instance
+ * of this class used by WizardDialog and ModalContext.
+ * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#clearBlocked()
+ * @see  org.eclipse.core.runtime.IProgressMonitorWithBlocking#setBlocked(IStatus)
+ * @see WizardDialog
+ * @since 3.0
+ */
+public interface IDialogBlockedHandler {
+    /**
+     * The blockage has been cleared. Clear the
+     * extra information and resume.
+     */
+    public void clearBlocked();
+
+    /**
+     * A blockage has occured. Show the blockage and
+     * forward any actions to blockingMonitor.
+     * <b>NOTE:</b> This will open any blocked notification immediately
+     * even if there is a modal shell open.
+     *
+     * @param parentShell The shell this is being sent from. If the parent
+     * shell is <code>null</code> the behavior will be the same as
+     * IDialogBlockedHandler#showBlocked(IProgressMonitor, IStatus, String)
+     *
+     * @param blocking The monitor to forward to. This is most
+     * important for calls to <code>cancel()</code>.
+     * @param blockingStatus The status that describes the blockage
+     * @param blockedName The name of the locked operation.
+     * @see IDialogBlockedHandler#showBlocked(IProgressMonitor, IStatus, String)
+     */
+    public void showBlocked(Shell parentShell, IProgressMonitor blocking,
+            IStatus blockingStatus, String blockedName);
+
+    /**
+     * A blockage has occured. Show the blockage when there is
+     * no longer any modal shells in the UI and forward any actions
+     * to blockingMonitor.
+     *
+     * <b>NOTE:</b> As no shell has been specified this method will
+     * not open any blocked notification until all other modal shells
+     * have been closed.
+     *
+     * @param blocking The monitor to forward to. This is most
+     * important for calls to <code>cancel()</code>.
+     * @param blockingStatus The status that describes the blockage
+     * @param blockedName The name of the locked operation.
+     */
+    public void showBlocked(IProgressMonitor blocking, IStatus blockingStatus,
+            String blockedName);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/IDialogConstants.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,371 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.IDialogConstants;
+
+/**
+ * Various dialog-related constants.
+ * <p>
+ * Within the dialog framework, all buttons are referred to by a button id.
+ * Various common buttons, like "OK", "Cancel", and "Finish", have pre-assigned
+ * button ids for convenience. If an application requires other dialog buttons,
+ * they should be assigned application-specific button ids counting up from
+ * <code>CLIENT_ID</code>.
+ * </p>
+ * <p>
+ * Button label constants are also provided for the common buttons. JFace
+ * automatically localizes these strings to the current locale; that is,
+ * <code>YES_LABEL</code> would be bound to the string <code>"Si"</code> in
+ * a Spanish locale, but to <code>"Oui"</code> in a French one.
+ * </p>
+ * <p>
+ * All margins, spacings, and sizes are given in "dialog units" (DLUs), where
+ * <ul>
+ * <li>1 horizontal DLU = 1/4 average character width</li>
+ * <li>1 vertical DLU = 1/8 average character height</li>
+ * </ul>
+ * </p>
+ */
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+
+/**
+ * IDialogConstants is the interface for common dialog strings and ids
+ * used throughout JFace.
+ * It is recommended that you use these labels and ids whereever
+ * for consistency with the JFace dialogs.
+ */
+public abstract class IDialogConstants {
+    static{
+    const{
+    // button ids
+
+    // Note:  if new button ids are added, see
+    // MessageDialogWithToggle.mapButtonLabelToButtonID(String, int)
+    /**
+     * Button id for an "Ok" button (value 0).
+     */
+    public int OK_ID = 0;
+
+    /**
+     * Button id for a "Cancel" button (value 1).
+     */
+    public int CANCEL_ID = 1;
+
+    /**
+     * Button id for a "Yes" button (value 2).
+     */
+    public int YES_ID = 2;
+
+    /**
+     * Button id for a "No" button (value 3).
+     */
+    public int NO_ID = 3;
+
+    /**
+     * Button id for a "Yes to All" button (value 4).
+     */
+    public int YES_TO_ALL_ID = 4;
+
+    /**
+     * Button id for a "Skip" button (value 5).
+     */
+    public int SKIP_ID = 5;
+
+    /**
+     * Button id for a "Stop" button (value 6).
+     */
+    public int STOP_ID = 6;
+
+    /**
+     * Button id for an "Abort" button (value 7).
+     */
+    public int ABORT_ID = 7;
+
+    /**
+     * Button id for a "Retry" button (value 8).
+     */
+    public int RETRY_ID = 8;
+
+    /**
+     * Button id for an "Ignore" button (value 9).
+     */
+    public int IGNORE_ID = 9;
+
+    /**
+     * Button id for a "Proceed" button (value 10).
+     */
+    public int PROCEED_ID = 10;
+
+    /**
+     * Button id for an "Open" button (value 11).
+     */
+    public int OPEN_ID = 11;
+
+    /**
+     * Button id for a "Close" button (value 12).
+     */
+    public int CLOSE_ID = 12;
+
+    /**
+     * Button id for a "Details" button (value 13).
+     */
+    public int DETAILS_ID = 13;
+
+    /**
+     * Button id for a "Back" button (value 14).
+     */
+    public int BACK_ID = 14;
+
+    /**
+     * Button id for a "Next" button (value 15).
+     */
+    public int NEXT_ID = 15;
+
+    /**
+     * Button id for a "Finish" button (value 16).
+     */
+    public int FINISH_ID = 16;
+
+    /**
+     * Button id for a "Help" button (value 17).
+     */
+    public int HELP_ID = 17;
+
+    /**
+     * Button id for a "Select All" button (value 18).
+     */
+    public int SELECT_ALL_ID = 18;
+
+    /**
+     * Button id for a "Deselect All" button (value 19).
+     */
+    public int DESELECT_ALL_ID = 19;
+
+    /**
+     * Button id for a "Select types" button (value 20).
+     */
+    public int SELECT_TYPES_ID = 20;
+
+    /**
+     * Button id for a "No to All" button (value 21).
+     */
+    public int NO_TO_ALL_ID = 21;
+
+    /**
+     * Starting button id reserved for internal use by JFace (value 256). JFace
+     * classes make ids by adding to this number.
+     */
+    public int INTERNAL_ID = 256;
+
+    /**
+     * Starting button id reserved for use by clients of JFace (value 1024).
+     * Clients of JFace should make ids by adding to this number.
+     */
+    public int CLIENT_ID = 1024;
+    } // const
+    // button labels
+    /**
+     * The label for OK buttons.
+     */
+    public String OK_LABEL;
+
+    /**
+     * The label for cancel buttons.
+     */
+    public String CANCEL_LABEL;
+
+    /**
+     * The label for yes buttons.
+     */
+    public String YES_LABEL;
+
+    /**
+     * The label for no buttons.
+     */
+    public String NO_LABEL;
+
+    /**
+     * The label for not to all buttons.
+     */
+    public String NO_TO_ALL_LABEL;
+
+    /**
+     * The label for yes to all buttons.
+     */
+    public String YES_TO_ALL_LABEL;
+
+    /**
+     * The label for skip buttons.
+     */
+    public String SKIP_LABEL;
+
+    /**
+     * The label for stop buttons.
+     */
+    public String STOP_LABEL;
+
+    /**
+     * The label for abort buttons.
+     */
+    public String ABORT_LABEL;
+
+    /**
+     * The label for retry buttons.
+     */
+    public String RETRY_LABEL;
+
+    /**
+     * The label for ignore buttons.
+     */
+    public String IGNORE_LABEL;
+
+    /**
+     * The label for proceed buttons.
+     */
+    public String PROCEED_LABEL;
+
+    /**
+     * The label for open buttons.
+     */
+    public String OPEN_LABEL;
+
+    /**
+     * The label for close buttons.
+     */
+    public String CLOSE_LABEL;
+
+    /**
+     * The label for show details buttons.
+     */
+    public String SHOW_DETAILS_LABEL;
+
+    /**
+     * The label for hide details buttons.
+     */
+    public String HIDE_DETAILS_LABEL;
+
+    /**
+     * The label for back buttons.
+     */
+    public String BACK_LABEL;
+
+    /**
+     * The label for next buttons.
+     */
+    public String NEXT_LABEL;
+
+    /**
+     * The label for finish buttons.
+     */
+    public String FINISH_LABEL;
+
+    /**
+     * The label for help buttons.
+     */
+    public String HELP_LABEL;
+
+    const{
+    // Margins, spacings, and sizes
+    /**
+     * Vertical margin in dialog units (value 7).
+     */
+    public int VERTICAL_MARGIN = 7;
+
+    /**
+     * Vertical spacing in dialog units (value 4).
+     */
+    public int VERTICAL_SPACING = 4;
+
+    /**
+     * Horizontal margin in dialog units (value 7).
+     */
+    public int HORIZONTAL_MARGIN = 7;
+
+    /**
+     * Horizontal spacing in dialog units (value 4).
+     */
+    public int HORIZONTAL_SPACING = 4;
+
+    /**
+     * Height of button bar in dialog units (value 25).
+     */
+    public int BUTTON_BAR_HEIGHT = 25;
+
+    /**
+     * Left margin in dialog units (value 20).
+     */
+    public int LEFT_MARGIN = 20;
+
+    /**
+     * Button margin in dialog units (value 4).
+     */
+    public int BUTTON_MARGIN = 4;
+
+    /**
+     * Button height in dialog units (value 14).
+     *
+     * @deprecated This constant is no longer in use.
+     * The button heights are now determined by the layout.
+     */
+    public int BUTTON_HEIGHT = 14;
+
+    /**
+     * Button width in dialog units (value 61).
+     */
+    public int BUTTON_WIDTH = 61;
+
+    /**
+     * Indent in dialog units (value 21).
+     */
+    public int INDENT = 21;
+
+    /**
+     * Small indent in dialog units (value 7).
+     */
+    public int SMALL_INDENT = 7;
+
+    /**
+     * Entry field width in dialog units (value 200).
+     */
+    public int ENTRY_FIELD_WIDTH = 200;
+
+    /**
+     * Minimum width of message area in dialog units (value 300).
+     */
+    public int MINIMUM_MESSAGE_AREA_WIDTH = 300;
+    }// const
+    }// static
+}
+static this(){
+    IDialogConstants.OK_LABEL = JFaceResources.getString("ok"); //$NON-NLS-1$
+    IDialogConstants.CANCEL_LABEL = JFaceResources.getString("cancel"); //$NON-NLS-1$
+    IDialogConstants.YES_LABEL = JFaceResources.getString("yes"); //$NON-NLS-1$
+    IDialogConstants.NO_LABEL = JFaceResources.getString("no"); //$NON-NLS-1$
+    IDialogConstants.NO_TO_ALL_LABEL = JFaceResources.getString("notoall"); //$NON-NLS-1$
+    IDialogConstants.YES_TO_ALL_LABEL = JFaceResources.getString("yestoall"); //$NON-NLS-1$
+    IDialogConstants.SKIP_LABEL = JFaceResources.getString("skip"); //$NON-NLS-1$
+    IDialogConstants.STOP_LABEL = JFaceResources.getString("stop"); //$NON-NLS-1$
+    IDialogConstants.ABORT_LABEL = JFaceResources.getString("abort"); //$NON-NLS-1$
+    IDialogConstants.RETRY_LABEL = JFaceResources.getString("retry"); //$NON-NLS-1$
+    IDialogConstants.IGNORE_LABEL = JFaceResources.getString("ignore"); //$NON-NLS-1$
+    IDialogConstants.PROCEED_LABEL = JFaceResources.getString("proceed"); //$NON-NLS-1$
+    IDialogConstants.OPEN_LABEL = JFaceResources.getString("open"); //$NON-NLS-1$
+    IDialogConstants.CLOSE_LABEL = JFaceResources.getString("close"); //$NON-NLS-1$
+    IDialogConstants.SHOW_DETAILS_LABEL = JFaceResources.getString("showDetails"); //$NON-NLS-1$
+    IDialogConstants.HIDE_DETAILS_LABEL = JFaceResources.getString("hideDetails"); //$NON-NLS-1$
+    IDialogConstants.BACK_LABEL = JFaceResources.getString("backButton"); //$NON-NLS-1$
+    IDialogConstants.NEXT_LABEL = JFaceResources.getString("nextButton"); //$NON-NLS-1$
+    IDialogConstants.FINISH_LABEL = JFaceResources.getString("finish"); //$NON-NLS-1$
+    IDialogConstants.HELP_LABEL = JFaceResources.getString("help"); //$NON-NLS-1$
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/IDialogPage.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.IDialogPage;
+
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Interface for a page in a multi-page dialog.
+ */
+public interface IDialogPage {
+    /**
+     * Creates the top level control for this dialog
+     * page under the given parent composite.
+     * <p>
+     * Implementors are responsible for ensuring that
+     * the created control can be accessed via <code>getControl</code>
+     * </p>
+     *
+     * @param parent the parent composite
+     */
+    public void createControl(Composite parent);
+
+    /**
+     * Disposes the SWT resources allocated by this
+     * dialog page.
+     */
+    public void dispose();
+
+    /**
+     * Returns the top level control for this dialog page.
+     * <p>
+     * May return <code>null</code> if the control
+     * has not been created yet.
+     * </p>
+     *
+     * @return the top level control or <code>null</code>
+     */
+    public Control getControl();
+
+    /**
+     * Returns this dialog page's description text.
+     *
+     * @return the description text for this dialog page,
+     *  or <code>null</code> if none
+     */
+    public String getDescription();
+
+    /**
+     * Returns the current error message for this dialog page.
+     * May be <code>null</code> to indicate no error message.
+     * <p>
+     * An error message should describe some error state,
+     * as opposed to a message which may simply provide instruction
+     * or information to the user.
+     * </p>
+     *
+     * @return the error message, or <code>null</code> if none
+     */
+    public String getErrorMessage();
+
+    /**
+     * Returns this dialog page's image.
+     *
+     * @return the image for this dialog page, or <code>null</code>
+     *  if none
+     */
+    public Image getImage();
+
+    /**
+     * Returns the current message for this wizard page.
+     * <p>
+     * A message provides instruction or information to the
+     * user, as opposed to an error message which should
+     * describe some error state.
+     * </p>
+     *
+     * @return the message, or <code>null</code> if none
+     */
+    public String getMessage();
+
+    /**
+     * Returns this dialog page's title.
+     *
+     * @return the title of this dialog page,
+     *  or <code>null</code> if none
+     */
+    public String getTitle();
+
+    /**
+     * Notifies that help has been requested for this dialog page.
+     */
+    public void performHelp();
+
+    /**
+     * Sets this dialog page's description text.
+     *
+     * @param description the description text for this dialog
+     *  page, or <code>null</code> if none
+     */
+    public void setDescription(String description);
+
+    /**
+     * Sets this dialog page's image.
+     *
+     * @param image the image for this dialog page,
+     *  or <code>null</code> if none
+     */
+    public void setImageDescriptor(ImageDescriptor image);
+
+    /**
+     * Set this dialog page's title.
+     *
+     * @param title the title of this dialog page,
+     *  or <code>null</code> if none
+     */
+    public void setTitle(String title);
+
+    /**
+     * Sets the visibility of this dialog page.
+     *
+     * @param visible <code>true</code> to make this page visible,
+     *  and <code>false</code> to hide it
+     */
+    public void setVisible(bool visible);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/IDialogSettings.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,273 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.IDialogSettings;
+
+// import java.io.IOException;
+// import java.io.Reader;
+// import java.io.Writer;
+
+import java.lang.all;
+import java.util.Set;
+
+static import tango.io.model.IConduit;
+
+/**
+ * An interface to a storage mechanism for making dialog settings persistent.
+ * The store manages a collection of key/value pairs. The keys must be strings
+ * and the values can be either, strings or array of strings. Convenience API to
+ * convert primitive types to strings is provided.
+ */
+public interface IDialogSettings {
+    /**
+     * Create a new section in the receiver and return it.
+     *
+     * @param name
+     *            the name of the new section
+     * @return the new section
+     */
+    public IDialogSettings addNewSection(String name);
+
+    /**
+     * Add a section in the receiver.
+     *
+     * @param section
+     *            the section to be added
+     */
+    public void addSection(IDialogSettings section);
+
+    /**
+     * Returns the value of the given key in this dialog settings.
+     *
+     * @param key
+     *            the key
+     * @return the value, or <code>null</code> if none
+     */
+    public String get(String key);
+
+    /**
+     * Returns the value, an array of strings, of the given key in this dialog
+     * settings.
+     *
+     * @param key
+     *            the key
+     * @return the array of string, or <code>null</code> if none
+     */
+    public String[] getArray(String key);
+
+    /**
+     * Convenience API. Convert the value of the given key in this dialog
+     * settings to a bool and return it.
+     *
+     * @param key
+     *            the key
+     * @return the bool value, or <code>false</code> if none
+     */
+    public bool getBoolean(String key);
+
+    /**
+     * Convenience API. Convert the value of the given key in this dialog
+     * settings to a double and return it.
+     *
+     * @param key
+     *            the key
+     * @return the value coverted to double, or throws
+     *         <code>NumberFormatException</code> if none
+     *
+     * @exception NumberFormatException
+     *                if the string value does not contain a parsable number.
+     * @see java.lang.Double#valueOf(java.lang.String)
+     */
+    public double getDouble(String key);
+
+    /**
+     * Convenience API. Convert the value of the given key in this dialog
+     * settings to a float and return it.
+     *
+     * @param key
+     *            the key
+     * @return the value coverted to float, or throws
+     *         <code>NumberFormatException</code> if none
+     *
+     * @exception NumberFormatException
+     *                if the string value does not contain a parsable number.
+     * @see java.lang.Float#valueOf(java.lang.String)
+     */
+    public float getFloat(String key);
+
+    /**
+     * Convenience API. Convert the value of the given key in this dialog
+     * settings to a int and return it.
+     *
+     * @param key
+     *            the key
+     * @return the value coverted to int, or throws
+     *         <code>NumberFormatException</code> if none
+     *
+     * @exception NumberFormatException
+     *                if the string value does not contain a parsable number.
+     * @see java.lang.Integer#valueOf(java.lang.String)
+     */
+    public int getInt(String key);
+
+    /**
+     * Convenience API. Convert the value of the given key in this dialog
+     * settings to a long and return it.
+     *
+     * @param key
+     *            the key
+     * @return the value coverted to long, or throws
+     *         <code>NumberFormatException</code> if none
+     *
+     * @exception NumberFormatException
+     *                if the string value does not contain a parsable number.
+     * @see java.lang.Long#valueOf(java.lang.String)
+     */
+    public long getLong(String key);
+
+    /**
+     * Returns the IDialogSettings name.
+     *
+     * @return the name
+     */
+    public String getName();
+
+    /**
+     * Returns the section with the given name in this dialog settings.
+     *
+     * @param sectionName
+     *            the key
+     * @return IDialogSettings (the section), or <code>null</code> if none
+     */
+    public IDialogSettings getSection(String sectionName);
+
+    /**
+     * Returns all the sections in this dialog settings.
+     *
+     * @return the section, or <code>null</code> if none
+     */
+    public IDialogSettings[] getSections();
+
+    /**
+     * Load a dialog settings from a stream and fill the receiver with its
+     * content.
+     *
+     * @param reader
+     *            a Reader specifying the stream where the settings are read
+     *            from.
+     * @throws IOException
+     */
+    public void load(tango.io.model.IConduit.InputStream reader);
+
+    /**
+     * Load a dialog settings from a file and fill the receiver with its
+     * content.
+     *
+     * @param fileName
+     *            the name of the file the settings are read from.
+     * @throws IOException
+     */
+    public void load(String fileName);
+
+    /**
+     * Adds the pair <code>key/value</code> to this dialog settings.
+     *
+     * @param key
+     *            the key.
+     * @param value
+     *            the value to be associated with the <code>key</code>
+     */
+    public void put(String key, String[] value);
+
+    /**
+     * Convenience API. Converts the double <code>value</code> to a string and
+     * adds the pair <code>key/value</code> to this dialog settings.
+     *
+     * @param key
+     *            the key.
+     * @param value
+     *            the value to be associated with the <code>key</code>
+     */
+    public void put(String key, double value);
+
+    /**
+     * Convenience API. Converts the float <code>value</code> to a string and
+     * adds the pair <code>key/value</code> to this dialog settings.
+     *
+     * @param key
+     *            the key.
+     * @param value
+     *            the value to be associated with the <code>key</code>
+     */
+    public void put(String key, float value);
+
+    /**
+     * Convenience API. Converts the int <code>value</code> to a string and
+     * adds the pair <code>key/value</code> to this dialog settings.
+     *
+     * @param key
+     *            the key.
+     * @param value
+     *            the value to be associated with the <code>key</code>
+     */
+    public void put(String key, int value);
+
+    /**
+     * Convenience API. Converts the long <code>value</code> to a string and
+     * adds the pair <code>key/value</code> to this dialog settings.
+     *
+     * @param key
+     *            the key.
+     * @param value
+     *            the value to be associated with the <code>key</code>
+     */
+    public void put(String key, long value);
+
+    /**
+     * Adds the pair <code>key/value</code> to this dialog settings.
+     *
+     * @param key
+     *            the key.
+     * @param value
+     *            the value to be associated with the <code>key</code>
+     */
+    public void put(String key, String value);
+
+    /**
+     * Convenience API. Converts the bool <code>value</code> to a string
+     * and adds the pair <code>key/value</code> to this dialog settings.
+     *
+     * @param key
+     *            the key.
+     * @param value
+     *            the value to be associated with the <code>key</code>
+     */
+    public void put(String key, bool value);
+
+    /**
+     * Save a dialog settings to a stream
+     *
+     * @param writer
+     *            a Writer specifying the stream the settings are written in.
+     * @throws IOException
+     */
+    public void save(tango.io.model.IConduit.OutputStream writer);
+
+    /**
+     * Save a dialog settings to a file.
+     *
+     * @param fileName
+     *            the name of the file the settings are written in.
+     * @throws IOException
+     */
+    public void save(String fileName);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/IInputValidator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.IInputValidator;
+
+import java.lang.all;
+
+/**
+ * The IInputValidator is the interface for simple validators.
+ * @see org.eclipse.jface.dialogs.InputDialog
+ */
+public interface IInputValidator {
+    /**
+     * Validates the given string.  Returns an error message to display
+     * if the new text is invalid.  Returns <code>null</code> if there
+     * is no error.  Note that the empty string is not treated the same
+     * as <code>null</code>; it indicates an error state but with no message
+     * to display.
+     *
+     * @param newText the text to check for validity
+     *
+     * @return an error message or <code>null</code> if no error
+     */
+    public String isValid(String newText);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/IMessageProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.IMessageProvider;
+
+import java.lang.all;
+
+/**
+ * Minimal interface to a message provider. Used for dialog pages which can
+ * provide a message with an icon.
+ *
+ * @since 2.0
+ */
+public interface IMessageProvider {
+    /**
+     * Constant for a regular message (value 0).
+     * <p>
+     * Typically this indicates that the message should be shown without an
+     * icon.
+     * </p>
+     */
+    public const static int NONE = 0;
+
+    /**
+     * Constant for an info message (value 1).
+     */
+    public const static int INFORMATION = 1;
+
+    /**
+     * Constant for a warning message (value 2).
+     */
+    public const static int WARNING = 2;
+
+    /**
+     * Constant for an error message (value 3).
+     */
+    public const static int ERROR = 3;
+
+    /**
+     * Returns the current message for this message provider.
+     * <p>
+     * A message provides instruction or information to the user.
+     * </p>
+     *
+     * @return the message, or <code>null</code> if none
+     */
+    public String getMessage();
+
+    /**
+     * Returns a value indicating if the message is a an information message, a
+     * warning message, or an error message.
+     * <p>
+     * Returns one of <code>NONE</code>,<code>INFORMATION</code>,
+     * <code>WARNING</code>, or <code>ERROR</code>.
+     * </p>
+     *
+     * @return the message type
+     */
+    public int getMessageType();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/IPageChangeProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.dialogs.IPageChangeProvider;
+
+import org.eclipse.jface.dialogs.IPageChangedListener;
+
+import java.lang.all;
+
+/**
+ * Minimal interface to a page change provider. Used for dialogs which can
+ * switch between multiple pages.
+ *
+ * @since 3.1
+ */
+public interface IPageChangeProvider {
+    /**
+     * Returns the currently selected page in the dialog.
+     *
+     * @return the selected page in the dialog or <code>null</code> if none is
+     *         selected. The type may be domain specific. In
+     *         the JFace provided dialogs this will be an instance of
+     *         <code>IDialogPage</code>.
+     */
+    Object getSelectedPage();
+
+    /**
+     * Adds a listener for page changes in this page change provider. Has no
+     * effect if an identical listener is already registered.
+     *
+     * @param listener
+     *            a page changed listener
+     */
+    void addPageChangedListener(IPageChangedListener listener);
+
+    /**
+     * Removes the given page change listener from this page change provider.
+     * Has no effect if an identical listener is not registered.
+     *
+     * @param listener
+     *            a page changed listener
+     */
+    void removePageChangedListener(IPageChangedListener listener);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/IPageChangedListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.dialogs.IPageChangedListener;
+
+import org.eclipse.jface.dialogs.PageChangedEvent;
+
+import java.lang.all;
+
+/**
+ * A listener which is notified when the current page of the multi-page dialog
+ * is changed.
+ *
+ * @see IPageChangeProvider
+ * @see PageChangedEvent
+ *
+ * @since 3.1
+ */
+public interface IPageChangedListener {
+    /**
+     * Notifies that the selected page has changed.
+     *
+     * @param event
+     *            event object describing the change
+     */
+    public void pageChanged(PageChangedEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/IPageChangingListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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:
+ *     Chris Gross (schtoo@schtoo.com) - initial API and implementation for bug 16179
+ *     IBM Corporation - revisions to initial contribution
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.IPageChangingListener;
+
+import org.eclipse.jface.dialogs.PageChangingEvent;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A listener which is notified when the current page of a multi-page dialog is
+ * changing. Use this listener to perform long-running work that should only be
+ * executed once, when the page is in the process of changing, rather then
+ * during validation of page controls.
+ *
+ * @see PageChangingEvent
+ * @since 3.3
+ */
+public interface IPageChangingListener {
+
+    /**
+     * Handle the an <code>IDialogPage</code> changing.
+     *
+     * The <code>doit</code> field of the <code>PageChangingEvent</code>
+     * must be set to false to prevent the page from changing.
+     *
+     * @param event
+     *            event object describing the change
+     */
+    public void handlePageChanging(PageChangingEvent event);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/IconAndMessageDialog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,302 @@
+/*******************************************************************************
+ * 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
+ *     Stefan Xenos, IBM - bug 156790: Adopt GridLayoutFactory within JFace
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.IconAndMessageDialog;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.accessibility.AccessibleAdapter;
+import org.eclipse.swt.accessibility.AccessibleEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.layout.LayoutConstants;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The IconAndMessageDialog is the abstract superclass of dialogs that have an
+ * icon and a message as the first two widgets. In this dialog the icon and
+ * message are direct children of the shell in order that they can be read by
+ * accessibility tools more easily.
+ */
+public abstract class IconAndMessageDialog : Dialog {
+    /**
+     * Message (a localized string).
+     */
+    protected String message;
+
+    /**
+     * Message label is the label the message is shown on.
+     */
+    protected Label messageLabel;
+
+    /**
+     * Return the label for the image.
+     */
+    protected Label imageLabel;
+
+    /**
+     * Constructor for IconAndMessageDialog.
+     *
+     * @param parentShell
+     *            the parent shell, or <code>null</code> to create a top-level
+     *            shell
+     */
+    public this(Shell parentShell) {
+        super(parentShell);
+    }
+
+    /**
+     * Create the area the message will be shown in.
+     * <p>
+     * The parent composite is assumed to use GridLayout as its layout manager,
+     * since the parent is typically the composite created in
+     * {@link Dialog#createDialogArea}.
+     * </p>
+     * 
+     * @param composite
+     *            The composite to parent from.
+     * @return Control
+     */
+    protected Control createMessageArea(Composite composite) {
+        // create composite
+        // create image
+        Image image = getImage();
+        if (image !is null) {
+            imageLabel = new Label(composite, SWT.NULL);
+            image.setBackground(imageLabel.getBackground());
+            imageLabel.setImage(image);
+            addAccessibleListeners(imageLabel, image);
+            GridDataFactory.fillDefaults().align_(SWT.CENTER, SWT.BEGINNING)
+                    .applyTo(imageLabel);
+        }
+        // create message
+        if (message !is null) {
+            messageLabel = new Label(composite, getMessageLabelStyle());
+            messageLabel.setText(message);
+            GridDataFactory
+                    .fillDefaults()
+                    .align_(SWT.FILL, SWT.BEGINNING)
+                    .grab(true, false)
+                    .hint(
+                            convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH),
+                            SWT.DEFAULT).applyTo(messageLabel);
+        }
+        return composite;
+    }
+
+    private String getAccessibleMessageFor(Image image) {
+        if (image.opEquals(getErrorImage())) {
+            return JFaceResources.getString("error");//$NON-NLS-1$
+        }
+
+        if (image.opEquals(getWarningImage())) {
+            return JFaceResources.getString("warning");//$NON-NLS-1$
+        }
+
+        if (image.opEquals(getInfoImage())) {
+            return JFaceResources.getString("info");//$NON-NLS-1$
+        }
+
+        if (image.opEquals(getQuestionImage())) {
+            return JFaceResources.getString("question"); //$NON-NLS-1$
+        }
+
+        return null;
+    }
+
+    /**
+     * Add an accessible listener to the label if it can be inferred from the
+     * image.
+     *
+     * @param label
+     * @param image
+     */
+    private void addAccessibleListeners(Label label, Image image) {
+        label.getAccessible().addAccessibleListener(new class(image) AccessibleAdapter {
+            Image image_;
+            this(Image i){
+                image_ = i;
+            }
+            public void getName(AccessibleEvent event) {
+                String accessibleMessage = getAccessibleMessageFor(image_);
+                if (accessibleMessage is null) {
+                    return;
+                }
+                event.result = accessibleMessage;
+            }
+        });
+    }
+
+    /**
+     * Returns the style for the message label.
+     *
+     * @return the style for the message label
+     *
+     * @since 3.0
+     */
+    protected int getMessageLabelStyle() {
+        return SWT.WRAP;
+    }
+
+    /*
+     * @see Dialog.createButtonBar()
+     */
+    protected override Control createButtonBar(Composite parent) {
+        Composite composite = new Composite(parent, SWT.NONE);
+        GridLayoutFactory.fillDefaults().numColumns(0) // this is incremented
+                // by createButton
+                .equalWidth(true).applyTo(composite);
+
+        GridDataFactory.fillDefaults().align_(SWT.END, SWT.CENTER).span(2, 1)
+                .applyTo(composite);
+        composite.setFont(parent.getFont());
+        // Add the buttons to the button bar.
+        createButtonsForButtonBar(composite);
+        return composite;
+    }
+
+    /**
+     * Returns the image to display beside the message in this dialog.
+     * <p>
+     * Subclasses may override.
+     * </p>
+     *
+     * @return the image to display beside the message
+     * @since 2.0
+     */
+    protected abstract Image getImage();
+
+    /*
+     * @see Dialog.createContents(Composite)
+     */
+    protected override Control createContents(Composite parent) {
+        // initialize the dialog units
+        initializeDialogUnits(parent);
+        Point defaultSpacing = LayoutConstants.getSpacing();
+        GridLayoutFactory.fillDefaults().margins(LayoutConstants.getMargins())
+                .spacing(defaultSpacing.x * 2,
+                defaultSpacing.y).numColumns(getColumnCount()).applyTo(parent);
+
+        GridDataFactory.fillDefaults().grab(true, true).applyTo(parent);
+        createDialogAndButtonArea(parent);
+        return parent;
+    }
+
+    /**
+     * Get the number of columns in the layout of the Shell of the dialog.
+     *
+     * @return int
+     * @since 3.3
+     */
+    int getColumnCount() {
+        return 2;
+    }
+
+    /**
+     * Create the dialog area and the button bar for the receiver.
+     *
+     * @param parent
+     */
+    protected void createDialogAndButtonArea(Composite parent) {
+        // create the dialog area and button bar
+        dialogArea = createDialogArea(parent);
+        buttonBar = createButtonBar(parent);
+        // Apply to the parent so that the message gets it too.
+        applyDialogFont(parent);
+    }
+
+    /**
+     * Return the <code>Image</code> to be used when displaying an error.
+     *
+     * @return image the error image
+     */
+    public Image getErrorImage() {
+        return getSWTImage(SWT.ICON_ERROR);
+    }
+
+    /**
+     * Return the <code>Image</code> to be used when displaying a warning.
+     *
+     * @return image the warning image
+     */
+    public Image getWarningImage() {
+        return getSWTImage(SWT.ICON_WARNING);
+    }
+
+    /**
+     * Return the <code>Image</code> to be used when displaying information.
+     *
+     * @return image the information image
+     */
+    public Image getInfoImage() {
+        return getSWTImage(SWT.ICON_INFORMATION);
+    }
+
+    /**
+     * Return the <code>Image</code> to be used when displaying a question.
+     *
+     * @return image the question image
+     */
+    public Image getQuestionImage() {
+        return getSWTImage(SWT.ICON_QUESTION);
+    }
+
+    /**
+     * Get an <code>Image</code> from the provide SWT image constant.
+     *
+     * @param imageID
+     *            the SWT image constant
+     * @return image the image
+     */
+    private Image getSWTImage(int imageID) {
+        Shell shell = getShell();
+        Display display;
+        if (shell is null) {
+            shell = getParentShell();
+        }
+        if (shell is null) {
+            display = Display.getCurrent();
+        } else {
+            display = shell.getDisplay();
+        }
+
+        Image[1] image;
+        display.syncExec(new class(display,imageID) Runnable {
+            int imageID_;
+            Display display_;
+            this(Display a,int b){
+                display_=a;
+                imageID_=b;
+            }
+            public void run() {
+                image[0] = display_.getSystemImage(imageID_);
+            }
+        });
+
+        return image[0];
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/ImageAndMessageArea.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,220 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.dialogs.ImageAndMessageArea;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.jface.fieldassist.DecoratedField;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.jface.fieldassist.TextControlCreator;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Instances of this class provide a message area to display a message and an
+ * associated image.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.2
+ * @deprecated As of 3.3, this class is no longer necessary.
+ *
+ */
+public class ImageAndMessageArea : Composite {
+
+    private int BORDER_MARGIN = IDialogConstants.HORIZONTAL_SPACING / 2;
+
+    private DecoratedField messageField;
+
+    private Composite container;
+
+    /**
+     * Constructs a new ImageAndMessageArea with an empty decorated field. Calls
+     * to <code>setText(String text)</code> and
+     * <code>setImage(Image image)</code> are required in order to fill the
+     * message area. Also, the instance will be invisible when initially
+     * created.
+     * <p>
+     * The style bit <code>SWT.WRAP</code> should be used if a larger message
+     * area is desired.
+     * </p>
+     *
+     * @param parent
+     *            the parent composite
+     * @param style
+     *            the SWT style bits. Using SWT.WRAP will create a larger
+     *            message area.
+     */
+    public this(Composite parent, int style) {
+        super(parent, style);
+        container = new Composite(this, style);
+        GridLayout glayout = new GridLayout();
+        glayout.numColumns = 2;
+        glayout.marginWidth = 0;
+        glayout.marginHeight = 0;
+        glayout.marginTop = BORDER_MARGIN;
+        glayout.marginBottom = BORDER_MARGIN;
+        container.setLayout(glayout);
+
+        messageField = new DecoratedField(container, SWT.READ_ONLY | style,
+                new TextControlCreator());
+        setFont(JFaceResources.getDialogFont());
+
+        GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
+        int lineHeight = (cast(Text) messageField.getControl()).getLineHeight();
+        if ((style & SWT.WRAP) > 0)
+            gd.heightHint = 2 * lineHeight;
+        else
+            gd.heightHint = lineHeight;
+
+        messageField.getLayoutControl().setLayoutData(gd);
+
+        addPaintListener(new class PaintListener {
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
+             */
+            public void paintControl(PaintEvent e) {
+                onPaint(e);
+            }
+        });
+
+        // sets the layout and size to account for the BORDER_MARGIN between
+        // the border drawn around the container and the decorated field.
+        setLayout(new class Layout {
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite,
+             *      bool)
+             */
+            public void layout(Composite parent, bool changed) {
+                Rectangle carea = getClientArea();
+                container.setBounds(carea.x + BORDER_MARGIN, carea.y
+                        + BORDER_MARGIN, carea.width - (2 * BORDER_MARGIN),
+                        carea.height - (2 * BORDER_MARGIN));
+            }
+
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.swt.widgets.Layout#computeSize(org.eclipse.swt.widgets.Composite,
+             *      int, int, bool)
+             */
+            public Point computeSize(Composite parent, int wHint, int hHint,
+                    bool changed) {
+                Point size;
+                size = container.computeSize(wHint, hHint, changed);
+
+                // size set to account for the BORDER_MARGIN on
+                // all sides of the decorated field
+                size.x += 4;
+                size.y += 4;
+                return size;
+            }
+        });
+        setVisible(false);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.swt.widgets.Control#setBackground(org.eclipse.swt.graphics.Color)
+     */
+    public override void setBackground(Color bg) {
+        super.setBackground(bg);
+        messageField.getLayoutControl().setBackground(bg);
+        messageField.getControl().setBackground(bg);
+        container.setBackground(bg);
+    }
+
+    /**
+     * Sets the text in the decorated field which will be displayed in the
+     * message area.
+     *
+     * @param text
+     *            the text to be displayed in the message area
+     *
+     * @see org.eclipse.swt.widgets.Text#setText(String string)
+     */
+    public void setText(String text) {
+        (cast(Text) messageField.getControl()).setText(text);
+    }
+
+    /**
+     * Adds an image to decorated field to be shown in the message area.
+     *
+     * @param image
+     *            desired image to be shown in the ImageAndMessageArea
+     */
+    public void setImage(Image image) {
+        FieldDecorationRegistry registry = FieldDecorationRegistry.getDefault();
+        registry.registerFieldDecoration("messageImage", null, image); //$NON-NLS-1$
+        messageField.addFieldDecoration(registry
+                .getFieldDecoration("messageImage"), //$NON-NLS-1$
+                SWT.LEFT | SWT.TOP, false);
+    }
+
+    /**
+     * Draws the message area composite with rounded corners.
+     */
+    private void onPaint(PaintEvent e) {
+        Rectangle carea = getClientArea();
+        e.gc.setForeground(getForeground());
+
+        // draws the polyline to be rounded in a 2 pixel squared area
+        e.gc.drawPolyline([ carea.x, carea.y + carea.height - 1,
+                carea.x, carea.y + 2, carea.x + 2, carea.y,
+                carea.x + carea.width - 3, carea.y, carea.x + carea.width - 1,
+                carea.y + 2, carea.x + carea.width - 1,
+                carea.y + carea.height - 1 ]);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.swt.widgets.Control#setFont(org.eclipse.swt.graphics.Font)
+     */
+    public override void setFont(Font font) {
+        super.setFont(font);
+        (cast(Text) messageField.getControl()).setFont(font);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.swt.widgets.Control#setToolTipText(java.lang.String)
+     */
+    public override void setToolTipText(String text) {
+        super.setToolTipText(text);
+        (cast(Text) messageField.getControl()).setToolTipText(text);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/InputDialog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,303 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.dialogs.InputDialog;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IInputValidator;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.jface.resource.StringConverter;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A simple input dialog for soliciting an input string from the user.
+ * <p>
+ * This concrete dialog class can be instantiated as is, or further subclassed as
+ * required.
+ * </p>
+ */
+public class InputDialog : Dialog {
+    /**
+     * The title of the dialog.
+     */
+    private String title;
+
+    /**
+     * The message to display, or <code>null</code> if none.
+     */
+    private String message;
+
+    /**
+     * The input value; the empty string by default.
+     */
+    private String value = "";//$NON-NLS-1$
+
+    /**
+     * The input validator, or <code>null</code> if none.
+     */
+    private IInputValidator validator;
+
+    /**
+     * Ok button widget.
+     */
+    private Button okButton;
+
+    /**
+     * Input text widget.
+     */
+    private Text text;
+
+    /**
+     * Error message label widget.
+     */
+    private Text errorMessageText;
+
+    /**
+     * Error message string.
+     */
+    private String errorMessage;
+
+    /**
+     * Creates an input dialog with OK and Cancel buttons. Note that the dialog
+     * will have no visual representation (no widgets) until it is told to open.
+     * <p>
+     * Note that the <code>open</code> method blocks for input dialogs.
+     * </p>
+     *
+     * @param parentShell
+     *            the parent shell, or <code>null</code> to create a top-level
+     *            shell
+     * @param dialogTitle
+     *            the dialog title, or <code>null</code> if none
+     * @param dialogMessage
+     *            the dialog message, or <code>null</code> if none
+     * @param initialValue
+     *            the initial input value, or <code>null</code> if none
+     *            (equivalent to the empty string)
+     * @param validator
+     *            an input validator, or <code>null</code> if none
+     */
+    public this(Shell parentShell, String dialogTitle,
+            String dialogMessage, String initialValue, IInputValidator validator) {
+        super(parentShell);
+        this.title = dialogTitle;
+        message = dialogMessage;
+        if (initialValue is null) {
+            value = "";//$NON-NLS-1$
+        } else {
+            value = initialValue;
+        }
+        this.validator = validator;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Dialog.
+     */
+    protected override void buttonPressed(int buttonId) {
+        if (buttonId is IDialogConstants.OK_ID) {
+            value = text.getText();
+        } else {
+            value = null;
+        }
+        super.buttonPressed(buttonId);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
+     */
+    protected override void configureShell(Shell shell) {
+        super.configureShell(shell);
+        if (title !is null) {
+            shell.setText(title);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
+     */
+    protected override void createButtonsForButtonBar(Composite parent) {
+        // create OK and Cancel buttons by default
+        okButton = createButton(parent, IDialogConstants.OK_ID,
+                IDialogConstants.OK_LABEL, true);
+        createButton(parent, IDialogConstants.CANCEL_ID,
+                IDialogConstants.CANCEL_LABEL, false);
+        //do this here because setting the text will set enablement on the ok
+        // button
+        text.setFocus();
+        if (value !is null) {
+            text.setText(value);
+            text.selectAll();
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Dialog.
+     */
+    protected override Control createDialogArea(Composite parent) {
+        // create composite
+        Composite composite = cast(Composite) super.createDialogArea(parent);
+        // create message
+        if (message !is null) {
+            Label label = new Label(composite, SWT.WRAP);
+            label.setText(message);
+            GridData data = new GridData(GridData.GRAB_HORIZONTAL
+                    | GridData.GRAB_VERTICAL | GridData.HORIZONTAL_ALIGN_FILL
+                    | GridData.VERTICAL_ALIGN_CENTER);
+            data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
+            label.setLayoutData(data);
+            label.setFont(parent.getFont());
+        }
+        text = new Text(composite, getInputTextStyle());
+        text.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
+                | GridData.HORIZONTAL_ALIGN_FILL));
+        text.addModifyListener(new class ModifyListener {
+            public void modifyText(ModifyEvent e) {
+                validateInput();
+            }
+        });
+        errorMessageText = new Text(composite, SWT.READ_ONLY | SWT.WRAP);
+        errorMessageText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
+                | GridData.HORIZONTAL_ALIGN_FILL));
+        errorMessageText.setBackground(errorMessageText.getDisplay()
+                .getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
+        // Set the error message text
+        // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=66292
+        setErrorMessage(errorMessage);
+
+        applyDialogFont(composite);
+        return composite;
+    }
+
+    /**
+     * Returns the error message label.
+     *
+     * @return the error message label
+     * @deprecated use setErrorMessage(String) instead
+     */
+    protected Label getErrorMessageLabel() {
+        return null;
+    }
+
+    /**
+     * Returns the ok button.
+     *
+     * @return the ok button
+     */
+    protected Button getOkButton() {
+        return okButton;
+    }
+
+    /**
+     * Returns the text area.
+     *
+     * @return the text area
+     */
+    protected Text getText() {
+        return text;
+    }
+
+    /**
+     * Returns the validator.
+     *
+     * @return the validator
+     */
+    protected IInputValidator getValidator() {
+        return validator;
+    }
+
+    /**
+     * Returns the string typed into this input dialog.
+     *
+     * @return the input string
+     */
+    public String getValue() {
+        return value;
+    }
+
+    /**
+     * Validates the input.
+     * <p>
+     * The default implementation of this framework method delegates the request
+     * to the supplied input validator object; if it finds the input invalid,
+     * the error message is displayed in the dialog's message line. This hook
+     * method is called whenever the text changes in the input field.
+     * </p>
+     */
+    protected void validateInput() {
+        String errorMessage = null;
+        if (validator !is null) {
+            errorMessage = validator.isValid(text.getText());
+        }
+        // Bug 16256: important not to treat "" (blank error) the same as null
+        // (no error)
+        setErrorMessage(errorMessage);
+    }
+
+    /**
+     * Sets or clears the error message.
+     * If not <code>null</code>, the OK button is disabled.
+     *
+     * @param errorMessage
+     *            the error message, or <code>null</code> to clear
+     * @since 3.0
+     */
+    public void setErrorMessage(String errorMessage) {
+        this.errorMessage = errorMessage;
+        if (errorMessageText !is null && !errorMessageText.isDisposed()) {
+            errorMessageText.setText(errorMessage is null ? " \n " : errorMessage); //$NON-NLS-1$
+            // Disable the error message text control if there is no error, or
+            // no error text (empty or whitespace only).  Hide it also to avoid
+            // color change.
+            // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=130281
+            bool hasError = errorMessage !is null && (StringConverter.removeWhiteSpaces(errorMessage)).length > 0;
+            errorMessageText.setEnabled(hasError);
+            errorMessageText.setVisible(hasError);
+            errorMessageText.getParent().update();
+            // Access the ok button by id, in case clients have overridden button creation.
+            // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=113643
+            Control button = getButton(IDialogConstants.OK_ID);
+            if (button !is null) {
+                button.setEnabled(errorMessage is null);
+            }
+        }
+    }
+    
+    /**
+     * Returns the style bits that should be used for the input text field.
+     * Defaults to a single line entry. Subclasses may override.
+     * 
+     * @return the integer style bits that should be used when creating the
+     *         input text
+     * 
+     * @since 3.4
+     */
+    protected int getInputTextStyle() {
+        return SWT.SINGLE | SWT.BORDER;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/MessageDialog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,491 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.dialogs.MessageDialog;
+
+import org.eclipse.jface.dialogs.IconAndMessageDialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A dialog for showing messages to the user.
+ * <p>
+ * This concrete dialog class can be instantiated as is, or further subclassed
+ * as required.
+ * </p>
+ */
+public class MessageDialog : IconAndMessageDialog {
+    /**
+     * Constant for a dialog with no image (value 0).
+     */
+    public const static int NONE = 0;
+
+    /**
+     * Constant for a dialog with an error image (value 1).
+     */
+    public const static int ERROR = 1;
+
+    /**
+     * Constant for a dialog with an info image (value 2).
+     */
+    public const static int INFORMATION = 2;
+
+    /**
+     * Constant for a dialog with a question image (value 3).
+     */
+    public const static int QUESTION = 3;
+
+    /**
+     * Constant for a dialog with a warning image (value 4).
+     */
+    public const static int WARNING = 4;
+
+    /**
+     * Labels for buttons in the button bar (localized strings).
+     */
+    private String[] buttonLabels;
+
+    /**
+     * The buttons. Parallels <code>buttonLabels</code>.
+     */
+    private Button[] buttons;
+
+    /**
+     * Index into <code>buttonLabels</code> of the default button.
+     */
+    private int defaultButtonIndex;
+
+    /**
+     * Dialog title (a localized string).
+     */
+    private String title;
+
+    /**
+     * Dialog title image.
+     */
+    private Image titleImage;
+
+    /**
+     * Image, or <code>null</code> if none.
+     */
+    private Image image = null;
+
+    /**
+     * The custom dialog area.
+     */
+    private Control customArea;
+
+    /**
+     * Create a message dialog. Note that the dialog will have no visual
+     * representation (no widgets) until it is told to open.
+     * <p>
+     * The labels of the buttons to appear in the button bar are supplied in
+     * this constructor as an array. The <code>open</code> method will return
+     * the index of the label in this array corresponding to the button that was
+     * pressed to close the dialog. If the dialog was dismissed without pressing
+     * a button (ESC, etc.) then -1 is returned. Note that the <code>open</code>
+     * method blocks.
+     * </p>
+     *
+     * @param parentShell
+     *            the parent shell
+     * @param dialogTitle
+     *            the dialog title, or <code>null</code> if none
+     * @param dialogTitleImage
+     *            the dialog title image, or <code>null</code> if none
+     * @param dialogMessage
+     *            the dialog message
+     * @param dialogImageType
+     *            one of the following values:
+     *            <ul>
+     *            <li><code>MessageDialog.NONE</code> for a dialog with no
+     *            image</li>
+     *            <li><code>MessageDialog.ERROR</code> for a dialog with an
+     *            error image</li>
+     *            <li><code>MessageDialog.INFORMATION</code> for a dialog
+     *            with an information image</li>
+     *            <li><code>MessageDialog.QUESTION </code> for a dialog with a
+     *            question image</li>
+     *            <li><code>MessageDialog.WARNING</code> for a dialog with a
+     *            warning image</li>
+     *            </ul>
+     * @param dialogButtonLabels
+     *            an array of labels for the buttons in the button bar
+     * @param defaultIndex
+     *            the index in the button label array of the default button
+     */
+    public this(Shell parentShell, String dialogTitle,
+            Image dialogTitleImage, String dialogMessage, int dialogImageType,
+            String[] dialogButtonLabels, int defaultIndex) {
+        super(parentShell);
+        this.title = dialogTitle;
+        this.titleImage = dialogTitleImage;
+        this.message = dialogMessage;
+
+        switch (dialogImageType) {
+        case ERROR: {
+            this.image = getErrorImage();
+            break;
+        }
+        case INFORMATION: {
+            this.image = getInfoImage();
+            break;
+        }
+        case QUESTION: {
+            this.image = getQuestionImage();
+            break;
+        }
+        case WARNING: {
+            this.image = getWarningImage();
+            break;
+        }
+        default:
+        }
+        this.buttonLabels = dialogButtonLabels;
+        this.defaultButtonIndex = defaultIndex;
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int)
+     */
+    protected override void buttonPressed(int buttonId) {
+        setReturnCode(buttonId);
+        close();
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
+     */
+    protected override void configureShell(Shell shell) {
+        super.configureShell(shell);
+        if (title !is null) {
+            shell.setText(title);
+        }
+        if (titleImage !is null) {
+            shell.setImage(titleImage);
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Dialog.
+     */
+    protected override void createButtonsForButtonBar(Composite parent) {
+        buttons = new Button[buttonLabels.length];
+        for (int i = 0; i < buttonLabels.length; i++) {
+            String label = buttonLabels[i];
+            Button button = createButton(parent, i, label,
+                    defaultButtonIndex is i);
+            buttons[i] = button;
+        }
+    }
+
+    /**
+     * Creates and returns the contents of an area of the dialog which appears
+     * below the message and above the button bar.
+     * <p>
+     * The default implementation of this framework method returns
+     * <code>null</code>. Subclasses may override.
+     * </p>
+     *
+     * @param parent
+     *            parent composite to contain the custom area
+     * @return the custom area control, or <code>null</code>
+     */
+    protected Control createCustomArea(Composite parent) {
+        return null;
+    }
+
+    /**
+     * This implementation of the <code>Dialog</code> framework method creates
+     * and lays out a composite and calls <code>createMessageArea</code> and
+     * <code>createCustomArea</code> to populate it. Subclasses should
+     * override <code>createCustomArea</code> to add contents below the
+     * message.
+     */
+    protected override Control createDialogArea(Composite parent) {
+        // create message area
+        createMessageArea(parent);
+        // create the top level composite for the dialog area
+        Composite composite = new Composite(parent, SWT.NONE);
+        GridLayout layout = new GridLayout();
+        layout.marginHeight = 0;
+        layout.marginWidth = 0;
+        composite.setLayout(layout);
+        GridData data = new GridData(GridData.FILL_BOTH);
+        data.horizontalSpan = 2;
+        composite.setLayoutData(data);
+        // allow subclasses to add custom controls
+        customArea = createCustomArea(composite);
+        //If it is null create a dummy label for spacing purposes
+        if (customArea is null) {
+            customArea = new Label(composite, SWT.NULL);
+        }
+        return composite;
+    }
+
+    /**
+     * Gets a button in this dialog's button bar.
+     *
+     * @param index
+     *            the index of the button in the dialog's button bar
+     * @return a button in the dialog's button bar
+     */
+    protected override Button getButton(int index) {
+        return buttons[index];
+    }
+
+    /**
+     * Returns the minimum message area width in pixels This determines the
+     * minimum width of the dialog.
+     * <p>
+     * Subclasses may override.
+     * </p>
+     *
+     * @return the minimum message area width (in pixels)
+     */
+    protected int getMinimumMessageWidth() {
+        return convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
+    }
+
+    /**
+     * Handle the shell close. Set the return code to <code>SWT.DEFAULT</code>
+     * as there has been no explicit close by the user.
+     *
+     * @see org.eclipse.jface.window.Window#handleShellCloseEvent()
+     */
+    protected override void handleShellCloseEvent() {
+        //Sets a return code of SWT.DEFAULT since none of the dialog buttons
+        // were pressed to close the dialog.
+        super.handleShellCloseEvent();
+        setReturnCode(SWT.DEFAULT);
+    }
+
+    /**
+     * Convenience method to open a simple confirm (OK/Cancel) dialog.
+     *
+     * @param parent
+     *            the parent shell of the dialog, or <code>null</code> if none
+     * @param title
+     *            the dialog's title, or <code>null</code> if none
+     * @param message
+     *            the message
+     * @return <code>true</code> if the user presses the OK button,
+     *         <code>false</code> otherwise
+     */
+    public static bool openConfirm(Shell parent, String title, String message) {
+        MessageDialog dialog = new MessageDialog(parent, title, null, // accept
+                // the
+                // default
+                // window
+                // icon
+                message, QUESTION, [ IDialogConstants.OK_LABEL,
+                        IDialogConstants.CANCEL_LABEL ], 0); // OK is the
+        // default
+        return dialog.open() is 0;
+    }
+
+    /**
+     * Convenience method to open a standard error dialog.
+     *
+     * @param parent
+     *            the parent shell of the dialog, or <code>null</code> if none
+     * @param title
+     *            the dialog's title, or <code>null</code> if none
+     * @param message
+     *            the message
+     */
+    public static void openError(Shell parent, String title, String message) {
+        MessageDialog dialog = new MessageDialog(parent, title, null, // accept
+                // the
+                // default
+                // window
+                // icon
+                message, ERROR, [ IDialogConstants.OK_LABEL ], 0); // ok
+        // is
+        // the
+        // default
+        dialog.open();
+        return;
+    }
+
+    /**
+     * Convenience method to open a standard information dialog.
+     *
+     * @param parent
+     *            the parent shell of the dialog, or <code>null</code> if none
+     * @param title
+     *            the dialog's title, or <code>null</code> if none
+     * @param message
+     *            the message
+     */
+    public static void openInformation(Shell parent, String title,
+            String message) {
+        MessageDialog dialog = new MessageDialog(parent, title, null, // accept
+                // the
+                // default
+                // window
+                // icon
+                message, INFORMATION,
+                [ IDialogConstants.OK_LABEL ], 0);
+        // ok is the default
+        dialog.open();
+        return;
+    }
+
+    /**
+     * Convenience method to open a simple Yes/No question dialog.
+     *
+     * @param parent
+     *            the parent shell of the dialog, or <code>null</code> if none
+     * @param title
+     *            the dialog's title, or <code>null</code> if none
+     * @param message
+     *            the message
+     * @return <code>true</code> if the user presses the OK button,
+     *         <code>false</code> otherwise
+     */
+    public static bool openQuestion(Shell parent, String title,
+            String message) {
+        MessageDialog dialog = new MessageDialog(parent, title, null, // accept
+                // the
+                // default
+                // window
+                // icon
+                message, QUESTION, [ IDialogConstants.YES_LABEL,
+                        IDialogConstants.NO_LABEL ], 0); // yes is the default
+        return dialog.open() is 0;
+    }
+
+    /**
+     * Convenience method to open a standard warning dialog.
+     *
+     * @param parent
+     *            the parent shell of the dialog, or <code>null</code> if none
+     * @param title
+     *            the dialog's title, or <code>null</code> if none
+     * @param message
+     *            the message
+     */
+    public static void openWarning(Shell parent, String title, String message) {
+        MessageDialog dialog = new MessageDialog(parent, title, null, // accept
+                // the
+                // default
+                // window
+                // icon
+                message, WARNING, [ IDialogConstants.OK_LABEL ], 0); // ok
+        // is
+        // the
+        // default
+        dialog.open();
+        return;
+    }
+
+    /*
+     * @see org.eclipse.jface.dialogs.Dialog#createButton(org.eclipse.swt.widgets.Composite,
+     *      int, java.lang.String, bool)
+     */
+    protected override Button createButton(Composite parent, int id, String label,
+            bool defaultButton) {
+        Button button = super.createButton(parent, id, label, defaultButton);
+        //Be sure to set the focus if the custom area cannot so as not
+        //to lose the defaultButton.
+        if (defaultButton && !customShouldTakeFocus()) {
+            button.setFocus();
+        }
+        return button;
+    }
+
+    /**
+     * Return whether or not we should apply the workaround where we take focus
+     * for the default button or if that should be determined by the dialog. By
+     * default only return true if the custom area is a label or CLabel that
+     * cannot take focus.
+     *
+     * @return bool
+     */
+    protected bool customShouldTakeFocus() {
+        if (cast(Label) customArea ) {
+            return false;
+        }
+        if (cast(CLabel) customArea ) {
+            return (customArea.getStyle() & SWT.NO_FOCUS) > 0;
+        }
+        return true;
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see org.eclipse.jface.dialogs.IconAndMessageDialog#getImage()
+     */
+    public override Image getImage() {
+        return image;
+    }
+
+    /**
+     * An accessor for the labels to use on the buttons.
+     *
+     * @return The button labels to used; never <code>null</code>.
+     */
+    protected String[] getButtonLabels() {
+        return buttonLabels;
+    }
+
+    /**
+     * An accessor for the index of the default button in the button array.
+     *
+     * @return The default button index.
+     */
+    protected int getDefaultButtonIndex() {
+        return defaultButtonIndex;
+    }
+
+    /**
+     * A mutator for the array of buttons in the button bar.
+     *
+     * @param buttons
+     *            The buttons in the button bar; must not be <code>null</code>.
+     */
+    protected void setButtons(Button[] buttons) {
+        if (buttons is null) {
+            throw new NullPointerException(
+                    "The array of buttons cannot be null.");} //$NON-NLS-1$
+        this.buttons = buttons;
+    }
+
+    /**
+     * A mutator for the button labels.
+     *
+     * @param buttonLabels
+     *            The button labels to use; must not be <code>null</code>.
+     */
+    protected void setButtonLabels(String[] buttonLabels) {
+        if (buttonLabels is null) {
+            throw new NullPointerException(
+                    "The array of button labels cannot be null.");} //$NON-NLS-1$
+        this.buttonLabels = buttonLabels;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/MessageDialogWithToggle.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,722 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.dialogs.MessageDialogWithToggle;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * <p>
+ * A message dialog which also allows the user to adjust a toggle setting. If a
+ * preference store is provided and the user selects the toggle, then the user's
+ * answer (yes/ok or no) will be persisted in the store. If no store is
+ * provided, then this information can be queried after the dialog closes.
+ * </p>
+ * <p>
+ * This type of dialog should be used whenever you want to user to be able to
+ * avoid being prompted in the future. It is <strong>strongly </strong>
+ * recommended that a cancel option be provided, so that the user has the option
+ * of making the decision at a later point in time. The semantic for a cancel
+ * button should be to cancel the operation (if it has not yet started), or stop
+ * the operation (if it has already started).
+ * </p>
+ * <p>
+ * It is the responsibility of the developer to provide a mechanism for the user
+ * to change this preference at some later point in time (e.g., through a
+ * preference page).
+ * </p>
+ *
+ * @since 3.0
+ */
+public class MessageDialogWithToggle : MessageDialog {
+
+    /**
+     * The value of the preference when the user has asked that the answer to
+     * the question always be "okay" or "yes".
+     */
+    public static const String ALWAYS = "always"; //$NON-NLS-1$
+
+    /**
+     * The value of the preference when the user has asked that the answer to
+     * the question always be "no".
+     */
+    public static const String NEVER = "never"; //$NON-NLS-1$
+
+    /**
+     * The value of the preference when the user wishes to prompted for an
+     * answer every time the question is to be asked.
+     */
+    public static const String PROMPT = "prompt"; //$NON-NLS-1$
+
+    /**
+     * Convenience method to open a standard error dialog.
+     *
+     * @param parent
+     *            the parent shell of the dialog, or <code>null</code> if none
+     * @param title
+     *            the dialog's title, or <code>null</code> if none
+     * @param message
+     *            the message
+     * @param toggleMessage
+     *            the message for the toggle control, or <code>null</code> for
+     *            the default message
+     * @param toggleState
+     *            the initial state for the toggle
+     * @param store
+     *            the IPreference store in which the user's preference should be
+     *            persisted; <code>null</code> if you don't want it persisted
+     *            automatically.
+     * @param key
+     *            the key to use when persisting the user's preference;
+     *            <code>null</code> if you don't want it persisted.
+     * @return the dialog, after being closed by the user, which the client can
+     *         only call <code>getReturnCode()</code> or
+     *         <code>getToggleState()</code>
+     */
+    public static MessageDialogWithToggle openError(Shell parent, String title,
+            String message, String toggleMessage, bool toggleState,
+            IPreferenceStore store, String key) {
+        MessageDialogWithToggle dialog = new MessageDialogWithToggle(parent,
+                title, null, // accept the default window icon
+                message, ERROR, [ IDialogConstants.OK_LABEL ], 0, // ok
+                // is
+                // the
+                // default
+                toggleMessage, toggleState);
+        dialog.prefStore = store;
+        dialog.prefKey = key;
+        dialog.open();
+        return dialog;
+    }
+
+    /**
+     * Convenience method to open a standard information dialog.
+     *
+     * @param parent
+     *            the parent shell of the dialog, or <code>null</code> if none
+     * @param title
+     *            the dialog's title, or <code>null</code> if none
+     * @param message
+     *            the message
+     * @param toggleMessage
+     *            the message for the toggle control, or <code>null</code> for
+     *            the default message
+     * @param toggleState
+     *            the initial state for the toggle
+     * @param store
+     *            the IPreference store in which the user's preference should be
+     *            persisted; <code>null</code> if you don't want it persisted
+     *            automatically.
+     * @param key
+     *            the key to use when persisting the user's preference;
+     *            <code>null</code> if you don't want it persisted.
+     *
+     * @return the dialog, after being closed by the user, which the client can
+     *         only call <code>getReturnCode()</code> or
+     *         <code>getToggleState()</code>
+     */
+    public static MessageDialogWithToggle openInformation(Shell parent,
+            String title, String message, String toggleMessage,
+            bool toggleState, IPreferenceStore store, String key) {
+        MessageDialogWithToggle dialog = new MessageDialogWithToggle(parent,
+                title, null, // accept the default window icon
+                message, INFORMATION,
+                [ IDialogConstants.OK_LABEL ], 0, // ok is the
+                // default
+                toggleMessage, toggleState);
+        dialog.prefStore = store;
+        dialog.prefKey = key;
+        dialog.open();
+        return dialog;
+    }
+
+    /**
+     * Convenience method to open a simple confirm (OK/Cancel) dialog.
+     *
+     * @param parent
+     *            the parent shell of the dialog, or <code>null</code> if none
+     * @param title
+     *            the dialog's title, or <code>null</code> if none
+     * @param message
+     *            the message
+     * @param toggleMessage
+     *            the message for the toggle control, or <code>null</code> for
+     *            the default message
+     * @param toggleState
+     *            the initial state for the toggle
+     * @param store
+     *            the IPreference store in which the user's preference should be
+     *            persisted; <code>null</code> if you don't want it persisted
+     *            automatically.
+     * @param key
+     *            the key to use when persisting the user's preference;
+     *            <code>null</code> if you don't want it persisted.
+     * @return the dialog, after being closed by the user, which the client can
+     *         only call <code>getReturnCode()</code> or
+     *         <code>getToggleState()</code>
+     */
+    public static MessageDialogWithToggle openOkCancelConfirm(Shell parent,
+            String title, String message, String toggleMessage,
+            bool toggleState, IPreferenceStore store, String key) {
+        MessageDialogWithToggle dialog = new MessageDialogWithToggle(parent,
+                title, null, // accept the default window icon
+                message, QUESTION, [ IDialogConstants.OK_LABEL,
+                        IDialogConstants.CANCEL_LABEL ], 0, // OK is the default
+                toggleMessage, toggleState);
+        dialog.prefStore = store;
+        dialog.prefKey = key;
+        dialog.open();
+        return dialog;
+    }
+
+    /**
+     * Convenience method to open a standard warning dialog.
+     *
+     * @param parent
+     *            the parent shell of the dialog, or <code>null</code> if none
+     * @param title
+     *            the dialog's title, or <code>null</code> if none
+     * @param message
+     *            the message
+     * @param toggleMessage
+     *            the message for the toggle control, or <code>null</code> for
+     *            the default message
+     * @param toggleState
+     *            the initial state for the toggle
+     * @param store
+     *            the IPreference store in which the user's preference should be
+     *            persisted; <code>null</code> if you don't want it persisted
+     *            automatically.
+     * @param key
+     *            the key to use when persisting the user's preference;
+     *            <code>null</code> if you don't want it persisted.
+     * @return the dialog, after being closed by the user, which the client can
+     *         only call <code>getReturnCode()</code> or
+     *         <code>getToggleState()</code>
+     */
+    public static MessageDialogWithToggle openWarning(Shell parent,
+            String title, String message, String toggleMessage,
+            bool toggleState, IPreferenceStore store, String key) {
+        MessageDialogWithToggle dialog = new MessageDialogWithToggle(parent,
+                title, null, // accept the default window icon
+                message, WARNING, [ IDialogConstants.OK_LABEL ],
+                0, // ok is the default
+                toggleMessage, toggleState);
+        dialog.prefStore = store;
+        dialog.prefKey = key;
+        dialog.open();
+        return dialog;
+    }
+
+    /**
+     * Convenience method to open a simple question Yes/No/Cancel dialog.
+     *
+     * @param parent
+     *            the parent shell of the dialog, or <code>null</code> if none
+     * @param title
+     *            the dialog's title, or <code>null</code> if none
+     * @param message
+     *            the message
+     * @param toggleMessage
+     *            the message for the toggle control, or <code>null</code> for
+     *            the default message
+     * @param toggleState
+     *            the initial state for the toggle
+     * @param store
+     *            the IPreference store in which the user's preference should be
+     *            persisted; <code>null</code> if you don't want it persisted
+     *            automatically.
+     * @param key
+     *            the key to use when persisting the user's preference;
+     *            <code>null</code> if you don't want it persisted.
+     * @return the dialog, after being closed by the user, which the client can
+     *         only call <code>getReturnCode()</code> or
+     *         <code>getToggleState()</code>
+     */
+    public static MessageDialogWithToggle openYesNoCancelQuestion(Shell parent,
+            String title, String message, String toggleMessage,
+            bool toggleState, IPreferenceStore store, String key) {
+        MessageDialogWithToggle dialog = new MessageDialogWithToggle(parent,
+                title, null, // accept the default window icon
+                message, QUESTION, [ IDialogConstants.YES_LABEL,
+                        IDialogConstants.NO_LABEL,
+                        IDialogConstants.CANCEL_LABEL ], 0, // YES is the
+                // default
+                toggleMessage, toggleState);
+        dialog.prefStore = store;
+        dialog.prefKey = key;
+        dialog.open();
+        return dialog;
+    }
+
+    /**
+     * Convenience method to open a simple Yes/No question dialog.
+     *
+     * @param parent
+     *            the parent shell of the dialog, or <code>null</code> if none
+     * @param title
+     *            the dialog's title, or <code>null</code> if none
+     * @param message
+     *            the message
+     * @param toggleMessage
+     *            the message for the toggle control, or <code>null</code> for
+     *            the default message
+     * @param toggleState
+     *            the initial state for the toggle
+     * @param store
+     *            the IPreference store in which the user's preference should be
+     *            persisted; <code>null</code> if you don't want it persisted
+     *            automatically.
+     * @param key
+     *            the key to use when persisting the user's preference;
+     *            <code>null</code> if you don't want it persisted.
+     *
+     * @return the dialog, after being closed by the user, which the client can
+     *         only call <code>getReturnCode()</code> or
+     *         <code>getToggleState()</code>
+     */
+    public static MessageDialogWithToggle openYesNoQuestion(Shell parent,
+            String title, String message, String toggleMessage,
+            bool toggleState, IPreferenceStore store, String key) {
+        MessageDialogWithToggle dialog = new MessageDialogWithToggle(parent,
+                title, null, // accept the default window icon
+                message, QUESTION, [ IDialogConstants.YES_LABEL,
+                        IDialogConstants.NO_LABEL ], 0, // yes is the default
+                toggleMessage, toggleState);
+        dialog.prefStore = store;
+        dialog.prefKey = key;
+        dialog.open();
+        return dialog;
+    }
+
+    /**
+     * The key at which the toggle state should be stored within the
+     * preferences. This value may be <code>null</code>, which indicates that
+     * no preference should be updated automatically. It is then the
+     * responsibility of the user of this API to use the information from the
+     * toggle. Note: a <code>prefStore</code> is also needed.
+     */
+    private String prefKey = null;
+
+    /**
+     * The preference store which will be affected by the toggle button. This
+     * value may be <code>null</code>, which indicates that no preference
+     * should be updated automatically. It is then the responsibility of the
+     * user of this API to use the information from the toggle. Note: a
+     * <code>prefKey</code> is also needed.
+     */
+    private IPreferenceStore prefStore = null;
+
+    /**
+     * The toggle button (widget). This value is <code>null</code> until the
+     * dialog is created.
+     */
+    private Button toggleButton = null;
+
+    /**
+     * The message displayed to the user, with the toggle button. This is the
+     * text besides the toggle. If it is <code>null</code>, this means that
+     * the default text for the toggle should be used.
+     */
+    private String toggleMessage;
+
+    /**
+     * The initial selected state of the toggle.
+     */
+    private bool toggleState;
+
+    /**
+     * Creates a message dialog with a toggle. See the superclass constructor
+     * for info on the other parameters.
+     *
+     * @param parentShell
+     *            the parent shell
+     * @param dialogTitle
+     *            the dialog title, or <code>null</code> if none
+     * @param image
+     *            the dialog title image, or <code>null</code> if none
+     * @param message
+     *            the dialog message
+     * @param dialogImageType
+     *            one of the following values:
+     *            <ul>
+     *            <li><code>MessageDialog.NONE</code> for a dialog with no
+     *            image</li>
+     *            <li><code>MessageDialog.ERROR</code> for a dialog with an
+     *            error image</li>
+     *            <li><code>MessageDialog.INFORMATION</code> for a dialog
+     *            with an information image</li>
+     *            <li><code>MessageDialog.QUESTION </code> for a dialog with a
+     *            question image</li>
+     *            <li><code>MessageDialog.WARNING</code> for a dialog with a
+     *            warning image</li>
+     *            </ul>
+     * @param dialogButtonLabels
+     *            an array of labels for the buttons in the button bar
+     * @param defaultIndex
+     *            the index in the button label array of the default button
+     * @param toggleMessage
+     *            the message for the toggle control, or <code>null</code> for
+     *            the default message
+     * @param toggleState
+     *            the initial state for the toggle
+     *
+     */
+    public this(Shell parentShell, String dialogTitle,
+            Image image, String message, int dialogImageType,
+            String[] dialogButtonLabels, int defaultIndex,
+            String toggleMessage, bool toggleState) {
+        super(parentShell, dialogTitle, image, message, dialogImageType,
+                dialogButtonLabels, defaultIndex);
+        this.toggleMessage = toggleMessage;
+        this.toggleState = toggleState;
+        setButtonLabels(dialogButtonLabels);
+    }
+
+    /**
+     * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int)
+     */
+    protected override void buttonPressed(int buttonId) {
+        super.buttonPressed(buttonId);
+
+        if (buttonId !is IDialogConstants.CANCEL_ID && toggleState
+                && prefStore !is null && prefKey !is null) {
+            switch (buttonId) {
+            case IDialogConstants.YES_ID:
+            case IDialogConstants.YES_TO_ALL_ID:
+            case IDialogConstants.PROCEED_ID:
+            case IDialogConstants.OK_ID:
+                prefStore.setValue(prefKey, ALWAYS);
+                break;
+            case IDialogConstants.NO_ID:
+            case IDialogConstants.NO_TO_ALL_ID:
+                prefStore.setValue(prefKey, NEVER);
+                break;
+            default:
+            }
+        }
+    }
+
+    /**
+     * @see Dialog#createButtonBar(Composite)
+     */
+    protected override void createButtonsForButtonBar(Composite parent) {
+        String[] buttonLabels = getButtonLabels();
+        Button[] buttons = new Button[buttonLabels.length];
+        int defaultButtonIndex = getDefaultButtonIndex();
+
+        int suggestedId = IDialogConstants.INTERNAL_ID;
+        for (int i = 0; i < buttonLabels.length; i++) {
+            String label = buttonLabels[i];
+            // get the JFace button ID that matches the label, or use the specified
+            // id if there is no match.
+            int id = mapButtonLabelToButtonID(label, suggestedId);
+
+            // if the suggested id was used, increment the default for next use
+            if (id is suggestedId) {
+                suggestedId++;
+            }
+
+            Button button = createButton(parent, id, label,
+                    defaultButtonIndex is i);
+            buttons[i] = button;
+
+        }
+        setButtons(buttons);
+    }
+
+    /**
+     * @see Dialog#createDialogArea(Composite)
+     */
+    protected override Control createDialogArea(Composite parent) {
+        Composite dialogAreaComposite = cast(Composite) super
+                .createDialogArea(parent);
+        setToggleButton(createToggleButton(dialogAreaComposite));
+        return dialogAreaComposite;
+    }
+
+    /**
+     * Creates a toggle button without any text or state.  The text and state
+     * will be created by <code>createDialogArea</code>.
+     *
+     * @param parent
+     *            The composite in which the toggle button should be placed;
+     *            must not be <code>null</code>.
+     * @return The added toggle button; never <code>null</code>.
+     */
+    protected Button createToggleButton(Composite parent) {
+        Button button = new Button(parent, SWT.CHECK | SWT.LEFT);
+
+        GridData data = new GridData(SWT.NONE);
+        data.horizontalSpan = 2;
+        button.setLayoutData(data);
+        button.setFont(parent.getFont());
+
+        button.addSelectionListener(new class(button) SelectionAdapter {
+            Button button_;
+            this(Button b){ button_=b; }
+            public void widgetSelected(SelectionEvent e) {
+                toggleState = button_.getSelection();
+            }
+
+        });
+
+        return button;
+    }
+
+    /**
+     * Returns the toggle button.
+     *
+     * @return the toggle button
+     */
+    protected Button getToggleButton() {
+        return toggleButton;
+    }
+
+    /**
+     * An accessor for the current preference store for this dialog.
+     *
+     * @return The preference store; this value may be <code>null</code> if no
+     *         preference is being used.
+     */
+    public IPreferenceStore getPrefStore() {
+        return prefStore;
+    }
+
+    /**
+     * An accessor for the current key of the toggle preference.
+     *
+     * @return The preference key; this value may be <code>null</code> if no
+     *         preference is being used.
+     */
+    public String getPrefKey() {
+        return prefKey;
+    }
+
+    /**
+     * Returns the toggle state. This can be called even after the dialog is
+     * closed.
+     *
+     * @return <code>true</code> if the toggle button is checked,
+     *         <code>false</code> if not
+     */
+    public bool getToggleState() {
+        return toggleState;
+    }
+
+    /**
+     * A mutator for the key of the preference to be modified by the toggle
+     * button.
+     *
+     * @param prefKey
+     *            The prefKey to set. If this value is <code>null</code>,
+     *            then no preference will be modified.
+     */
+    public void setPrefKey(String prefKey) {
+        this.prefKey = prefKey;
+    }
+
+    /**
+     * A mutator for the preference store to be modified by the toggle button.
+     *
+     * @param prefStore
+     *            The prefStore to set. If this value is <code>null</code>,
+     *            then no preference will be modified.
+     */
+    public void setPrefStore(IPreferenceStore prefStore) {
+        this.prefStore = prefStore;
+    }
+
+    /**
+     * A mutator for the button providing the toggle option. If the button
+     * exists, then it will automatically get the text set to the current toggle
+     * message, and its selection state set to the current selection state.
+     *
+     * @param button
+     *            The button to use; must not be <code>null</code>.
+     */
+    protected void setToggleButton(Button button) {
+        if (button is null) {
+            throw new NullPointerException(
+                    "A message dialog with toggle may not have a null toggle button.");} //$NON-NLS-1$
+
+        if (!button.isDisposed()) {
+            String text;
+            if (toggleMessage is null) {
+                text = JFaceResources
+                        .getString("MessageDialogWithToggle.defaultToggleMessage"); //$NON-NLS-1$
+            } else {
+                text = toggleMessage;
+            }
+            button.setText(text);
+            button.setSelection(toggleState);
+        }
+
+        this.toggleButton = button;
+    }
+
+    /**
+     * A mutator for the text on the toggle button. The button will
+     * automatically get updated with the new text, if it exists.
+     *
+     * @param message
+     *            The new text of the toggle button; if it is <code>null</code>,
+     *            then used the default toggle message.
+     */
+    protected void setToggleMessage(String message) {
+        this.toggleMessage = message;
+
+        if ((toggleButton !is null) && (!toggleButton.isDisposed())) {
+            String text;
+            if (toggleMessage is null) {
+                text = JFaceResources
+                        .getString("MessageDialogWithToggle.defaultToggleMessage"); //$NON-NLS-1$
+            } else {
+                text = toggleMessage;
+            }
+            toggleButton.setText(text);
+        }
+    }
+
+    /**
+     * A mutator for the state of the toggle button. This method will update the
+     * button, if it exists.
+     *
+     * @param toggleState
+     *            The desired state of the toggle button (<code>true</code>
+     *            means the toggle should be selected).
+     */
+    public void setToggleState(bool toggleState) {
+        this.toggleState = toggleState;
+
+        // Update the button, if it exists.
+        if ((toggleButton !is null) && (!toggleButton.isDisposed())) {
+            toggleButton.setSelection(toggleState);
+        }
+    }
+
+    /**
+     * Attempt to find a standard JFace button id that matches the specified button
+     * label.  If no match can be found, use the default id provided.
+     *
+     * @param buttonLabel the button label whose id is sought
+     * @param defaultId the id to use for the button if there is no standard id
+     * @return the id for the specified button label
+     */
+    private int mapButtonLabelToButtonID(String buttonLabel, int defaultId) {
+        // Not pretty but does the job...
+        if (IDialogConstants.OK_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.OK_ID;
+        }
+
+        if (IDialogConstants.YES_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.YES_ID;
+        }
+
+        if (IDialogConstants.NO_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.NO_ID;
+        }
+
+        if (IDialogConstants.CANCEL_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.CANCEL_ID;
+        }
+
+        if (IDialogConstants.YES_TO_ALL_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.YES_TO_ALL_ID;
+        }
+
+        if (IDialogConstants.SKIP_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.SKIP_ID;
+        }
+
+        if (IDialogConstants.STOP_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.STOP_ID;
+        }
+
+        if (IDialogConstants.ABORT_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.ABORT_ID;
+        }
+
+        if (IDialogConstants.RETRY_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.RETRY_ID;
+        }
+
+        if (IDialogConstants.IGNORE_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.IGNORE_ID;
+        }
+
+        if (IDialogConstants.PROCEED_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.PROCEED_ID;
+        }
+
+        if (IDialogConstants.OPEN_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.OPEN_ID;
+        }
+
+        if (IDialogConstants.CLOSE_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.CLOSE_ID;
+        }
+
+        if (IDialogConstants.BACK_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.BACK_ID;
+        }
+
+        if (IDialogConstants.NEXT_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.NEXT_ID;
+        }
+
+        if (IDialogConstants.FINISH_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.FINISH_ID;
+        }
+
+        if (IDialogConstants.HELP_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.HELP_ID;
+        }
+
+        if (IDialogConstants.NO_TO_ALL_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.NO_TO_ALL_ID;
+        }
+
+        if (IDialogConstants.SHOW_DETAILS_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.DETAILS_ID;
+        }
+        
+        if (IDialogConstants.HIDE_DETAILS_LABEL.equals(buttonLabel)) {
+            return IDialogConstants.DETAILS_ID;
+        }
+        
+        //          No XXX_LABEL in IDialogConstants for these. Unlikely
+        //            they would be used in a message dialog though.
+        //          public int SELECT_ALL_ID = 18;
+        //          public int DESELECT_ALL_ID = 19;
+        //          public int SELECT_TYPES_ID = 20;
+
+        return defaultId;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/PageChangedEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.PageChangedEvent;
+
+import org.eclipse.jface.dialogs.IPageChangeProvider;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.EventObject;
+
+/**
+ * Event object describing a page selection change. The source of these events
+ * is a page change provider.
+ *
+ * @see IPageChangeProvider
+ * @see IPageChangedListener
+ *
+ * @since 3.1
+ */
+public class PageChangedEvent : EventObject {
+
+    /**
+     * Generated serial version UID for this class.
+     *
+     * @since 3.1
+     */
+    private static const long serialVersionUID = 3835149545519723574L;
+
+    /**
+     * The selected page.
+     */
+    protected Object selectedPage;
+
+    /**
+     * Creates a new event for the given source and selected page.
+     *
+     * @param source
+     *            the page change provider
+     * @param selectedPage
+     *            the selected page. In the JFace provided dialogs this
+     *            will be an <code>IDialogPage</code>.
+     */
+    public this(IPageChangeProvider source,
+            Object selectedPage) {
+        super( cast(Object) source);
+        Assert.isNotNull(selectedPage);
+        this.selectedPage = selectedPage;
+    }
+
+    /**
+     * Returns the selected page.
+     *
+     * @return the selected page. In dialogs implemented by JFace,
+     *      this will be an <code>IDialogPage</code>.
+     */
+    public Object getSelectedPage() {
+        return selectedPage;
+    }
+
+    /**
+     * Returns the page change provider that is the source of this event.
+     *
+     * @return the originating page change provider
+     */
+    public IPageChangeProvider getPageChangeProvider() {
+        return cast(IPageChangeProvider) getSource();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/PageChangingEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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:
+ *     Chris Gross (schtoo@schtoo.com) - initial API and implementation for bug 16179
+ *     IBM Corporation - revisions to initial contribution
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.PageChangingEvent;
+
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.EventObject;
+import java.util.Set;
+
+/**
+ * Event object describing an <code>IDialogPage</code> in the midst of changing.
+ *
+ * @see IPageChangingListener
+ * @since 3.3
+ */
+public class PageChangingEvent : EventObject {
+
+
+    private static const long serialVersionUID = 1L;
+
+    private Object currentPage;
+
+    private Object targetPage;
+
+    /**
+     * Public field that dictates if the page change will successfully change.
+     *
+     * Set this field to <code>false</code> to prevent the page from changing.
+     *
+     * Default value is <code>true</code>.
+     */
+    public bool doit = true;
+
+    /**
+     * Creates a new event for the given source, selected (current) page and
+     * direction.
+     *
+     * @param source
+     *            the page changing provider (the source of this event)
+     * @param currentPage
+     *            the current page. In the JFace provided dialogs this will be
+     *            an <code>IDialogPage</code>.
+     * @param targetPage
+     *            the target page. In the JFace provided dialogs this will be an
+     *            <code>IDialogPage</code>.
+     */
+    public this(Object source, Object currentPage, Object targetPage) {
+        super(source);
+        Assert.isNotNull(currentPage);
+        Assert.isNotNull(targetPage);
+        this.currentPage = currentPage;
+        this.targetPage = targetPage;
+    }
+
+    /**
+     * Returns the current page from which the page change originates.
+     *
+     * @return the current page. In dialogs implemented by JFace,
+     *      this will be an <code>IDialogPage</code>.
+     */
+    public Object getCurrentPage() {
+        return currentPage;
+    }
+
+    /**
+     * Returns the target page to change to.
+     *
+     * @return the target page. In dialogs implemented by JFace,
+     *      this will be an <code>IDialogPage</code>.
+     */
+    public Object getTargetPage() {
+        return targetPage;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/PopupDialog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1625 @@
+/*******************************************************************************
+ * 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
+ *     Stefan Xenos, IBM - bug 156790: Adopt GridLayoutFactory within JFace
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.PopupDialog;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.Dialog;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.swt.widgets.Tracker;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.GroupMarker;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.window.Window;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * A lightweight, transient dialog that is popped up to show contextual or
+ * temporal information and is easily dismissed. Clients control whether the
+ * dialog should be able to receive input focus. An optional title area at the
+ * top and an optional info area at the bottom can be used to provide additional
+ * information.
+ * <p>
+ * Because the dialog is short-lived, most of the configuration of the dialog is
+ * done in the constructor. Set methods are only provided for those values that
+ * are expected to be dynamically computed based on a particular instance's
+ * internal state.
+ * <p>
+ * Clients are expected to override the creation of the main dialog area, and
+ * may optionally override the creation of the title area and info area in order
+ * to add content. In general, however, the creation of stylistic features, such
+ * as the dialog menu, separator styles, and fonts, is kept private so that all
+ * popup dialogs will have a similar appearance.
+ *
+ * @since 3.2
+ */
+public class PopupDialog : Window {
+
+    /**
+     *
+     */
+    private static GridDataFactory LAYOUTDATA_GRAB_BOTH_;
+    private static GridDataFactory LAYOUTDATA_GRAB_BOTH(){
+        if( LAYOUTDATA_GRAB_BOTH_ is null ){
+            synchronized( PopupDialog.classinfo ){
+                if( LAYOUTDATA_GRAB_BOTH_ is null ){
+                    LAYOUTDATA_GRAB_BOTH_ = GridDataFactory.fillDefaults().grab(true,true);
+                }
+            }
+        }
+        return LAYOUTDATA_GRAB_BOTH_;
+    }
+
+    /**
+     * The dialog settings key name for stored dialog x location.
+     */
+    private static const String DIALOG_ORIGIN_X = "DIALOG_X_ORIGIN"; //$NON-NLS-1$
+
+    /**
+     * The dialog settings key name for stored dialog y location.
+     */
+    private static const String DIALOG_ORIGIN_Y = "DIALOG_Y_ORIGIN"; //$NON-NLS-1$
+
+    /**
+     * The dialog settings key name for stored dialog width.
+     */
+    private static const String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$
+
+    /**
+     * The dialog settings key name for stored dialog height.
+     */
+    private static const String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$
+
+    /**
+     * The dialog settings key name for remembering if the persisted bounds
+     * should be accessed.
+     *
+     * @deprecated Since 3.4, this is retained only for backward compatibility.
+     */
+    private static final String DIALOG_USE_PERSISTED_BOUNDS = "DIALOG_USE_PERSISTED_BOUNDS"; //$NON-NLS-1$
+
+    /**
+     * The dialog settings key name for remembering if the bounds persisted
+     * prior to 3.4 have been migrated to the 3.4 settings.
+     *
+     * @since 3.4
+     * @deprecated This is marked deprecated at its introduction to discourage
+     *             future dependency
+     */
+    private static final String DIALOG_VALUE_MIGRATED_TO_34 = "hasBeenMigratedTo34"; //$NON-NLS-1$
+
+    /**
+     * The dialog settings key name for remembering if the persisted size should
+     * be accessed.
+     */
+    private static final String DIALOG_USE_PERSISTED_SIZE = "DIALOG_USE_PERSISTED_SIZE"; //$NON-NLS-1$
+
+    /**
+     * The dialog settings key name for remembering if the persisted location
+     * should be accessed.
+     */
+    private static final String DIALOG_USE_PERSISTED_LOCATION = "DIALOG_USE_PERSISTED_LOCATION"; //$NON-NLS-1$
+
+    /**
+     * Move action for the dialog.
+     */
+    private class MoveAction : Action {
+
+        this() {
+            super(JFaceResources.getString("PopupDialog.move"), //$NON-NLS-1$
+                    IAction.AS_PUSH_BUTTON);
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.jface.action.IAction#run()
+         */
+        public override void run() {
+            performTrackerAction(SWT.NONE);
+        }
+
+    }
+
+    /**
+     * Resize action for the dialog.
+     */
+    private class ResizeAction : Action {
+
+        this() {
+            super(JFaceResources.getString("PopupDialog.resize"), //$NON-NLS-1$
+                    IAction.AS_PUSH_BUTTON);
+        }
+
+        /*
+         * @see org.eclipse.jface.action.Action#run()
+         */
+        public override void run() {
+            performTrackerAction(SWT.RESIZE);
+        }
+    }
+
+    /**
+     *
+     * Remember bounds action for the dialog.
+     */
+    private class PersistBoundsAction : Action {
+
+        this() {
+            super(JFaceResources.getString("PopupDialog.persistBounds"), //$NON-NLS-1$
+                    IAction.AS_CHECK_BOX);
+            setChecked(persistLocation && persistSize);
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.jface.action.IAction#run()
+         */
+        public override void run() {
+            persistSize = isChecked();
+            persistLocation = persistSize;
+        }
+    }
+
+    /**
+     *
+     * Remember bounds action for the dialog.
+     */
+    private class PersistSizeAction : Action {
+
+        this() {
+            super(JFaceResources.getString("PopupDialog.persistSize"), //$NON-NLS-1$
+                    IAction.AS_CHECK_BOX);
+            setChecked(persistSize);
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.jface.action.IAction#run()
+         */
+        public void run() {
+            persistSize = isChecked();
+        }
+    }
+
+    /**
+     *
+     * Remember location action for the dialog.
+     */
+    private class PersistLocationAction : Action {
+
+        this() {
+            super(JFaceResources.getString("PopupDialog.persistLocation"), //$NON-NLS-1$
+                    IAction.AS_CHECK_BOX);
+            setChecked(persistLocation);
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.jface.action.IAction#run()
+         */
+        public void run() {
+            persistLocation = isChecked();
+        }
+    }
+
+    /**
+     * Shell style appropriate for a simple hover popup that cannot get focus.
+     *
+     */
+    public const static int HOVER_SHELLSTYLE = SWT.NO_FOCUS | SWT.ON_TOP
+            | SWT.TOOL;
+
+    /**
+     * Shell style appropriate for an info popup that can get focus.
+     */
+    public const static int INFOPOPUP_SHELLSTYLE = SWT.TOOL;
+
+    /**
+     * Shell style appropriate for a resizable info popup that can get focus.
+     */
+    public const static int INFOPOPUPRESIZE_SHELLSTYLE = SWT.RESIZE;
+
+    /**
+     * Margin width (in pixels) to be used in layouts inside popup dialogs
+     * (value is 0).
+     */
+    public const static int POPUP_MARGINWIDTH = 0;
+
+    /**
+     * Margin height (in pixels) to be used in layouts inside popup dialogs
+     * (value is 0).
+     */
+    public const static int POPUP_MARGINHEIGHT = 0;
+
+    /**
+     * Vertical spacing (in pixels) between cells in the layouts inside popup
+     * dialogs (value is 1).
+     */
+    public const static int POPUP_VERTICALSPACING = 1;
+
+    /**
+     * Vertical spacing (in pixels) between cells in the layouts inside popup
+     * dialogs (value is 1).
+     */
+    public const static int POPUP_HORIZONTALSPACING = 1;
+
+    /**
+     * Image registry key for menu image.
+     *
+     * @since 3.4
+     */
+    public static const String POPUP_IMG_MENU = "popup_menu_image"; //$NON-NLS-1$
+
+    /**
+     * Image registry key for disabled menu image.
+     *
+     * @since 3.4
+     */
+    public static const String POPUP_IMG_MENU_DISABLED = "popup_menu_image_diabled"; //$NON-NLS-1$
+
+    /**
+     *
+     */
+    private static GridLayoutFactory POPUP_LAYOUT_FACTORY_;
+    private static GridLayoutFactory POPUP_LAYOUT_FACTORY() {
+        if( POPUP_LAYOUT_FACTORY_ is null ){
+            synchronized( PopupDialog.classinfo ){
+                if( POPUP_LAYOUT_FACTORY_ is null ){
+                    POPUP_LAYOUT_FACTORY_ = GridLayoutFactory
+                        .fillDefaults().margins(POPUP_MARGINWIDTH, POPUP_MARGINHEIGHT)
+                        .spacing(POPUP_HORIZONTALSPACING, POPUP_VERTICALSPACING);
+                }
+            }
+        }
+        return POPUP_LAYOUT_FACTORY_;
+    }
+
+    /**
+     * The dialog's toolbar for the move and resize capabilities.
+     */
+    private ToolBar toolBar = null;
+
+    /**
+     * The dialog's menu manager.
+     */
+    private MenuManager menuManager = null;
+
+    /**
+     * The control representing the main dialog area.
+     */
+    private Control dialogArea;
+
+    /**
+     * Labels that contain title and info text. Cached so they can be updated
+     * dynamically if possible.
+     */
+    private Label titleLabel, infoLabel;
+
+    /**
+     * Separator controls. Cached so they can be excluded from color changes.
+     */
+    private Control titleSeparator, infoSeparator;
+
+    /**
+     * Font to be used for the info area text. Computed based on the dialog's
+     * font.
+     */
+    private Font infoFont;
+
+    /**
+     * Font to be used for the title area text. Computed based on the dialog's
+     * font.
+     */
+    private Font titleFont;
+
+    /**
+     * Flags indicating whether we are listening for shell deactivate events,
+     * either those or our parent's. Used to prevent closure when a menu command
+     * is chosen or a secondary popup is launched.
+     */
+    private bool listenToDeactivate;
+
+    private bool listenToParentDeactivate;
+
+    private Listener parentDeactivateListener;
+
+    /**
+     * Flag indicating whether focus should be taken when the dialog is opened.
+     */
+    private bool takeFocusOnOpen = false;
+
+    /**
+     * Flag specifying whether a menu should be shown that allows the user to
+     * move and resize.
+     */
+    private bool showDialogMenu_ = false;
+
+    /**
+     * Flag specifying whether menu actions allowing the user to choose whether
+     * the dialog bounds and location should be persisted are to be shown.
+     */
+    private bool showPersistActions = false;
+
+    /**
+     * Flag specifying whether the size of the popup should be persisted. This
+     * flag is used as initial default and updated by the menu if it is shown.
+     */
+    private bool persistSize = false;
+
+    /**
+     * Flag specifying whether the location of the popup should be persisted.
+     * This flag is used as initial default and updated by the menu if it is
+     * shown.
+     */
+    private bool persistLocation = false;
+    /**
+     * Flag specifying whether to use new 3.4 API instead of the old one.
+     *
+     * @since 3.4
+     */
+    private bool isUsing34API = true;
+
+    /**
+     * Text to be shown in an optional title area (on top).
+     */
+    private String titleText;
+
+    /**
+     * Text to be shown in an optional info area (at the bottom).
+     */
+    private String infoText;
+
+    /**
+     * Constructs a new instance of <code>PopupDialog</code>.
+     *
+     * @param parent
+     *            The parent shell.
+     * @param shellStyle
+     *            The shell style.
+     * @param takeFocusOnOpen
+     *            A bool indicating whether focus should be taken by this
+     *            popup when it opens.
+     * @param persistBounds
+     *            A bool indicating whether the bounds (size and location) of
+     *            the dialog should be persisted upon close of the dialog. The
+     *            bounds can only be persisted if the dialog settings for
+     *            persisting the bounds are also specified. If a menu action
+     *            will be provided that allows the user to control this feature,
+     *            then the last known value of the user's setting will be used
+     *            instead of this flag.
+     * @param showDialogMenu
+     *            A bool indicating whether a menu for moving and resizing
+     *            the popup should be provided.
+     * @param showPersistActions
+     *            A bool indicating whether actions allowing the user to
+     *            control the persisting of the dialog size and location should
+     *            be shown in the dialog menu. This parameter has no effect if
+     *            <code>showDialogMenu</code> is <code>false</code>.
+     * @param titleText
+     *            Text to be shown in an upper title area, or <code>null</code>
+     *            if there is no title.
+     * @param infoText
+     *            Text to be shown in a lower info area, or <code>null</code>
+     *            if there is no info area.
+     *
+     * @see PopupDialog#getDialogSettings()
+     * @deprecated As of 3.4, replaced by
+     *             {@link #PopupDialog(Shell, int, bool, bool, bool, bool, bool, String, String)}
+     */
+    public this(Shell parent, int shellStyle, bool takeFocusOnOpen,
+            bool persistBounds, bool showDialogMenu,
+            bool showPersistActions, String titleText, String infoText) {
+        this(parent, shellStyle, takeFocusOnOpen, persistBounds, persistBounds,
+                showDialogMenu, showPersistActions, titleText, infoText, false);
+    }
+
+    /**
+     * Constructs a new instance of <code>PopupDialog</code>.
+     *
+     * @param parent
+     *            The parent shell.
+     * @param shellStyle
+     *            The shell style.
+     * @param takeFocusOnOpen
+     *            A bool indicating whether focus should be taken by this
+     *            popup when it opens.
+     * @param persistSize
+     *            A bool indicating whether the size should be persisted upon
+     *            close of the dialog. The size can only be persisted if the
+     *            dialog settings for persisting the bounds are also specified.
+     *            If a menu action will be provided that allows the user to
+     *            control this feature and the user hasn't changed that setting,
+     *            then this flag is used as initial default for the menu.
+     * @param persistLocation
+     *            A bool indicating whether the location should be persisted
+     *            upon close of the dialog. The location can only be persisted
+     *            if the dialog settings for persisting the bounds are also
+     *            specified. If a menu action will be provided that allows the
+     *            user to control this feature and the user hasn't changed that
+     *            setting, then this flag is used as initial default for the
+     *            menu. default for the menu until the user changed it.
+     * @param showDialogMenu
+     *            A bool indicating whether a menu for moving and resizing
+     *            the popup should be provided.
+     * @param showPersistActions
+     *            A bool indicating whether actions allowing the user to
+     *            control the persisting of the dialog bounds and location
+     *            should be shown in the dialog menu. This parameter has no
+     *            effect if <code>showDialogMenu</code> is <code>false</code>.
+     * @param titleText
+     *            Text to be shown in an upper title area, or <code>null</code>
+     *            if there is no title.
+     * @param infoText
+     *            Text to be shown in a lower info area, or <code>null</code>
+     *            if there is no info area.
+     *
+     * @see PopupDialog#getDialogSettings()
+     *
+     * @since 3.4
+     */
+    public this(Shell parent, int shellStyle, bool takeFocusOnOpen,
+            bool persistSize, bool persistLocation,
+            bool showDialogMenu, bool showPersistActions,
+            String titleText, String infoText) {
+        this(parent, shellStyle, takeFocusOnOpen, persistSize, persistLocation,
+                showDialogMenu, showPersistActions, titleText, infoText, true);
+
+    }
+
+    /**
+     * Constructs a new instance of <code>PopupDialog</code>.
+     *
+     * @param parent
+     *            The parent shell.
+     * @param shellStyle
+     *            The shell style.
+     * @param takeFocusOnOpen
+     *            A bool indicating whether focus should be taken by this
+     *            popup when it opens.
+     * @param persistSize
+     *            A bool indicating whether the size should be persisted upon
+     *            close of the dialog. The size can only be persisted if the
+     *            dialog settings for persisting the bounds are also specified.
+     *            If a menu action will be provided that allows the user to
+     *            control this feature and the user hasn't changed that setting,
+     *            then this flag is used as initial default for the menu.
+     * @param persistLocation
+     *            A bool indicating whether the location should be persisted
+     *            upon close of the dialog. The location can only be persisted
+     *            if the dialog settings for persisting the bounds are also
+     *            specified. If a menu action will be provided that allows the
+     *            user to control this feature and the user hasn't changed that
+     *            setting, then this flag is used as initial default for the
+     *            menu. default for the menu until the user changed it.
+     * @param showDialogMenu
+     *            A bool indicating whether a menu for moving and resizing
+     *            the popup should be provided.
+     * @param showPersistActions
+     *            A bool indicating whether actions allowing the user to
+     *            control the persisting of the dialog bounds and location
+     *            should be shown in the dialog menu. This parameter has no
+     *            effect if <code>showDialogMenu</code> is <code>false</code>.
+     * @param titleText
+     *            Text to be shown in an upper title area, or <code>null</code>
+     *            if there is no title.
+     * @param infoText
+     *            Text to be shown in a lower info area, or <code>null</code>
+     *            if there is no info area.
+     * @param use34API
+     *            <code>true</code> if 3.4 API should be used
+     *
+     * @see PopupDialog#getDialogSettings()
+     *
+     * @since 3.4
+     */
+    private this(Shell parent, int shellStyle, bool takeFocusOnOpen,
+            bool persistSize, bool persistLocation,
+            bool showDialogMenu, bool showPersistActions,
+            String titleText, String infoText, bool use34API) {
+        super(parent);
+        // Prior to 3.4, we encouraged use of SWT.NO_TRIM and provided a
+        // border using a black composite background and margin. Now we
+        // use SWT.TOOL to get the border for some cases and this conflicts
+        // with SWT.NO_TRIM. Clients who previously have used SWT.NO_TRIM
+        // and still had a border drawn for them would find their border go
+        // away unless we do the following:
+        // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=219743
+        if ((shellStyle & SWT.NO_TRIM) !is 0) {
+            shellStyle &= ~(SWT.NO_TRIM | SWT.SHELL_TRIM);
+        }
+
+        setShellStyle(shellStyle);
+        this.takeFocusOnOpen = takeFocusOnOpen;
+        this.showDialogMenu_ = showDialogMenu_;
+        this.showPersistActions = showPersistActions;
+        this.titleText = titleText;
+        this.infoText = infoText;
+
+        setBlockOnOpen(false);
+
+        this.isUsing34API = use34API;
+
+        this.persistSize = persistSize;
+        this.persistLocation = persistLocation;
+
+        migrateBoundsSetting();
+
+        initializeWidgetState();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.window.Window#configureShell(Shell)
+     */
+    protected override void configureShell(Shell shell) {
+        GridLayoutFactory.fillDefaults().margins(0, 0).spacing(5, 5).applyTo(
+                shell);
+
+        shell.addListener(SWT.Deactivate, new class Listener {
+            public void handleEvent(Event event) {
+                /*
+                 * Close if we are deactivating and have no child shells. If we
+                 * have child shells, we are deactivating due to their opening.
+                 * On X, we receive this when a menu child (such as the system
+                 * menu) of the shell opens, but I have not found a way to
+                 * distinguish that case here. Hence bug #113577 still exists.
+                 */
+                if (listenToDeactivate && event.widget is getShell()
+                        && getShell().getShells().length is 0) {
+                    asyncClose();
+                } else {
+                    /*
+                     * We typically ignore deactivates to work around
+                     * platform-specific event ordering. Now that we've ignored
+                     * whatever we were supposed to, start listening to
+                     * deactivates. Example issues can be found in
+                     * https://bugs.eclipse.org/bugs/show_bug.cgi?id=123392
+                     */
+                    listenToDeactivate = true;
+                }
+            }
+        });
+        // Set this true whenever we activate. It may have been turned
+        // off by a menu or secondary popup showing.
+        shell.addListener(SWT.Activate, new class Listener {
+            public void handleEvent(Event event) {
+                // ignore this event if we have launched a child
+                if (event.widget is getShell()
+                        && getShell().getShells().length is 0) {
+                    listenToDeactivate = true;
+                    // Typically we start listening for parent deactivate after
+                    // we are activated, except on the Mac, where the deactivate
+                    // is received after activate.
+                    // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=100668
+                    listenToParentDeactivate = !"carbon".equals(SWT.getPlatform()); //$NON-NLS-1$
+                }
+            }
+        });
+
+        if ((getShellStyle() & SWT.ON_TOP) !is 0 && shell.getParent() !is null) {
+            parentDeactivateListener = new class Listener {
+                public void handleEvent(Event event) {
+                    if (listenToParentDeactivate) {
+                        asyncClose();
+                    } else {
+                        // Our first deactivate, now start listening on the Mac.
+                        listenToParentDeactivate = listenToDeactivate;
+                    }
+                }
+            };
+            shell.getParent().addListener(SWT.Deactivate,
+                    parentDeactivateListener);
+        }
+
+        shell.addDisposeListener(new class DisposeListener {
+            public void widgetDisposed(DisposeEvent event) {
+                handleDispose();
+            }
+        });
+    }
+
+    private void asyncClose() {
+        // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=152010
+        getShell().getDisplay().asyncExec(new class Runnable {
+            public void run() {
+                close();
+            }
+        });
+    }
+
+    /**
+     * The <code>PopupDialog</code> implementation of this <code>Window</code>
+     * method creates and lays out the top level composite for the dialog. It
+     * then calls the <code>createTitleMenuArea</code>,
+     * <code>createDialogArea</code>, and <code>createInfoTextArea</code>
+     * methods to create an optional title and menu area on the top, a dialog
+     * area in the center, and an optional info text area at the bottom.
+     * Overriding <code>createDialogArea</code> and (optionally)
+     * <code>createTitleMenuArea</code> and <code>createTitleMenuArea</code>
+     * are recommended rather than overriding this method.
+     *
+     * @param parent
+     *            the composite used to parent the contents.
+     *
+     * @return the control representing the contents.
+     */
+    protected override Control createContents(Composite parent) {
+        Composite composite = new Composite(parent, SWT.NONE);
+        POPUP_LAYOUT_FACTORY.applyTo(composite);
+        LAYOUTDATA_GRAB_BOTH.applyTo(composite);
+
+        // Title area
+        if (hasTitleArea()) {
+            createTitleMenuArea(composite);
+            titleSeparator = createHorizontalSeparator(composite);
+        }
+        // Content
+        dialogArea = createDialogArea(composite);
+        // Create a grid data layout data if one was not provided.
+        // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=118025
+        if (dialogArea.getLayoutData() is null) {
+            LAYOUTDATA_GRAB_BOTH.applyTo(dialogArea);
+        }
+
+        // Info field
+        if (hasInfoArea()) {
+            infoSeparator = createHorizontalSeparator(composite);
+            createInfoTextArea(composite);
+        }
+
+        applyColors(composite);
+        applyFonts(composite);
+        return composite;
+    }
+
+    /**
+     * Creates and returns the contents of the dialog (the area below the title
+     * area and above the info text area.
+     * <p>
+     * The <code>PopupDialog</code> implementation of this framework method
+     * creates and returns a new <code>Composite</code> with standard margins
+     * and spacing.
+     * <p>
+     * The returned control's layout data must be an instance of
+     * <code>GridData</code>. This method must not modify the parent's
+     * layout.
+     * <p>
+     * Subclasses must override this method but may call <code>super</code> as
+     * in the following example:
+     *
+     * <pre>
+     * Composite composite = (Composite) super.createDialogArea(parent);
+     * //add controls to composite as necessary
+     * return composite;
+     * </pre>
+     *
+     * @param parent
+     *            the parent composite to contain the dialog area
+     * @return the dialog area control
+     */
+    protected Control createDialogArea(Composite parent) {
+        Composite composite = new Composite(parent, SWT.NONE);
+        POPUP_LAYOUT_FACTORY.applyTo(composite);
+        LAYOUTDATA_GRAB_BOTH.applyTo(composite);
+        return composite;
+    }
+
+    /**
+     * Returns the control that should get initial focus. Subclasses may
+     * override this method.
+     *
+     * @return the Control that should receive focus when the popup opens.
+     */
+    protected Control getFocusControl() {
+        return dialogArea;
+    }
+
+    /**
+     * Sets the tab order for the popup. Clients should override to introduce
+     * specific tab ordering.
+     *
+     * @param composite
+     *            the composite in which all content, including the title area
+     *            and info area, was created. This composite's parent is the
+     *            shell.
+     */
+    protected void setTabOrder(Composite composite) {
+        // default is to do nothing
+    }
+
+    /**
+     * Returns a bool indicating whether the popup should have a title area
+     * at the top of the dialog. Subclasses may override. Default behavior is to
+     * have a title area if there is to be a menu or title text.
+     *
+     * @return <code>true</code> if a title area should be created,
+     *         <code>false</code> if it should not.
+     */
+    protected bool hasTitleArea() {
+        return titleText !is null || showDialogMenu_;
+    }
+
+    /**
+     * Returns a bool indicating whether the popup should have an info area
+     * at the bottom of the dialog. Subclasses may override. Default behavior is
+     * to have an info area if info text was provided at the time of creation.
+     *
+     * @return <code>true</code> if a title area should be created,
+     *         <code>false</code> if it should not.
+     */
+    protected bool hasInfoArea() {
+        return infoText !is null;
+    }
+
+    /**
+     * Creates the title and menu area. Subclasses typically need not override
+     * this method, but instead should use the constructor parameters
+     * <code>showDialogMenu</code> and <code>showPersistAction</code> to
+     * indicate whether a menu should be shown, and
+     * <code>createTitleControl</code> to to customize the presentation of the
+     * title.
+     *
+     * <p>
+     * If this method is overridden, the returned control's layout data must be
+     * an instance of <code>GridData</code>. This method must not modify the
+     * parent's layout.
+     *
+     * @param parent
+     *            The parent composite.
+     * @return The Control representing the title and menu area.
+     */
+    protected Control createTitleMenuArea(Composite parent) {
+
+        Composite titleAreaComposite = new Composite(parent, SWT.NONE);
+        POPUP_LAYOUT_FACTORY.copy().numColumns(2).applyTo(titleAreaComposite);
+        GridDataFactory.fillDefaults().align_(SWT.FILL, SWT.CENTER).grab(true,
+                false).applyTo(titleAreaComposite);
+
+        createTitleControl(titleAreaComposite);
+
+        if (showDialogMenu_) {
+            createDialogMenu(titleAreaComposite);
+        }
+        return titleAreaComposite;
+    }
+
+    /**
+     * Creates the control to be used to represent the dialog's title text.
+     * Subclasses may override if a different control is desired for
+     * representing the title text, or if something different than the title
+     * should be displayed in location where the title text typically is shown.
+     *
+     * <p>
+     * If this method is overridden, the returned control's layout data must be
+     * an instance of <code>GridData</code>. This method must not modify the
+     * parent's layout.
+     *
+     * @param parent
+     *            The parent composite.
+     * @return The Control representing the title area.
+     */
+    protected Control createTitleControl(Composite parent) {
+        titleLabel = new Label(parent, SWT.NONE);
+
+        GridDataFactory.fillDefaults().align_(SWT.FILL, SWT.CENTER).grab(true,
+                false).span(showDialogMenu_ ? 1 : 2, 1).applyTo(titleLabel);
+
+        if (titleText !is null) {
+            titleLabel.setText(titleText);
+        }
+        return titleLabel;
+    }
+
+    /**
+     * Creates the optional info text area. This method is only called if the
+     * <code>hasInfoArea()</code> method returns true. Subclasses typically
+     * need not override this method, but may do so.
+     *
+     * <p>
+     * If this method is overridden, the returned control's layout data must be
+     * an instance of <code>GridData</code>. This method must not modify the
+     * parent's layout.
+     *
+     *
+     * @param parent
+     *            The parent composite.
+     * @return The control representing the info text area.
+     *
+     * @see PopupDialog#hasInfoArea()
+     * @see PopupDialog#createTitleControl(Composite)
+     */
+    protected Control createInfoTextArea(Composite parent) {
+        // Status label
+        infoLabel = new Label(parent, SWT.RIGHT);
+        infoLabel.setText(infoText);
+
+        GridDataFactory.fillDefaults().grab(true, false).align_(SWT.FILL,
+                SWT.BEGINNING).applyTo(infoLabel);
+        infoLabel.setForeground(parent.getDisplay().getSystemColor(
+                SWT.COLOR_WIDGET_DARK_SHADOW));
+        return infoLabel;
+    }
+
+    /**
+     * Create a horizontal separator for the given parent.
+     *
+     * @param parent
+     *            The parent composite.
+     * @return The Control representing the horizontal separator.
+     */
+    private Control createHorizontalSeparator(Composite parent) {
+        Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL
+                | SWT.LINE_DOT);
+        GridDataFactory.fillDefaults().align_(SWT.FILL, SWT.CENTER).grab(true,
+                false).applyTo(separator);
+        return separator;
+    }
+
+    /**
+     * Create the dialog's menu for the move and resize actions.
+     *
+     * @param parent
+     *            The parent composite.
+     */
+    private void createDialogMenu(Composite parent) {
+
+        toolBar = new ToolBar(parent, SWT.FLAT);
+        ToolItem viewMenuButton = new ToolItem(toolBar, SWT.PUSH, 0);
+
+        GridDataFactory.fillDefaults().align_(SWT.END, SWT.CENTER).applyTo(
+                toolBar);
+        viewMenuButton.setImage(JFaceResources.getImage(POPUP_IMG_MENU));
+        viewMenuButton.setDisabledImage(JFaceResources
+                .getImage(POPUP_IMG_MENU_DISABLED));
+        viewMenuButton.setToolTipText(JFaceResources
+                .getString("PopupDialog.menuTooltip")); //$NON-NLS-1$
+        viewMenuButton.addSelectionListener(new class SelectionAdapter {
+            public void widgetSelected(SelectionEvent e) {
+                showDialogMenu();
+            }
+        });
+        // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=177183
+        toolBar.addMouseListener(new class MouseAdapter {
+            public void mouseDown(MouseEvent e) {
+                showDialogMenu();
+            }
+        });
+    }
+
+    /**
+     * Fill the dialog's menu. Subclasses may extend or override.
+     *
+     * @param dialogMenu
+     *            The dialog's menu.
+     */
+    protected void fillDialogMenu(IMenuManager dialogMenu) {
+        dialogMenu.add(new GroupMarker("SystemMenuStart")); //$NON-NLS-1$
+        dialogMenu.add(new MoveAction());
+        dialogMenu.add(new ResizeAction());
+        if (showPersistActions) {
+            if (isUsing34API) {
+                dialogMenu.add(new PersistLocationAction());
+                dialogMenu.add(new PersistSizeAction());
+            } else {
+                dialogMenu.add(new PersistBoundsAction());
+            }
+        }
+        dialogMenu.add(new Separator("SystemMenuEnd")); //$NON-NLS-1$
+    }
+
+    /**
+     * Perform the requested tracker action (resize or move).
+     *
+     * @param style
+     *            The track style (resize or move).
+     */
+    private void performTrackerAction(int style) {
+        Shell shell = getShell();
+        if (shell is null || shell.isDisposed()) {
+            return;
+        }
+
+        Tracker tracker = new Tracker(shell.getDisplay(), style);
+        tracker.setStippled(true);
+        Rectangle[] r = [ shell.getBounds() ];
+        tracker.setRectangles(r);
+
+        // Ignore any deactivate events caused by opening the tracker.
+        // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=120656
+        bool oldListenToDeactivate = listenToDeactivate;
+        listenToDeactivate = false;
+        if (tracker.open()) {
+            if (shell !is null && !shell.isDisposed()) {
+                shell.setBounds(tracker.getRectangles()[0]);
+            }
+        }
+        listenToDeactivate = oldListenToDeactivate;
+    }
+
+    /**
+     * Show the dialog's menu. This message has no effect if the receiver was
+     * not configured to show a menu. Clients may call this method in order to
+     * trigger the menu via keystrokes or other gestures. Subclasses typically
+     * do not override method.
+     */
+    protected void showDialogMenu() {
+        if (!showDialogMenu_) {
+            return;
+        }
+
+        if (menuManager is null) {
+            menuManager = new MenuManager();
+            fillDialogMenu(menuManager);
+        }
+        // Setting this flag works around a problem that remains on X only,
+        // whereby activating the menu deactivates our shell.
+        listenToDeactivate = !"gtk".equals(SWT.getPlatform()); //$NON-NLS-1$
+
+        Menu menu = menuManager.createContextMenu(getShell());
+        Rectangle bounds = toolBar.getBounds();
+        Point topLeft = new Point(bounds.x, bounds.y + bounds.height);
+        topLeft = getShell().toDisplay(topLeft);
+        menu.setLocation(topLeft.x, topLeft.y);
+        menu.setVisible(true);
+    }
+
+    /**
+     * Set the text to be shown in the popup's info area. This message has no
+     * effect if there was no info text supplied when the dialog first opened.
+     * Subclasses may override this method.
+     *
+     * @param text
+     *            the text to be shown when the info area is displayed.
+     *
+     */
+    protected void setInfoText(String text) {
+        infoText = text;
+        if (infoLabel !is null) {
+            infoLabel.setText(text);
+        }
+    }
+
+    /**
+     * Set the text to be shown in the popup's title area. This message has no
+     * effect if there was no title label specified when the dialog was
+     * originally opened. Subclasses may override this method.
+     *
+     * @param text
+     *            the text to be shown when the title area is displayed.
+     *
+     */
+    protected void setTitleText(String text) {
+        titleText = text;
+        if (titleLabel !is null) {
+            titleLabel.setText(text);
+        }
+    }
+
+    /**
+     * Return a bool indicating whether this dialog will persist its bounds.
+     * This value is initially set in the dialog's constructor, but can be
+     * modified if the persist bounds action is shown on the menu and the user
+     * has changed its value. Subclasses may override this method.
+     *
+     * @return <code>true</code> if the dialog's bounds will be persisted,
+     *         <code>false</code> if it will not.
+     *
+     * @deprecated As of 3.4, please use {@link #getPersistLocation()} or
+     *             {@link #getPersistSize()} to determine separately whether
+     *             size or location should be persisted.
+     */
+    protected bool getPersistBounds() {
+        return persistLocation && persistSize;
+    }
+
+    /**
+     * Return a bool indicating whether this dialog will persist its
+     * location. This value is initially set in the dialog's constructor, but
+     * can be modified if the persist location action is shown on the menu and
+     * the user has changed its value. Subclasses may override this method.
+     *
+     * @return <code>true</code> if the dialog's location will be persisted,
+     *         <code>false</code> if it will not.
+     *
+     * @see #getPersistSize()
+     * @since 3.4
+     */
+    protected bool getPersistLocation() {
+        return persistLocation;
+    }
+
+    /**
+     * Return a bool indicating whether this dialog will persist its size.
+     * This value is initially set in the dialog's constructor, but can be
+     * modified if the persist size action is shown on the menu and the user has
+     * changed its value. Subclasses may override this method.
+     *
+     * @return <code>true</code> if the dialog's size will be persisted,
+     *         <code>false</code> if it will not.
+     *
+     * @see #getPersistLocation()
+     * @since 3.4
+     */
+    protected bool getPersistSize() {
+        return persistSize;
+    }
+
+    /**
+     * Opens this window, creating it first if it has not yet been created.
+     * <p>
+     * This method is reimplemented for special configuration of PopupDialogs.
+     * It never blocks on open, immediately returning <code>OK</code> if the
+     * open is successful, or <code>CANCEL</code> if it is not. It provides
+     * framework hooks that allow subclasses to set the focus and tab order, and
+     * avoids the use of <code>shell.open()</code> in cases where the focus
+     * should not be given to the shell initially.
+     *
+     * @return the return code
+     *
+     * @see org.eclipse.jface.window.Window#open()
+     */
+    public override int open() {
+
+        Shell shell = getShell();
+        if (shell is null || shell.isDisposed()) {
+            shell = null;
+            // create the window
+            create();
+            shell = getShell();
+        }
+
+        // provide a hook for adjusting the bounds. This is only
+        // necessary when there is content driven sizing that must be
+        // adjusted each time the dialog is opened.
+        adjustBounds();
+
+        // limit the shell size to the display size
+        constrainShellSize();
+
+        // set up the tab order for the dialog
+        setTabOrder(cast(Composite) getContents());
+
+        // initialize flags for listening to deactivate
+        listenToDeactivate = false;
+        listenToParentDeactivate = false;
+
+        // open the window
+        if (takeFocusOnOpen) {
+            shell.open();
+            getFocusControl().setFocus();
+        } else {
+            shell.setVisible(true);
+        }
+
+        return OK;
+
+    }
+
+    /**
+     * Closes this window, disposes its shell, and removes this window from its
+     * window manager (if it has one).
+     * <p>
+     * This method is extended to save the dialog bounds and initialize widget
+     * state so that the widgets can be recreated if the dialog is reopened.
+     * This method may be extended (<code>super.close</code> must be called).
+     * </p>
+     *
+     * @return <code>true</code> if the window is (or was already) closed, and
+     *         <code>false</code> if it is still open
+     */
+    public override bool close() {
+        // If already closed, there is nothing to do.
+        // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=127505
+        if (getShell() is null || getShell().isDisposed()) {
+            return true;
+        }
+
+        saveDialogBounds(getShell());
+        // Widgets are about to be disposed, so null out any state
+        // related to them that was not handled in dispose listeners.
+        // We do this before disposal so that any received activate or
+        // deactivate events are duly ignored.
+        initializeWidgetState();
+
+        if (parentDeactivateListener !is null) {
+            getShell().getParent().removeListener(SWT.Deactivate,
+                    parentDeactivateListener);
+            parentDeactivateListener = null;
+        }
+
+        return super.close();
+    }
+
+    /**
+     * Gets the dialog settings that should be used for remembering the bounds
+     * of the dialog. Subclasses should override this method when they wish to
+     * persist the bounds of the dialog.
+     *
+     * @return settings the dialog settings used to store the dialog's location
+     *         and/or size, or <code>null</code> if the dialog's bounds should
+     *         never be stored.
+     */
+    protected IDialogSettings getDialogSettings() {
+        return null;
+    }
+
+    /**
+     * Saves the bounds of the shell in the appropriate dialog settings. The
+     * bounds are recorded relative to the parent shell, if there is one, or
+     * display coordinates if there is no parent shell. Subclasses typically
+     * need not override this method, but may extend it (calling
+     * <code>super.saveDialogBounds</code> if additional bounds information
+     * should be stored. Clients may also call this method to persist the bounds
+     * at times other than closing the dialog.
+     *
+     * @param shell
+     *            The shell whose bounds are to be stored
+     */
+    protected void saveDialogBounds(Shell shell) {
+        IDialogSettings settings = getDialogSettings();
+        if (settings !is null) {
+            Point shellLocation = shell.getLocation();
+            Point shellSize = shell.getSize();
+            Shell parent = getParentShell();
+            if (parent !is null) {
+                Point parentLocation = parent.getLocation();
+                shellLocation.x -= parentLocation.x;
+                shellLocation.y -= parentLocation.y;
+            }
+            String prefix = this.classinfo.name;
+            if (persistSize) {
+                settings.put(prefix ~ DIALOG_WIDTH, shellSize.x);
+                settings.put(prefix ~ DIALOG_HEIGHT, shellSize.y);
+            }
+            if (persistLocation) {
+                settings.put(prefix ~ DIALOG_ORIGIN_X, shellLocation.x);
+                settings.put(prefix ~ DIALOG_ORIGIN_Y, shellLocation.y);
+            }
+            if (showPersistActions && showDialogMenu_) {
+                settings.put(this.classinfo.name ~ DIALOG_USE_PERSISTED_SIZE,
+                        persistSize);
+                settings.put(this.classinfo.name
+                        ~ DIALOG_USE_PERSISTED_LOCATION, persistLocation);
+
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.window.Window#getInitialSize()
+     */
+    protected override Point getInitialSize() {
+        Point result = getDefaultSize();
+        if (persistSize) {
+            IDialogSettings settings = getDialogSettings();
+            if (settings !is null) {
+                try {
+                    int width = settings.getInt(this.classinfo.name
+                            ~ DIALOG_WIDTH);
+                    int height = settings.getInt(this.classinfo.name
+                            ~ DIALOG_HEIGHT);
+                    result = new Point(width, height);
+
+                } catch (NumberFormatException e) {
+                }
+            }
+        }
+        // No attempt is made to constrain the bounds. The default
+        // constraining behavior in Window will be used.
+        return result;
+    }
+
+    /**
+     * Return the default size to use for the shell. This default size is used
+     * if the dialog does not have any persisted size to restore. The default
+     * implementation returns the preferred size of the shell. Subclasses should
+     * override this method when an alternate default size is desired, rather
+     * than overriding {@link #getInitialSize()}.
+     *
+     * @return the initial size of the shell
+     *
+     * @see #getPersistSize()
+     * @since 3.4
+     */
+    protected Point getDefaultSize() {
+        return super.getInitialSize();
+    }
+
+    /**
+     * Returns the default location to use for the shell. This default location
+     * is used if the dialog does not have any persisted location to restore.
+     * The default implementation uses the location computed by
+     * {@link org.eclipse.jface.window.Window#getInitialLocation(Point)}.
+     * Subclasses should override this method when an alternate default location
+     * is desired, rather than overriding {@link #getInitialLocation(Point)}.
+     *
+     * @param initialSize
+     *            the initial size of the shell, as returned by
+     *            <code>getInitialSize</code>.
+     * @return the initial location of the shell
+     *
+     * @see #getPersistLocation()
+     * @since 3.4
+     */
+    protected Point getDefaultLocation(Point initialSize) {
+        return super.getInitialLocation(initialSize);
+    }
+
+    /**
+     * Adjust the bounds of the popup as necessary prior to opening the dialog.
+     * Default is to do nothing, which honors any bounds set directly by clients
+     * or those that have been saved in the dialog settings. Subclasses should
+     * override this method when there are bounds computations that must be
+     * checked each time the dialog is opened.
+     */
+    protected void adjustBounds() {
+    }
+
+    /**
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.window.Window#getInitialLocation(org.eclipse.swt.graphics.Point)
+     */
+    protected override Point getInitialLocation(Point initialSize) {
+        Point result = getDefaultLocation(initialSize);
+        if (persistLocation) {
+            IDialogSettings settings = getDialogSettings();
+            if (settings !is null) {
+                try {
+                    int x = settings.getInt(this.classinfo.name
+                            ~ DIALOG_ORIGIN_X);
+                    int y = settings.getInt(this.classinfo.name
+                            ~ DIALOG_ORIGIN_Y);
+                    result = new Point(x, y);
+                    // The coordinates were stored relative to the parent shell.
+                    // Convert to display coordinates.
+                    Shell parent = getParentShell();
+                    if (parent !is null) {
+                        Point parentLocation = parent.getLocation();
+                        result.x += parentLocation.x;
+                        result.y += parentLocation.y;
+                    }
+                } catch (NumberFormatException e) {
+                }
+            }
+        }
+        // No attempt is made to constrain the bounds. The default
+        // constraining behavior in Window will be used.
+        return result;
+    }
+
+    /**
+     * Apply any desired color to the specified composite and its children.
+     *
+     * @param composite
+     *            the contents composite
+     */
+    private void applyColors(Composite composite) {
+        // The getForeground() and getBackground() methods
+        // should not answer null, but IColorProvider clients
+        // are accustomed to null meaning use the default, so we guard
+        // against this assumption.
+        Color color = getForeground();
+        if (color is null)
+            color = getDefaultForeground();
+        applyForegroundColor(color, composite, getForegroundColorExclusions());
+        color = getBackground();
+        if (color is null)
+            color = getDefaultBackground();
+        applyBackgroundColor(color, composite, getBackgroundColorExclusions());
+    }
+
+    /**
+     * Get the foreground color that should be used for this popup. Subclasses
+     * may override.
+     *
+     * @return the foreground color to be used. Should not be <code>null</code>.
+     *
+     * @since 3.4
+     *
+     * @see #getForegroundColorExclusions()
+     */
+    protected Color getForeground() {
+        return getDefaultForeground();
+    }
+
+    /**
+     * Get the background color that should be used for this popup. Subclasses
+     * may override.
+     *
+     * @return the background color to be used. Should not be <code>null</code>.
+     *
+     * @since 3.4
+     *
+     * @see #getBackgroundColorExclusions()
+     */
+    protected Color getBackground() {
+        return getDefaultBackground();
+    }
+
+    /**
+     * Return the default foreground color used for popup dialogs.
+     *
+     * @return the default foreground color.
+     */
+    private Color getDefaultForeground() {
+        return getShell().getDisplay()
+                .getSystemColor(SWT.COLOR_INFO_FOREGROUND);
+    }
+
+    /**
+     * Return the default background color used for popup dialogs.
+     *
+     * @return the default background color
+     */
+    private Color getDefaultBackground() {
+        return getShell().getDisplay()
+                .getSystemColor(SWT.COLOR_INFO_BACKGROUND);
+    }
+
+    /**
+     * Apply any desired fonts to the specified composite and its children.
+     *
+     * @param composite
+     *            the contents composite
+     */
+    private void applyFonts(Composite composite) {
+        Dialog.applyDialogFont(composite);
+
+        if (titleLabel !is null) {
+            Font font = titleLabel.getFont();
+            FontData[] fontDatas = font.getFontData();
+            for (int i = 0; i < fontDatas.length; i++) {
+                fontDatas[i].setStyle(SWT.BOLD);
+            }
+            titleFont = new Font(titleLabel.getDisplay(), fontDatas);
+            titleLabel.setFont(titleFont);
+        }
+
+        if (infoLabel !is null) {
+            Font font = infoLabel.getFont();
+            FontData[] fontDatas = font.getFontData();
+            for (int i = 0; i < fontDatas.length; i++) {
+                fontDatas[i].setHeight(fontDatas[i].getHeight() * 9 / 10);
+            }
+            infoFont = new Font(infoLabel.getDisplay(), fontDatas);
+            infoLabel.setFont(infoFont);
+        }
+    }
+
+    /**
+     * Set the specified foreground color for the specified control and all of
+     * its children, except for those specified in the list of exclusions.
+     *
+     * @param color
+     *            the color to use as the foreground color
+     * @param control
+     *            the control whose color is to be changed
+     * @param exclusions
+     *            a list of controls who are to be excluded from getting their
+     *            color assigned
+     */
+    private void applyForegroundColor(Color color, Control control,
+            List exclusions) {
+        if (!exclusions.contains(control)) {
+            control.setForeground(color);
+        }
+        if ( auto comp = cast(Composite)control ) {
+            Control[] children = comp.getChildren();
+            for (int i = 0; i < children.length; i++) {
+                applyForegroundColor(color, children[i], exclusions);
+            }
+        }
+    }
+
+    /**
+     * Set the specified background color for the specified control and all of
+     * its children, except for those specified in the list of exclusions.
+     *
+     * @param color
+     *            the color to use as the background color
+     * @param control
+     *            the control whose color is to be changed
+     * @param exclusions
+     *            a list of controls who are to be excluded from getting their
+     *            color assigned
+     */
+    private void applyBackgroundColor(Color color, Control control,
+            List exclusions) {
+        if (!exclusions.contains(control)) {
+            control.setBackground(color);
+        }
+        if (auto comp = cast(Composite)control ) {
+            Control[] children = comp.getChildren();
+            for (int i = 0; i < children.length; i++) {
+                applyBackgroundColor(color, children[i], exclusions);
+            }
+        }
+    }
+
+    /**
+     * Set the specified foreground color for the specified control and all of
+     * its children. Subclasses may override this method, but typically do not.
+     * If a subclass wishes to exclude a particular control in its contents from
+     * getting the specified foreground color, it may instead override
+     * {@link #getForegroundColorExclusions()}.
+     *
+     * @param color
+     *            the color to use as the foreground color
+     * @param control
+     *            the control whose color is to be changed
+     * @see PopupDialog#getForegroundColorExclusions()
+     */
+    protected void applyForegroundColor(Color color, Control control) {
+        applyForegroundColor(color, control, getForegroundColorExclusions());
+    }
+
+    /**
+     * Set the specified background color for the specified control and all of
+     * its children. Subclasses may override this method, but typically do not.
+     * If a subclass wishes to exclude a particular control in its contents from
+     * getting the specified background color, it may instead override
+     * {@link #getBackgroundColorExclusions()}
+     *
+     * @param color
+     *            the color to use as the background color
+     * @param control
+     *            the control whose color is to be changed
+     * @see PopupDialog#getBackgroundColorExclusions()
+     */
+    protected void applyBackgroundColor(Color color, Control control) {
+        applyBackgroundColor(color, control, getBackgroundColorExclusions());
+    }
+
+    /**
+     * Return a list of controls which should never have their foreground color
+     * reset. Subclasses may extend this method, but should always call
+     * <code>super.getForegroundColorExclusions</code> to aggregate the list.
+     *
+     *
+     * @return the List of controls
+     */
+    protected List getForegroundColorExclusions() {
+        List list = new ArrayList(3);
+        if (infoLabel !is null) {
+            list.add(infoLabel);
+        }
+        if (titleSeparator !is null) {
+            list.add(titleSeparator);
+        }
+        if (infoSeparator !is null) {
+            list.add(infoSeparator);
+        }
+        return list;
+    }
+
+    /**
+     * Return a list of controls which should never have their background color
+     * reset. Subclasses may extend this method, but should always call
+     * <code>super.getBackgroundColorExclusions</code> to aggregate the list.
+     *
+     * @return the List of controls
+     */
+    protected List getBackgroundColorExclusions() {
+        List list = new ArrayList(2);
+        if (titleSeparator !is null) {
+            list.add(titleSeparator);
+        }
+        if (infoSeparator !is null) {
+            list.add(infoSeparator);
+        }
+        return list;
+    }
+
+    /**
+     * Initialize any state related to the widgetry that should be set up each
+     * time widgets are created.
+     */
+    private void initializeWidgetState() {
+        menuManager = null;
+        dialogArea = null;
+        titleLabel = null;
+        titleSeparator = null;
+        infoSeparator = null;
+        infoLabel = null;
+        toolBar = null;
+
+        // If the menu item for persisting bounds is displayed, use the stored
+        // value to determine whether any persisted bounds should be honored at
+        // all.
+        if (showDialogMenu_ && showPersistActions) {
+            IDialogSettings settings = getDialogSettings();
+            if (settings !is null) {
+                String key = this.classinfo.name ~ DIALOG_USE_PERSISTED_SIZE;
+                if (settings.get(key) !is null || !isUsing34API)
+                    persistSize = settings.getBoolean(key);
+                key = this.classinfo.name ~ DIALOG_USE_PERSISTED_LOCATION;
+                if (settings.get(key) !is null || !isUsing34API)
+                    persistLocation = settings.getBoolean(key);
+            }
+        }
+    }
+
+    private void migrateBoundsSetting() {
+        IDialogSettings settings = getDialogSettings();
+        if (settings is null)
+            return;
+
+        final String className = this.classinfo.name;
+
+        String key = className ~ DIALOG_USE_PERSISTED_BOUNDS;
+        String value = settings.get(key);
+        if (value is null || DIALOG_VALUE_MIGRATED_TO_34.equals(value))
+            return;
+
+        bool storeBounds = settings.getBoolean(key);
+        settings.put(className ~ DIALOG_USE_PERSISTED_LOCATION, storeBounds);
+        settings.put(className ~ DIALOG_USE_PERSISTED_SIZE, storeBounds);
+        settings.put(key, DIALOG_VALUE_MIGRATED_TO_34);
+    }
+
+    /**
+     * The dialog is being disposed. Dispose of any resources allocated.
+     *
+     */
+    private void handleDispose() {
+        if (infoFont !is null && !infoFont.isDisposed()) {
+            infoFont.dispose();
+        }
+        infoFont = null;
+        if (titleFont !is null && !titleFont.isDisposed()) {
+            titleFont.dispose();
+        }
+        titleFont = null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/ProgressIndicator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * 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
+ *     Mark Siegel <mark.siegel@businessobjects.com> - Fix for Bug 184533
+ *              [Progress] ProgressIndicator uses hardcoded style for ProgressBar
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.ProgressIndicator;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StackLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.ProgressBar;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A control for showing progress feedback for a long running operation. This
+ * control supports both determinate and indeterminate SWT progress bars. For
+ * indeterminate progress, we don't have to know the total amount of work in
+ * advance and no <code>worked</code> method needs to be called.
+ */
+public class ProgressIndicator : Composite {
+    private const static int PROGRESS_MAX = 1000; // value to use for max in
+
+    // progress bar
+    private bool animated = true;
+
+    private StackLayout layout_;
+
+    private ProgressBar determinateProgressBar;
+
+    private ProgressBar indeterminateProgressBar;
+
+    private double totalWork;
+
+    private double sumWorked;
+
+    /**
+     * Create a ProgressIndicator as a child under the given parent.
+     *
+     * @param parent
+     *            The widgets parent
+     */
+    public this(Composite parent) {
+        this(parent, SWT.NONE);
+    }
+
+    /**
+     * Create a ProgressIndicator as a child under the given parent.
+     *
+     * @param parent
+     *            The widgets parent
+     * @param style the SWT style constants for progress monitors created
+     *  by the receiver.
+     * @since 3.4
+     */
+    public this(Composite parent, int style) {
+        super(parent, SWT.NULL);
+
+         // Enforce horizontal only if vertical isn't set
+        if ((style & SWT.VERTICAL) is 0)
+            style |= SWT.HORIZONTAL;
+
+        determinateProgressBar = new ProgressBar(this, style);
+        indeterminateProgressBar = new ProgressBar(this, style
+                | SWT.INDETERMINATE);
+        layout_ = new StackLayout();
+        setLayout(layout_);
+    }
+
+    /**
+     * Initialize the progress bar to be animated.
+     */
+    public void beginAnimatedTask() {
+        done();
+        layout_.topControl = indeterminateProgressBar;
+        layout();
+        animated = true;
+    }
+
+    /**
+     * Initialize the progress bar.
+     *
+     * @param max
+     *            The maximum value.
+     */
+    public void beginTask(int max) {
+        done();
+        this.totalWork = max;
+        this.sumWorked = 0;
+        determinateProgressBar.setMinimum(0);
+        determinateProgressBar.setMaximum(PROGRESS_MAX);
+        determinateProgressBar.setSelection(0);
+        layout_.topControl = determinateProgressBar;
+        layout();
+        animated = false;
+    }
+
+    /**
+     * Progress is done.
+     */
+    public void done() {
+        if (!animated) {
+            determinateProgressBar.setMinimum(0);
+            determinateProgressBar.setMaximum(0);
+            determinateProgressBar.setSelection(0);
+        }
+        layout_.topControl = null;
+        layout();
+    }
+
+    /**
+     * Moves the progress indicator to the end.
+     */
+    public void sendRemainingWork() {
+        worked(totalWork - sumWorked);
+    }
+
+    /**
+     * Moves the progress indicator by the given amount of work units
+     * @param work the amount of work to increment by.
+     */
+    public void worked(double work) {
+        if (work is 0 || animated) {
+            return;
+        }
+        sumWorked += work;
+        if (sumWorked > totalWork) {
+            sumWorked = totalWork;
+        }
+        if (sumWorked < 0) {
+            sumWorked = 0;
+        }
+        int value = cast(int) (sumWorked / totalWork * PROGRESS_MAX);
+        if (determinateProgressBar.getSelection() < value) {
+            determinateProgressBar.setSelection(value);
+        }
+    }
+
+    /**
+     * Show the receiver as showing an error.
+     * @since 3.4
+     */
+    public void showError() {
+        determinateProgressBar.setState(SWT.ERROR);
+        indeterminateProgressBar.setState(SWT.ERROR);
+    }
+
+    /**
+     * Show the receiver as being paused.
+     * @since 3.4
+     */
+    public void showPaused() {
+        determinateProgressBar.setState(SWT.PAUSED);
+        indeterminateProgressBar.setState(SWT.PAUSED);
+    }
+
+    /**
+     * Reset the progress bar to it's normal style.
+     * @since 3.4
+     */
+    public void showNormal() {
+        determinateProgressBar.setState(SWT.NORMAL);
+        indeterminateProgressBar.setState(SWT.NORMAL);
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/ProgressMonitorDialog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,701 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.dialogs.ProgressMonitorDialog;
+
+import org.eclipse.jface.dialogs.IconAndMessageDialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.ProgressIndicator;
+
+// import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IProgressMonitorWithBlocking;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.operation.IRunnableContext;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.operation.ModalContext;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A modal dialog that displays progress during a long running operation.
+ * <p>
+ * This concrete dialog class can be instantiated as is, or further subclassed
+ * as required.
+ * </p>
+ * <p>
+ * Typical usage is:
+ *
+ * <pre>
+ *
+ *
+ *    try {
+ *       IRunnableWithProgress op = ...;
+ *       new ProgressMonitorDialog(activeShell).run(true, true, op);
+ *    } catch (InvocationTargetException e) {
+ *       // handle exception
+ *    } catch (InterruptedException e) {
+ *       // handle cancelation
+ *    }
+ *
+ *
+ * </pre>
+ *
+ * </p>
+ * <p>
+ * Note that the ProgressMonitorDialog is not intended to be used with multiple
+ * runnables - this dialog should be discarded after completion of one
+ * IRunnableWithProgress and a new one instantiated for use by a second or
+ * sebsequent IRunnableWithProgress to ensure proper initialization.
+ * </p>
+ * <p>
+ * Note that not forking the process will result in it running in the UI which
+ * may starve the UI. The most obvious symptom of this problem is non
+ * responsiveness of the cancel button. If you are running within the UI Thread
+ * you should do the bulk of your work in another Thread to prevent starvation.
+ * It is recommended that fork is set to true in most cases.
+ * </p>
+ */
+public class ProgressMonitorDialog : IconAndMessageDialog,
+        IRunnableContext {
+    /**
+     * Name to use for task when normal task name is empty string.
+     */
+    private static String DEFAULT_TASKNAME;
+
+    static this() {
+        DEFAULT_TASKNAME = JFaceResources
+            .getString("ProgressMonitorDialog.message"); //$NON-NLS-1$
+    }
+    /**
+     * Constants for label and monitor size
+     */
+    private static int LABEL_DLUS = 21;
+
+    private static int BAR_DLUS = 9;
+
+    /**
+     * The progress indicator control.
+     */
+    protected ProgressIndicator progressIndicator;
+
+    /**
+     * The label control for the task. Kept for backwards compatibility.
+     */
+    protected Label taskLabel;
+
+    /**
+     * The label control for the subtask.
+     */
+    protected Label subTaskLabel;
+
+    /**
+     * The Cancel button control.
+     */
+    protected Button cancel;
+
+    /**
+     * Indicates whether the Cancel button is to be shown.
+     */
+    protected bool operationCancelableState = false;
+
+    /**
+     * Indicates whether the Cancel button is to be enabled.
+     */
+    protected bool enableCancelButton;
+
+    /**
+     * The progress monitor.
+     */
+    private ProgressMonitor progressMonitor;
+
+    /**
+     * The name of the current task (used by ProgressMonitor).
+     */
+    private String task;
+
+    /**
+     * The nesting depth of currently running runnables.
+     */
+    private int nestingDepth;
+
+    /**
+     * The cursor used in the cancel button;
+     */
+    protected Cursor arrowCursor;
+
+    /**
+     * The cursor used in the shell;
+     */
+    private Cursor waitCursor;
+
+    /**
+     * Flag indicating whether to open or merely create the dialog before run.
+     */
+    private bool openOnRun = true;
+
+    /**
+     * Internal progress monitor implementation.
+     */
+    private class ProgressMonitor : IProgressMonitorWithBlocking {
+        private String fSubTask = "";//$NON-NLS-1$
+
+        private bool fIsCanceled;
+
+        /**
+         * is the process forked
+         */
+        protected bool forked = false;
+
+        /**
+         * is locked
+         */
+        protected bool locked = false;
+
+        public void beginTask(String name, int totalWork) {
+            if (progressIndicator.isDisposed()) {
+                return;
+            }
+            if (name is null) {
+                task = "";//$NON-NLS-1$
+            } else {
+                task = name;
+            }
+            String s = task;
+            if (s.length <= 0) {
+                s = DEFAULT_TASKNAME;
+            }
+            setMessage(s, false);
+            if (!forked) {
+                update();
+            }
+            if (totalWork is UNKNOWN) {
+                progressIndicator.beginAnimatedTask();
+            } else {
+                progressIndicator.beginTask(totalWork);
+            }
+        }
+
+        public void done() {
+            if (!progressIndicator.isDisposed()) {
+                progressIndicator.sendRemainingWork();
+                progressIndicator.done();
+            }
+        }
+
+        public void setTaskName(String name) {
+            if (name is null) {
+                task = "";//$NON-NLS-1$
+            } else {
+                task = name;
+            }
+            String s = task;
+            if (s.length <= 0) {
+                s = DEFAULT_TASKNAME;
+            }
+            setMessage(s, false);
+            if (!forked) {
+                update();
+            }
+        }
+
+        public bool isCanceled() {
+            return fIsCanceled;
+        }
+
+        public void setCanceled(bool b) {
+            fIsCanceled = b;
+            if (locked) {
+                clearBlocked();
+            }
+        }
+
+        public void subTask(String name) {
+            if (subTaskLabel.isDisposed()) {
+                return;
+            }
+            if (name is null) {
+                fSubTask = "";//$NON-NLS-1$
+            } else {
+                fSubTask = name;
+            }
+            subTaskLabel.setText(shortenText(fSubTask, subTaskLabel));
+            if (!forked) {
+                subTaskLabel.update();
+            }
+        }
+
+        public void worked(int work) {
+            internalWorked(work);
+        }
+
+        public void internalWorked(double work) {
+            if (!progressIndicator.isDisposed()) {
+                progressIndicator.worked(work);
+            }
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#clearBlocked()
+         */
+        public void clearBlocked() {
+            if (getShell().isDisposed())
+                return;
+            locked = false;
+            updateForClearBlocked();
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#setBlocked(org.eclipse.core.runtime.IStatus)
+         */
+        public void setBlocked(IStatus reason) {
+            if (getShell().isDisposed())
+                return;
+            locked = true;
+            updateForSetBlocked(reason);
+        }
+    }
+
+    /**
+     * Clear blocked state from the receiver.
+     */
+    protected void updateForClearBlocked() {
+        progressIndicator.showNormal();
+        setMessage(task, true);
+        imageLabel.setImage(getImage());
+
+    }
+
+    /**
+     * Set blocked state from the receiver.
+     *
+     * @param reason
+     *            IStatus that gives the details
+     */
+    protected void updateForSetBlocked(IStatus reason) {
+        progressIndicator.showPaused();
+        setMessage(reason.getMessage(), true);
+        imageLabel.setImage(getImage());
+
+    }
+
+    /**
+     * Creates a progress monitor dialog under the given shell. The dialog has a
+     * standard title and no image. <code>open</code> is non-blocking.
+     *
+     * @param parent
+     *            the parent shell, or <code>null</code> to create a top-level
+     *            shell
+     */
+    public this(Shell parent) {
+        progressMonitor = new ProgressMonitor();
+        super(parent);
+        // no close button on the shell style
+        if (isResizable()) {
+            setShellStyle(getDefaultOrientation() | SWT.BORDER | SWT.TITLE
+                    | SWT.APPLICATION_MODAL | SWT.RESIZE | SWT.MAX);
+        } else {
+            setShellStyle(getDefaultOrientation() | SWT.BORDER | SWT.TITLE
+                    | SWT.APPLICATION_MODAL);
+        }
+        setBlockOnOpen(false);
+    }
+
+    /**
+     * Enables the cancel button (asynchronously).
+     *
+     * @param b
+     *            The state to set the button to.
+     */
+    private void asyncSetOperationCancelButtonEnabled(bool b) {
+        if (getShell() !is null) {
+            getShell().getDisplay().asyncExec(new class Runnable {
+                bool b_;
+                this(){ b_=b; }
+                public void run() {
+                    setOperationCancelButtonEnabled(b);
+                }
+            });
+        }
+    }
+
+    /**
+     * The cancel button has been pressed.
+     *
+     * @since 3.0
+     */
+    protected override void cancelPressed() {
+        // NOTE: this was previously done from a listener installed on the
+        // cancel button. On GTK, the listener installed by
+        // Dialog.createButton is called first and this was throwing an
+        // exception because the cancel button was already disposed
+        cancel.setEnabled(false);
+        progressMonitor.setCanceled(true);
+        super.cancelPressed();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Window.
+     */
+    /**
+     * The <code>ProgressMonitorDialog</code> implementation of this method
+     * only closes the dialog if there are no currently running runnables.
+     */
+    public override bool close() {
+        if (getNestingDepth() <= 0) {
+            clearCursors();
+            return super.close();
+        }
+        return false;
+    }
+
+    /**
+     * Clear the cursors in the dialog.
+     *
+     * @since 3.0
+     */
+    protected void clearCursors() {
+        if (cancel !is null && !cancel.isDisposed()) {
+            cancel.setCursor(null);
+        }
+        Shell shell = getShell();
+        if (shell !is null && !shell.isDisposed()) {
+            shell.setCursor(null);
+        }
+        if (arrowCursor !is null) {
+            arrowCursor.dispose();
+        }
+        if (waitCursor !is null) {
+            waitCursor.dispose();
+        }
+        arrowCursor = null;
+        waitCursor = null;
+    }
+
+    /*
+     * (non-Javadoc) Method declared in Window.
+     */
+    protected override void configureShell(Shell shell) {
+        super.configureShell(shell);
+        shell.setText(JFaceResources.getString("ProgressMonitorDialog.title")); //$NON-NLS-1$
+        if (waitCursor is null) {
+            waitCursor = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT);
+        }
+        shell.setCursor(waitCursor);
+        // Add a listener to set the message properly when the dialog becomes
+        // visible
+        // SWT FIXME: workaround for DMD 1.028 anon class problem
+        shell.addListener(SWT.Show, dgListener( &_handleEvent, shell));
+    }
+
+    // SWT FIXME: workaround for DMD 1.028 anon class problem
+    private void _handleEvent(Event event, Shell shell_) {
+        // We need to async the message update since the Show precedes
+        // visibility
+        shell_.getDisplay().asyncExec(dgRunnable( &_setMessage ));
+    }
+    // SWT FIXME: workaround for DMD 1.028 anon class problem
+    private void _setMessage() {
+        setMessage(message, true);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Dialog.
+     */
+    protected override void createButtonsForButtonBar(Composite parent) {
+        // cancel button
+        createCancelButton(parent);
+    }
+
+    /**
+     * Creates the cancel button.
+     *
+     * @param parent
+     *            the parent composite
+     * @since 3.0
+     */
+    protected void createCancelButton(Composite parent) {
+        cancel = createButton(parent, IDialogConstants.CANCEL_ID,
+                IDialogConstants.CANCEL_LABEL, true);
+        if (arrowCursor is null) {
+            arrowCursor = new Cursor(cancel.getDisplay(), SWT.CURSOR_ARROW);
+        }
+        cancel.setCursor(arrowCursor);
+        setOperationCancelButtonEnabled(enableCancelButton);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Dialog.
+     */
+    protected override Control createDialogArea(Composite parent) {
+        setMessage(DEFAULT_TASKNAME, false);
+        createMessageArea(parent);
+        // Only set for backwards compatibility
+        taskLabel = messageLabel;
+        // progress indicator
+        progressIndicator = new ProgressIndicator(parent);
+        GridData gd = new GridData();
+        gd.heightHint = convertVerticalDLUsToPixels(BAR_DLUS);
+        gd.horizontalAlignment = GridData.FILL;
+        gd.grabExcessHorizontalSpace = true;
+        gd.horizontalSpan = 2;
+        progressIndicator.setLayoutData(gd);
+        // label showing current task
+        subTaskLabel = new Label(parent, SWT.LEFT | SWT.WRAP);
+        gd = new GridData(GridData.FILL_HORIZONTAL);
+        gd.heightHint = convertVerticalDLUsToPixels(LABEL_DLUS);
+        gd.horizontalSpan = 2;
+        subTaskLabel.setLayoutData(gd);
+        subTaskLabel.setFont(parent.getFont());
+        return parent;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.window.Window#getInitialSize()
+     */
+    protected override Point getInitialSize() {
+        Point calculatedSize = super.getInitialSize();
+        if (calculatedSize.x < 450) {
+            calculatedSize.x = 450;
+        }
+        return calculatedSize;
+    }
+
+    /**
+     * Returns the progress monitor to use for operations run in this progress
+     * dialog.
+     *
+     * @return the progress monitor
+     */
+    public IProgressMonitor getProgressMonitor() {
+        return progressMonitor;
+    }
+
+    /**
+     * This implementation of IRunnableContext#run(bool, bool,
+     * IRunnableWithProgress) runs the given <code>IRunnableWithProgress</code>
+     * using the progress monitor for this progress dialog and blocks until the
+     * runnable has been run, regardless of the value of <code>fork</code>.
+     * The dialog is opened before the runnable is run, and closed after it
+     * completes. It is recommended that <code>fork</code> is set to true in
+     * most cases. If <code>fork</code> is set to <code>false</code>, the
+     * runnable will run in the UI thread and it is the runnable's
+     * responsibility to call <code>Display.readAndDispatch()</code> to ensure
+     * UI responsiveness.
+     */
+    public void run(bool fork, bool cancelable,
+            IRunnableWithProgress runnable) {
+        setCancelable(cancelable);
+        try {
+            aboutToRun();
+            // Let the progress monitor know if they need to update in UI Thread
+            progressMonitor.forked = fork;
+            ModalContext.run(runnable, fork, getProgressMonitor(), getShell()
+                    .getDisplay());
+        } finally {
+            finishedRun();
+        }
+    }
+
+    /**
+     * Returns whether the dialog should be opened before the operation is run.
+     * Defaults to <code>true</code>
+     *
+     * @return <code>true</code> to open the dialog before run,
+     *         <code>false</code> to only create the dialog, but not open it
+     * @since 3.0
+     */
+    public bool getOpenOnRun() {
+        return openOnRun;
+    }
+
+    /**
+     * Sets whether the dialog should be opened before the operation is run.
+     * NOTE: Setting this to false and not forking a process may starve any
+     * asyncExec that tries to open the dialog later.
+     *
+     * @param openOnRun
+     *            <code>true</code> to open the dialog before run,
+     *            <code>false</code> to only create the dialog, but not open
+     *            it
+     * @since 3.0
+     */
+    public void setOpenOnRun(bool openOnRun) {
+        this.openOnRun = openOnRun;
+    }
+
+    /**
+     * Returns the nesting depth of running operations.
+     *
+     * @return the nesting depth of running operations
+     * @since 3.0
+     */
+    protected int getNestingDepth() {
+        return nestingDepth;
+    }
+
+    /**
+     * Increments the nesting depth of running operations.
+     *
+     * @since 3.0
+     */
+    protected void incrementNestingDepth() {
+        nestingDepth++;
+    }
+
+    /**
+     * Decrements the nesting depth of running operations.
+     *
+     * @since 3.0
+     *
+     */
+    protected void decrementNestingDepth() {
+        nestingDepth--;
+    }
+
+    /**
+     * Called just before the operation is run. Default behaviour is to open or
+     * create the dialog, based on the setting of <code>getOpenOnRun</code>,
+     * and increment the nesting depth.
+     *
+     * @since 3.0
+     */
+    protected void aboutToRun() {
+        if (getOpenOnRun()) {
+            open();
+        } else {
+            create();
+        }
+        incrementNestingDepth();
+    }
+
+    /**
+     * Called just after the operation is run. Default behaviour is to decrement
+     * the nesting depth, and close the dialog.
+     *
+     * @since 3.0
+     */
+    protected void finishedRun() {
+        decrementNestingDepth();
+        close();
+    }
+
+    /**
+     * Sets whether the progress dialog is cancelable or not.
+     *
+     * @param cancelable
+     *            <code>true</code> if the end user can cancel this progress
+     *            dialog, and <code>false</code> if it cannot be canceled
+     */
+    public void setCancelable(bool cancelable) {
+        if (cancel is null) {
+            enableCancelButton = cancelable;
+        } else {
+            asyncSetOperationCancelButtonEnabled(cancelable);
+        }
+    }
+
+    /**
+     * Helper to enable/disable Cancel button for this dialog.
+     *
+     * @param b
+     *            <code>true</code> to enable the cancel button, and
+     *            <code>false</code> to disable it
+     * @since 3.0
+     */
+    protected void setOperationCancelButtonEnabled(bool b) {
+        operationCancelableState = b;
+        cancel.setEnabled(b);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.IconAndMessageDialog#getImage()
+     */
+    protected override Image getImage() {
+        return getInfoImage();
+    }
+
+    /**
+     * Set the message in the message label.
+     *
+     * @param messageString
+     *            The string for the new message.
+     * @param force
+     *            If force is true then always set the message text.
+     */
+    private void setMessage(String messageString, bool force) {
+        // must not set null text in a label
+        message = messageString is null ? "" : messageString; //$NON-NLS-1$
+        if (messageLabel is null || messageLabel.isDisposed()) {
+            return;
+        }
+        if (force || messageLabel.isVisible()) {
+            messageLabel.setToolTipText(message);
+            messageLabel.setText(shortenText(message, messageLabel));
+        }
+    }
+
+    /**
+     * Update the message label. Required if the monitor is forked.
+     */
+    private void update() {
+        if (messageLabel is null || messageLabel.isDisposed()) {
+            return;
+        }
+        messageLabel.update();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.window.Window#open()
+     */
+    public override int open() {
+        // Check to be sure it is not already done. If it is just return OK.
+        if (!getOpenOnRun()) {
+            if (getNestingDepth() is 0) {
+                return OK;
+            }
+        }
+        int result = super.open();
+        // update message label just in case beginTask() has been invoked
+        // already
+        if (task is null || task.length is 0)
+            setMessage(DEFAULT_TASKNAME, true);
+        else
+            setMessage(task, true);
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/StatusDialog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,307 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.StatusDialog;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.TrayDialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.JFaceColors;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.Policy;
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * An abstract base class for dialogs with a status bar and OK/CANCEL buttons.
+ * The status message is specified in an IStatus which can be of severity ERROR,
+ * WARNING, INFO or OK. The OK button is enabled or disabled depending on the
+ * status.
+ *
+ * @since 3.1
+ */
+public abstract class StatusDialog : TrayDialog {
+
+    private Button fOkButton;
+
+    private MessageLine fStatusLine;
+
+    private IStatus fLastStatus;
+
+    private String fTitle;
+
+    private Image fImage;
+
+    private bool fStatusLineAboveButtons = true;
+
+    /**
+     * A message line displaying a status.
+     */
+    private class MessageLine : CLabel {
+
+        private Color fNormalMsgAreaBackground;
+
+        /**
+         * Creates a new message line as a child of the given parent.
+         *
+         * @param parent
+         */
+        public this(Composite parent) {
+            this(parent, SWT.LEFT);
+        }
+
+        /**
+         * Creates a new message line as a child of the parent and with the
+         * given SWT stylebits.
+         *
+         * @param parent
+         * @param style
+         */
+        public this(Composite parent, int style) {
+            super(parent, style);
+            fNormalMsgAreaBackground = getBackground();
+        }
+
+        /**
+         * Find an image assocated with the status.
+         *
+         * @param status
+         * @return Image
+         */
+        private Image findImage(IStatus status) {
+            if (status.isOK()) {
+                return null;
+            } else if (status.matches(IStatus.ERROR)) {
+                return JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_ERROR);
+            } else if (status.matches(IStatus.WARNING)) {
+                return JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_WARNING);
+            } else if (status.matches(IStatus.INFO)) {
+                return JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_INFO);
+            }
+            return null;
+        }
+
+        /**
+         * Sets the message and image to the given status.
+         *
+         * @param status
+         *            IStatus or <code>null</code>. <code>null</code> will
+         *            set the empty text and no image.
+         */
+        public void setErrorStatus(IStatus status) {
+            if (status !is null && !status.isOK()) {
+                String message = status.getMessage();
+                if (message !is null && message.length > 0) {
+                    setText(message);
+                    // unqualified call of setImage is too ambiguous for
+                    // Foundation 1.0 compiler
+                    // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=140576
+                    this.outer.setImage(findImage(status));
+                    setBackground(JFaceColors.getErrorBackground(getDisplay()));
+                    return;
+                }
+            }
+            setText(""); //$NON-NLS-1$
+            // unqualified call of setImage is too ambiguous for Foundation 1.0
+            // compiler
+            // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=140576
+            this.outer.setImage(null);
+            setBackground(fNormalMsgAreaBackground);
+        }
+    }
+
+    /**
+     * Creates an instance of a status dialog.
+     *
+     * @param parent
+     *            the parent Shell of the dialog
+     */
+    public this(Shell parent) {
+        super(parent);
+        fLastStatus = new Status(IStatus.OK, Policy.JFACE, IStatus.OK,
+                Util.ZERO_LENGTH_STRING, null);
+    }
+
+    /**
+     * Specifies whether status line appears to the left of the buttons
+     * (default) or above them.
+     *
+     * @param aboveButtons
+     *            if <code>true</code> status line is placed above buttons; if
+     *            <code>false</code> to the right
+     */
+    public void setStatusLineAboveButtons(bool aboveButtons) {
+        fStatusLineAboveButtons = aboveButtons;
+    }
+
+    /**
+     * Update the dialog's status line to reflect the given status. It is safe
+     * to call this method before the dialog has been opened.
+     *
+     * @param status
+     *            the status to set
+     */
+    protected void updateStatus(IStatus status) {
+        fLastStatus = status;
+        if (fStatusLine !is null && !fStatusLine.isDisposed()) {
+            updateButtonsEnableState(status);
+            fStatusLine.setErrorStatus(status);
+        }
+    }
+
+    /**
+     * Returns the last status.
+     *
+     * @return IStatus
+     */
+    public IStatus getStatus() {
+        return fLastStatus;
+    }
+
+    /**
+     * Updates the status of the ok button to reflect the given status.
+     * Subclasses may override this method to update additional buttons.
+     *
+     * @param status
+     *            the status.
+     */
+    protected void updateButtonsEnableState(IStatus status) {
+        if (fOkButton !is null && !fOkButton.isDisposed()) {
+            fOkButton.setEnabled(!status.matches(IStatus.ERROR));
+        }
+    }
+
+    /*
+     * @see Window#create(Shell)
+     */
+    protected override void configureShell(Shell shell) {
+        super.configureShell(shell);
+        if (fTitle !is null) {
+            shell.setText(fTitle);
+        }
+    }
+
+    /*
+     * @see Window#create()
+     */
+    public override void create() {
+        super.create();
+        if (fLastStatus !is null) {
+            // policy: dialogs are not allowed to come up with an error message
+            if (fLastStatus.matches(IStatus.ERROR)) {
+                // remove the message
+                fLastStatus = new Status(IStatus.ERROR,
+                        fLastStatus.getPlugin(), fLastStatus.getCode(),
+                        "", fLastStatus.getException()); //$NON-NLS-1$
+            }
+            updateStatus(fLastStatus);
+        }
+    }
+
+    /*
+     * @see Dialog#createButtonsForButtonBar(Composite)
+     */
+    protected override void createButtonsForButtonBar(Composite parent) {
+        fOkButton = createButton(parent, IDialogConstants.OK_ID,
+                IDialogConstants.OK_LABEL, true);
+        createButton(parent, IDialogConstants.CANCEL_ID,
+                IDialogConstants.CANCEL_LABEL, false);
+    }
+
+    /*
+     * @see Dialog#createButtonBar(Composite)
+     */
+    protected override Control createButtonBar(Composite parent) {
+        Composite composite = new Composite(parent, SWT.NULL);
+        GridLayout layout = new GridLayout();
+
+        if (fStatusLineAboveButtons) {
+            layout.numColumns = 1;
+        } else {
+            layout.numColumns = 2;
+        }
+
+        layout.marginHeight = 0;
+        layout.marginLeft = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
+        layout.marginWidth = 0;
+        composite.setLayout(layout);
+        composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+        if (!fStatusLineAboveButtons && isHelpAvailable()) {
+            createHelpControl(composite);
+        }
+        fStatusLine = new MessageLine(composite);
+        fStatusLine.setAlignment(SWT.LEFT);
+        GridData statusData = new GridData(GridData.FILL_HORIZONTAL);
+        fStatusLine.setErrorStatus(null);
+        if (fStatusLineAboveButtons && isHelpAvailable()) {
+            statusData.horizontalSpan = 2;
+            createHelpControl(composite);
+        }
+        fStatusLine.setLayoutData(statusData);
+        applyDialogFont(composite);
+
+        /*
+         * Create the rest of the button bar, but tell it not to create a help
+         * button (we've already created it).
+         */
+        bool helpAvailable = isHelpAvailable();
+        setHelpAvailable(false);
+        super.createButtonBar(composite);
+        setHelpAvailable(helpAvailable);
+        return composite;
+    }
+
+    /**
+     * Sets the title for this dialog.
+     *
+     * @param title
+     *            the title.
+     */
+    public void setTitle(String title) {
+        fTitle = title !is null ? title : ""; //$NON-NLS-1$
+        Shell shell = getShell();
+        if ((shell !is null) && !shell.isDisposed()) {
+            shell.setText(fTitle);
+        }
+    }
+
+    /**
+     * Sets the image for this dialog.
+     *
+     * @param image
+     *            the image.
+     */
+    public void setImage(Image image) {
+        fImage = image;
+        Shell shell = getShell();
+        if ((shell !is null) && !shell.isDisposed()) {
+            shell.setImage(fImage);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/TitleAreaDialog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,637 @@
+/*******************************************************************************
+ * 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
+ *     Konstantin Scheglov <scheglov_ke@nlmk.ru > - Fix for bug 41172
+ *     [Dialogs] Bug with Image in TitleAreaDialog
+ *     Sebastian Davids <sdavids@gmx.de> - Fix for bug 82064
+ *     [Dialogs] TitleAreaDialog#setTitleImage cannot be called before open()
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.dialogs.TitleAreaDialog;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TrayDialog;
+import org.eclipse.jface.dialogs.IMessageProvider;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.jface.resource.JFaceColors;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A dialog that has a title area for displaying a title and an image as well as
+ * a common area for displaying a description, a message, or an error message.
+ * <p>
+ * This dialog class may be subclassed.
+ */
+public class TitleAreaDialog : TrayDialog {
+    /**
+     * Image registry key for error message image.
+     */
+    public static const String DLG_IMG_TITLE_ERROR = DLG_IMG_MESSAGE_ERROR;
+
+    /**
+     * Image registry key for banner image (value
+     * <code>"dialog_title_banner_image"</code>).
+     */
+    public static const String DLG_IMG_TITLE_BANNER = "dialog_title_banner_image";//$NON-NLS-1$
+
+    /**
+     * Message type constant used to display an info icon with the message.
+     *
+     * @since 2.0
+     * @deprecated
+     */
+    public const static String INFO_MESSAGE = "INFO_MESSAGE"; //$NON-NLS-1$
+
+    /**
+     * Message type constant used to display a warning icon with the message.
+     *
+     * @since 2.0
+     * @deprecated
+     */
+    public const static String WARNING_MESSAGE = "WARNING_MESSAGE"; //$NON-NLS-1$
+
+    // Space between an image and a label
+    private static const int H_GAP_IMAGE = 5;
+
+    // Minimum dialog width (in dialog units)
+    private static const int MIN_DIALOG_WIDTH = 350;
+
+    // Minimum dialog height (in dialog units)
+    private static const int MIN_DIALOG_HEIGHT = 150;
+
+    private Label titleLabel;
+
+    private Label titleImageLabel;
+
+    private Label bottomFillerLabel;
+
+    private Label leftFillerLabel;
+
+    private RGB titleAreaRGB;
+
+    Color titleAreaColor;
+
+    private String message = ""; //$NON-NLS-1$
+
+    private String errorMessage;
+
+    private Text messageLabel;
+
+    private Composite workArea;
+
+    private Label messageImageLabel;
+
+    private Image messageImage;
+
+    private bool showingError = false;
+
+    private bool titleImageLargest = true;
+
+    private int messageLabelHeight;
+
+    private Image titleAreaImage;
+
+    /**
+     * Instantiate a new title area dialog.
+     *
+     * @param parentShell
+     *            the parent SWT shell
+     */
+    public this(Shell parentShell) {
+        super(parentShell);
+    }
+
+    /*
+     * @see Dialog.createContents(Composite)
+     */
+    protected override Control createContents(Composite parent) {
+        // create the overall composite
+        Composite contents = new Composite(parent, SWT.NONE);
+        contents.setLayoutData(new GridData(GridData.FILL_BOTH));
+        // initialize the dialog units
+        initializeDialogUnits(contents);
+        FormLayout layout = new FormLayout();
+        contents.setLayout(layout);
+        // Now create a work area for the rest of the dialog
+        workArea = new Composite(contents, SWT.NONE);
+        GridLayout childLayout = new GridLayout();
+        childLayout.marginHeight = 0;
+        childLayout.marginWidth = 0;
+        childLayout.verticalSpacing = 0;
+        workArea.setLayout(childLayout);
+        Control top = createTitleArea(contents);
+        resetWorkAreaAttachments(top);
+        workArea.setFont(JFaceResources.getDialogFont());
+        // initialize the dialog units
+        initializeDialogUnits(workArea);
+        // create the dialog area and button bar
+        dialogArea = createDialogArea(workArea);
+        buttonBar = createButtonBar(workArea);
+        return contents;
+    }
+
+    /**
+     * Creates and returns the contents of the upper part of this dialog (above
+     * the button bar).
+     * <p>
+     * The <code>Dialog</code> implementation of this framework method creates
+     * and returns a new <code>Composite</code> with no margins and spacing.
+     * Subclasses should override.
+     * </p>
+     *
+     * @param parent
+     *            The parent composite to contain the dialog area
+     * @return the dialog area control
+     */
+    protected override Control createDialogArea(Composite parent) {
+        // create the top level composite for the dialog area
+        Composite composite = new Composite(parent, SWT.NONE);
+        GridLayout layout = new GridLayout();
+        layout.marginHeight = 0;
+        layout.marginWidth = 0;
+        layout.verticalSpacing = 0;
+        layout.horizontalSpacing = 0;
+        composite.setLayout(layout);
+        composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+        composite.setFont(parent.getFont());
+        // Build the separator line
+        Label titleBarSeparator = new Label(composite, SWT.HORIZONTAL
+                | SWT.SEPARATOR);
+        titleBarSeparator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        return composite;
+    }
+
+    /**
+     * Creates the dialog's title area.
+     *
+     * @param parent
+     *            the SWT parent for the title area widgets
+     * @return Control with the highest x axis value.
+     */
+    private Control createTitleArea(Composite parent) {
+
+        // add a dispose listener
+        parent.addDisposeListener(new class DisposeListener {
+            public void widgetDisposed(DisposeEvent e) {
+                if (titleAreaColor !is null) {
+                    titleAreaColor.dispose();
+                }
+            }
+        });
+        // Determine the background color of the title bar
+        Display display = parent.getDisplay();
+        Color background;
+        Color foreground;
+        if (titleAreaRGB !is null) {
+            titleAreaColor = new Color(display, titleAreaRGB);
+            background = titleAreaColor;
+            foreground = null;
+        } else {
+            background = JFaceColors.getBannerBackground(display);
+            foreground = JFaceColors.getBannerForeground(display);
+        }
+
+        parent.setBackground(background);
+        int verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
+        int horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
+        // Dialog image @ right
+        titleImageLabel = new Label(parent, SWT.CENTER);
+        titleImageLabel.setBackground(background);
+        if (titleAreaImage is null)
+            titleImageLabel.setImage(JFaceResources
+                    .getImage(DLG_IMG_TITLE_BANNER));
+        else
+            titleImageLabel.setImage(titleAreaImage);
+
+        FormData imageData = new FormData();
+        imageData.top = new FormAttachment(0, 0);
+        // Note: do not use horizontalSpacing on the right as that would be a
+        // regression from
+        // the R2.x style where there was no margin on the right and images are
+        // flush to the right
+        // hand side. see reopened comments in 41172
+        imageData.right = new FormAttachment(100, 0); // horizontalSpacing
+        titleImageLabel.setLayoutData(imageData);
+        // Title label @ top, left
+        titleLabel = new Label(parent, SWT.LEFT);
+        JFaceColors.setColors(titleLabel, foreground, background);
+        titleLabel.setFont(JFaceResources.getBannerFont());
+        titleLabel.setText(" ");//$NON-NLS-1$
+        FormData titleData = new FormData();
+        titleData.top = new FormAttachment(0, verticalSpacing);
+        titleData.right = new FormAttachment(titleImageLabel);
+        titleData.left = new FormAttachment(0, horizontalSpacing);
+        titleLabel.setLayoutData(titleData);
+        // Message image @ bottom, left
+        messageImageLabel = new Label(parent, SWT.CENTER);
+        messageImageLabel.setBackground(background);
+        // Message label @ bottom, center
+        messageLabel = new Text(parent, SWT.WRAP | SWT.READ_ONLY);
+        JFaceColors.setColors(messageLabel, foreground, background);
+        messageLabel.setText(" \n "); // two lines//$NON-NLS-1$
+        messageLabel.setFont(JFaceResources.getDialogFont());
+        messageLabelHeight = messageLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
+        // Filler labels
+        leftFillerLabel = new Label(parent, SWT.CENTER);
+        leftFillerLabel.setBackground(background);
+        bottomFillerLabel = new Label(parent, SWT.CENTER);
+        bottomFillerLabel.setBackground(background);
+        setLayoutsForNormalMessage(verticalSpacing, horizontalSpacing);
+        determineTitleImageLargest();
+        if (titleImageLargest)
+            return titleImageLabel;
+        return messageLabel;
+    }
+
+    /**
+     * Determine if the title image is larger than the title message and message
+     * area. This is used for layout decisions.
+     */
+    private void determineTitleImageLargest() {
+        int titleY = titleImageLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
+        int verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
+        int labelY = titleLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
+        labelY += verticalSpacing;
+        labelY += messageLabelHeight;
+        labelY += verticalSpacing;
+        titleImageLargest = titleY > labelY;
+    }
+
+    /**
+     * Set the layout values for the messageLabel, messageImageLabel and
+     * fillerLabel for the case where there is a normal message.
+     *
+     * @param verticalSpacing
+     *            int The spacing between widgets on the vertical axis.
+     * @param horizontalSpacing
+     *            int The spacing between widgets on the horizontal axis.
+     */
+    private void setLayoutsForNormalMessage(int verticalSpacing,
+            int horizontalSpacing) {
+        FormData messageImageData = new FormData();
+        messageImageData.top = new FormAttachment(titleLabel, verticalSpacing);
+        messageImageData.left = new FormAttachment(0, H_GAP_IMAGE);
+        messageImageLabel.setLayoutData(messageImageData);
+        FormData messageLabelData = new FormData();
+        messageLabelData.top = new FormAttachment(titleLabel, verticalSpacing);
+        messageLabelData.right = new FormAttachment(titleImageLabel);
+        messageLabelData.left = new FormAttachment(messageImageLabel,
+                horizontalSpacing);
+        messageLabelData.height = messageLabelHeight;
+        if (titleImageLargest)
+            messageLabelData.bottom = new FormAttachment(titleImageLabel, 0,
+                    SWT.BOTTOM);
+        messageLabel.setLayoutData(messageLabelData);
+        FormData fillerData = new FormData();
+        fillerData.left = new FormAttachment(0, horizontalSpacing);
+        fillerData.top = new FormAttachment(messageImageLabel, 0);
+        fillerData.bottom = new FormAttachment(messageLabel, 0, SWT.BOTTOM);
+        bottomFillerLabel.setLayoutData(fillerData);
+        FormData data = new FormData();
+        data.top = new FormAttachment(messageImageLabel, 0, SWT.TOP);
+        data.left = new FormAttachment(0, 0);
+        data.bottom = new FormAttachment(messageImageLabel, 0, SWT.BOTTOM);
+        data.right = new FormAttachment(messageImageLabel, 0);
+        leftFillerLabel.setLayoutData(data);
+    }
+
+    /**
+     * The <code>TitleAreaDialog</code> implementation of this
+     * <code>Window</code> methods returns an initial size which is at least
+     * some reasonable minimum.
+     *
+     * @return the initial size of the dialog
+     */
+    protected override Point getInitialSize() {
+        Point shellSize = super.getInitialSize();
+        return new Point(Math.max(
+                convertHorizontalDLUsToPixels(MIN_DIALOG_WIDTH), shellSize.x),
+                Math.max(convertVerticalDLUsToPixels(MIN_DIALOG_HEIGHT),
+                        shellSize.y));
+    }
+
+    /**
+     * Retained for backward compatibility.
+     *
+     * Returns the title area composite. There is no composite in this
+     * implementation so the shell is returned.
+     *
+     * @return Composite
+     * @deprecated
+     */
+    protected Composite getTitleArea() {
+        return getShell();
+    }
+
+    /**
+     * Returns the title image label.
+     *
+     * @return the title image label
+     */
+    protected Label getTitleImageLabel() {
+        return titleImageLabel;
+    }
+
+    /**
+     * Display the given error message. The currently displayed message is saved
+     * and will be redisplayed when the error message is set to
+     * <code>null</code>.
+     *
+     * @param newErrorMessage
+     *            the newErrorMessage to display or <code>null</code>
+     */
+    public void setErrorMessage(String newErrorMessage) {
+        // Any change?
+        if (errorMessage is null ? newErrorMessage is null : errorMessage
+                .equals(newErrorMessage))
+            return;
+        errorMessage = newErrorMessage;
+
+        // Clear or set error message.
+        if (errorMessage is null) {
+            if (showingError) {
+                // we were previously showing an error
+                showingError = false;
+            }
+            // show the message
+            // avoid calling setMessage in case it is overridden to call
+            // setErrorMessage,
+            // which would result in a recursive infinite loop
+            if (message is null) // this should probably never happen since
+                // setMessage does this conversion....
+                message = ""; //$NON-NLS-1$
+            updateMessage(message);
+            messageImageLabel.setImage(messageImage);
+            setImageLabelVisible(messageImage !is null);
+        } else {
+            // Add in a space for layout purposes but do not
+            // change the instance variable
+            String displayedErrorMessage = " " ~ errorMessage; //$NON-NLS-1$
+            updateMessage(displayedErrorMessage);
+            if (!showingError) {
+                // we were not previously showing an error
+                showingError = true;
+                messageImageLabel.setImage(JFaceResources
+                        .getImage(DLG_IMG_TITLE_ERROR));
+                setImageLabelVisible(true);
+            }
+        }
+        layoutForNewMessage();
+    }
+
+    /**
+     * Re-layout the labels for the new message.
+     */
+    private void layoutForNewMessage() {
+        int verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
+        int horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
+        // If there are no images then layout as normal
+        if (errorMessage is null && messageImage is null) {
+            setImageLabelVisible(false);
+            setLayoutsForNormalMessage(verticalSpacing, horizontalSpacing);
+        } else {
+            messageImageLabel.setVisible(true);
+            bottomFillerLabel.setVisible(true);
+            leftFillerLabel.setVisible(true);
+            /**
+             * Note that we do not use horizontalSpacing here as when the
+             * background of the messages changes there will be gaps between the
+             * icon label and the message that are the background color of the
+             * shell. We add a leading space elsewhere to compendate for this.
+             */
+            FormData data = new FormData();
+            data.left = new FormAttachment(0, H_GAP_IMAGE);
+            data.top = new FormAttachment(titleLabel, verticalSpacing);
+            messageImageLabel.setLayoutData(data);
+            data = new FormData();
+            data.top = new FormAttachment(messageImageLabel, 0);
+            data.left = new FormAttachment(0, 0);
+            data.bottom = new FormAttachment(messageLabel, 0, SWT.BOTTOM);
+            data.right = new FormAttachment(messageImageLabel, 0, SWT.RIGHT);
+            bottomFillerLabel.setLayoutData(data);
+            data = new FormData();
+            data.top = new FormAttachment(messageImageLabel, 0, SWT.TOP);
+            data.left = new FormAttachment(0, 0);
+            data.bottom = new FormAttachment(messageImageLabel, 0, SWT.BOTTOM);
+            data.right = new FormAttachment(messageImageLabel, 0);
+            leftFillerLabel.setLayoutData(data);
+            FormData messageLabelData = new FormData();
+            messageLabelData.top = new FormAttachment(titleLabel,
+                    verticalSpacing);
+            messageLabelData.right = new FormAttachment(titleImageLabel);
+            messageLabelData.left = new FormAttachment(messageImageLabel, 0);
+            messageLabelData.height = messageLabelHeight;
+            if (titleImageLargest)
+                messageLabelData.bottom = new FormAttachment(titleImageLabel,
+                        0, SWT.BOTTOM);
+            messageLabel.setLayoutData(messageLabelData);
+        }
+        // Do not layout before the dialog area has been created
+        // to avoid incomplete calculations.
+        if (dialogArea !is null)
+            workArea.getParent().layout(true);
+    }
+
+    /**
+     * Set the message text. If the message line currently displays an error,
+     * the message is saved and will be redisplayed when the error message is
+     * set to <code>null</code>.
+     * <p>
+     * Shortcut for <code>setMessage(newMessage, IMessageProvider.NONE)</code>
+     * </p>
+     * This method should be called after the dialog has been opened as it
+     * updates the message label immediately.
+     *
+     * @param newMessage
+     *            the message, or <code>null</code> to clear the message
+     */
+    public void setMessage(String newMessage) {
+        setMessage(newMessage, IMessageProvider.NONE);
+    }
+
+    /**
+     * Sets the message for this dialog with an indication of what type of
+     * message it is.
+     * <p>
+     * The valid message types are one of <code>NONE</code>,
+     * <code>INFORMATION</code>,<code>WARNING</code>, or
+     * <code>ERROR</code>.
+     * </p>
+     * <p>
+     * Note that for backward compatibility, a message of type
+     * <code>ERROR</code> is different than an error message (set using
+     * <code>setErrorMessage</code>). An error message overrides the current
+     * message until the error message is cleared. This method replaces the
+     * current message and does not affect the error message.
+     * </p>
+     *
+     * @param newMessage
+     *            the message, or <code>null</code> to clear the message
+     * @param newType
+     *            the message type
+     * @since 2.0
+     */
+    public void setMessage(String newMessage, int newType) {
+        Image newImage = null;
+        if (newMessage !is null) {
+            switch (newType) {
+            case IMessageProvider.NONE:
+                break;
+            case IMessageProvider.INFORMATION:
+                newImage = JFaceResources.getImage(DLG_IMG_MESSAGE_INFO);
+                break;
+            case IMessageProvider.WARNING:
+                newImage = JFaceResources.getImage(DLG_IMG_MESSAGE_WARNING);
+                break;
+            case IMessageProvider.ERROR:
+                newImage = JFaceResources.getImage(DLG_IMG_MESSAGE_ERROR);
+                break;
+            default:
+            }
+        }
+        showMessage(newMessage, newImage);
+    }
+
+    /**
+     * Show the new message and image.
+     *
+     * @param newMessage
+     * @param newImage
+     */
+    private void showMessage(String newMessage, Image newImage) {
+        // Any change?
+        if (message.equals(newMessage) && messageImage is newImage) {
+            return;
+        }
+        message = newMessage;
+        if (message is null)
+            message = "";//$NON-NLS-1$
+        // Message string to be shown - if there is an image then add in
+        // a space to the message for layout purposes
+        String shownMessage = (newImage is null) ? message : " " ~ message; //$NON-NLS-1$
+        messageImage = newImage;
+        if (!showingError) {
+            // we are not showing an error
+            updateMessage(shownMessage);
+            messageImageLabel.setImage(messageImage);
+            setImageLabelVisible(messageImage !is null);
+            layoutForNewMessage();
+        }
+    }
+
+    /**
+     * Update the contents of the messageLabel.
+     *
+     * @param newMessage
+     *            the message to use
+     */
+    private void updateMessage(String newMessage) {
+        messageLabel.setText(newMessage);
+    }
+
+    /**
+     * Sets the title to be shown in the title area of this dialog.
+     *
+     * @param newTitle
+     *            the title show
+     */
+    public void setTitle(String newTitle) {
+        if (titleLabel is null)
+            return;
+        String title = newTitle;
+        if (title is null)
+            title = "";//$NON-NLS-1$
+        titleLabel.setText(title);
+    }
+
+    /**
+     * Sets the title bar color for this dialog.
+     *
+     * @param color
+     *            the title bar color
+     */
+    public void setTitleAreaColor(RGB color) {
+        titleAreaRGB = color;
+    }
+
+    /**
+     * Sets the title image to be shown in the title area of this dialog.
+     *
+     * @param newTitleImage
+     *            the title image to be shown
+     */
+    public void setTitleImage(Image newTitleImage) {
+
+        titleAreaImage = newTitleImage;
+        if (titleImageLabel !is null) {
+            titleImageLabel.setImage(newTitleImage);
+            titleImageLabel.setVisible(newTitleImage !is null);
+            if (newTitleImage !is null) {
+                determineTitleImageLargest();
+                Control top;
+                if (titleImageLargest)
+                    top = titleImageLabel;
+                else
+                    top = messageLabel;
+                resetWorkAreaAttachments(top);
+            }
+        }
+    }
+
+    /**
+     * Make the label used for displaying error images visible depending on
+     * bool.
+     *
+     * @param visible
+     *            If <code>true</code> make the image visible, if not then
+     *            make it not visible.
+     */
+    private void setImageLabelVisible(bool visible) {
+        messageImageLabel.setVisible(visible);
+        bottomFillerLabel.setVisible(visible);
+        leftFillerLabel.setVisible(visible);
+    }
+
+    /**
+     * Reset the attachment of the workArea to now attach to top as the top
+     * control.
+     *
+     * @param top
+     */
+    private void resetWorkAreaAttachments(Control top) {
+        FormData childData = new FormData();
+        childData.top = new FormAttachment(top);
+        childData.right = new FormAttachment(100, 0);
+        childData.left = new FormAttachment(0, 0);
+        childData.bottom = new FormAttachment(100, 0);
+        workArea.setLayoutData(childData);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/dialogs/TrayDialog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,414 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.jface.dialogs.TrayDialog;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.DialogTray;
+import org.eclipse.jface.dialogs.IDialogConstants;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Sash;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.window.IShellProvider;
+
+import java.lang.all;
+import java.util.Set;
+import tango.core.Exception;
+
+/**
+ * A <code>TrayDialog</code> is a specialized <code>Dialog</code> that can contain
+ * a tray on its side. The tray's content is provided as a <code>DialogTray</code>.
+ * <p>
+ * It is recommended to subclass this class instead of <code>Dialog</code> in all
+ * cases except where the dialog should never show a tray. For example, dialogs
+ * which are very short, simple, and quick to dismiss (e.g. a message dialog with
+ * an OK button) should subclass <code>Dialog</code>.
+ * </p>
+ * <p>
+ * Note: Trays are not supported on dialogs that use a custom layout on the <code>
+ * Shell</code> by overriding <code>Window#getLayout()</code>.
+ * </p>
+ *
+ * @see org.eclipse.jface.dialogs.DialogTray
+ * @see org.eclipse.jface.window.Window#getLayout()
+ * @since 3.2
+ */
+public abstract class TrayDialog : Dialog {
+
+    private static bool dialogHelpAvailable = false;
+
+    /*
+     * The dialog's tray (null if none).
+     */
+    private DialogTray tray;
+
+    /*
+     * The tray's control.
+     */
+    private Control trayControl;
+
+    /*
+     * The separator to the left of the sash.
+     */
+    private Label leftSeparator;
+
+    /*
+     * The separator to the right of the sash.
+     */
+    private Label rightSeparator;
+
+    /*
+     * The sash that allows the user to resize the tray.
+     */
+    private Sash sash;
+
+    /*
+     * Whether or not help is available for this dialog.
+     */
+    private bool helpAvailable;
+
+    /**
+     * Creates a tray dialog instance. Note that the window will have no visual
+     * representation (no widgets) until it is told to open.
+     *
+     * @param shell the parent shell, or <code>null</code> to create a top-level shell
+     */
+    protected this(Shell shell) {
+        super(shell);
+        helpAvailable = isDialogHelpAvailable();
+    }
+
+    /**
+     * Creates a tray dialog with the given parent.
+     *
+     * @param parentShell the object that returns the current parent shell
+     */
+    protected this(IShellProvider parentShell) {
+        super(parentShell);
+        helpAvailable = isDialogHelpAvailable();
+    }
+
+    /**
+     * Closes this dialog's tray, disposing its widgets.
+     *
+     * @throws IllegalStateException if the tray was not open
+     */
+    public void closeTray() {
+        if (getTray() is null) {
+            throw new IllegalStateException("Tray was not open"); //$NON-NLS-1$
+        }
+        int trayWidth = trayControl.getSize().x + leftSeparator.getSize().x + sash.getSize().x + rightSeparator.getSize().x;
+        trayControl.dispose();
+        trayControl = null;
+        tray = null;
+        leftSeparator.dispose();
+        leftSeparator = null;
+        rightSeparator.dispose();
+        rightSeparator = null;
+        sash.dispose();
+        sash = null;
+        Shell shell = getShell();
+        Rectangle bounds = shell.getBounds();
+        shell.setBounds(bounds.x + ((getDefaultOrientation() is SWT.RIGHT_TO_LEFT) ? trayWidth : 0), bounds.y, bounds.width - trayWidth, bounds.height);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.dialogs.Dialog#close()
+     */
+    public override bool close() {
+        /*
+         * Close the tray to ensure that those dialogs that remember their
+         * size do not store the tray size.
+         */
+        if (getTray() !is null) {
+            closeTray();
+        }
+        return super.close();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.dialogs.Dialog#createButtonBar(org.eclipse.swt.widgets.Composite)
+     */
+    protected override Control createButtonBar(Composite parent) {
+        Composite composite = new Composite(parent, SWT.NONE);
+        GridLayout layout = new GridLayout();
+        layout.marginWidth = 0;
+        layout.marginHeight = 0;
+        layout.horizontalSpacing = 0;
+        composite.setLayout(layout);
+        composite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
+        composite.setFont(parent.getFont());
+
+        // create help control if needed
+        if (isHelpAvailable()) {
+            Control helpControl = createHelpControl(composite);
+            (cast(GridData) helpControl.getLayoutData()).horizontalIndent = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
+        }
+        Control buttonSection = super.createButtonBar(composite);
+        (cast(GridData) buttonSection.getLayoutData()).grabExcessHorizontalSpace = true;
+        return composite;
+    }
+
+    /**
+     * Creates a new help control that provides access to context help.
+     * <p>
+     * The <code>TrayDialog</code> implementation of this method creates
+     * the control, registers it for selection events including selection,
+     * Note that the parent's layout is assumed to be a <code>GridLayout</code>
+     * and the number of columns in this layout is incremented. Subclasses may
+     * override.
+     * </p>
+     *
+     * @param parent the parent composite
+     * @return the help control
+     */
+    protected Control createHelpControl(Composite parent) {
+        Image helpImage = JFaceResources.getImage(DLG_IMG_HELP);
+        if (helpImage !is null) {
+            return createHelpImageButton(parent, helpImage);
+        }
+        return createHelpLink(parent);
+    }
+
+    /*
+     * Creates a button with a help image. This is only used if there
+     * is an image available.
+     */
+    private ToolBar createHelpImageButton(Composite parent, Image image) {
+        ToolBar toolBar = new ToolBar(parent, SWT.FLAT | SWT.NO_FOCUS);
+        (cast(GridLayout) parent.getLayout()).numColumns++;
+        toolBar.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_CENTER));
+        Cursor cursor = new Cursor(parent.getDisplay(), SWT.CURSOR_HAND);
+        toolBar.setCursor(cursor);
+        toolBar.addDisposeListener(new class(cursor) DisposeListener {
+            Cursor cursor_;
+            this(Cursor c){
+                cursor_=c;
+            }
+            public void widgetDisposed(DisposeEvent e) {
+                cursor_.dispose();
+            }
+        });
+        ToolItem item = new ToolItem(toolBar, SWT.NONE);
+        item.setImage(image);
+        item.setToolTipText(JFaceResources.getString("helpToolTip")); //$NON-NLS-1$
+        item.addSelectionListener(new class SelectionAdapter {
+            public void widgetSelected(SelectionEvent e) {
+                helpPressed();
+            }
+        });
+        return toolBar;
+    }
+
+    /*
+     * Creates a help link. This is used when there is no help image
+     * available.
+     */
+    private Link createHelpLink(Composite parent) {
+        Link link = new Link(parent, SWT.WRAP | SWT.NO_FOCUS);
+        (cast(GridLayout) parent.getLayout()).numColumns++;
+        link.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_CENTER));
+        link.setText("<a>"~IDialogConstants.HELP_LABEL~"</a>"); //$NON-NLS-1$ //$NON-NLS-2$
+        link.setToolTipText(IDialogConstants.HELP_LABEL);
+        link.addSelectionListener(new class SelectionAdapter {
+            public void widgetSelected(SelectionEvent e) {
+                helpPressed();
+            }
+        });
+        return link;
+    }
+
+    /*
+     * Returns whether or not the given layout can support the addition of a tray.
+     */
+    private bool isCompatibleLayout(Layout layout) {
+        if (layout !is null && cast(GridLayout)layout ) {
+            GridLayout grid = cast(GridLayout)layout;
+            return !grid.makeColumnsEqualWidth && (grid.horizontalSpacing is 0) &&
+                    (grid.marginWidth is 0) && (grid.marginHeight is 0) &&
+                    (grid.numColumns is 5);
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether or not context help is available for this dialog. This
+     * can affect whether or not the dialog will display additional help
+     * mechanisms such as a help control in the button bar.
+     *
+     * @return whether or not context help is available for this dialog
+     */
+    public bool isHelpAvailable() {
+        return helpAvailable;
+    }
+
+    /**
+     * The tray dialog's default layout is a modified version of the default
+     * <code>Window</code> layout that can accomodate a tray, however it still
+     * conforms to the description of the <code>Window</code> default layout.
+     * <p>
+     * Note: Trays may not be supported with all custom layouts on the dialog's
+     * Shell. To avoid problems, use a single outer <code>Composite</code> for
+     * your dialog area, and set your custom layout on that <code>Composite</code>.
+     * </p>
+     *
+     * @see org.eclipse.jface.window.Window#getLayout()
+     * @return a newly created layout or <code>null</code> for no layout
+     */
+    protected override Layout getLayout() {
+        GridLayout layout = cast(GridLayout)super.getLayout();
+        layout.numColumns = 5;
+        layout.horizontalSpacing = 0;
+        return layout;
+    }
+
+    /**
+     * Returns the tray currently shown in the dialog, or <code>null</code>
+     * if there is no tray.
+     *
+     * @return the dialog's current tray, or <code>null</code> if there is none
+     */
+    public DialogTray getTray() {
+        return tray;
+    }
+
+    /*
+     * Called when the help control is invoked. This emulates the keyboard
+     * context help behavior (e.g. F1 on Windows). It traverses the widget
+     * tree upward until it finds a widget that has a help listener on it,
+     * then invokes a help event on that widget.
+     */
+    private void helpPressed() {
+        if (getShell() !is null) {
+            Control c = getShell().getDisplay().getFocusControl();
+            while (c !is null) {
+                if (c.isListening(SWT.Help)) {
+                    c.notifyListeners(SWT.Help, new Event());
+                    break;
+                }
+                c = c.getParent();
+            }
+        }
+    }
+
+    /**
+     * Constructs the tray's widgets and displays the tray in this dialog. The
+     * dialog's size will be adjusted to accomodate the tray.
+     *
+     * @param tray the tray to show in this dialog
+     * @throws IllegalStateException if the dialog already has a tray open
+     * @throws UnsupportedOperationException if the dialog does not support trays,
+     *            for example if it uses a custom layout.
+     */
+    public void openTray(DialogTray tray) {
+        if (tray is null) {
+            throw new NullPointerException("Tray was null"); //$NON-NLS-1$
+        }
+        if (getTray() !is null) {
+            throw new IllegalStateException("Tray was already open"); //$NON-NLS-1$
+        }
+        if (!isCompatibleLayout(getShell().getLayout())) {
+            throw new UnsupportedOperationException("Trays not supported with custom layouts"); //$NON-NLS-1$
+        }
+        Shell shell = getShell();
+        leftSeparator = new Label(shell, SWT.SEPARATOR | SWT.VERTICAL);
+        leftSeparator.setLayoutData(new GridData(GridData.FILL_VERTICAL));
+        sash = new Sash(shell, SWT.VERTICAL);
+        sash.setLayoutData(new GridData(GridData.FILL_VERTICAL));
+        rightSeparator = new Label(shell, SWT.SEPARATOR | SWT.VERTICAL);
+        rightSeparator.setLayoutData(new GridData(GridData.FILL_VERTICAL));
+        trayControl = tray.createContents_package(shell);
+        Rectangle clientArea = shell.getClientArea();
+        GridData data = new GridData(GridData.FILL_VERTICAL);
+        data.widthHint = trayControl.computeSize(SWT.DEFAULT, clientArea.height).x;
+        trayControl.setLayoutData(data);
+        int trayWidth = leftSeparator.computeSize(SWT.DEFAULT, clientArea.height).x + sash.computeSize(SWT.DEFAULT, clientArea.height).x + rightSeparator.computeSize(SWT.DEFAULT, clientArea.height).x + data.widthHint;
+        Rectangle bounds = shell.getBounds();
+        shell.setBounds(bounds.x - ((getDefaultOrientation() is SWT.RIGHT_TO_LEFT) ? trayWidth : 0), bounds.y, bounds.width + trayWidth, bounds.height);
+        sash.addListener(SWT.Selection, new class(shell, data) Listener {
+            Shell shell_;
+            GridData data_;
+            this(Shell a,GridData b){
+                shell_=a;
+                data_=b;
+            }
+            public void handleEvent(Event event) {
+                if (event.detail !is SWT.DRAG) {
+                    Rectangle clientArea = shell_.getClientArea();
+                    int newWidth = clientArea.width - event.x - (sash.getSize().x + rightSeparator.getSize().x);
+                    if (newWidth !is data_.widthHint) {
+                        data_.widthHint = newWidth;
+                        shell_.layout();
+                    }
+                }
+            }
+        });
+        this.tray = tray;
+    }
+
+    /**
+     * Sets whether or not context help is available for this dialog. This
+     * can affect whether or not the dialog will display additional help
+     * mechanisms such as a help control in the button bar.
+     *
+     * @param helpAvailable whether or not context help is available for the dialog
+     */
+    public void setHelpAvailable(bool helpAvailable) {
+        this.helpAvailable = helpAvailable;
+    }
+
+    /**
+     * Tests if dialogs that have help control should show it
+     * all the time or only when explicitly requested for
+     * each dialog instance.
+     *
+     * @return <code>true</code> if dialogs that support help
+     * control should show it by default, <code>false</code> otherwise.
+     * @since 3.2
+     */
+    public static bool isDialogHelpAvailable() {
+        return dialogHelpAvailable;
+    }
+
+    /**
+     * Sets whether JFace dialogs that support help control should
+     * show the control by default. If set to <code>false</code>,
+     * help control can still be shown on a per-dialog basis.
+     *
+     * @param helpAvailable <code>true</code> to show the help
+     * control, <code>false</code> otherwise.
+     * @since 3.2
+     */
+    public static void setDialogHelpAvailable(bool helpAvailable) {
+        dialogHelpAvailable = helpAvailable;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/AutoCompleteField.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.fieldassist.AutoCompleteField;
+
+import org.eclipse.jface.fieldassist.IControlContentAdapter;
+import org.eclipse.jface.fieldassist.SimpleContentProposalProvider;
+import org.eclipse.jface.fieldassist.ContentProposalAdapter;
+
+import org.eclipse.swt.widgets.Control;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * AutoCompleteField is a class which attempts to auto-complete a user's
+ * keystrokes by activating a popup that filters a list of proposals according
+ * to the content typed by the user.
+ *
+ * @see ContentProposalAdapter
+ * @see SimpleContentProposalProvider
+ *
+ * @since 3.3
+ */
+public class AutoCompleteField {
+
+    private SimpleContentProposalProvider proposalProvider;
+    private ContentProposalAdapter adapter;
+
+    /**
+     * Construct an AutoComplete field on the specified control, whose
+     * completions are characterized by the specified array of Strings.
+     *
+     * @param control
+     *            the control for which autocomplete is desired. May not be
+     *            <code>null</code>.
+     * @param controlContentAdapter
+     *            the <code>IControlContentAdapter</code> used to obtain and
+     *            update the control's contents. May not be <code>null</code>.
+     * @param proposals
+     *            the array of Strings representing valid content proposals for
+     *            the field.
+     */
+    public this(Control control,
+            IControlContentAdapter controlContentAdapter, String[] proposals) {
+        proposalProvider = new SimpleContentProposalProvider(proposals);
+        proposalProvider.setFiltering(true);
+        adapter = new ContentProposalAdapter(control, controlContentAdapter,
+                proposalProvider, null, null);
+        adapter.setPropagateKeys(true);
+        adapter
+                .setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
+    }
+
+    /**
+     * Set the Strings to be used as content proposals.
+     *
+     * @param proposals
+     *            the array of Strings to be used as proposals.
+     */
+    public void setProposals(String[] proposals) {
+        proposalProvider.setProposals(proposals);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/ComboContentAdapter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.fieldassist.ComboContentAdapter;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.jface.fieldassist.IControlContentAdapter;
+import org.eclipse.jface.fieldassist.IControlContentAdapter2;
+
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Control;
+
+import java.lang.all;
+import java.util.Set;
+import tango.text.Text;
+alias tango.text.Text.Text!(char) StringBuffer;
+/**
+ * An {@link IControlContentAdapter} for SWT Combo controls. This is a
+ * convenience class for easily creating a {@link ContentProposalAdapter} for
+ * combo fields.
+ *
+ * @since 3.2
+ */
+public class ComboContentAdapter : IControlContentAdapter,
+        IControlContentAdapter2 {
+
+    /*
+     * Set to <code>true</code> if we should compute the text
+     * vertical bounds rather than just use the field size.
+     * Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=164748
+     * The corresponding SWT bug is
+     * https://bugs.eclipse.org/bugs/show_bug.cgi?id=44072
+     */
+    private static final bool COMPUTE_TEXT_USING_CLIENTAREA = !"carbon".equals(SWT.getPlatform()); //$NON-NLS-1$
+
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.taskassistance.IControlContentAdapter#getControlContents(org.eclipse.swt.widgets.Control)
+     */
+    public String getControlContents(Control control) {
+        return (cast(Combo) control).getText();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.fieldassist.IControlContentAdapter#setControlContents(org.eclipse.swt.widgets.Control,
+     *      java.lang.String, int)
+     */
+    public void setControlContents(Control control, String text,
+            int cursorPosition) {
+        (cast(Combo) control).setText(text);
+        (cast(Combo) control)
+                .setSelection(new Point(cursorPosition, cursorPosition));
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.fieldassist.IControlContentAdapter#insertControlContents(org.eclipse.swt.widgets.Control,
+     *      java.lang.String, int)
+     */
+    public void insertControlContents(Control control, String text,
+            int cursorPosition) {
+        Combo combo = cast(Combo) control;
+        String contents = combo.getText();
+        Point selection = combo.getSelection();
+        StringBuffer sb = new StringBuffer();
+        sb.append(contents.substring(0, selection.x));
+        sb.append(text);
+        if (selection.y < contents.length) {
+            sb.append(contents.substring(selection.y, contents.length));
+        }
+        combo.setText(sb.toString());
+        selection.x = selection.x + cursorPosition;
+        selection.y = selection.x;
+        combo.setSelection(selection);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.fieldassist.IControlContentAdapter#getCursorPosition(org.eclipse.swt.widgets.Control)
+     */
+    public int getCursorPosition(Control control) {
+        return (cast(Combo) control).getSelection().x;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.fieldassist.IControlContentAdapter#getInsertionBounds(org.eclipse.swt.widgets.Control)
+     */
+    public Rectangle getInsertionBounds(Control control) {
+        // This doesn't take horizontal scrolling into affect.
+        // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=204599
+        Combo combo = cast(Combo) control;
+        int position = combo.getSelection().y;
+        String contents = combo.getText();
+        GC gc = new GC(combo);
+        gc.setFont(combo.getFont());
+        Point extent = gc.textExtent(contents.substring(0, Math.min(position,
+                contents.length)));
+        gc.dispose();
+        if (COMPUTE_TEXT_USING_CLIENTAREA) {
+            return new Rectangle(combo.getClientArea().x + extent.x, combo
+                .getClientArea().y, 1, combo.getClientArea().height);
+        }
+        return new Rectangle(extent.x, 0, 1, combo.getSize().y);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.fieldassist.IControlContentAdapter#setCursorPosition(org.eclipse.swt.widgets.Control,
+     *      int)
+     */
+    public void setCursorPosition(Control control, int index) {
+        (cast(Combo) control).setSelection(new Point(index, index));
+    }
+
+    /**
+     * @see org.eclipse.jface.fieldassist.IControlContentAdapter2#getSelection(org.eclipse.swt.widgets.Control)
+     *
+     * @since 3.4
+     */
+    public Point getSelection(Control control) {
+        return (cast(Combo) control).getSelection();
+    }
+
+    /**
+     * @see org.eclipse.jface.fieldassist.IControlContentAdapter2#setSelection(org.eclipse.swt.widgets.Control,
+     *      org.eclipse.swt.graphics.Point)
+     *
+     * @since 3.4
+     */
+    public void setSelection(Control control, Point range) {
+        (cast(Combo) control).setSelection(range);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/ContentProposalAdapter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,2117 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.fieldassist.ContentProposalAdapter;
+
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+import org.eclipse.jface.fieldassist.IControlContentAdapter;
+import org.eclipse.jface.fieldassist.IControlContentAdapter2;
+import org.eclipse.jface.fieldassist.IContentProposalListener;
+import org.eclipse.jface.fieldassist.IContentProposalListener2;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.bindings.keys.KeyStroke;
+import org.eclipse.jface.dialogs.PopupDialog;
+import org.eclipse.jface.preference.JFacePreferences;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.ILabelProvider;
+
+import java.lang.all;
+import java.util.Set;
+import java.lang.JThread;
+static import tango.text.Text;
+import tango.io.Stdout;
+import tango.text.convert.Format;
+alias tango.text.Text.Text!(char) StringBuffer;
+/**
+ * ContentProposalAdapter can be used to attach content proposal behavior to a
+ * control. This behavior includes obtaining proposals, opening a popup dialog,
+ * managing the content of the control relative to the selections in the popup,
+ * and optionally opening up a secondary popup to further describe proposals.
+ * <p>
+ * A number of configurable options are provided to determine how the control
+ * content is altered when a proposal is chosen, how the content proposal popup
+ * is activated, and whether any filtering should be done on the proposals as
+ * the user types characters.
+ * <p>
+ * This class is not intended to be subclassed.
+ *
+ * @since 3.2
+ */
+public class ContentProposalAdapter {
+
+    /*
+     * The lightweight popup used to show content proposals for a text field. If
+     * additional information exists for a proposal, then selecting that
+     * proposal will result in the information being displayed in a secondary
+     * popup.
+     */
+    class ContentProposalPopup : PopupDialog {
+        /*
+         * The listener we install on the popup and related controls to
+         * determine when to close the popup. Some events (move, resize, close,
+         * deactivate) trigger closure as soon as they are received, simply
+         * because one of the registered listeners received them. Other events
+         * depend on additional circumstances.
+         */
+        private final class PopupCloserListener : Listener {
+            private bool scrollbarClicked = false;
+
+            public void handleEvent(Event e) {
+
+                // If focus is leaving an important widget or the field's
+                // shell is deactivating
+                if (e.type is SWT.FocusOut) {
+                    scrollbarClicked = false;
+                    /*
+                     * Ignore this event if it's only happening because focus is
+                     * moving between the popup shells, their controls, or a
+                     * scrollbar. Do this in an async since the focus is not
+                     * actually switched when this event is received.
+                     */
+                    e.display.asyncExec(new class(e) Runnable {
+                        Event e_;
+                        this(Event e__){ e_=e__; }
+                        public void run() {
+                            if (isValid()) {
+                                if (scrollbarClicked || hasFocus()) {
+                                    return;
+                                }
+                                // Workaround a problem on X and Mac, whereby at
+                                // this point, the focus control is not known.
+                                // This can happen, for example, when resizing
+                                // the popup shell on the Mac.
+                                // Check the active shell.
+                                Shell activeShell = e_.display.getActiveShell();
+                                if (activeShell is getShell()
+                                        || (infoPopup !is null && infoPopup
+                                                .getShell() is activeShell)) {
+                                    return;
+                                }
+                                /*
+                                 * System.out.println(e);
+                                 * System.out.println(e.display.getFocusControl());
+                                 * System.out.println(e.display.getActiveShell());
+                                 */
+                                close();
+                            }
+                        }
+                    });
+                    return;
+                }
+
+                // Scroll bar has been clicked. Remember this for focus event
+                // processing.
+                if (e.type is SWT.Selection) {
+                    scrollbarClicked = true;
+                    return;
+                }
+                // For all other events, merely getting them dictates closure.
+                close();
+            }
+
+            // Install the listeners for events that need to be monitored for
+            // popup closure.
+            void installListeners() {
+                // Listeners on this popup's table and scroll bar
+                proposalTable.addListener(SWT.FocusOut, this);
+                ScrollBar scrollbar = proposalTable.getVerticalBar();
+                if (scrollbar !is null) {
+                    scrollbar.addListener(SWT.Selection, this);
+                }
+
+                // Listeners on this popup's shell
+                getShell().addListener(SWT.Deactivate, this);
+                getShell().addListener(SWT.Close, this);
+
+                // Listeners on the target control
+                control.addListener(SWT.MouseDoubleClick, this);
+                control.addListener(SWT.MouseDown, this);
+                control.addListener(SWT.Dispose, this);
+                control.addListener(SWT.FocusOut, this);
+                // Listeners on the target control's shell
+                Shell controlShell = control.getShell();
+                controlShell.addListener(SWT.Move, this);
+                controlShell.addListener(SWT.Resize, this);
+
+            }
+
+            // Remove installed listeners
+            void removeListeners() {
+                if (isValid()) {
+                    proposalTable.removeListener(SWT.FocusOut, this);
+                    ScrollBar scrollbar = proposalTable.getVerticalBar();
+                    if (scrollbar !is null) {
+                        scrollbar.removeListener(SWT.Selection, this);
+                    }
+
+                    getShell().removeListener(SWT.Deactivate, this);
+                    getShell().removeListener(SWT.Close, this);
+                }
+
+                if (control !is null && !control.isDisposed()) {
+
+                    control.removeListener(SWT.MouseDoubleClick, this);
+                    control.removeListener(SWT.MouseDown, this);
+                    control.removeListener(SWT.Dispose, this);
+                    control.removeListener(SWT.FocusOut, this);
+
+                    Shell controlShell = control.getShell();
+                    controlShell.removeListener(SWT.Move, this);
+                    controlShell.removeListener(SWT.Resize, this);
+                }
+            }
+        }
+
+        /*
+         * The listener we will install on the target control.
+         */
+        private final class TargetControlListener : Listener {
+            // Key events from the control
+            public void handleEvent(Event e) {
+                if (!isValid()) {
+                    return;
+                }
+
+                char key = e.character;
+
+                // Traverse events are handled depending on whether the
+                // event has a character.
+                if (e.type is SWT.Traverse) {
+                    // If the traverse event contains a legitimate character,
+                    // then we must set doit false so that the widget will
+                    // receive the key event. We return immediately so that
+                    // the character is handled only in the key event.
+                    // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=132101
+                    if (key !is 0) {
+                        e.doit = false;
+                        return;
+                    }
+                    // Traversal does not contain a character. Set doit true
+                    // to indicate TRAVERSE_NONE will occur and that no key
+                    // event will be triggered. We will check for navigation
+                    // keys below.
+                    e.detail = SWT.TRAVERSE_NONE;
+                    e.doit = true;
+                } else {
+                    // Default is to only propagate when configured that way.
+                    // Some keys will always set doit to false anyway.
+                    e.doit = propagateKeys;
+                }
+
+                // No character. Check for navigation keys.
+
+                if (key is 0) {
+                    int newSelection = proposalTable.getSelectionIndex();
+                    int visibleRows = (proposalTable.getSize().y / proposalTable
+                            .getItemHeight()) - 1;
+                    switch (e.keyCode) {
+                    case SWT.ARROW_UP:
+                        newSelection -= 1;
+                        if (newSelection < 0) {
+                            newSelection = proposalTable.getItemCount() - 1;
+                        }
+                        // Not typical - usually we get this as a Traverse and
+                        // therefore it never propagates. Added for consistency.
+                        if (e.type is SWT.KeyDown) {
+                            // don't propagate to control
+                            e.doit = false;
+                        }
+
+                        break;
+
+                    case SWT.ARROW_DOWN:
+                        newSelection += 1;
+                        if (newSelection > proposalTable.getItemCount() - 1) {
+                            newSelection = 0;
+                        }
+                        // Not typical - usually we get this as a Traverse and
+                        // therefore it never propagates. Added for consistency.
+                        if (e.type is SWT.KeyDown) {
+                            // don't propagate to control
+                            e.doit = false;
+                        }
+
+                        break;
+
+                    case SWT.PAGE_DOWN:
+                        newSelection += visibleRows;
+                        if (newSelection >= proposalTable.getItemCount()) {
+                            newSelection = proposalTable.getItemCount() - 1;
+                        }
+                        if (e.type is SWT.KeyDown) {
+                            // don't propagate to control
+                            e.doit = false;
+                        }
+                        break;
+
+                    case SWT.PAGE_UP:
+                        newSelection -= visibleRows;
+                        if (newSelection < 0) {
+                            newSelection = 0;
+                        }
+                        if (e.type is SWT.KeyDown) {
+                            // don't propagate to control
+                            e.doit = false;
+                        }
+                        break;
+
+                    case SWT.HOME:
+                        newSelection = 0;
+                        if (e.type is SWT.KeyDown) {
+                            // don't propagate to control
+                            e.doit = false;
+                        }
+                        break;
+
+                    case SWT.END:
+                        newSelection = proposalTable.getItemCount() - 1;
+                        if (e.type is SWT.KeyDown) {
+                            // don't propagate to control
+                            e.doit = false;
+                        }
+                        break;
+
+                    // If received as a Traverse, these should propagate
+                    // to the control as keydown. If received as a keydown,
+                    // proposals should be recomputed since the cursor
+                    // position has changed.
+                    case SWT.ARROW_LEFT:
+                    case SWT.ARROW_RIGHT:
+                        if (e.type is SWT.Traverse) {
+                            e.doit = false;
+                        } else {
+                            e.doit = true;
+                            String contents = getControlContentAdapter()
+                                    .getControlContents(getControl());
+                            // If there are no contents, changes in cursor
+                            // position have no effect. Note also that we do
+                            // not affect the filter text on ARROW_LEFT as
+                            // we would with BS.
+                            if (contents.length > 0) {
+                                asyncRecomputeProposals(filterText);
+                            }
+                        }
+                        break;
+
+                    // Any unknown keycodes will cause the popup to close.
+                    // Modifier keys are explicitly checked and ignored because
+                    // they are not complete yet (no character).
+                    default:
+                        if (e.keyCode !is SWT.CAPS_LOCK && e.keyCode !is SWT.MOD1
+                                && e.keyCode !is SWT.MOD2
+                                && e.keyCode !is SWT.MOD3
+                                && e.keyCode !is SWT.MOD4) {
+                            close();
+                        }
+                        return;
+                    }
+
+                    // If any of these navigation events caused a new selection,
+                    // then handle that now and return.
+                    if (newSelection >= 0) {
+                        selectProposal(newSelection);
+                    }
+                    return;
+                }
+
+                // key !is 0
+                // Check for special keys involved in cancelling, accepting, or
+                // filtering the proposals.
+                switch (key) {
+                case SWT.ESC:
+                    e.doit = false;
+                    close();
+                    break;
+
+                case SWT.LF:
+                case SWT.CR:
+                    e.doit = false;
+                    Object p = cast(Object)getSelectedProposal();
+                    if (p !is null) {
+                        acceptCurrentProposal();
+                    } else {
+                        close();
+                    }
+                    break;
+
+                case SWT.TAB:
+                    e.doit = false;
+                    getShell().setFocus();
+                    return;
+
+                case SWT.BS:
+                    // Backspace should back out of any stored filter text
+                    if (filterStyle !is FILTER_NONE) {
+                        // We have no filter to back out of, so do nothing
+                        if (filterText.length is 0) {
+                            return;
+                        }
+                        // There is filter to back out of
+                        filterText = filterText.substring(0, filterText
+                                .length - 1);
+                        asyncRecomputeProposals(filterText);
+                        return;
+                    }
+                    // There is no filtering provided by us, but some
+                    // clients provide their own filtering based on content.
+                    // Recompute the proposals if the cursor position
+                    // will change (is not at 0).
+                    int pos = getControlContentAdapter().getCursorPosition(
+                            getControl());
+                    // We rely on the fact that the contents and pos do not yet
+                    // reflect the result of the BS. If the contents were
+                    // already empty, then BS should not cause
+                    // a recompute.
+                    if (pos > 0) {
+                        asyncRecomputeProposals(filterText);
+                    }
+                    break;
+
+                default:
+                    // If the key is a defined unicode character, and not one of
+                    // the special cases processed above, update the filter text
+                    // and filter the proposals.
+                    if (CharacterIsDefined(key)) {
+                        if (filterStyle is FILTER_CUMULATIVE) {
+                            filterText = filterText ~ dcharToString(key);
+                        } else if (filterStyle is FILTER_CHARACTER) {
+                            filterText = dcharToString(key);
+                        }
+                        // Recompute proposals after processing this event.
+                        asyncRecomputeProposals(filterText);
+                    }
+                    break;
+                }
+            }
+        }
+
+        /*
+         * Internal class used to implement the secondary popup.
+         */
+        private class InfoPopupDialog : PopupDialog {
+
+            /*
+             * The text control that displays the text.
+             */
+            private Text text;
+
+            /*
+             * The String shown in the popup.
+             */
+            private String contents = EMPTY;
+
+            /*
+             * Construct an info-popup with the specified parent.
+             */
+            this(Shell parent) {
+                super(parent, PopupDialog.HOVER_SHELLSTYLE, false, false,
+                        false, false, null, null);
+            }
+
+            /*
+             * Create a text control for showing the info about a proposal.
+             */
+            protected override Control createDialogArea(Composite parent) {
+                text = new Text(parent, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP
+                        | SWT.NO_FOCUS);
+
+                // Use the compact margins employed by PopupDialog.
+                GridData gd = new GridData(GridData.BEGINNING
+                        | GridData.FILL_BOTH);
+                gd.horizontalIndent = PopupDialog.POPUP_HORIZONTALSPACING;
+                gd.verticalIndent = PopupDialog.POPUP_VERTICALSPACING;
+                text.setLayoutData(gd);
+                text.setText(contents);
+
+                // since SWT.NO_FOCUS is only a hint...
+                text.addFocusListener(new class FocusAdapter {
+                    public void focusGained(FocusEvent event) {
+                        this.outer.close();
+                    }
+                });
+                return text;
+            }
+
+            /*
+             * Adjust the bounds so that we appear adjacent to our parent shell
+             */
+            protected override void adjustBounds() {
+                Rectangle parentBounds = getParentShell().getBounds();
+                Rectangle proposedBounds;
+                // Try placing the info popup to the right
+                Rectangle rightProposedBounds = new Rectangle(parentBounds.x
+                        + parentBounds.width
+                        + PopupDialog.POPUP_HORIZONTALSPACING, parentBounds.y
+                        + PopupDialog.POPUP_VERTICALSPACING,
+                        parentBounds.width, parentBounds.height);
+                rightProposedBounds = getConstrainedShellBounds(rightProposedBounds);
+                // If it won't fit on the right, try the left
+                if (rightProposedBounds.intersects(parentBounds)) {
+                    Rectangle leftProposedBounds = new Rectangle(parentBounds.x
+                            - parentBounds.width - POPUP_HORIZONTALSPACING - 1,
+                            parentBounds.y, parentBounds.width,
+                            parentBounds.height);
+                    leftProposedBounds = getConstrainedShellBounds(leftProposedBounds);
+                    // If it won't fit on the left, choose the proposed bounds
+                    // that fits the best
+                    if (leftProposedBounds.intersects(parentBounds)) {
+                        if (rightProposedBounds.x - parentBounds.x >= parentBounds.x
+                                - leftProposedBounds.x) {
+                            rightProposedBounds.x = parentBounds.x
+                                    + parentBounds.width
+                                    + PopupDialog.POPUP_HORIZONTALSPACING;
+                            proposedBounds = rightProposedBounds;
+                        } else {
+                            leftProposedBounds.width = parentBounds.x
+                                    - POPUP_HORIZONTALSPACING
+                                    - leftProposedBounds.x;
+                            proposedBounds = leftProposedBounds;
+                        }
+                    } else {
+                        // use the proposed bounds on the left
+                        proposedBounds = leftProposedBounds;
+                    }
+                } else {
+                    // use the proposed bounds on the right
+                    proposedBounds = rightProposedBounds;
+                }
+                getShell().setBounds(proposedBounds);
+            }
+
+            /*
+             * (non-Javadoc)
+             * @see org.eclipse.jface.dialogs.PopupDialog#getForeground()
+             */
+            protected Color getForeground() {
+                return control.getDisplay().
+                        getSystemColor(SWT.COLOR_INFO_FOREGROUND);
+            }
+
+            /*
+             * (non-Javadoc)
+             * @see org.eclipse.jface.dialogs.PopupDialog#getBackground()
+             */
+            protected Color getBackground() {
+                return control.getDisplay().
+                        getSystemColor(SWT.COLOR_INFO_BACKGROUND);
+            }
+
+            /*
+             * Set the text contents of the popup.
+             */
+            void setContents(String newContents) {
+                if (newContents is null) {
+                    newContents = EMPTY;
+                }
+                this.contents = newContents;
+                if (text !is null && !text.isDisposed()) {
+                    text.setText(contents);
+                }
+            }
+
+            /*
+             * Return whether the popup has focus.
+             */
+            bool hasFocus() {
+                if (text is null || text.isDisposed()) {
+                    return false;
+                }
+                return text.getShell().isFocusControl()
+                        || text.isFocusControl();
+            }
+        }
+
+        /*
+         * The listener installed on the target control.
+         */
+        private Listener targetControlListener;
+
+        /*
+         * The listener installed in order to close the popup.
+         */
+        private PopupCloserListener popupCloser;
+
+        /*
+         * The table used to show the list of proposals.
+         */
+        private Table proposalTable;
+
+        /*
+         * The proposals to be shown (cached to avoid repeated requests).
+         */
+        private IContentProposal[] proposals;
+
+        /*
+         * Secondary popup used to show detailed information about the selected
+         * proposal..
+         */
+        private InfoPopupDialog infoPopup;
+
+        /*
+         * Flag indicating whether there is a pending secondary popup update.
+         */
+        private bool pendingDescriptionUpdate = false;
+
+        /*
+         * Filter text - tracked while popup is open, only if we are told to
+         * filter
+         */
+        private String filterText = EMPTY;
+
+        /**
+         * Constructs a new instance of this popup, specifying the control for
+         * which this popup is showing content, and how the proposals should be
+         * obtained and displayed.
+         *
+         * @param infoText
+         *            Text to be shown in a lower info area, or
+         *            <code>null</code> if there is no info area.
+         */
+        this(String infoText, IContentProposal[] proposals) {
+            // IMPORTANT: Use of SWT.ON_TOP is critical here for ensuring
+            // that the target control retains focus on Mac and Linux. Without
+            // it, the focus will disappear, keystrokes will not go to the
+            // popup, and the popup closer will wrongly close the popup.
+            // On platforms where SWT.ON_TOP overrides SWT.RESIZE, we will live
+            // with this.
+            // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=126138
+            super(control.getShell(), SWT.RESIZE | SWT.ON_TOP, false, false,
+                    false, false, null, infoText);
+            this.proposals = proposals;
+        }
+
+        /*
+         * (non-Javadoc)
+         * @see org.eclipse.jface.dialogs.PopupDialog#getForeground()
+         */
+        protected Color getForeground() {
+            return JFaceResources.getColorRegistry().get(
+                    JFacePreferences.CONTENT_ASSIST_FOREGROUND_COLOR);
+        }
+
+        /*
+         * (non-Javadoc)
+         * @see org.eclipse.jface.dialogs.PopupDialog#getBackground()
+         */
+        protected Color getBackground() {
+            return JFaceResources.getColorRegistry().get(
+                    JFacePreferences.CONTENT_ASSIST_BACKGROUND_COLOR);
+        }
+
+        /*
+         * Creates the content area for the proposal popup. This creates a table
+         * and places it inside the composite. The table will contain a list of
+         * all the proposals.
+         *
+         * @param parent The parent composite to contain the dialog area; must
+         * not be <code>null</code>.
+         */
+        protected override final Control createDialogArea(Composite parent) {
+            // Use virtual where appropriate (see flag definition).
+            if (USE_VIRTUAL) {
+                proposalTable = new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL
+                        | SWT.VIRTUAL);
+
+                Listener listener = new class Listener {
+                    public void handleEvent(Event event) {
+                        handleSetData(event);
+                    }
+                };
+                proposalTable.addListener(SWT.SetData, listener);
+            } else {
+                proposalTable = new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL);
+            }
+
+            // set the proposals to force population of the table.
+            setProposals(filterProposals(proposals, filterText));
+
+            proposalTable.setHeaderVisible(false);
+            proposalTable.addSelectionListener(new class SelectionListener {
+
+                public void widgetSelected(SelectionEvent e) {
+                    // If a proposal has been selected, show it in the secondary
+                    // popup. Otherwise close the popup.
+                    if (e.item is null) {
+                        if (infoPopup !is null) {
+                            infoPopup.close();
+                        }
+                    } else {
+                        showProposalDescription();
+                    }
+                }
+
+                // Default selection was made. Accept the current proposal.
+                public void widgetDefaultSelected(SelectionEvent e) {
+                    acceptCurrentProposal();
+                }
+            });
+            return proposalTable;
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.jface.dialogs.PopupDialog.adjustBounds()
+         */
+        protected override void adjustBounds() {
+            // Get our control's location in display coordinates.
+            Point location = control.getDisplay().map(control.getParent(),
+                    null, control.getLocation());
+            int initialX = location.x + POPUP_OFFSET;
+            int initialY = location.y + control.getSize().y + POPUP_OFFSET;
+            // If we are inserting content, use the cursor position to
+            // position the control.
+            if (getProposalAcceptanceStyle() is PROPOSAL_INSERT) {
+                Rectangle insertionBounds = controlContentAdapter
+                        .getInsertionBounds(control);
+                initialX = initialX + insertionBounds.x;
+                initialY = location.y + insertionBounds.y
+                        + insertionBounds.height;
+            }
+
+            // If there is no specified size, force it by setting
+            // up a layout on the table.
+            if (popupSize is null) {
+                GridData data = new GridData(GridData.FILL_BOTH);
+                data.heightHint = proposalTable.getItemHeight()
+                        * POPUP_CHAR_HEIGHT;
+                data.widthHint = Math.max(control.getSize().x,
+                        POPUP_MINIMUM_WIDTH);
+                proposalTable.setLayoutData(data);
+                getShell().pack();
+                popupSize = getShell().getSize();
+            }
+            getShell().setBounds(initialX, initialY, popupSize.x, popupSize.y);
+
+            // Now set up a listener to monitor any changes in size.
+            getShell().addListener(SWT.Resize, new class Listener {
+                public void handleEvent(Event e) {
+                    popupSize = getShell().getSize();
+                    if (infoPopup !is null) {
+                        infoPopup.adjustBounds();
+                    }
+                }
+            });
+        }
+
+        /*
+         * Handle the set data event. Set the item data of the requested item to
+         * the corresponding proposal in the proposal cache.
+         */
+        private void handleSetData(Event event) {
+            TableItem item = cast(TableItem) event.item;
+            int index = proposalTable.indexOf(item);
+
+            if (0 <= index && index < proposals.length) {
+                IContentProposal current = proposals[index];
+                item.setText(getString(current));
+                item.setImage(getImage(current));
+                item.setData(cast(Object)current);
+            } else {
+                // this should not happen, but does on win32
+            }
+        }
+
+        /*
+         * Caches the specified proposals and repopulates the table if it has
+         * been created.
+         */
+        private void setProposals(IContentProposal[] newProposals) {
+            if (newProposals is null || newProposals.length is 0) {
+                newProposals = getEmptyProposalArray();
+            }
+            this.proposals = newProposals;
+
+            // If there is a table
+            if (isValid()) {
+                final int newSize = newProposals.length;
+                if (USE_VIRTUAL) {
+                    // Set and clear the virtual table. Data will be
+                    // provided in the SWT.SetData event handler.
+                    proposalTable.setItemCount(newSize);
+                    proposalTable.clearAll();
+                } else {
+                    // Populate the table manually
+                    proposalTable.setRedraw(false);
+                    proposalTable.setItemCount(newSize);
+                    TableItem[] items = proposalTable.getItems();
+                    for (int i = 0; i < items.length; i++) {
+                        TableItem item = items[i];
+                        IContentProposal proposal = newProposals[i];
+                        item.setText(getString(proposal));
+                        item.setImage(getImage(proposal));
+                        item.setData(cast(Object)proposal);
+                    }
+                    proposalTable.setRedraw(true);
+                }
+                // Default to the first selection if there is content.
+                if (newProposals.length > 0) {
+                    selectProposal(0);
+                } else {
+                    // No selection, close the secondary popup if it was open
+                    if (infoPopup !is null) {
+                        infoPopup.close();
+                    }
+
+                }
+            }
+        }
+
+        /*
+         * Get the string for the specified proposal. Always return a String of
+         * some kind.
+         */
+        private String getString(IContentProposal proposal) {
+            if (proposal is null) {
+                return EMPTY;
+            }
+            if (labelProvider is null) {
+                return proposal.getLabel() is null ? proposal.getContent()
+                        : proposal.getLabel();
+            }
+            return labelProvider.getText(cast(Object)proposal);
+        }
+
+        /*
+         * Get the image for the specified proposal. If there is no image
+         * available, return null.
+         */
+        private Image getImage(IContentProposal proposal) {
+            if (proposal is null || labelProvider is null) {
+                return null;
+            }
+            return labelProvider.getImage(cast(Object)proposal);
+        }
+
+        /*
+         * Return an empty array. Used so that something always shows in the
+         * proposal popup, even if no proposal provider was specified.
+         */
+        private IContentProposal[] getEmptyProposalArray() {
+            return new IContentProposal[0];
+        }
+
+        /*
+         * Answer true if the popup is valid, which means the table has been
+         * created and not disposed.
+         */
+        private bool isValid() {
+            return proposalTable !is null && !proposalTable.isDisposed();
+        }
+
+        /*
+         * Return whether the receiver has focus. Since 3.4, this includes a
+         * check for whether the info popup has focus.
+         */
+        private bool hasFocus() {
+            if (!isValid()) {
+                return false;
+            }
+            if (getShell().isFocusControl() || proposalTable.isFocusControl()) {
+                return true;
+            }
+            if (infoPopup !is null && infoPopup.hasFocus()) {
+                return true;
+            }
+            return false;
+        }
+
+        /*
+         * Return the current selected proposal.
+         */
+        private IContentProposal getSelectedProposal() {
+            if (isValid()) {
+                int i = proposalTable.getSelectionIndex();
+                if (proposals is null || i < 0 || i >= proposals.length) {
+                    return null;
+                }
+                return proposals[i];
+            }
+            return null;
+        }
+
+        /*
+         * Select the proposal at the given index.
+         */
+        private void selectProposal(int index) {
+            Assert
+                    .isTrue(index >= 0,
+                            "Proposal index should never be negative"); //$NON-NLS-1$
+            if (!isValid() || proposals is null || index >= proposals.length) {
+                return;
+            }
+            proposalTable.setSelection(index);
+            proposalTable.showSelection();
+
+            showProposalDescription();
+        }
+
+        /**
+         * Opens this ContentProposalPopup. This method is extended in order to
+         * add the control listener when the popup is opened and to invoke the
+         * secondary popup if applicable.
+         *
+         * @return the return code
+         *
+         * @see org.eclipse.jface.window.Window#open()
+         */
+        public override int open() {
+            int value = super.open();
+            if (popupCloser is null) {
+                popupCloser = new PopupCloserListener();
+            }
+            popupCloser.installListeners();
+            IContentProposal p = getSelectedProposal();
+            if (p !is null) {
+                showProposalDescription();
+            }
+            return value;
+        }
+
+        /**
+         * Closes this popup. This method is extended to remove the control
+         * listener.
+         *
+         * @return <code>true</code> if the window is (or was already) closed,
+         *         and <code>false</code> if it is still open
+         */
+        public override bool close() {
+            popupCloser.removeListeners();
+            if (infoPopup !is null) {
+                infoPopup.close();
+            }
+            bool ret = super.close();
+            notifyPopupClosed();
+            return ret;
+        }
+
+        /*
+         * Show the currently selected proposal's description in a secondary
+         * popup.
+         */
+        private void showProposalDescription() {
+            // If we do not already have a pending update, then
+            // create a thread now that will show the proposal description
+            if (!pendingDescriptionUpdate) {
+                // Create a thread that will sleep for the specified delay
+                // before creating the popup. We do not use Jobs since this
+                // code must be able to run independently of the Eclipse
+                // runtime.
+                auto r = new class() Runnable {
+                    public void run() {
+                        pendingDescriptionUpdate = true;
+
+                        try {
+                            JThread.sleep( POPUP_DELAY );
+                        }
+                        catch (InterruptedException e) {
+                        }
+
+                        if (!isValid()) {
+                            return;
+                        }
+                        getShell().getDisplay().syncExec(new class() Runnable {
+                            public void run() {
+                                // Query the current selection since we have
+                                // been delayed
+                                IContentProposal p = getSelectedProposal();
+                                if (p !is null) {
+                                    String description = p.getDescription();
+                                    if (description !is null) {
+                                        if (infoPopup is null) {
+                                            infoPopup = new InfoPopupDialog(
+                                                    getShell());
+                                            infoPopup.open();
+                                            infoPopup
+                                                    .getShell()
+                                                    .addDisposeListener(
+                                                            new class DisposeListener {
+                                                                public void widgetDisposed(
+                                                                        DisposeEvent event) {
+                                                                    infoPopup = null;
+                                                                }
+                                                            });
+                                        }
+                                        infoPopup.setContents(p
+                                                .getDescription());
+                                    } else if (infoPopup !is null) {
+                                        infoPopup.close();
+                                    }
+                                    pendingDescriptionUpdate = false;
+
+                                }
+                            }
+                        });
+                    }
+                };
+                JThread t = new JThread(r);
+                t.start();
+            }
+        }
+
+        /*
+         * Accept the current proposal.
+         */
+        private void acceptCurrentProposal() {
+            // Close before accepting the proposal. This is important
+            // so that the cursor position can be properly restored at
+            // acceptance, which does not work without focus on some controls.
+            // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=127108
+            IContentProposal proposal = getSelectedProposal();
+            close();
+            proposalAccepted(proposal);
+        }
+
+        /*
+         * Request the proposals from the proposal provider, and recompute any
+         * caches. Repopulate the popup if it is open.
+         */
+        private void recomputeProposals(String filterText) {
+            IContentProposal[] allProposals = getProposals();
+            // If the non-filtered proposal list is empty, we should
+            // close the popup.
+            // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=147377
+            if (allProposals.length is 0) {
+                proposals = allProposals;
+                close();
+            } else {
+                // Keep the popup open, but filter by any provided filter text
+                setProposals(filterProposals(allProposals, filterText));
+            }
+        }
+
+        /*
+         * In an async block, request the proposals. This is used when clients
+         * are in the middle of processing an event that affects the widget
+         * content. By using an async, we ensure that the widget content is up
+         * to date with the event.
+         */
+        private void asyncRecomputeProposals(String filterText) {
+            if (isValid()) {
+                control.getDisplay().asyncExec(new class(filterText) Runnable {
+                    String filterText_;
+                    this(String a){filterText_=a;}
+                    public void run() {
+                        recordCursorPosition();
+                        recomputeProposals(filterText_);
+                    }
+                });
+            } else {
+                recomputeProposals(filterText);
+            }
+        }
+
+        /*
+         * Filter the provided list of content proposals according to the filter
+         * text.
+         */
+        private IContentProposal[] filterProposals(
+                IContentProposal[] proposals, String filterString) {
+            if (filterString.length is 0) {
+                return proposals;
+            }
+
+            // Check each string for a match. Use the string displayed to the
+            // user, not the proposal content.
+            scope IContentProposal[] list = new IContentProposal[proposals.length];
+            int idx = 0;
+            for (int i = 0; i < proposals.length; i++) {
+                String string = getString(proposals[i]);
+                if (string.length >= filterString.length
+                        && string.substring(0, filterString.length)
+                                .equalsIgnoreCase(filterString)) {
+                    list[idx++] = proposals[i];
+                }
+
+            }
+            return list[ 0 .. idx ].dup;
+        }
+
+        Listener getTargetControlListener() {
+            if (targetControlListener is null) {
+                targetControlListener = new TargetControlListener();
+            }
+            return targetControlListener;
+        }
+    }
+
+    /**
+     * Flag that controls the printing of debug info.
+     */
+    public static const bool DEBUG = false;
+
+    /**
+     * Indicates that a chosen proposal should be inserted into the field.
+     */
+    public static const int PROPOSAL_INSERT = 1;
+
+    /**
+     * Indicates that a chosen proposal should replace the entire contents of
+     * the field.
+     */
+    public static const int PROPOSAL_REPLACE = 2;
+
+    /**
+     * Indicates that the contents of the control should not be modified when a
+     * proposal is chosen. This is typically used when a client needs more
+     * specialized behavior when a proposal is chosen. In this case, clients
+     * typically register an IContentProposalListener so that they are notified
+     * when a proposal is chosen.
+     */
+    public static const int PROPOSAL_IGNORE = 3;
+
+    /**
+     * Indicates that there should be no filter applied as keys are typed in the
+     * popup.
+     */
+    public static const int FILTER_NONE = 1;
+
+    /**
+     * Indicates that a single character filter applies as keys are typed in the
+     * popup.
+     */
+    public static const int FILTER_CHARACTER = 2;
+
+    /**
+     * Indicates that a cumulative filter applies as keys are typed in the
+     * popup. That is, each character typed will be added to the filter.
+     *
+     * @deprecated As of 3.4, filtering that is sensitive to changes in the
+     *             control content should be performed by the supplied
+     *             {@link IContentProposalProvider}, such as that performed by
+     *             {@link SimpleContentProposalProvider}
+     */
+    public static const int FILTER_CUMULATIVE = 3;
+
+    /*
+     * Set to <code>true</code> to use a Table with SWT.VIRTUAL. This is a
+     * workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=98585#c40
+     * The corresponding SWT bug is
+     * https://bugs.eclipse.org/bugs/show_bug.cgi?id=90321
+     */
+    private static const bool USE_VIRTUAL = !"motif".equals(SWT.getPlatform()); //$NON-NLS-1$
+
+    /*
+     * The delay before showing a secondary popup.
+     */
+    private static const int POPUP_DELAY = 750;
+
+    /*
+     * The character height hint for the popup. May be overridden by using
+     * setInitialPopupSize.
+     */
+    private static const int POPUP_CHAR_HEIGHT = 10;
+
+    /*
+     * The minimum pixel width for the popup. May be overridden by using
+     * setInitialPopupSize.
+     */
+    private static const int POPUP_MINIMUM_WIDTH = 300;
+
+    /*
+     * The pixel offset of the popup from the bottom corner of the control.
+     */
+    private static const int POPUP_OFFSET = 3;
+
+    /*
+     * Empty string.
+     */
+    private static const String EMPTY = ""; //$NON-NLS-1$
+
+    /*
+     * The object that provides content proposals.
+     */
+    private IContentProposalProvider proposalProvider;
+
+    /*
+     * A label provider used to display proposals in the popup, and to extract
+     * Strings from non-String proposals.
+     */
+    private ILabelProvider labelProvider;
+
+    /*
+     * The control for which content proposals are provided.
+     */
+    private Control control;
+
+    /*
+     * The adapter used to extract the String contents from an arbitrary
+     * control.
+     */
+    private IControlContentAdapter controlContentAdapter;
+
+    /*
+     * The popup used to show proposals.
+     */
+    private ContentProposalPopup popup;
+
+    /*
+     * The keystroke that signifies content proposals should be shown.
+     */
+    private KeyStroke triggerKeyStroke;
+
+    /*
+     * The String containing characters that auto-activate the popup.
+     */
+    private String autoActivateString;
+
+    /*
+     * Integer that indicates how an accepted proposal should affect the
+     * control. One of PROPOSAL_IGNORE, PROPOSAL_INSERT, or PROPOSAL_REPLACE.
+     * Default value is PROPOSAL_INSERT.
+     */
+    private int proposalAcceptanceStyle = PROPOSAL_INSERT;
+
+    /*
+     * A bool that indicates whether key events received while the proposal
+     * popup is open should also be propagated to the control. Default value is
+     * true.
+     */
+    private bool propagateKeys = true;
+
+    /*
+     * Integer that indicates the filtering style. One of FILTER_CHARACTER,
+     * FILTER_CUMULATIVE, FILTER_NONE.
+     */
+    private int filterStyle = FILTER_NONE;
+
+    /*
+     * The listener we install on the control.
+     */
+    private Listener controlListener;
+
+    /*
+     * The list of IContentProposalListener listeners.
+     */
+    private ListenerList proposalListeners;
+
+    /*
+     * The list of IContentProposalListener2 listeners.
+     */
+    private ListenerList proposalListeners2;
+
+    /*
+     * Flag that indicates whether the adapter is enabled. In some cases,
+     * adapters may be installed but depend upon outside state.
+     */
+    private bool isEnabled_ = true;
+
+    /*
+     * The delay in milliseconds used when autoactivating the popup.
+     */
+    private int autoActivationDelay = 0;
+
+    /*
+     * A bool indicating whether a keystroke has been received. Used to see
+     * if an autoactivation delay was interrupted by a keystroke.
+     */
+    private bool receivedKeyDown;
+
+    /*
+     * The desired size in pixels of the proposal popup.
+     */
+    private Point popupSize;
+
+    /*
+     * The remembered position of the insertion position. Not all controls will
+     * restore the insertion position if the proposal popup gets focus, so we
+     * need to remember it.
+     */
+    private int insertionPos = -1;
+
+    /*
+     * The remembered selection range. Not all controls will restore the
+     * selection position if the proposal popup gets focus, so we need to
+     * remember it.
+     */
+    private Point selectionRange;
+
+    /*
+     * A flag that indicates that we are watching modify events
+     */
+    private bool watchModify = false;
+
+    /**
+     * Construct a content proposal adapter that can assist the user with
+     * choosing content for the field.
+     *
+     * @param control
+     *            the control for which the adapter is providing content assist.
+     *            May not be <code>null</code>.
+     * @param controlContentAdapter
+     *            the <code>IControlContentAdapter</code> used to obtain and
+     *            update the control's contents as proposals are accepted. May
+     *            not be <code>null</code>.
+     * @param proposalProvider
+     *            the <code>IContentProposalProvider</code> used to obtain
+     *            content proposals for this control, or <code>null</code> if
+     *            no content proposal is available.
+     * @param keyStroke
+     *            the keystroke that will invoke the content proposal popup. If
+     *            this value is <code>null</code>, then proposals will be
+     *            activated automatically when any of the auto activation
+     *            characters are typed.
+     * @param autoActivationCharacters
+     *            An array of characters that trigger auto-activation of content
+     *            proposal. If specified, these characters will trigger
+     *            auto-activation of the proposal popup, regardless of whether
+     *            an explicit invocation keyStroke was specified. If this
+     *            parameter is <code>null</code>, then only a specified
+     *            keyStroke will invoke content proposal. If this parameter is
+     *            <code>null</code> and the keyStroke parameter is
+     *            <code>null</code>, then all alphanumeric characters will
+     *            auto-activate content proposal.
+     */
+    public this(Control control,
+            IControlContentAdapter controlContentAdapter,
+            IContentProposalProvider proposalProvider, KeyStroke keyStroke,
+            char[] autoActivationCharacters) {
+        //DWT_Init
+        proposalListeners = new ListenerList();
+        proposalListeners2 = new ListenerList();
+        selectionRange = new Point(-1, -1);
+        //super();
+        // We always assume the control and content adapter are valid.
+        Assert.isNotNull(cast(Object)control);
+        Assert.isNotNull(cast(Object)controlContentAdapter);
+        this.control = control;
+        this.controlContentAdapter = controlContentAdapter;
+
+        // The rest of these may be null
+        this.proposalProvider = proposalProvider;
+        this.triggerKeyStroke = keyStroke;
+        if (autoActivationCharacters.length !is 0 ) {
+            this.autoActivateString = autoActivationCharacters;
+        }
+        addControlListener(control);
+    }
+
+    /**
+     * Get the control on which the content proposal adapter is installed.
+     *
+     * @return the control on which the proposal adapter is installed.
+     */
+    public Control getControl() {
+        return control;
+    }
+
+    /**
+     * Get the label provider that is used to show proposals.
+     *
+     * @return the {@link ILabelProvider} used to show proposals, or
+     *         <code>null</code> if one has not been installed.
+     */
+    public ILabelProvider getLabelProvider() {
+        return labelProvider;
+    }
+
+    /**
+     * Return a bool indicating whether the receiver is enabled.
+     *
+     * @return <code>true</code> if the adapter is enabled, and
+     *         <code>false</code> if it is not.
+     */
+    public bool isEnabled() {
+        return isEnabled_;
+    }
+
+    /**
+     * Set the label provider that is used to show proposals. The lifecycle of
+     * the specified label provider is not managed by this adapter. Clients must
+     * dispose the label provider when it is no longer needed.
+     *
+     * @param labelProvider
+     *            the (@link ILabelProvider} used to show proposals.
+     */
+    public void setLabelProvider(ILabelProvider labelProvider) {
+        this.labelProvider = labelProvider;
+    }
+
+    /**
+     * Return the proposal provider that provides content proposals given the
+     * current content of the field. A value of <code>null</code> indicates
+     * that there are no content proposals available for the field.
+     *
+     * @return the {@link IContentProposalProvider} used to show proposals. May
+     *         be <code>null</code>.
+     */
+    public IContentProposalProvider getContentProposalProvider() {
+        return proposalProvider;
+    }
+
+    /**
+     * Set the content proposal provider that is used to show proposals.
+     *
+     * @param proposalProvider
+     *            the {@link IContentProposalProvider} used to show proposals
+     */
+    public void setContentProposalProvider(
+            IContentProposalProvider proposalProvider) {
+        this.proposalProvider = proposalProvider;
+    }
+
+    /**
+     * Return the array of characters on which the popup is autoactivated.
+     *
+     * @return An array of characters that trigger auto-activation of content
+     *         proposal. If specified, these characters will trigger
+     *         auto-activation of the proposal popup, regardless of whether an
+     *         explicit invocation keyStroke was specified. If this parameter is
+     *         <code>null</code>, then only a specified keyStroke will invoke
+     *         content proposal. If this value is <code>null</code> and the
+     *         keyStroke value is <code>null</code>, then all alphanumeric
+     *         characters will auto-activate content proposal.
+     */
+    public char[] getAutoActivationCharacters() {
+        if (autoActivateString is null) {
+            return null;
+        }
+        return autoActivateString/+.toCharArray()+/;
+    }
+
+    /**
+     * Set the array of characters that will trigger autoactivation of the
+     * popup.
+     *
+     * @param autoActivationCharacters
+     *            An array of characters that trigger auto-activation of content
+     *            proposal. If specified, these characters will trigger
+     *            auto-activation of the proposal popup, regardless of whether
+     *            an explicit invocation keyStroke was specified. If this
+     *            parameter is <code>null</code>, then only a specified
+     *            keyStroke will invoke content proposal. If this parameter is
+     *            <code>null</code> and the keyStroke value is
+     *            <code>null</code>, then all alphanumeric characters will
+     *            auto-activate content proposal.
+     *
+     */
+    public void setAutoActivationCharacters(char[] autoActivationCharacters) {
+        if (autoActivationCharacters.length is 0) {
+            this.autoActivateString = null;
+        } else {
+            this.autoActivateString = autoActivationCharacters;
+        }
+    }
+
+    /**
+     * Set the delay, in milliseconds, used before any autoactivation is
+     * triggered.
+     *
+     * @return the time in milliseconds that will pass before a popup is
+     *         automatically opened
+     */
+    public int getAutoActivationDelay() {
+        return autoActivationDelay;
+
+    }
+
+    /**
+     * Set the delay, in milliseconds, used before autoactivation is triggered.
+     *
+     * @param delay
+     *            the time in milliseconds that will pass before a popup is
+     *            automatically opened
+     */
+    public void setAutoActivationDelay(int delay) {
+        autoActivationDelay = delay;
+
+    }
+
+    /**
+     * Get the integer style that indicates how an accepted proposal affects the
+     * control's content.
+     *
+     * @return a constant indicating how an accepted proposal should affect the
+     *         control's content. Should be one of <code>PROPOSAL_INSERT</code>,
+     *         <code>PROPOSAL_REPLACE</code>, or <code>PROPOSAL_IGNORE</code>.
+     *         (Default is <code>PROPOSAL_INSERT</code>).
+     */
+    public int getProposalAcceptanceStyle() {
+        return proposalAcceptanceStyle;
+    }
+
+    /**
+     * Set the integer style that indicates how an accepted proposal affects the
+     * control's content.
+     *
+     * @param acceptance
+     *            a constant indicating how an accepted proposal should affect
+     *            the control's content. Should be one of
+     *            <code>PROPOSAL_INSERT</code>, <code>PROPOSAL_REPLACE</code>,
+     *            or <code>PROPOSAL_IGNORE</code>
+     */
+    public void setProposalAcceptanceStyle(int acceptance) {
+        proposalAcceptanceStyle = acceptance;
+    }
+
+    /**
+     * Return the integer style that indicates how keystrokes affect the content
+     * of the proposal popup while it is open.
+     *
+     * @return a constant indicating how keystrokes in the proposal popup affect
+     *         filtering of the proposals shown. <code>FILTER_NONE</code>
+     *         specifies that no filtering will occur in the content proposal
+     *         list as keys are typed. <code>FILTER_CHARACTER</code> specifies
+     *         the content of the popup will be filtered by the most recently
+     *         typed character. <code>FILTER_CUMULATIVE</code> is deprecated
+     *         and no longer recommended. It specifies that the content of the
+     *         popup will be filtered by a string containing all the characters
+     *         typed since the popup has been open. The default is
+     *         <code>FILTER_NONE</code>.
+     */
+    public int getFilterStyle() {
+        return filterStyle;
+    }
+
+    /**
+     * Set the integer style that indicates how keystrokes affect the content of
+     * the proposal popup while it is open. Popup-based filtering is useful for
+     * narrowing and navigating the list of proposals provided once the popup is
+     * open. Filtering of the proposals will occur even when the control content
+     * is not affected by user typing. Note that automatic filtering is not used
+     * to achieve content-sensitive filtering such as auto-completion. Filtering
+     * that is sensitive to changes in the control content should be performed
+     * by the supplied {@link IContentProposalProvider}.
+     *
+     * @param filterStyle
+     *            a constant indicating how keystrokes received in the proposal
+     *            popup affect filtering of the proposals shown.
+     *            <code>FILTER_NONE</code> specifies that no automatic
+     *            filtering of the content proposal list will occur as keys are
+     *            typed in the popup. <code>FILTER_CHARACTER</code> specifies
+     *            that the content of the popup will be filtered by the most
+     *            recently typed character. <code>FILTER_CUMULATIVE</code> is
+     *            deprecated and no longer recommended. It specifies that the
+     *            content of the popup will be filtered by a string containing
+     *            all the characters typed since the popup has been open.
+     */
+    public void setFilterStyle(int filterStyle) {
+        this.filterStyle = filterStyle;
+    }
+
+    /**
+     * Return the size, in pixels, of the content proposal popup.
+     *
+     * @return a Point specifying the last width and height, in pixels, of the
+     *         content proposal popup.
+     */
+    public Point getPopupSize() {
+        return popupSize;
+    }
+
+    /**
+     * Set the size, in pixels, of the content proposal popup. This size will be
+     * used the next time the content proposal popup is opened.
+     *
+     * @param size
+     *            a Point specifying the desired width and height, in pixels, of
+     *            the content proposal popup.
+     */
+    public void setPopupSize(Point size) {
+        popupSize = size;
+    }
+
+    /**
+     * Get the bool that indicates whether key events (including
+     * auto-activation characters) received by the content proposal popup should
+     * also be propagated to the adapted control when the proposal popup is
+     * open.
+     *
+     * @return a bool that indicates whether key events (including
+     *         auto-activation characters) should be propagated to the adapted
+     *         control when the proposal popup is open. Default value is
+     *         <code>true</code>.
+     */
+    public bool getPropagateKeys() {
+        return propagateKeys;
+    }
+
+    /**
+     * Set the bool that indicates whether key events (including
+     * auto-activation characters) received by the content proposal popup should
+     * also be propagated to the adapted control when the proposal popup is
+     * open.
+     *
+     * @param propagateKeys
+     *            a bool that indicates whether key events (including
+     *            auto-activation characters) should be propagated to the
+     *            adapted control when the proposal popup is open.
+     */
+    public void setPropagateKeys(bool propagateKeys) {
+        this.propagateKeys = propagateKeys;
+    }
+
+    /**
+     * Return the content adapter that can get or retrieve the text contents
+     * from the adapter's control. This method is used when a client, such as a
+     * content proposal listener, needs to update the control's contents
+     * manually.
+     *
+     * @return the {@link IControlContentAdapter} which can update the control
+     *         text.
+     */
+    public IControlContentAdapter getControlContentAdapter() {
+        return controlContentAdapter;
+    }
+
+    /**
+     * Set the bool flag that determines whether the adapter is enabled.
+     *
+     * @param enabled
+     *            <code>true</code> if the adapter is enabled and responding
+     *            to user input, <code>false</code> if it is ignoring user
+     *            input.
+     *
+     */
+    public void setEnabled(bool enabled) {
+        // If we are disabling it while it's proposing content, close the
+        // content proposal popup.
+        if (isEnabled_ && !enabled) {
+            if (popup !is null) {
+                popup.close();
+            }
+        }
+        isEnabled_ = enabled;
+    }
+
+    /**
+     * Add the specified listener to the list of content proposal listeners that
+     * are notified when content proposals are chosen.
+     * </p>
+     *
+     * @param listener
+     *            the IContentProposalListener to be added as a listener. Must
+     *            not be <code>null</code>. If an attempt is made to register
+     *            an instance which is already registered with this instance,
+     *            this method has no effect.
+     *
+     * @see org.eclipse.jface.fieldassist.IContentProposalListener
+     */
+    public void addContentProposalListener(IContentProposalListener listener) {
+        proposalListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes the specified listener from the list of content proposal
+     * listeners that are notified when content proposals are chosen.
+     * </p>
+     *
+     * @param listener
+     *            the IContentProposalListener to be removed as a listener. Must
+     *            not be <code>null</code>. If the listener has not already
+     *            been registered, this method has no effect.
+     *
+     * @since 3.3
+     * @see org.eclipse.jface.fieldassist.IContentProposalListener
+     */
+    public void removeContentProposalListener(IContentProposalListener listener) {
+        proposalListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Add the specified listener to the list of content proposal listeners that
+     * are notified when a content proposal popup is opened or closed.
+     * </p>
+     *
+     * @param listener
+     *            the IContentProposalListener2 to be added as a listener. Must
+     *            not be <code>null</code>. If an attempt is made to register
+     *            an instance which is already registered with this instance,
+     *            this method has no effect.
+     *
+     * @since 3.3
+     * @see org.eclipse.jface.fieldassist.IContentProposalListener2
+     */
+    public void addContentProposalListener(IContentProposalListener2 listener) {
+        proposalListeners2.add(cast(Object)listener);
+    }
+
+    /**
+     * Remove the specified listener from the list of content proposal listeners
+     * that are notified when a content proposal popup is opened or closed.
+     * </p>
+     *
+     * @param listener
+     *            the IContentProposalListener2 to be removed as a listener.
+     *            Must not be <code>null</code>. If the listener has not
+     *            already been registered, this method has no effect.
+     *
+     * @since 3.3
+     * @see org.eclipse.jface.fieldassist.IContentProposalListener2
+     */
+    public void removeContentProposalListener(IContentProposalListener2 listener) {
+        proposalListeners2.remove(cast(Object)listener);
+    }
+
+    /*
+     * Add our listener to the control. Debug information to be left in until
+     * this support is stable on all platforms.
+     */
+    private void addControlListener(Control control) {
+        if (DEBUG) {
+            Stdout.formatln("ContentProposalListener#installControlListener()"); //$NON-NLS-1$
+        }
+
+        if (controlListener !is null) {
+            return;
+        }
+        controlListener = new class Listener {
+            public void handleEvent(Event e) {
+                if (!isEnabled_) {
+                    return;
+                }
+
+                switch (e.type) {
+                case SWT.Traverse:
+                case SWT.KeyDown:
+                    if (DEBUG) {
+                        StringBuffer sb;
+                        if (e.type is SWT.Traverse) {
+                            sb = new StringBuffer("Traverse"); //$NON-NLS-1$
+                        } else {
+                            sb = new StringBuffer("KeyDown"); //$NON-NLS-1$
+                        }
+                        sb.append(" received by adapter"); //$NON-NLS-1$
+                        dump(sb.toString(), e);
+                    }
+                    // If the popup is open, it gets first shot at the
+                    // keystroke and should set the doit flags appropriately.
+                    if (popup !is null) {
+                        popup.getTargetControlListener().handleEvent(e);
+                        if (DEBUG) {
+                            StringBuffer sb;
+                            if (e.type is SWT.Traverse) {
+                                sb = new StringBuffer("Traverse"); //$NON-NLS-1$
+                            } else {
+                                sb = new StringBuffer("KeyDown"); //$NON-NLS-1$
+                            }
+                            sb.append(" after being handled by popup"); //$NON-NLS-1$
+                            dump(sb.toString(), e);
+                        }
+
+                        return;
+                    }
+
+                    // We were only listening to traverse events for the popup
+                    if (e.type is SWT.Traverse) {
+                        return;
+                    }
+
+                    // The popup is not open. We are looking at keydown events
+                    // for a trigger to open the popup.
+                    if (triggerKeyStroke !is null) {
+                        // Either there are no modifiers for the trigger and we
+                        // check the character field...
+                        if ((triggerKeyStroke.getModifierKeys() is KeyStroke.NO_KEY && triggerKeyStroke
+                                .getNaturalKey() is e.character)
+                                ||
+                                // ...or there are modifiers, in which case the
+                                // keycode and state must match
+                                (triggerKeyStroke.getNaturalKey() is e.keyCode && ((triggerKeyStroke
+                                        .getModifierKeys() & e.stateMask) is triggerKeyStroke
+                                        .getModifierKeys()))) {
+                            // We never propagate the keystroke for an explicit
+                            // keystroke invocation of the popup
+                            e.doit = false;
+                            openProposalPopup(false);
+                            return;
+                        }
+                    }
+                    /*
+                     * The triggering keystroke was not invoked. If a character
+                     * was typed, compare it to the autoactivation characters.
+                     */
+                    if (e.character !is 0) {
+                        if (autoActivateString !is null) {
+                            if (autoActivateString.indexOf(e.character) >= 0) {
+                                autoActivate();
+                            } else {
+                                // No autoactivation occurred, so record the key
+                                // down as a means to interrupt any
+                                // autoactivation
+                                // that is pending due to autoactivation delay.
+                                receivedKeyDown = true;
+                            }
+                        } else {
+                            // The autoactivate string is null. If the trigger
+                            // is also null, we want to act on any modification
+                            // to the content.  Set a flag so we'll catch this
+                            // in the modify event.
+                            if (triggerKeyStroke is null) {
+                                watchModify = true;
+                            }
+                        }
+                    }
+                    break;
+
+                // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=147377
+                // Given that we will close the popup when there are no valid
+                // proposals, we must reopen it when there are. This means
+                // we should check modifications in those cases.
+                // See also https://bugs.eclipse.org/bugs/show_bug.cgi?id=183650
+                // The watchModify flag ensures that we don't autoactivate if
+                // the content change was caused by something other than typing.
+                case SWT.Modify:
+                    if (triggerKeyStroke is null && autoActivateString is null
+                            && watchModify) {
+                        if (DEBUG) {
+                            dump("Modify event triggers autoactivation", e); //$NON-NLS-1$
+                        }
+                        watchModify = false;
+                        // We don't autoactivate if the net change is no
+                        // content.  In other words, backspacing to empty
+                        // should never cause a popup to open.
+                        if (!isControlContentEmpty()) {
+                            autoActivate();
+                        }
+                    }
+                    break;
+                default:
+                    break;
+                }
+            }
+
+            /**
+             * Dump the given events to "standard" output.
+             *
+             * @param who
+             *            who is dumping the event
+             * @param e
+             *            the event
+             */
+            private void dump(String who, Event e) {
+                StringBuffer sb = new StringBuffer(
+                        "--- [ContentProposalAdapter]\n"); //$NON-NLS-1$
+                sb.append(who);
+                sb.append(Format(" - e: keyCode={}{}", e.keyCode, hex(e.keyCode))); //$NON-NLS-1$
+                sb.append(Format("; character={}{}", e.character, hex(e.character))); //$NON-NLS-1$
+                sb.append(Format("; stateMask={}{}", e.stateMask, hex(e.stateMask))); //$NON-NLS-1$
+                sb.append(Format("; doit={}", e.doit)); //$NON-NLS-1$
+                sb.append(Format("; detail={}", e.detail, hex(e.detail))); //$NON-NLS-1$
+                sb.append(Format("; widget={}", e.widget)); //$NON-NLS-1$
+                Stdout.formatln("{}",sb.toString);
+            }
+
+            private String hex(int i) {
+                return Format("[0x{:X}]", i); //$NON-NLS-1$
+            }
+        };
+        control.addListener(SWT.KeyDown, controlListener);
+        control.addListener(SWT.Traverse, controlListener);
+        control.addListener(SWT.Modify, controlListener);
+
+        if (DEBUG) {
+            Stdout.formatln("ContentProposalAdapter#installControlListener() - installed"); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Open the proposal popup and display the proposals provided by the
+     * proposal provider. If there are no proposals to be shown, do not show the
+     * popup. This method returns immediately. That is, it does not wait for the
+     * popup to open or a proposal to be selected.
+     *
+     * @param autoActivated
+     *            a bool indicating whether the popup was autoactivated. If
+     *            false, a beep will sound when no proposals can be shown.
+     */
+    private void openProposalPopup(bool autoActivated) {
+        if (isValid()) {
+            if (popup is null) {
+                // Check whether there are any proposals to be shown.
+                recordCursorPosition(); // must be done before getting proposals
+                IContentProposal[] proposals = getProposals();
+                if (proposals.length > 0) {
+                    if (DEBUG) {
+                        Stdout.formatln("POPUP OPENED BY PRECEDING EVENT"); //$NON-NLS-1$
+                    }
+                    recordCursorPosition();
+                    popup = new ContentProposalPopup(null, proposals);
+                    popup.open();
+                    popup.getShell().addDisposeListener(new class DisposeListener {
+                        public void widgetDisposed(DisposeEvent event) {
+                            popup = null;
+                        }
+                    });
+                    notifyPopupOpened();
+                } else if (!autoActivated) {
+                    getControl().getDisplay().beep();
+                }
+            }
+        }
+    }
+
+    /**
+     * Open the proposal popup and display the proposals provided by the
+     * proposal provider. This method returns immediately. That is, it does not
+     * wait for a proposal to be selected. This method is used by subclasses to
+     * explicitly invoke the opening of the popup. If there are no proposals to
+     * show, the popup will not open and a beep will be sounded.
+     */
+    protected void openProposalPopup() {
+        openProposalPopup(false);
+    }
+
+    /**
+     * Close the proposal popup without accepting a proposal. This method
+     * returns immediately, and has no effect if the proposal popup was not
+     * open. This method is used by subclasses to explicitly close the popup
+     * based on additional logic.
+     *
+     * @since 3.3
+     */
+    protected void closeProposalPopup() {
+        if (popup !is null) {
+            popup.close();
+        }
+    }
+
+    /*
+     * A content proposal has been accepted. Update the control contents
+     * accordingly and notify any listeners.
+     *
+     * @param proposal the accepted proposal
+     */
+    private void proposalAccepted(IContentProposal proposal) {
+        switch (proposalAcceptanceStyle) {
+        case (PROPOSAL_REPLACE):
+            setControlContent(proposal.getContent(), proposal
+                    .getCursorPosition());
+            break;
+        case (PROPOSAL_INSERT):
+            insertControlContent(proposal.getContent(), proposal
+                    .getCursorPosition());
+            break;
+        default:
+            // do nothing. Typically a listener is installed to handle this in
+            // a custom way.
+            break;
+        }
+
+        // In all cases, notify listeners of an accepted proposal.
+        notifyProposalAccepted(proposal);
+    }
+
+    /*
+     * Set the text content of the control to the specified text, setting the
+     * cursorPosition at the desired location within the new contents.
+     */
+    private void setControlContent(String text, int cursorPosition) {
+        if (isValid()) {
+            // should already be false, but just in case.
+            watchModify = false;
+            controlContentAdapter.setControlContents(control, text,
+                    cursorPosition);
+        }
+    }
+
+    /*
+     * Insert the specified text into the control content, setting the
+     * cursorPosition at the desired location within the new contents.
+     */
+    private void insertControlContent(String text, int cursorPosition) {
+        if (isValid()) {
+            // should already be false, but just in case.
+            watchModify = false;
+            // Not all controls preserve their selection index when they lose
+            // focus, so we must set it explicitly here to what it was before
+            // the popup opened.
+            // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=127108
+            // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=139063
+            if ((null !is cast(IControlContentAdapter2)controlContentAdapter)
+                    && selectionRange.x !is -1) {
+                (cast(IControlContentAdapter2) controlContentAdapter).setSelection(
+                        control, selectionRange);
+            } else if (insertionPos !is -1) {
+                controlContentAdapter.setCursorPosition(control, insertionPos);
+            }
+            controlContentAdapter.insertControlContents(control, text,
+                    cursorPosition);
+        }
+    }
+
+    /*
+     * Check that the control and content adapter are valid.
+     */
+    private bool isValid() {
+        return control !is null && !control.isDisposed()
+                && controlContentAdapter !is null;
+    }
+
+    /*
+     * Record the control's cursor position.
+     */
+    private void recordCursorPosition() {
+        if (isValid()) {
+            IControlContentAdapter adapter = getControlContentAdapter();
+            insertionPos = adapter.getCursorPosition(control);
+            // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=139063
+            if (null !is cast(IControlContentAdapter2)adapter ) {
+                selectionRange = (cast(IControlContentAdapter2) adapter)
+                        .getSelection(control);
+            }
+
+        }
+    }
+
+    /*
+     * Get the proposals from the proposal provider. Gets all of the proposals
+     * without doing any filtering.
+     */
+    private IContentProposal[] getProposals() {
+        if (proposalProvider is null || !isValid()) {
+            return null;
+        }
+        if (DEBUG) {
+            Stdout.formatln(">>> obtaining proposals from provider"); //$NON-NLS-1$
+        }
+        int position = insertionPos;
+        if (position is -1) {
+            position = getControlContentAdapter().getCursorPosition(
+                    getControl());
+        }
+        String contents = getControlContentAdapter().getControlContents(
+                getControl());
+        IContentProposal[] proposals = proposalProvider.getProposals(contents,
+                position);
+        return proposals;
+    }
+
+    /**
+     * Autoactivation has been triggered. Open the popup using any specified
+     * delay.
+     */
+    private void autoActivate() {
+        if (autoActivationDelay > 0) {
+            auto r = new class Runnable{
+                public void run(){
+                    receivedKeyDown = false;
+                    try {
+                        JThread.sleep(autoActivationDelay);
+                    } catch (InterruptedException e) {
+                    }
+                    if (!isValid() || receivedKeyDown) {
+                        return;
+                    }
+                    getControl().getDisplay().syncExec(new class Runnable {
+                        public void run() {
+                            openProposalPopup(true);
+                        }
+                    });
+                }
+            };
+            JThread t = new JThread(r);
+            t.start();
+        } else {
+            // Since we do not sleep, we must open the popup
+            // in an async exec. This is necessary because
+            // this method may be called in the middle of handling
+            // some event that will cause the cursor position or
+            // other important info to change as a result of this
+            // event occurring.
+            getControl().getDisplay().asyncExec(new class Runnable {
+                public void run() {
+                    if (isValid()) {
+                        openProposalPopup(true);
+                    }
+                }
+            });
+        }
+    }
+
+    /*
+     * A proposal has been accepted. Notify interested listeners.
+     */
+    private void notifyProposalAccepted(IContentProposal proposal) {
+        if (DEBUG) {
+            Stdout.formatln("Notify listeners - proposal accepted."); //$NON-NLS-1$
+        }
+        Object[] listenerArray = proposalListeners.getListeners();
+        for (int i = 0; i < listenerArray.length; i++) {
+            (cast(IContentProposalListener) listenerArray[i])
+                    .proposalAccepted(proposal);
+        }
+    }
+
+    /*
+     * The proposal popup has opened. Notify interested listeners.
+     */
+    private void notifyPopupOpened() {
+        if (DEBUG) {
+            Stdout.formatln("Notify listeners - popup opened."); //$NON-NLS-1$
+        }
+        Object[] listenerArray = proposalListeners2.getListeners();
+        for (int i = 0; i < listenerArray.length; i++) {
+            (cast(IContentProposalListener2) listenerArray[i])
+                    .proposalPopupOpened(this);
+        }
+    }
+
+    /*
+     * The proposal popup has closed. Notify interested listeners.
+     */
+    private void notifyPopupClosed() {
+        if (DEBUG) {
+            Stdout.formatln("Notify listeners - popup closed."); //$NON-NLS-1$
+        }
+        Object[] listenerArray = proposalListeners2.getListeners();
+        for (int i = 0; i < listenerArray.length; i++) {
+            (cast(IContentProposalListener2) listenerArray[i])
+                    .proposalPopupClosed(this);
+        }
+    }
+
+    /**
+     * Returns whether the content proposal popup has the focus. This includes
+     * both the primary popup and any secondary info popup that may have focus.
+     *
+     * @return <code>true</code> if the proposal popup or its secondary info
+     *         popup has the focus
+     * @since 3.4
+     */
+    public bool hasProposalPopupFocus() {
+        return popup !is null && popup.hasFocus();
+    }
+
+    /*
+     * Return whether the control content is empty
+     */
+    private bool isControlContentEmpty() {
+        return getControlContentAdapter().getControlContents(getControl())
+                .length is 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/ControlDecoration.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1217 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.fieldassist.ControlDecoration;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.MenuDetectEvent;
+import org.eclipse.swt.events.MenuDetectListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.Region;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.core.runtime.ListenerList;
+
+import java.lang.all;
+import java.util.Set;
+import tango.io.Stdout;
+
+/**
+ * ControlDecoration renders an image decoration near a control. It allows
+ * clients to specify an image and a position for the image relative to the
+ * control. A ControlDecoration may be assigned description text, which can
+ * optionally be shown when the user hovers over the image. Clients can decorate
+ * any kind of control.
+ * <p>
+ * Decoration images always appear on the left or right side of the field, never
+ * above or below it. Decorations can be positioned at the top, center, or
+ * bottom of either side of the control. Future implementations may provide
+ * additional positioning options for decorations.
+ * <p>
+ * ControlDecoration renders the image adjacent to the specified (already
+ * created) control, with no guarantee that it won't be clipped or otherwise
+ * obscured or overlapped by adjacent controls, including another
+ * ControlDecoration placed in the same location. Clients should ensure that
+ * there is adequate space adjacent to the control to show the decoration
+ * properly.
+ * <p>
+ * Clients using ControlDecoration should typically ensure that enough margin
+ * space is reserved for a decoration by altering the layout data margins,
+ * although this is not assumed or required by the ControlDecoration
+ * implementation.
+ * <p>
+ * This class is intended to be instantiated and used by clients. It is not
+ * intended to be subclassed by clients.
+ *
+ * @since 3.3
+ *
+ * @see FieldDecoration
+ * @see FieldDecorationRegistry
+ */
+public class ControlDecoration {
+    /**
+     * Debug flag for tracing
+     */
+    private static bool DEBUG = false;
+
+    /**
+     * Cached platform flags for dealing with platform-specific issues.
+     */
+    private static bool CARBON(){
+        return "carbon".equals(SWT.getPlatform()); //$NON-NLS-1$
+    }
+
+    /**
+     * The associated control
+     */
+    private Control control;
+
+    /**
+     * The composite on which to render the decoration and hook mouse events, or
+     * null if we are hooking all parent composites.
+     */
+    private Composite composite;
+
+    /**
+     * The associated image.
+     */
+    private Image image;
+
+    /**
+     * The associated description text.
+     */
+    private String descriptionText;
+    /**
+     * The position of the decoration.
+     */
+    private int position;
+
+    /**
+     * The decoration's visibility flag
+     */
+    private bool visible = true;
+
+    /**
+     * Boolean indicating whether the decoration should only be shown when the
+     * control has focus
+     */
+    private bool showOnlyOnFocus = false;
+
+    /**
+     * Boolean indicating whether the decoration should show its description
+     * text in a hover when the user hovers over the decoration.
+     */
+    private bool showHover = true;
+
+    /**
+     * Margin width used between the decorator and the control.
+     */
+    private int marginWidth = 0;
+
+    /**
+     * Registered selection listeners.
+     */
+    private ListenerList selectionListeners;
+
+    /**
+     * Registered menu detect listeners.
+     */
+    private ListenerList menuDetectListeners;
+
+    /**
+     * The focus listener
+     */
+    private FocusListener focusListener;
+
+    /**
+     * The dispose listener
+     */
+    private DisposeListener disposeListener;
+
+    /**
+     * The paint listener installed for drawing the decoration
+     */
+    private PaintListener paintListener;
+
+    /**
+     * The mouse listener installed for tracking the hover
+     */
+    private MouseTrackListener mouseTrackListener;
+
+    /**
+     * The mouse move listener installed for tracking the hover
+     */
+    private MouseMoveListener mouseMoveListener;
+
+    /**
+     * The untyped listener installed for notifying external listeners
+     */
+    private Listener compositeListener;
+
+    /**
+     * Control that we last installed a move listener on. We only want one at a
+     * time.
+     */
+    private Control moveListeningTarget = null;
+
+    /**
+     * Debug counter used to match add and remove listeners
+     */
+    private int listenerInstalls = 0;
+
+    /**
+     * The current rectangle used for tracking mouse moves
+     */
+    private Rectangle decorationRectangle;
+
+    /**
+     * An internal flag tracking whether we have focus. We use this rather than
+     * isFocusControl() so that we can set the flag as soon as we get the focus
+     * callback, rather than having to do an asyncExec in the middle of a focus
+     * callback to ensure that isFocusControl() represents the outcome of the
+     * event.
+     */
+    private bool hasFocus = false;
+
+    /**
+     * The hover used for showing description text
+     */
+    private Hover hover;
+
+    /**
+     * The hover used to show a decoration image's description.
+     */
+    class Hover {
+        private static const String EMPTY = ""; //$NON-NLS-1$
+
+        /**
+         * Offset of info hover arrow from the left or right side.
+         */
+        private int hao = 10;
+
+        /**
+         * Width of info hover arrow.
+         */
+        private int haw = 8;
+
+        /**
+         * Height of info hover arrow.
+         */
+        private int hah = 10;
+
+        /**
+         * Margin around info hover text.
+         */
+        private int hm = 2;
+
+        /**
+         * This info hover's shell.
+         */
+        Shell hoverShell;
+
+        /**
+         * The info hover text.
+         */
+        String text = EMPTY;
+
+        /**
+         * The region used to manage the shell shape
+         */
+        Region region;
+
+        /**
+         * Boolean indicating whether the last computed polygon location had an
+         * arrow on left. (true if left, false if right).
+         */
+        bool arrowOnLeft = true;
+
+        /*
+         * Create a hover parented by the specified shell.
+         */
+        this(Shell parent) {
+            Display display = parent.getDisplay();
+            hoverShell = new Shell(parent, SWT.NO_TRIM | SWT.ON_TOP
+                    | SWT.NO_FOCUS | SWT.TOOL);
+            hoverShell.setBackground(display
+                    .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+            hoverShell.setForeground(display
+                    .getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+            hoverShell.addPaintListener(new class PaintListener {
+                public void paintControl(PaintEvent pe) {
+                    pe.gc.drawText(text, hm, hm);
+                    if (!CARBON) {
+                        pe.gc.drawPolygon(getPolygon(true));
+                    }
+                }
+            });
+            hoverShell.addMouseListener(new class MouseAdapter {
+                public void mouseDown(MouseEvent e) {
+                    hideHover();
+                }
+            });
+        }
+
+        /*
+         * Compute a polygon that represents a hover with an arrow pointer. If
+         * border is true, compute the polygon inset by 1-pixel border. Consult
+         * the arrowOnLeft flag to determine which side the arrow is on.
+         */
+        int[] getPolygon(bool border) {
+            Point e = getExtent();
+            int b = border ? 1 : 0;
+            if (arrowOnLeft) {
+                return [ 0, 0, e.x - b, 0, e.x - b, e.y - b,
+                        hao + haw, e.y - b, hao + haw / 2, e.y + hah - b, hao,
+                        e.y - b, 0, e.y - b, 0, 0 ];
+            }
+            return [ 0, 0, e.x - b, 0, e.x - b, e.y - b,
+                    e.x - hao - b, e.y - b, e.x - hao - haw / 2, e.y + hah - b,
+                    e.x - hao - haw, e.y - b, 0, e.y - b, 0, 0 ];
+        }
+
+        /*
+         * Dispose the hover, it is no longer needed. Dispose any resources
+         * allocated by the hover.
+         */
+        void dispose() {
+            if (!hoverShell.isDisposed()) {
+                hoverShell.dispose();
+            }
+            if (region !is null) {
+                region.dispose();
+            }
+        }
+
+        /*
+         * Set the visibility of the hover.
+         */
+        void setVisible(bool visible) {
+            if (visible) {
+                if (!hoverShell.isVisible()) {
+                    hoverShell.setVisible(true);
+                }
+            } else {
+                if (hoverShell.isVisible()) {
+                    hoverShell.setVisible(false);
+                }
+            }
+        }
+
+        /*
+         * Set the text of the hover to the specified text. Recompute the size
+         * and location of the hover to hover near the decoration rectangle,
+         * pointing the arrow toward the target control.
+         */
+        void setText(String t, Rectangle decorationRectangle,
+                Control targetControl) {
+            if (t is null) {
+                t = EMPTY;
+            }
+            if (!t.equals(text)) {
+                Point oldSize = getExtent();
+                text = t;
+                hoverShell.redraw();
+                Point newSize = getExtent();
+                if (!oldSize.opEquals(newSize)) {
+                    // set a flag that indicates the direction of arrow
+                    arrowOnLeft = decorationRectangle.x <= targetControl
+                            .getLocation().x;
+                    setNewShape();
+                }
+            }
+
+            Point extent = getExtent();
+            int y = -extent.y - hah + 1;
+            int x = arrowOnLeft ? -hao + haw / 2 : -extent.x + hao + haw / 2;
+
+            hoverShell.setLocation(control.getParent().toDisplay(
+                    decorationRectangle.x + x, decorationRectangle.y + y));
+        }
+
+        /*
+         * Return whether or not the hover (shell) is visible.
+         */
+        bool isVisible() {
+            return hoverShell.isVisible();
+        }
+
+        /*
+         * Compute the extent of the hover for the current text.
+         */
+        Point getExtent() {
+            GC gc = new GC(hoverShell);
+            Point e = gc.textExtent(text);
+            gc.dispose();
+            e.x += hm * 2;
+            e.y += hm * 2;
+            return e;
+        }
+
+        /*
+         * Compute a new shape for the hover shell.
+         */
+        void setNewShape() {
+            Region oldRegion = region;
+            region = new Region();
+            region.add(getPolygon(false));
+            hoverShell.setRegion(region);
+            if (oldRegion !is null) {
+                oldRegion.dispose();
+            }
+
+        }
+    }
+
+    /**
+     * Construct a ControlDecoration for decorating the specified control at the
+     * specified position relative to the control. Render the decoration on top
+     * of any Control that happens to appear at the specified location.
+     * <p>
+     * SWT constants are used to specify the position of the decoration relative
+     * to the control. The position should include style bits describing both
+     * the vertical and horizontal orientation. <code>SWT.LEFT</code> and
+     * <code>SWT.RIGHT</code> describe the horizontal placement of the
+     * decoration relative to the control, and the constants
+     * <code>SWT.TOP</code>, <code>SWT.CENTER</code>, and
+     * <code>SWT.BOTTOM</code> describe the vertical alignment of the
+     * decoration relative to the control. Decorations always appear on either
+     * the left or right side of the control, never above or below it. For
+     * example, a decoration appearing on the left side of the field, at the
+     * top, is specified as SWT.LEFT | SWT.TOP. If no position style bits are
+     * specified, the control decoration will be positioned to the left and
+     * center of the control (<code>SWT.LEFT | SWT.CENTER</code>).
+     * </p>
+     *
+     * @param control
+     *            the control to be decorated
+     * @param position
+     *            bit-wise or of position constants (<code>SWT.TOP</code>,
+     *            <code>SWT.BOTTOM</code>, <code>SWT.LEFT</code>,
+     *            <code>SWT.RIGHT</code>, and <code>SWT.CENTER</code>).
+     */
+    public this(Control control, int position) {
+        this(control, position, null);
+
+    }
+
+    /**
+     * Construct a ControlDecoration for decorating the specified control at the
+     * specified position relative to the control. Render the decoration only on
+     * the specified Composite or its children. The decoration will be clipped
+     * if it does not appear within the visible bounds of the composite or its
+     * child composites.
+     * <p>
+     * SWT constants are used to specify the position of the decoration relative
+     * to the control. The position should include style bits describing both
+     * the vertical and horizontal orientation. <code>SWT.LEFT</code> and
+     * <code>SWT.RIGHT</code> describe the horizontal placement of the
+     * decoration relative to the control, and the constants
+     * <code>SWT.TOP</code>, <code>SWT.CENTER</code>, and
+     * <code>SWT.BOTTOM</code> describe the vertical alignment of the
+     * decoration relative to the control. Decorations always appear on either
+     * the left or right side of the control, never above or below it. For
+     * example, a decoration appearing on the left side of the field, at the
+     * top, is specified as SWT.LEFT | SWT.TOP. If no position style bits are
+     * specified, the control decoration will be positioned to the left and
+     * center of the control (<code>SWT.LEFT | SWT.CENTER</code>).
+     * </p>
+     *
+     * @param control
+     *            the control to be decorated
+     * @param position
+     *            bit-wise or of position constants (<code>SWT.TOP</code>,
+     *            <code>SWT.BOTTOM</code>, <code>SWT.LEFT</code>,
+     *            <code>SWT.RIGHT</code>, and <code>SWT.CENTER</code>).
+     * @param composite
+     *            The SWT composite within which the decoration should be
+     *            rendered. The decoration will be clipped to this composite,
+     *            but it may be rendered on a child of the composite. The
+     *            decoration will not be visible if the specified composite or
+     *            its child composites are not visible in the space relative to
+     *            the control, where the decoration is to be rendered. If this
+     *            value is <code>null</code>, then the decoration will be
+     *            rendered on whichever composite (or composites) are located in
+     *            the specified position.
+     */
+    public this(Control control, int position, Composite composite) {
+        selectionListeners = new ListenerList();
+        menuDetectListeners = new ListenerList();
+        this.position = position;
+        this.control = control;
+        this.composite = composite;
+
+        addControlListeners();
+
+    }
+
+    /**
+     * Adds the listener to the collection of listeners who will be notified
+     * when the platform-specific context menu trigger has occurred, by sending
+     * it one of the messages defined in the <code>MenuDetectListener</code>
+     * interface.
+     * <p>
+     * The <code>widget</code> field in the SelectionEvent will contain the
+     * Composite on which the decoration is rendered that received the click.
+     * The <code>x</code> and <code>y</code> fields will be in coordinates
+     * relative to the display. The <code>data</code> field will contain the
+     * decoration that received the event.
+     * </p>
+     *
+     * @param listener
+     *            the listener which should be notified
+     *
+     * @see org.eclipse.swt.events.MenuDetectListener
+     * @see org.eclipse.swt.events.MenuDetectEvent
+     * @see #removeMenuDetectListener
+     */
+    public void addMenuDetectListener(MenuDetectListener listener) {
+        menuDetectListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes the listener from the collection of listeners who will be
+     * notified when the platform-specific context menu trigger has occurred.
+     *
+     * @param listener
+     *            the listener which should no longer be notified. This message
+     *            has no effect if the listener was not previously added to the
+     *            receiver.
+     *
+     * @see org.eclipse.swt.events.MenuDetectListener
+     * @see #addMenuDetectListener
+     */
+    public void removeMenuDetectListener(MenuDetectListener listener) {
+        menuDetectListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Adds the listener to the collection of listeners who will be notified
+     * when the decoration is selected, by sending it one of the messages
+     * defined in the <code>SelectionListener</code> interface.
+     * <p>
+     * <code>widgetSelected</code> is called when the decoration is selected
+     * (by mouse click). <code>widgetDefaultSelected</code> is called when the
+     * decoration is double-clicked.
+     * </p>
+     * <p>
+     * The <code>widget</code> field in the SelectionEvent will contain the
+     * Composite on which the decoration is rendered that received the click.
+     * The <code>x</code> and <code>y</code> fields will be in coordinates
+     * relative to that widget. The <code>data</code> field will contain the
+     * decoration that received the event.
+     * </p>
+     *
+     * @param listener
+     *            the listener which should be notified
+     *
+     * @see org.eclipse.swt.events.SelectionListener
+     * @see org.eclipse.swt.events.SelectionEvent
+     * @see #removeSelectionListener
+     */
+    public void addSelectionListener(SelectionListener listener) {
+        selectionListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes the listener from the collection of listeners who will be
+     * notified when the decoration is selected.
+     *
+     * @param listener
+     *            the listener which should no longer be notified. This message
+     *            has no effect if the listener was not previously added to the
+     *            receiver.
+     *
+     * @see org.eclipse.swt.events.SelectionListener
+     * @see #addSelectionListener
+     */
+    public void removeSelectionListener(SelectionListener listener) {
+        selectionListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Dispose this ControlDecoration. Unhook any listeners that have been
+     * installed on the target control. This method has no effect if the
+     * receiver is already disposed.
+     */
+    public void dispose() {
+        if (control is null) {
+            return;
+        }
+        if (hover !is null) {
+            hover.dispose();
+            hover = null;
+        }
+        removeControlListeners();
+        control = null;
+    }
+
+    /**
+     * Get the control that is decorated by the receiver.
+     *
+     * @return the Control decorated by the receiver. May be <code>null</code>
+     *         if the control has been uninstalled.
+     */
+    public Control getControl() {
+        return control;
+    }
+
+    /**
+     * Add any listeners needed on the target control and on the composite where
+     * the decoration is to be rendered.
+     */
+    private void addControlListeners() {
+        disposeListener = new class DisposeListener {
+            public void widgetDisposed(DisposeEvent event) {
+                dispose();
+            }
+        };
+        printAddListener(control, "DISPOSE"); //$NON-NLS-1$
+        control.addDisposeListener(disposeListener);
+
+        focusListener = new class FocusListener {
+            public void focusGained(FocusEvent event) {
+                hasFocus = true;
+                if (showOnlyOnFocus) {
+                    update();
+                }
+            }
+
+            public void focusLost(FocusEvent event) {
+                hasFocus = false;
+                if (showOnlyOnFocus) {
+                    update();
+                }
+            }
+        };
+        printAddListener(control, "FOCUS"); //$NON-NLS-1$
+        control.addFocusListener(focusListener);
+
+        // Listener for painting the decoration
+        paintListener = new class PaintListener {
+            public void paintControl(PaintEvent event) {
+                Control control = cast(Control) event.widget;
+                Rectangle rect = getDecorationRectangle(control);
+                if (shouldShowDecoration()) {
+                    event.gc.drawImage(getImage(), rect.x, rect.y);
+                }
+            }
+        };
+
+        // Listener for tracking the end of a hover. Only installed
+        // after a hover begins.
+        mouseMoveListener = new class MouseMoveListener {
+            public void mouseMove(MouseEvent event) {
+                if (showHover) {
+                    if (!decorationRectangle.contains(event.x, event.y)) {
+                        hideHover();
+                        // No need to listen any longer
+                        printRemoveListener(event.widget, "MOUSEMOVE"); //$NON-NLS-1$
+                        (cast(Control) event.widget)
+                                .removeMouseMoveListener(mouseMoveListener);
+                        moveListeningTarget = null;
+                    }
+                }
+            }
+        };
+
+        // Listener for tracking the beginning of a hover. Always installed.
+        mouseTrackListener = new class MouseTrackListener {
+            public void mouseExit(MouseEvent event) {
+                // Just in case we didn't catch it before.
+                Control target = cast(Control) event.widget;
+                if (target is moveListeningTarget) {
+                    printRemoveListener(target, "MOUSEMOVE"); //$NON-NLS-1$
+                    target.removeMouseMoveListener(mouseMoveListener);
+                    moveListeningTarget = null;
+                }
+                hideHover();
+            }
+
+            public void mouseHover(MouseEvent event) {
+                if (showHover) {
+                    decorationRectangle = getDecorationRectangle(cast(Control) event.widget);
+                    if (decorationRectangle.contains(event.x, event.y)) {
+                        showHoverText(getDescriptionText());
+                        Control target = cast(Control) event.widget;
+                        if (moveListeningTarget is null) {
+                            printAddListener(target, "MOUSEMOVE"); //$NON-NLS-1$
+                            target.addMouseMoveListener(mouseMoveListener);
+                            moveListeningTarget = target;
+                        } else if (target !is moveListeningTarget) {
+                            printRemoveListener(moveListeningTarget,
+                                    "MOUSEMOVE"); //$NON-NLS-1$
+                            moveListeningTarget
+                                    .removeMouseMoveListener(mouseMoveListener);
+                            printAddListener(target, "MOUSEMOVE"); //$NON-NLS-1$
+                            target.addMouseMoveListener(mouseMoveListener);
+                            moveListeningTarget = target;
+                        } else {
+                            // It is already installed on this control.
+                        }
+                    }
+                }
+            }
+
+            public void mouseEnter(MouseEvent event) {
+                // Nothing to do until a hover occurs.
+            }
+        };
+
+        compositeListener = new class Listener {
+            public void handleEvent(Event event) {
+                // Don't forward events if decoration is not showing
+                if (!visible) {
+                    return;
+                }
+                // Notify listeners if any are registered.
+                switch (event.type) {
+                case SWT.MouseDown:
+                    if (!selectionListeners.isEmpty())
+                        notifySelectionListeners(event);
+                    break;
+                case SWT.MouseDoubleClick:
+                    if (!selectionListeners.isEmpty())
+                        notifySelectionListeners(event);
+                    break;
+                case SWT.MenuDetect:
+                    if (!menuDetectListeners.isEmpty())
+                        notifyMenuDetectListeners(event);
+                    break;
+                default:
+                }
+            }
+        };
+
+        // We do not know which parent in the control hierarchy
+        // is providing the decoration space, so hook all the way up, until
+        // the shell or the specified parent composite is reached.
+        Composite c = control.getParent();
+        while (c !is null) {
+            installCompositeListeners(c);
+            if (composite !is null && composite is c) {
+                // We just installed on the specified composite, so stop.
+                c = null;
+            } else if (cast(Shell)c ) {
+                // We just installed on a shell, so don't go further
+                c = null;
+            } else {
+                c = c.getParent();
+            }
+        }
+        // force a redraw of the decoration area so our paint listener
+        // is notified.
+        update();
+    }
+
+    /*
+     * Install the listeners used to paint and track mouse events on the
+     * composite.
+     */
+    private void installCompositeListeners(Composite c) {
+        if (!c.isDisposed()) {
+            printAddListener(c, "PAINT"); //$NON-NLS-1$
+            c.addPaintListener(paintListener);
+            printAddListener(c, "MOUSETRACK"); //$NON-NLS-1$
+            c.addMouseTrackListener(mouseTrackListener);
+            printAddListener(c, "SWT.MenuDetect"); //$NON-NLS-1$
+            c.addListener(SWT.MenuDetect, compositeListener);
+            printAddListener(c, "SWT.MouseDown"); //$NON-NLS-1$
+            c.addListener(SWT.MouseDown, compositeListener);
+            printAddListener(c, "SWT.MouseDoubleClick"); //$NON-NLS-1$
+            c.addListener(SWT.MouseDoubleClick, compositeListener);
+        }
+    }
+
+    /*
+     * Remove the listeners used to paint and track mouse events on the
+     * composite.
+     */
+    private void removeCompositeListeners(Composite c) {
+        if (!c.isDisposed()) {
+            printRemoveListener(c, "PAINT"); //$NON-NLS-1$
+            c.removePaintListener(paintListener);
+            printRemoveListener(c, "MOUSETRACK"); //$NON-NLS-1$
+            c.removeMouseTrackListener(mouseTrackListener);
+            printRemoveListener(c, "SWT.MenuDetect"); //$NON-NLS-1$
+            c.removeListener(SWT.MenuDetect, compositeListener);
+            printRemoveListener(c, "SWT.MouseDown"); //$NON-NLS-1$
+            c.removeListener(SWT.MouseDown, compositeListener);
+            printRemoveListener(c, "SWT.MouseDoubleClick"); //$NON-NLS-1$
+            c.removeListener(SWT.MouseDoubleClick, compositeListener);
+        }
+    }
+
+    private void notifySelectionListeners(Event event) {
+        if (!(cast(Control)event.widget )) {
+            return;
+        }
+        if (getDecorationRectangle(cast(Control) event.widget).contains(event.x,
+                event.y)) {
+            SelectionEvent clientEvent = new SelectionEvent(event);
+            clientEvent.data = this;
+            if (getImage() !is null) {
+                clientEvent.height = getImage().getBounds().height;
+                clientEvent.width = getImage().getBounds().width;
+            }
+            Object[] listeners;
+            switch (event.type) {
+            case SWT.MouseDoubleClick:
+                if (event.button is 1) {
+                    listeners = selectionListeners.getListeners();
+                    for (int i = 0; i < listeners.length; i++) {
+                        (cast(SelectionListener) listeners[i])
+                                .widgetDefaultSelected(clientEvent);
+                    }
+                }
+                break;
+            case SWT.MouseDown:
+                if (event.button is 1) {
+                    listeners = selectionListeners.getListeners();
+                    for (int i = 0; i < listeners.length; i++) {
+                        (cast(SelectionListener) listeners[i])
+                                .widgetSelected(clientEvent);
+                    }
+                }
+                break;
+            default:
+            }
+        }
+    }
+
+    private void notifyMenuDetectListeners(Event event) {
+        if (getDecorationRectangle(null).contains(event.x, event.y)) {
+            MenuDetectEvent clientEvent = new MenuDetectEvent(event);
+            clientEvent.data = this;
+            Object[] listeners = menuDetectListeners.getListeners();
+            for (int i = 0; i < listeners.length; i++) {
+                (cast(MenuDetectListener) listeners[i]).menuDetected(clientEvent);
+
+            }
+        }
+    }
+
+    /**
+     * Show the specified text using the same hover dialog as is used to show
+     * decorator descriptions. When {@link #setShowHover(bool)} has been set
+     * to <code>true</code>, a decoration's description text will be shown in
+     * an info hover over the field's control whenever the mouse hovers over the
+     * decoration. This method can be used to show a decoration's description
+     * text at other times (such as when the control receives focus), or to show
+     * other text associated with the field. The hover will not be shown if the
+     * decoration is hidden.
+     *
+     * @param text
+     *            the text to be shown in the info hover, or <code>null</code>
+     *            if no text should be shown.
+     */
+    public void showHoverText(String text) {
+        if (control is null) {
+            return;
+        }
+        showHoverText(text, control);
+    }
+
+    /**
+     * Hide any hover popups that are currently showing on the control. When
+     * {@link #setShowHover(bool)} has been set to <code>true</code>, a
+     * decoration's description text will be shown in an info hover over the
+     * field's control as long as the mouse hovers over the decoration, and will
+     * be hidden when the mouse exits the decoration. This method can be used to
+     * hide a hover, whether it was shown explicitly using
+     * {@link #showHoverText(String)}, or was showing because the user was
+     * hovering in the decoration.
+     * <p>
+     * This message has no effect if there is no current hover.
+     *
+     */
+    public void hideHover() {
+        if (hover !is null) {
+            hover.setVisible(false);
+        }
+    }
+
+    /**
+     * Show the control decoration. This message has no effect if the decoration
+     * is already showing. If {@link #setShowOnlyOnFocus(bool)} is set to
+     * <code>true</code>, the decoration will only be shown if the control
+     * has focus.
+     */
+    public void show() {
+        if (!visible) {
+            visible = true;
+            update();
+        }
+    }
+
+    /**
+     * Hide the control decoration and any associated hovers. This message has
+     * no effect if the decoration is already hidden.
+     */
+    public void hide() {
+        if (visible) {
+            visible = false;
+            hideHover();
+            update();
+        }
+    }
+
+    /**
+     * Get the description text that may be shown in a hover for this
+     * decoration.
+     *
+     * @return the text to be shown as a description for the decoration, or
+     *         <code>null</code> if none has been set.
+     */
+    public String getDescriptionText() {
+        return descriptionText;
+    }
+
+    /**
+     * Set the image shown in this control decoration. Update the rendered
+     * decoration.
+     *
+     * @param text
+     *            the text to be shown as a description for the decoration, or
+     *            <code>null</code> if none has been set.
+     */
+    public void setDescriptionText(String text) {
+        this.descriptionText = text;
+        update();
+    }
+
+    /**
+     * Get the image shown in this control decoration.
+     *
+     * @return the image to be shown adjacent to the control, or
+     *         <code>null</code> if one has not been set.
+     */
+    public Image getImage() {
+        return image;
+    }
+
+    /**
+     * Set the image shown in this control decoration. Update the rendered
+     * decoration.
+     *
+     * @param image
+     *            the image to be shown adjacent to the control. Should never be
+     *            <code>null</code>.
+     */
+    public void setImage(Image image) {
+        this.image = image;
+        update();
+    }
+
+    /**
+     * Get the bool that controls whether the decoration is shown only when
+     * the control has focus. The default value of this setting is
+     * <code>false</code>.
+     *
+     * @return <code>true</code> if the decoration should only be shown when
+     *         the control has focus, and <code>false</code> if it should
+     *         always be shown. Note that if the control is not capable of
+     *         receiving focus (<code>SWT.NO_FOCUS</code>), then the
+     *         decoration will never show when this value is <code>true</code>.
+     */
+    public bool getShowOnlyOnFocus() {
+        return showOnlyOnFocus;
+    }
+
+    /**
+     * Set the bool that controls whether the decoration is shown only when
+     * the control has focus. The default value of this setting is
+     * <code>false</code>.
+     *
+     * @param showOnlyOnFocus
+     *            <code>true</code> if the decoration should only be shown
+     *            when the control has focus, and <code>false</code> if it
+     *            should always be shown. Note that if the control is not
+     *            capable of receiving focus (<code>SWT.NO_FOCUS</code>),
+     *            then the decoration will never show when this value is
+     *            <code>true</code>.
+     */
+    public void setShowOnlyOnFocus(bool showOnlyOnFocus) {
+        this.showOnlyOnFocus = showOnlyOnFocus;
+        update();
+    }
+
+    /**
+     * Get the bool that controls whether the decoration's description text
+     * should be shown in a hover when the user hovers over the decoration. The
+     * default value of this setting is <code>true</code>.
+     *
+     * @return <code>true</code> if a hover popup containing the decoration's
+     *         description text should be shown when the user hovers over the
+     *         decoration, and <code>false</code> if a hover should not be
+     *         shown.
+     */
+    public bool getShowHover() {
+        return showHover;
+    }
+
+    /**
+     * Set the bool that controls whether the decoration's description text
+     * should be shown in a hover when the user hovers over the decoration. The
+     * default value of this setting is <code>true</code>.
+     *
+     * @param showHover
+     *            <code>true</code> if a hover popup containing the
+     *            decoration's description text should be shown when the user
+     *            hovers over the decoration, and <code>false</code> if a
+     *            hover should not be shown.
+     */
+    public void setShowHover(bool showHover) {
+        this.showHover = showHover;
+        update();
+    }
+
+    /**
+     * Get the margin width in pixels that should be used between the decorator
+     * and the horizontal edge of the control. The default value of this setting
+     * is <code>0</code>.
+     *
+     * @return the number of pixels that should be reserved between the
+     *         horizontal edge of the control and the adjacent edge of the
+     *         decoration.
+     */
+    public int getMarginWidth() {
+        return marginWidth;
+    }
+
+    /**
+     * Set the margin width in pixels that should be used between the decorator
+     * and the horizontal edge of the control. The default value of this setting
+     * is <code>0</code>.
+     *
+     * @param marginWidth
+     *            the number of pixels that should be reserved between the
+     *            horizontal edge of the control and the adjacent edge of the
+     *            decoration.
+     */
+    public void setMarginWidth(int marginWidth) {
+        this.marginWidth = marginWidth;
+        update();
+    }
+
+    /**
+     * Something has changed, requiring redraw. Redraw the decoration and update
+     * the hover text if appropriate.
+     */
+    protected void update() {
+        if (control is null || control.isDisposed()) {
+            return;
+        }
+        Rectangle rect = getDecorationRectangle(control.getShell());
+        // Redraw this rectangle in all children
+        control.getShell()
+                .redraw(rect.x, rect.y, rect.width, rect.height, true);
+        control.getShell().update();
+        if (hover !is null && getDescriptionText() !is null) {
+            hover.setText(getDescriptionText(), getDecorationRectangle(control
+                    .getParent()), control);
+        }
+    }
+
+    /*
+     * Show the specified text in the hover, positioning the hover near the
+     * specified control.
+     */
+    private void showHoverText(String text, Control hoverNear) {
+        // If we aren't to show a hover, don't do anything.
+        if (!showHover) {
+            return;
+        }
+
+        // If we are not visible, don't show the hover.
+        if (!visible) {
+            return;
+        }
+        // If there is no text, don't do anything.
+        if (text is null) {
+            hideHover();
+            return;
+        }
+
+        // If there is no control, nothing to do
+        if (control is null) {
+            return;
+        }
+        // Create the hover if it's not showing
+        if (hover is null) {
+            hover = new Hover(hoverNear.getShell());
+        }
+        hover.setText(text, getDecorationRectangle(control.getParent()),
+                control);
+        hover.setVisible(true);
+    }
+
+    /*
+     * Remove any listeners installed on the controls.
+     */
+    private void removeControlListeners() {
+        if (control is null) {
+            return;
+        }
+        printRemoveListener(control, "FOCUS"); //$NON-NLS-1$
+        control.removeFocusListener(focusListener);
+        focusListener = null;
+
+        printRemoveListener(control, "DISPOSE"); //$NON-NLS-1$
+        control.removeDisposeListener(disposeListener);
+        disposeListener = null;
+
+        Composite c = control.getParent();
+        while (c !is null) {
+            removeCompositeListeners(c);
+            if (composite !is null && composite is c) {
+                // We previously installed listeners only to the specified
+                // composite, so stop.
+                c = null;
+            } else if (cast(Shell)c ) {
+                // We previously installed listeners only up to the first Shell
+                // encountered, so stop.
+                c = null;
+            } else {
+                c = c.getParent();
+            }
+        }
+        paintListener = null;
+        mouseTrackListener = null;
+        compositeListener = null;
+
+        // We may have a remaining mouse move listener installed
+        if (moveListeningTarget !is null) {
+            printRemoveListener(moveListeningTarget, "MOUSEMOVE"); //$NON-NLS-1$
+            moveListeningTarget.removeMouseMoveListener(mouseMoveListener);
+            moveListeningTarget = null;
+            mouseMoveListener = null;
+        }
+        if (DEBUG) {
+            if (listenerInstalls > 0) {
+                Stdout.formatln("LISTENER LEAK>>>CHECK TRACE ABOVE"); //$NON-NLS-1$
+            } else if (listenerInstalls < 0) {
+                Stdout.formatln("REMOVED UNREGISTERED LISTENERS>>>CHECK TRACE ABOVE"); //$NON-NLS-1$
+            } else {
+                Stdout.formatln("ALL INSTALLED LISTENERS WERE REMOVED."); //$NON-NLS-1$
+            }
+        }
+    }
+
+    /**
+     * Return the rectangle in which the decoration should be rendered, in
+     * coordinates relative to the specified control. If the specified control
+     * is null, return the rectangle in display coordinates.
+     *
+     * @param targetControl
+     *            the control whose coordinates should be used
+     * @return the rectangle in which the decoration should be rendered
+     */
+    protected Rectangle getDecorationRectangle(Control targetControl) {
+        if (getImage() is null || control is null) {
+            return new Rectangle(0, 0, 0, 0);
+        }
+        // Compute the bounds first relative to the control's parent.
+        Rectangle imageBounds = getImage().getBounds();
+        Rectangle controlBounds = control.getBounds();
+        int x, y;
+        // Compute x
+        if ((position & SWT.RIGHT) is SWT.RIGHT) {
+            x = controlBounds.x + controlBounds.width + marginWidth;
+        } else {
+            // default is left
+            x = controlBounds.x - imageBounds.width - marginWidth;
+        }
+        // Compute y
+        if ((position & SWT.TOP) is SWT.TOP) {
+            y = controlBounds.y;
+        } else if ((position & SWT.BOTTOM) is SWT.BOTTOM) {
+            y = controlBounds.y + control.getBounds().height
+                    - imageBounds.height;
+        } else {
+            // default is center
+            y = controlBounds.y
+                    + (control.getBounds().height - imageBounds.height) / 2;
+        }
+
+        // Now convert to coordinates relative to the target control.
+        Point globalPoint = control.getParent().toDisplay(x, y);
+        Point targetPoint;
+        if (targetControl is null) {
+            targetPoint = globalPoint;
+        } else {
+            targetPoint = targetControl.toControl(globalPoint);
+        }
+        return new Rectangle(targetPoint.x, targetPoint.y, imageBounds.width,
+                imageBounds.height);
+    }
+
+    /*
+     * Return true if the decoration should be shown, false if it should not.
+     */
+    private bool shouldShowDecoration() {
+        if (!visible) {
+            return false;
+        }
+        if (control is null || control.isDisposed() || getImage() is null) {
+            return false;
+        }
+
+        if (!control.isVisible()) {
+            return false;
+        }
+        if (showOnlyOnFocus) {
+            return hasFocus;
+        }
+        return true;
+    }
+
+    /*
+     * If in debug mode, print info about adding the specified listener.
+     */
+    private void printAddListener(Widget widget, String listenerType) {
+        listenerInstalls++;
+        if (DEBUG) {
+            Stdout.formatln("Added listener>>>{} to>>>{}", listenerType, widget); //$NON-NLS-1$//$NON-NLS-2$
+        }
+    }
+
+    /*
+     * If in debug mode, print info about adding the specified listener.
+     */
+    private void printRemoveListener(Widget widget, String listenerType) {
+        listenerInstalls--;
+        if (DEBUG) {
+            Stdout.formatln("Removed listener>>>{} from>>>{}", listenerType, widget); //$NON-NLS-1$//$NON-NLS-2$
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/DecoratedField.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,890 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.fieldassist.DecoratedField;
+
+import org.eclipse.jface.fieldassist.FieldDecoration;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.jface.fieldassist.IControlCreator;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Region;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * DecoratedField manages image decorations around a control. It allows clients
+ * to specify an image decoration and a position for the decoration relative to
+ * the field. Decorations may be assigned descriptions, which are shown when the
+ * user hovers over the decoration. Clients can decorate any kind of control by
+ * supplying a {@link IControlCreator} to create the control that is decorated.
+ * <p>
+ * Decorations always appear on either horizontal side of the field, never above
+ * or below it. Decorations can be positioned at the top or bottom of either
+ * side. Future implementations may provide additional positioning options for
+ * decorations.
+ * <p>
+ * By default, DecoratedField will consult the {@link FieldDecorationRegistry}
+ * to determine how much space should be reserved for each decoration. This
+ * allows fields with decorations from different sources to align properly on
+ * the same dialog, since the registry tracks the size of all decorations
+ * registered. Therefore, it is recommended, but not required, that clients of
+ * DecoratedField register the decorations used. In cases where alignment
+ * between different fields is not a concern, clients can use
+ * <code>setUseMaximumDecorationWidth(false)</code> and need not register
+ * their decorations.
+ * <p>
+ * This class is not intended to be subclassed.
+ *
+ * @since 3.2
+ * @deprecated As of 3.3, clients should use {@link ControlDecoration} instead.
+ */
+public class DecoratedField {
+
+    /**
+     * Cached platform flags for dealing with platform-specific issues.
+     */
+    private static bool CARBON(){
+        return "carbon".equals(SWT.getPlatform()); //$NON-NLS-1$
+    }
+
+    /**
+     * Constants describing the array indices used to hold the decorations in
+     * array slots.
+     */
+
+    private static const int LEFT_TOP = 0;
+
+    private static const int LEFT_BOTTOM = 1;
+
+    private static const int RIGHT_TOP = 2;
+
+    private static const int RIGHT_BOTTOM = 3;
+
+    private static const int DECORATION_SLOTS = 4;
+
+    /**
+     * Simple data structure class for specifying the internals for a field
+     * decoration. This class contains data specific to the implementation of
+     * field decorations as labels attached to the field. Clients should use
+     * <code>FieldDecoration</code> for specifying a decoration.
+     */
+    private class FieldDecorationData {
+
+        /* Package */FieldDecoration decoration;
+
+        /* Package */Label label;
+
+        /* Package */FormData data;
+
+        /* Package */bool showOnFocus;
+
+        /* Package */bool visible = true;
+
+        /**
+         * Create a decoration data representing the specified decoration, using
+         * the specified label and form data for its representation.
+         *
+         * @param decoration
+         *            the decoration whose data is kept.
+         * @param label
+         *            the label used to represent the decoration.
+         * @param formData
+         *            the form data used to attach the decoration to its field.
+         * @param showOnFocus
+         *            a bool specifying whether the decoration should only be
+         *            shown when the field has focus.
+         */
+        this(FieldDecoration decoration, Label label,
+                FormData formData, bool showOnFocus) {
+            this.decoration = decoration;
+            this.label = label;
+            this.data = formData;
+            this.showOnFocus = showOnFocus;
+        }
+    }
+
+    /**
+     * Decorations keyed by position.
+     */
+    private FieldDecorationData[] decDatas;
+
+    /**
+     * The associated control
+     */
+    private Control control;
+
+    /**
+     * The composite with form layout used to manage decorations.
+     */
+    private Composite form;
+
+    /**
+     * The bool that indicates whether the maximum decoration width is used
+     * when allocating space for decorations.
+     */
+    private bool useMaxDecorationWidth = true;
+
+    /**
+     * The hover used for showing description text
+     */
+    private Hover hover;
+
+    /**
+     * The hover used to show a decoration image's description.
+     */
+    class Hover {
+        private static const String EMPTY = ""; //$NON-NLS-1$
+
+        /**
+         * Offset of info hover arrow from the left or right side.
+         */
+        private int hao = 10;
+
+        /**
+         * Width of info hover arrow.
+         */
+        private int haw = 8;
+
+        /**
+         * Height of info hover arrow.
+         */
+        private int hah = 10;
+
+        /**
+         * Margin around info hover text.
+         */
+        private int hm = 2;
+
+        /**
+         * This info hover's shell.
+         */
+        Shell hoverShell;
+
+        /**
+         * The info hover text.
+         */
+        String text = EMPTY;
+
+        /**
+         * The region used to manage the shell shape
+         */
+        Region region;
+
+        /**
+         * Boolean indicating whether the last computed polygon location had an
+         * arrow on left. (true if left, false if right).
+         */
+        bool arrowOnLeft = true;
+
+        /*
+         * Create a hover parented by the specified shell.
+         */
+        this(Shell parent) {
+            final Display display = parent.getDisplay();
+            hoverShell = new Shell(parent, SWT.NO_TRIM | SWT.ON_TOP
+                    | SWT.NO_FOCUS | SWT.TOOL);
+            hoverShell.setBackground(display
+                    .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+            hoverShell.setForeground(display
+                    .getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+            hoverShell.addPaintListener(new class PaintListener {
+                public void paintControl(PaintEvent pe) {
+                    pe.gc.drawString(text, hm, hm);
+                    if (!CARBON) {
+                        pe.gc.drawPolygon(getPolygon(true));
+                    }
+                }
+            });
+            hoverShell.addMouseListener(new class MouseAdapter {
+                public void mouseDown(MouseEvent e) {
+                    hideHover();
+                }
+            });
+        }
+
+        /*
+         * Compute a polygon that represents a hover with an arrow pointer. If
+         * border is true, compute the polygon inset by 1-pixel border. Consult
+         * the arrowOnLeft flag to determine which side the arrow is on.
+         */
+        int[] getPolygon(bool border) {
+            Point e = getExtent();
+            int b = border ? 1 : 0;
+            if (arrowOnLeft) {
+                return [ 0, 0, e.x - b, 0, e.x - b, e.y - b,
+                        hao + haw, e.y - b, hao + haw / 2, e.y + hah - b, hao,
+                        e.y - b, 0, e.y - b, 0, 0 ];
+            }
+            return [ 0, 0, e.x - b, 0, e.x - b, e.y - b,
+                    e.x - hao - b, e.y - b, e.x - hao - haw / 2, e.y + hah - b,
+                    e.x - hao - haw, e.y - b, 0, e.y - b, 0, 0 ];
+        }
+
+        /*
+         * Dispose the hover, it is no longer needed. Dispose any resources
+         * allocated by the hover.
+         */
+        void dispose() {
+            if (!hoverShell.isDisposed()) {
+                hoverShell.dispose();
+            }
+            if (region !is null) {
+                region.dispose();
+            }
+        }
+
+        /*
+         * Set the visibility of the hover.
+         */
+        void setVisible(bool visible) {
+            if (visible) {
+                if (!hoverShell.isVisible()) {
+                    hoverShell.setVisible(true);
+                }
+            } else {
+                if (hoverShell.isVisible()) {
+                    hoverShell.setVisible(false);
+                }
+            }
+        }
+
+        /*
+         * Set the text of the hover to the specified text. Recompute the size
+         * and location of the hover to hover near the specified control,
+         * pointing the arrow toward the target control.
+         */
+        void setText(String t, Control hoverNear, Control targetControl) {
+            if (t is null) {
+                t = EMPTY;
+            }
+            if (!t.equals(text)) {
+                Point oldSize = getExtent();
+                text = t;
+                hoverShell.redraw();
+                Point newSize = getExtent();
+                if (!oldSize.opEquals(newSize)) {
+                    // set a flag that indicates the direction of arrow
+                    arrowOnLeft = hoverNear.getLocation().x <= targetControl
+                            .getLocation().x;
+                    setNewShape();
+                }
+            }
+
+            if (hoverNear !is null) {
+                Point extent = getExtent();
+                int y = -extent.y - hah + 1;
+                int x = arrowOnLeft ? -hao + haw / 2 : -extent.x + hao + haw
+                        / 2;
+
+                hoverShell.setLocation(hoverNear.toDisplay(x, y));
+            }
+
+        }
+
+        /*
+         * Return whether or not the hover (shell) is visible.
+         */
+        bool isVisible() {
+            return hoverShell.isVisible();
+        }
+
+        /*
+         * Compute the extent of the hover for the current text.
+         */
+        Point getExtent() {
+            GC gc = new GC(hoverShell);
+            Point e = gc.textExtent(text);
+            gc.dispose();
+            e.x += hm * 2;
+            e.y += hm * 2;
+            return e;
+        }
+
+        /*
+         * Compute a new shape for the hover shell.
+         */
+        void setNewShape() {
+            Region oldRegion = region;
+            region = new Region();
+            region.add(getPolygon(false));
+            hoverShell.setRegion(region);
+            if (oldRegion !is null) {
+                oldRegion.dispose();
+            }
+
+        }
+    }
+
+    /**
+     * Construct a decorated field which is parented by the specified composite
+     * and has the given style bits. Use the controlCreator to create the
+     * specific kind of control that is decorated inside the field.
+     *
+     * @param parent
+     *            the parent of the decorated field.
+     * @param style
+     *            the desired style bits for the field.
+     * @param controlCreator
+     *            the IControlCreator used to specify the specific kind of
+     *            control that is to be decorated.
+     *
+     * @see IControlCreator
+     */
+    public this(Composite parent, int style,
+            IControlCreator controlCreator) {
+        decDatas = new FieldDecorationData[DECORATION_SLOTS];
+        this.form = createForm(parent);
+        this.control = controlCreator.createControl(form, style);
+
+        addControlListeners();
+        form.setTabList([ control ]);
+
+        // Set up the initial layout data.
+        FormData data = new FormData();
+        data.left = new FormAttachment(0, 0);
+        data.top = new FormAttachment(0, 0);
+        data.right = new FormAttachment(100, 0);
+        data.bottom = new FormAttachment(100, 0);
+        control.setLayoutData(data);
+
+    }
+
+    /**
+     * Adds an image decoration to the field.
+     *
+     * @param decoration
+     *            A FieldDecoration describing the image and description for the
+     *            decoration
+     *
+     * @param position
+     *            The SWT constant indicating the position of the decoration
+     *            relative to the field's control. The position should include
+     *            style bits describing both the vertical and horizontal
+     *            orientation. <code>SWT.LEFT</code> and
+     *            <code>SWT.RIGHT</code> describe the horizontal placement of
+     *            the decoration relative to the field, and the constants
+     *            <code>SWT.TOP</code> and <code>SWT.BOTTOM</code> describe
+     *            the vertical alignment of the decoration relative to the
+     *            field. Decorations always appear on either horizontal side of
+     *            the field, never above or below it. For example, a decoration
+     *            appearing on the left side of the field, at the top, is
+     *            specified as SWT.LEFT | SWT.TOP. If an image decoration
+     *            already exists in the specified position, it will be replaced
+     *            by the one specified.
+     * @param showOnFocus
+     *            <code>true</code> if the decoration should only be shown
+     *            when the associated control has focus, <code>false</code> if
+     *            it should always be shown.
+     *
+     */
+    public void addFieldDecoration(FieldDecoration decoration, int position,
+            bool showOnFocus) {
+        Label label;
+        FormData formData;
+        int i = indexForPosition(position);
+        if (decDatas[i] is null) {
+            formData = createFormDataForIndex(i, decoration.getImage());
+            label = new Label(form, SWT.HORIZONTAL | SWT.VERTICAL | SWT.CENTER);
+            label.addMouseTrackListener(new class(label) MouseTrackListener {
+                Label label_;
+                this(Label a){
+                    label_=a;
+                }
+                public void mouseHover(MouseEvent event) {
+                    FieldDecorationData decData = cast(FieldDecorationData) event.widget
+                            .getData();
+                    String desc = decData.decoration.getDescription();
+                    if (desc !is null) {
+                        showHoverText(desc, label_);
+                    }
+                }
+
+                public void mouseEnter(MouseEvent event) {
+                }
+
+                public void mouseExit(MouseEvent event) {
+                    hideHover();
+                }
+            });
+            decDatas[i] = new FieldDecorationData(decoration, label, formData,
+                    showOnFocus);
+        } else {
+            label = decDatas[i].label;
+            formData = decDatas[i].data;
+            decDatas[i].decoration = decoration;
+            decDatas[i].showOnFocus = showOnFocus;
+        }
+        label.setImage(decDatas[i].decoration.getImage());
+        label.setData(decDatas[i]);
+        label.setLayoutData(formData);
+        label.setVisible(!showOnFocus);
+
+        // Since sizes may have changed or there could be a new position
+        // defined, we need to update layout data on the control.
+        updateControlAttachments(i, decDatas[i]);
+    }
+
+    /*
+     * A decoration at the specified index has been added. Update the control's
+     * attachments if it has not previously been attached on that side or if it
+     * was attached to a decoration with a lesser width.
+     */
+    private void updateControlAttachments(int index, FieldDecorationData decData) {
+        FormData formData = cast(FormData) control.getLayoutData();
+        int newWidth = widthOf(decData.decoration.getImage());
+        // opposing represents the location of the decoration above or below
+        // the one in question.
+        int opposing;
+
+        switch (index) {
+        case LEFT_TOP:
+        case LEFT_BOTTOM:
+            if (index is LEFT_TOP) {
+                opposing = LEFT_BOTTOM;
+            } else {
+                opposing = LEFT_TOP;
+            }
+            if (decDatas[opposing] is null) {
+                // No decorator on the opposing side.
+                // Attach the control to this decorator
+                formData.left = new FormAttachment(decData.label);
+            } else if (decDatas[opposing].data.width < newWidth) {
+                // Decorator on opposing side is the smaller one. Attach
+                // control to the new one.
+                formData.left = new FormAttachment(decData.label);
+                // Center align the smaller one relative to the larger one.
+                decDatas[opposing].data.left.alignment = SWT.CENTER;
+                decDatas[opposing].data.left.control = decData.label;
+            } else {
+                // The new decorator is the smaller one. Keep the
+                // control attached to the opposing one.
+                formData = null;
+                // Horizontally center the smaller one relative to the larger
+                // one.
+                decData.data.left.alignment = SWT.CENTER;
+                decData.data.left.control = decDatas[opposing].label;
+            }
+            break;
+        /*
+         * The only real difference in right side cases is that we are attaching
+         * the right side of the control to the wider decoration rather than the
+         * left side of the control. Other concerns (horizontally aligning the
+         * smaller decoration relative to the larger one) are the same.
+         */
+        case RIGHT_TOP:
+        case RIGHT_BOTTOM:
+            if (index is RIGHT_TOP) {
+                opposing = RIGHT_BOTTOM;
+            } else {
+                opposing = RIGHT_TOP;
+            }
+            if (decDatas[opposing] is null) {
+                // No decorator on the opposing side.
+                // Attach the control to this decorator.
+                formData.right = new FormAttachment(decData.label);
+            } else if (decDatas[opposing].data.width < newWidth) {
+                // Decorator on opposing side is the smaller one. Attach
+                // control to the new one.
+                formData.right = new FormAttachment(decData.label);
+                // Center align the smaller one to the larger one.
+                // Note that this could be done using the left or right
+                // attachment, we use the right since it is already
+                // created for all right-side decorations.
+                decDatas[opposing].data.right.alignment = SWT.CENTER;
+                decDatas[opposing].data.right.control = decData.label;
+            } else {
+                // The new decorator is the smaller one. Keep the
+                // control attached to the opposing one.
+                formData = null;
+                // Horizontally center align the smaller one to the
+                // larger one.
+                decData.data.right.alignment = SWT.CENTER;
+                decData.data.right.control = decDatas[opposing].label;
+            }
+            break;
+        default:
+            return;
+        }
+        if (formData !is null) {
+            // Form data was updated.
+            control.setLayoutData(formData);
+            form.layout();
+        }
+    }
+
+    /**
+     * Get the control that is decorated by the receiver.
+     *
+     * @return the Control decorated by the receiver, or <code>null</code> if
+     *         none has been created yet.
+     */
+    public Control getControl() {
+        return control;
+    }
+
+    /**
+     * Get the control that represents the decorated field. This composite
+     * should be used to lay out the field within its parent.
+     *
+     * @return the Control that should be layed out in the field's parent's
+     *         layout. This is typically not the control itself, since
+     *         additional controls are used to represent the decorations.
+     */
+    public Control getLayoutControl() {
+        return form;
+    }
+
+    /**
+     * Create the parent composite and a form layout that will be used to manage
+     * decorations.
+     */
+    private Composite createForm(Composite parent) {
+        Composite composite = new Composite(parent, SWT.NO_FOCUS);
+        // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=126553
+        composite.setBackgroundMode(SWT.INHERIT_DEFAULT);
+        composite.setLayout(new FormLayout());
+        return composite;
+    }
+
+    /**
+     * Add any listeners needed on the target control.
+     */
+    private void addControlListeners() {
+        control.addDisposeListener(new class DisposeListener {
+            public void widgetDisposed(DisposeEvent event) {
+                if (hover !is null) {
+                    hover.dispose();
+                }
+            }
+        });
+        control.addFocusListener(new class FocusListener {
+            public void focusGained(FocusEvent event) {
+                controlFocusGained();
+            }
+
+            public void focusLost(FocusEvent event) {
+                controlFocusLost();
+            }
+
+        });
+    }
+
+    /*
+     * Return the index in the array of decoration datas that represents the
+     * specified SWT position.
+     *
+     * @param position The SWT constant indicating the position of the
+     * decoration relative to the field's control. The position should include
+     * style bits describing both the vertical and horizontal orientation.
+     * <code>SWT.LEFT</code> and <code>SWT.RIGHT</code> describe the
+     * horizontal placement of the decoration relative to the field, and the
+     * constants <code>SWT.TOP</code> and <code>SWT.BOTTOM</code> describe
+     * the vertical alignment of the decoration relative to the field.
+     * Decorations always appear on either horizontal side of the field, never
+     * above or below it. For example, a decoration appearing on the left side
+     * of the field, at the top, is specified as SWT.LEFT | SWT.TOP.
+     *
+     * @return index the index in the array of decorations that represents the
+     * specified SWT position. If the position is not an expected position, the
+     * index representing the top left position will be returned.
+     *
+     */
+    private int indexForPosition(int position) {
+        switch (position) {
+        case SWT.LEFT | SWT.BOTTOM:
+            return LEFT_BOTTOM;
+        case SWT.RIGHT | SWT.TOP:
+            return RIGHT_TOP;
+        case SWT.RIGHT | SWT.BOTTOM:
+            return RIGHT_BOTTOM;
+        default:
+            return LEFT_TOP;
+        }
+    }
+
+    /*
+     * Create a form data that will place the decoration at the specified
+     * position.
+     *
+     * @param index the index in the decDatas describing the position of the
+     * decoration.
+     *
+     * @param image the image shown in the decoration.
+     *
+     */
+    private FormData createFormDataForIndex(int index, Image image) {
+        Assert.isTrue(index >= 0 && index < DECORATION_SLOTS,
+                "Index out of range"); //$NON-NLS-1$
+
+        FormData data = new FormData();
+        switch (index) {
+        case LEFT_TOP:
+            data.left = new FormAttachment(0, 0);
+            data.top = new FormAttachment(0, 0);
+            break;
+        case LEFT_BOTTOM:
+            data.left = new FormAttachment(0, 0);
+            data.bottom = new FormAttachment(100, 0);
+            break;
+        case RIGHT_TOP:
+            data.right = new FormAttachment(100, 0);
+            data.top = new FormAttachment(0, 0);
+            break;
+        case RIGHT_BOTTOM:
+            data.right = new FormAttachment(100, 0);
+            data.bottom = new FormAttachment(100, 0);
+            break;
+        default:
+        }
+        data.width = widthOf(image);
+        data.height = SWT.DEFAULT;
+
+        return data;
+    }
+
+    /**
+     * Show the specified text using the same hover dialog as is used to show
+     * decorator descriptions. Normally, a decoration's description text will be
+     * shown in an info hover over the field's control whenever the mouse hovers
+     * over the decoration. This method can be used to show a decoration's
+     * description text at other times (such as when the control receives
+     * focus), or to show other text associated with the field.
+     *
+     * <p>
+     * If there is currently a hover visible, the hover's text will be replaced
+     * with the specified text.
+     *
+     * @param text
+     *            the text to be shown in the info hover, or <code>null</code>
+     *            if no text should be shown.
+     */
+    public void showHoverText(String text) {
+        showHoverText(text, control);
+    }
+
+    /**
+     * Hide any hover popups that are currently showing on the control.
+     * Normally, a decoration's description text will be shown in an info hover
+     * over the field's control as long as the mouse hovers over the decoration,
+     * and will be hidden when the mouse exits the control. This method can be
+     * used to hide a hover that was shown using <code>showHoverText</code>,
+     * or to programatically hide the current decoration hover.
+     *
+     * <p>
+     * This message has no effect if there is no current hover.
+     *
+     */
+    public void hideHover() {
+        if (hover !is null) {
+            hover.setVisible(false);
+        }
+    }
+
+    /*
+     * The target control gained focus. Any decorations that should show only
+     * when they have the focus should be shown here.
+     */
+    private void controlFocusGained() {
+        for (int i = 0; i < DECORATION_SLOTS; i++) {
+            if (decDatas[i] !is null && decDatas[i].showOnFocus) {
+                setVisible(decDatas[i], true);
+            }
+        }
+    }
+
+    /*
+     * The target control lost focus. Any decorations that should show only when
+     * they have the focus should be hidden here.
+     */
+    private void controlFocusLost() {
+        for (int i = 0; i < DECORATION_SLOTS; i++) {
+            if (decDatas[i] !is null && decDatas[i].showOnFocus) {
+                setVisible(decDatas[i], false);
+            }
+        }
+    }
+
+    /**
+     * Show the specified decoration. This message has no effect if the
+     * decoration is already showing, or was not already added to the field
+     * using <code>addFieldDecoration</code>.
+     *
+     * @param decoration
+     *            the decoration to be shown.
+     */
+    public void showDecoration(FieldDecoration decoration) {
+        FieldDecorationData data = getDecorationData(decoration);
+        if (data is null) {
+            return;
+        }
+        // record the fact that client would like it to be visible
+        data.visible = true;
+        // even if it is supposed to be shown, if the field does not have focus,
+        // do not show it (yet)
+        if (!data.showOnFocus || control.isFocusControl()) {
+            setVisible(data, true);
+        }
+    }
+
+    /**
+     * Hide the specified decoration. This message has no effect if the
+     * decoration is already hidden, or was not already added to the field using
+     * <code>addFieldDecoration</code>.
+     *
+     * @param decoration
+     *            the decoration to be hidden.
+     */
+    public void hideDecoration(FieldDecoration decoration) {
+        FieldDecorationData data = getDecorationData(decoration);
+        if (data is null) {
+            return;
+        }
+        // Store the desired visibility in the decData. We remember the
+        // client's instructions so that changes in visibility caused by
+        // field focus changes won't violate the client's visibility setting.
+        data.visible = false;
+        setVisible(data, false);
+    }
+
+    /**
+     * Update the specified decoration. This message should be used if the image
+     * or description in the decoration have changed. This message has no
+     * immediate effect if the decoration is not visible, and no effect at all
+     * if the decoration was not previously added to the field.
+     *
+     * @param decoration
+     *            the decoration to be hidden.
+     */
+    public void updateDecoration(FieldDecoration decoration) {
+        FieldDecorationData data = getDecorationData(decoration);
+        if (data is null) {
+            return;
+        }
+        if (data.label !is null) {
+            data.label.setImage(decoration.getImage());
+            // If the decoration is being shown, and a hover is active,
+            // update the hover text to display the new description.
+            if (data.label.getVisible() is true && hover !is null) {
+                showHoverText(decoration.getDescription(), data.label);
+            }
+        }
+    }
+
+    /*
+     * Set the visibility of the specified decoration data. This method does not
+     * change the visibility value stored in the decData, but instead consults
+     * it to determine how the visibility should be changed. This method is
+     * called any time visibility of a decoration might change, whether by
+     * client API or focus changes.
+     */
+    private void setVisible(FieldDecorationData decData, bool visible) {
+        // Check the decData visibility flag, since it contains the client's
+        // instructions for visibility.
+        if (visible && decData.visible) {
+            decData.label.setVisible(true);
+        } else {
+            decData.label.setVisible(false);
+        }
+    }
+
+    /*
+     * Get the FieldDecorationData that corresponds to the given decoration.
+     */
+    private FieldDecorationData getDecorationData(FieldDecoration dec) {
+        for (int i = 0; i < DECORATION_SLOTS; i++) {
+            if (decDatas[i] !is null && dec is decDatas[i].decoration
+                    && decDatas[i].label !is null
+                    && !decDatas[i].label.isDisposed()) {
+                return decDatas[i];
+            }
+        }
+        return null;
+    }
+
+    /*
+     * Show the specified text in the hover, positioning the hover near the
+     * specified control.
+     */
+    private void showHoverText(String text, Control hoverNear) {
+        if (text is null) {
+            hideHover();
+            return;
+        }
+
+        if (hover is null) {
+            hover = new Hover(hoverNear.getShell());
+        }
+        hover.setText(text, hoverNear, control);
+        hover.setVisible(true);
+    }
+
+    /**
+     * Set a bool that indicates whether the receiver should use the
+     * decoration registry's maximum decoration width when allocating space for
+     * decorations. The default value is <code>true</code>. Using the maximum
+     * decoration width is useful so that decorated fields on the same dialog
+     * that have different decoration widths will all align. This also allows
+     * client dialogs to align non-decorated fields with decorated fields by
+     * consulting the maximum decoration width.
+     * </p>
+     * <p>
+     * Clients may wish to set this value to <code>false</code> in cases where
+     * space usage is more important than alignment of fields. This value must
+     * be set before the decorations are added in order to ensure proper
+     * alignment.
+     * </p>
+     *
+     * @param useMaximumWidth
+     *            <code>true</code> if the maximum decoration width should be
+     *            used as the size for all decorations, <code>false</code> if
+     *            only the decoration size should be used.
+     *
+     * @see FieldDecorationRegistry#getMaximumDecorationWidth()
+     */
+    public void setUseMaximumDecorationWidth(bool useMaximumWidth) {
+        useMaxDecorationWidth = useMaximumWidth;
+    }
+
+    /*
+     * Return the width appropriate for the specified decoration image.
+     */
+    private int widthOf(Image image) {
+        if (image is null) {
+            return 0;
+        }
+        return useMaxDecorationWidth ? FieldDecorationRegistry.getDefault()
+                .getMaximumDecorationWidth() : image.getBounds().width;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/FieldAssistColors.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,224 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.fieldassist.FieldAssistColors;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.jface.resource.JFaceColors;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import tango.io.Stdout;
+
+/**
+ * FieldAssistColors defines protocol for retrieving colors that can be used to
+ * provide visual cues with fields. For consistency with JFace dialogs and
+ * wizards, it is recommended that FieldAssistColors is used when colors are
+ * used to annotate fields.
+ * <p>
+ * Color resources that are returned using methods in this class are maintained
+ * in the JFace color registries, or by SWT. Users of any color resources
+ * provided by this class are not responsible for the lifecycle of the color.
+ * Colors provided by this class should never be disposed by clients. In some
+ * cases, clients are provided information, such as RGB values, in order to
+ * create their own color resources. In these cases, the client should manage
+ * the lifecycle of any created resource.
+ *
+ * @since 3.2
+ * @deprecated As of 3.3, this class is no longer necessary.
+ */
+public class FieldAssistColors {
+
+    private static bool DEBUG = false;
+
+    /*
+     * Keys are background colors, values are the color with the alpha value
+     * applied
+     */
+    private static Map requiredFieldColorMap;
+
+    /*
+     * Keys are colors we have created, values are the displays on which they
+     * were created.
+     */
+    private static Map displays;
+
+    static this(){
+        requiredFieldColorMap = new HashMap();
+        displays = new HashMap();
+    }
+
+    /**
+     * Compute the RGB of the color that should be used for the background of a
+     * control to indicate that the control has an error. Because the color
+     * suitable for indicating an error depends on the colors set into the
+     * control, this color is always computed dynamically and provided as an RGB
+     * value. Clients who use this RGB to create a Color resource are
+     * responsible for managing the life cycle of the color.
+     * <p>
+     * This color is computed dynamically each time that it is queried. Clients
+     * should typically call this method once, create a color from the RGB
+     * provided, and dispose of the color when finished using it.
+     *
+     * @param control
+     *            the control for which the background color should be computed.
+     * @return the RGB value indicating a background color appropriate for
+     *         indicating an error in the control.
+     */
+    public static RGB computeErrorFieldBackgroundRGB(Control control) {
+        /*
+         * Use a 10% alpha of the error color applied on top of the widget
+         * background color.
+         */
+        Color dest = control.getBackground();
+        Color src = JFaceColors.getErrorText(control.getDisplay());
+        int destRed = dest.getRed();
+        int destGreen = dest.getGreen();
+        int destBlue = dest.getBlue();
+
+        // 10% alpha
+        int alpha = cast(int) (0xFF * 0.10f);
+        // Alpha blending math
+        destRed += (src.getRed() - destRed) * alpha / 0xFF;
+        destGreen += (src.getGreen() - destGreen) * alpha / 0xFF;
+        destBlue += (src.getBlue() - destBlue) * alpha / 0xFF;
+
+        return new RGB(destRed, destGreen, destBlue);
+    }
+
+    /**
+     * Return the color that should be used for the background of a control to
+     * indicate that the control is a required field and does not have content.
+     * <p>
+     * This color is managed by FieldAssistResources and should never be
+     * disposed by clients.
+     *
+     * @param control
+     *            the control on which the background color will be used.
+     * @return the color used to indicate that a field is required.
+     */
+    public static Color getRequiredFieldBackgroundColor(Control control) {
+        final Display display = control.getDisplay();
+
+        // If we are in high contrast mode, then don't apply an alpha
+        if (display.getHighContrast()) {
+            return control.getBackground();
+        }
+
+        // See if a color has already been computed
+        Object storedColor = requiredFieldColorMap.get(control.getBackground());
+        if (storedColor !is null) {
+            return cast(Color) storedColor;
+        }
+
+        // There is no color already created, so we must create one.
+        // Use a 15% alpha of yellow on top of the widget background.
+        Color dest = control.getBackground();
+        Color src = display.getSystemColor(SWT.COLOR_YELLOW);
+        int destRed = dest.getRed();
+        int destGreen = dest.getGreen();
+        int destBlue = dest.getBlue();
+
+        // 15% alpha
+        int alpha = cast(int) (0xFF * 0.15f);
+        // Alpha blending math
+        destRed += (src.getRed() - destRed) * alpha / 0xFF;
+        destGreen += (src.getGreen() - destGreen) * alpha / 0xFF;
+        destBlue += (src.getBlue() - destBlue) * alpha / 0xFF;
+
+        // create the color
+        Color color = new Color(display, destRed, destGreen, destBlue);
+        // record the color in a map using the original color as the key
+        requiredFieldColorMap.put(dest, color);
+        // If we have never created a color on this display before, install
+        // a dispose exec on the display.
+        if (!displays.containsKey(display)) {
+            display.disposeExec(new class Runnable {
+                public void run() {
+                    disposeColors(display);
+                }
+            });
+        }
+        // Record the color and its display in a map for later disposal.
+        displays.put(color, display);
+        return color;
+    }
+
+    /*
+     * Dispose any colors that were allocated for the given display.
+     */
+    private static void disposeColors(Display display) {
+        List toBeRemoved = new ArrayList(1);
+
+        if (DEBUG) {
+            Stdout.formatln("Display map is {}", (cast(Object)displays).toString()); //$NON-NLS-1$
+            Stdout.formatln("Color map is {}", (cast(Object)requiredFieldColorMap).toString()); //$NON-NLS-1$
+        }
+
+        // Look for any stored colors that were created on this display
+        for (Iterator i = displays.keySet().iterator(); i.hasNext();) {
+            Color color = cast(Color) i.next();
+            if ((cast(Display) displays.get(color)).opEquals(display)) {
+                // The color is on this display. Mark it for removal.
+                toBeRemoved.add(color);
+
+                // Now look for any references to it in the required field color
+                // map
+                List toBeRemovedFromRequiredMap = new ArrayList(1);
+                for (Iterator iter = requiredFieldColorMap.keySet().iterator(); iter
+                        .hasNext();) {
+                    Color bgColor = cast(Color) iter.next();
+                    if ((cast(Color) requiredFieldColorMap.get(bgColor))
+                            .opEquals(color)) {
+                        // mark it for removal from the required field color map
+                        toBeRemovedFromRequiredMap.add(bgColor);
+                    }
+                }
+                // Remove references in the required field map now that
+                // we are done iterating.
+                for (int j = 0; j < toBeRemovedFromRequiredMap.size(); j++) {
+                    requiredFieldColorMap.remove(toBeRemovedFromRequiredMap
+                            .get(j));
+                }
+            }
+        }
+        // Remove references in the display map now that we are
+        // done iterating
+        for (int i = 0; i < toBeRemoved.size(); i++) {
+            Color color = cast(Color) toBeRemoved.get(i);
+            // Removing from the display map must be done before disposing the
+            // color or else the comparison between this color and the one
+            // in the map will fail.
+            displays.remove(color);
+            // Dispose it
+            if (DEBUG) {
+                Stdout.formatln("Disposing color {}", color.toString()); //$NON-NLS-1$
+            }
+            color.dispose();
+        }
+        if (DEBUG) {
+            Stdout.formatln("Display map is {}", (cast(Object)displays).toString()); //$NON-NLS-1$
+            Stdout.formatln("Color map is {}", (cast(Object)requiredFieldColorMap).toString()); //$NON-NLS-1$
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/FieldDecoration.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.jface.fieldassist.FieldDecoration;
+
+import org.eclipse.swt.graphics.Image;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * FieldDecoration is a simple data structure class for specifying a decoration
+ * for a field. A decoration may be rendered in different ways depending on the
+ * type of field it is used with.
+ *
+ * @see FieldDecorationRegistry
+ *
+ * @since 3.2
+ */
+public class FieldDecoration {
+
+    /*
+     * The image to be shown in the decoration.
+     */
+    private Image image;
+
+    /*
+     * The description to show in the decoration's hover.
+     */
+    private String description;
+
+    /**
+     * Create a decoration for a field with the specified image and description
+     * text.
+     *
+     * @param image
+     *            the image shown in the decoration. A <code>null</code> image
+     *            will result in a blank decoration, which may be used to
+     *            reserve space near the field.
+     * @param description
+     *            the description shown when the user hovers over the
+     *            decoration. A <code>null</code> description indicates that
+     *            there will be no hover for the decoration.
+     */
+    public this(Image image, String description) {
+        this.image = image;
+        this.description = description;
+    }
+
+    /**
+     * Return the image shown in the decoration, or <code>null</code> if no
+     * image is specified.
+     *
+     * @return the image shown in the decoration. A return value of
+     *         <code>null</code> signifies a blank decoration.
+     */
+    public Image getImage() {
+        return image;
+    }
+
+    /**
+     * Set the image shown in the decoration, or <code>null</code> if no image
+     * is specified. It is up to the caller to update any decorated fields that
+     * are showing the description in order to display the new image.
+     *
+     * @param image
+     *            the image shown in the decoration. A value of
+     *            <code>null</code> signifies a blank decoration.
+     */
+    public void setImage(Image image) {
+        this.image = image;
+    }
+
+    /**
+     * Return the description for the decoration shown when the user hovers over
+     * the decoration.
+     *
+     * @return the String description of the decoration. A return value of
+     *         <code>null</code> indicates that no description will be shown.
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Set the description for the decoration shown when the user hovers over
+     * the decoration. It is up to the caller to update any decorated fields
+     * showing the description.
+     *
+     * @param description
+     *            the String description of the decoration. A value of
+     *            <code>null</code> indicates that no description will be
+     *            shown.
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/FieldDecorationRegistry.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,417 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+
+import org.eclipse.jface.fieldassist.FieldDecoration;
+
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.Set;
+
+/**
+ * FieldDecorationRegistry is a common registry used to define shared field
+ * decorations within an application. Unlike resource registries, the
+ * FieldDecorationRegistry does not perform any lifecycle management of the
+ * decorations.
+ * </p>
+ * <p>
+ * Clients may specify images for the decorations in several different ways.
+ * Images may be described by their image id in a specified
+ * {@link ImageRegistry}. In this case, the life cycle of the image is managed
+ * by the image registry, and the decoration registry will not attempt to obtain
+ * an image from the image registry until the decoration is actually requested.
+ * In cases where the client has access to an already-created image, the image
+ * itself can be specified when registering the decoration. In this case, the
+ * life cycle should be managed by the specifying client.
+ * </p>
+ *
+ * @see FieldDecoration
+ * @see ImageRegistry
+ *
+ * @since 3.2
+ */
+public class FieldDecorationRegistry {
+
+    /**
+     * Decoration id for the decoration that should be used to cue the user that
+     * content proposals are available.
+     */
+    public static const String DEC_CONTENT_PROPOSAL = "DEC_CONTENT_PROPOSAL"; //$NON-NLS-1$
+
+    /**
+     * Decoration id for the decoration that should be used to cue the user that
+     * a field is required.
+     */
+    public static const String DEC_REQUIRED = "DEC_REQUIRED"; //$NON-NLS-1$
+
+    /**
+     * Decoration id for the decoration that should be used to cue the user that
+     * a field has an error.
+     */
+    public static const String DEC_ERROR = "DEC_ERROR"; //$NON-NLS-1$
+
+    /**
+     * Decoration id for the decoration that should be used to cue the user that
+     * a field has a warning.
+     */
+    public static const String DEC_WARNING = "DEC_WARNING"; //$NON-NLS-1$
+
+    /**
+     * Decoration id for the decoration that should be used to cue the user that
+     * a field has additional information.
+     *
+     * @since 3.3
+     */
+    public static const String DEC_INFORMATION = "DEC_INFORMATION"; //$NON-NLS-1$
+
+    /**
+     * Decoration id for the decoration that should be used to cue the user that
+     * a field has an error with quick fix available.
+     *
+     * @since 3.3
+     */
+    public static const String DEC_ERROR_QUICKFIX = "DEC_ERRORQUICKFIX"; //$NON-NLS-1$
+
+    /*
+     * Image id's
+     */
+    private static const String IMG_DEC_FIELD_CONTENT_PROPOSAL = "org.eclipse.jface.fieldassist.IMG_DEC_FIELD_CONTENT_PROPOSAL"; //$NON-NLS-1$
+
+    private static const String IMG_DEC_FIELD_REQUIRED = "org.eclipse.jface.fieldassist.IMG_DEC_FIELD_REQUIRED"; //$NON-NLS-1$
+
+    private static const String IMG_DEC_FIELD_ERROR = "org.eclipse.jface.fieldassist.IMG_DEC_FIELD_ERROR"; //$NON-NLS-1$
+
+    private static const String IMG_DEC_FIELD_ERROR_QUICKFIX = "org.eclipse.jface.fieldassist.IMG_DEC_FIELD_ERROR_QUICKFIX"; //$NON-NLS-1$
+
+    private static const String IMG_DEC_FIELD_WARNING = "org.eclipse.jface.fieldassist.IMG_DEC_FIELD_WARNING"; //$NON-NLS-1$
+
+    private static const String IMG_DEC_FIELD_INFO = "org.eclipse.jface.fieldassist.IMG_DEC_FIELD_INFO"; //$NON-NLS-1$
+
+    /*
+     * Declare images and decorations immediately.
+     */
+    private static void static_this(FieldDecorationRegistry reg) {
+        ImageRegistry imageRegistry = JFaceResources.getImageRegistry();
+
+        // Define the images used in the standard decorations.
+        imageRegistry.put(IMG_DEC_FIELD_CONTENT_PROPOSAL, ImageDescriptor.createFromFile(
+            getImportData!("org.eclipse.jface.fieldassist.images.contassist_ovr.gif")));//$NON-NLS-1$
+        imageRegistry.put(IMG_DEC_FIELD_ERROR, ImageDescriptor.createFromFile(
+            getImportData!("org.eclipse.jface.fieldassist.images.error_ovr.gif")));//$NON-NLS-1$
+
+        imageRegistry.put(IMG_DEC_FIELD_WARNING, ImageDescriptor.createFromFile(
+            getImportData!("org.eclipse.jface.fieldassist.images.warn_ovr.gif")));//$NON-NLS-1$
+
+        imageRegistry.put(IMG_DEC_FIELD_REQUIRED, ImageDescriptor.createFromFile(
+            getImportData!("org.eclipse.jface.fieldassist.images.required_field_cue.gif")));//$NON-NLS-1$
+
+        imageRegistry.put(IMG_DEC_FIELD_ERROR_QUICKFIX, ImageDescriptor.createFromFile(
+            getImportData!("org.eclipse.jface.fieldassist.images.errorqf_ovr.gif")));//$NON-NLS-1$
+
+        imageRegistry.put(IMG_DEC_FIELD_INFO, ImageDescriptor.createFromFile(
+            getImportData!("org.eclipse.jface.fieldassist.images.info_ovr.gif")));//$NON-NLS-1$
+
+        // Define the standard decorations. Some do not have standard
+        // descriptions. Use null in these cases.
+        reg
+                .registerFieldDecoration(
+                        DEC_CONTENT_PROPOSAL,
+                        JFaceResources
+                                .getString("FieldDecorationRegistry.contentAssistMessage"), //$NON-NLS-1$
+                        IMG_DEC_FIELD_CONTENT_PROPOSAL, imageRegistry);
+
+        reg.registerFieldDecoration(
+                DEC_ERROR,
+                JFaceResources
+                        .getString("FieldDecorationRegistry.errorMessage"), //$NON-NLS-1$
+                IMG_DEC_FIELD_ERROR, imageRegistry);
+
+        reg.registerFieldDecoration(
+                DEC_ERROR_QUICKFIX,
+                JFaceResources
+                        .getString("FieldDecorationRegistry.errorQuickFixMessage"), //$NON-NLS-1$
+                IMG_DEC_FIELD_ERROR_QUICKFIX, imageRegistry);
+
+        reg.registerFieldDecoration(DEC_WARNING, null,
+                IMG_DEC_FIELD_WARNING, imageRegistry);
+
+        reg.registerFieldDecoration(DEC_INFORMATION, null,
+                IMG_DEC_FIELD_INFO, imageRegistry);
+
+        reg
+                .registerFieldDecoration(
+                        DEC_REQUIRED,
+                        JFaceResources
+                                .getString("FieldDecorationRegistry.requiredFieldMessage"), //$NON-NLS-1$
+                        IMG_DEC_FIELD_REQUIRED, imageRegistry);
+
+    }
+
+    /*
+     * Data structure that holds onto the decoration image info and description,
+     * and can produce a decorator on request.
+     */
+    class Entry {
+        private String description;
+
+        private String imageId;
+
+        private ImageRegistry imageRegistry;
+
+        private Image image;
+
+        private FieldDecoration decoration;
+
+        this(String description, String imageId, ImageRegistry registry) {
+            this.description = description;
+            this.imageId = imageId;
+            this.imageRegistry = registry;
+        }
+
+        this(String description, Image image) {
+            this.description = description;
+            this.image = image;
+        }
+
+        FieldDecoration getDecoration() {
+            if (decoration is null) {
+                if (image is null) {
+                    if (imageRegistry is null) {
+                        imageRegistry = JFaceResources.getImageRegistry();
+                    }
+                    image = imageRegistry.get(imageId);
+                }
+                decoration = new FieldDecoration(image, description);
+            }
+            // Null out all other fields now that the decoration has an image
+            description = null;
+            imageId = null;
+            imageRegistry = null;
+            image = null;
+
+            return decoration;
+        }
+    }
+
+    /**
+     * Default instance of the registry. Applications may install their own
+     * registry.
+     */
+    private static FieldDecorationRegistry defaultInstance;
+
+    /**
+     * Maximum width and height used by decorations in this registry. Clients
+     * may use these values to reserve space in dialogs for decorations or to
+     * adjust layouts so that decorated and non-decorated fields line up.
+     */
+    private int maxDecorationWidth = 0;
+    private int maxDecorationHeight = 0;
+
+    private HashMap /* <String id, FieldDecoration> */decorations;
+
+    /**
+     * Get the default FieldDecorationRegistry.
+     *
+     * @return the singleton FieldDecorationRegistry that is used to manage
+     *         shared field decorations.
+     */
+    public static FieldDecorationRegistry getDefault() {
+        if (defaultInstance is null) {
+            synchronized(FieldDecorationRegistry.classinfo){
+                if (defaultInstance is null) {
+                    defaultInstance = new FieldDecorationRegistry();
+                    static_this(defaultInstance);
+                }
+            }
+        }
+        return defaultInstance;
+    }
+
+    /**
+     * Set the default FieldDecorationRegistry.
+     *
+     * @param defaultRegistry
+     *            the singleton FieldDecorationRegistry that is used to manage
+     *            shared field decorations.
+     */
+    public static void setDefault(FieldDecorationRegistry defaultRegistry) {
+        defaultInstance = defaultRegistry;
+    }
+
+    /**
+     * Construct a FieldDecorationRegistry.
+     */
+    public this() {
+        decorations = new HashMap();
+        maxDecorationWidth = 0;
+        maxDecorationHeight = 0;
+    }
+
+    /**
+     * Get the maximum width (in pixels) of any decoration retrieved so far in
+     * the registry. This value changes as decorations are added and retrieved.
+     * This value can be used by clients to reserve space or otherwise compute
+     * margins when aligning non-decorated fields with decorated fields.
+     *
+     * @return the maximum width in pixels of any accessed decoration
+     */
+    public int getMaximumDecorationWidth() {
+        return maxDecorationWidth;
+    }
+
+    /**
+     * Get the maximum height (in pixels) of any decoration retrieved so far in
+     * the registry. This value changes as decorations are added and retrieved.
+     * This value can be used by clients to reserve space or otherwise compute
+     * margins when aligning non-decorated fields with decorated fields.
+     *
+     *
+     * @return the maximum height in pixels of any accessed decoration
+     */
+    public int getMaximumDecorationHeight() {
+        return maxDecorationHeight;
+    }
+
+    /**
+     * Registers a field decoration using the specified id. The lifecyle of the
+     * supplied image should be managed by the client. That is, it will never be
+     * disposed by this registry and the decoration should be removed from the
+     * registry if the image is ever disposed elsewhere.
+     *
+     * @param id
+     *            the String id used to identify and access the decoration.
+     * @param description
+     *            the String description to be used in the decoration, or
+     *            <code>null</code> if the decoration has no description.
+     * @param image
+     *            the image to be used in the decoration
+     */
+    public void registerFieldDecoration(String id, String description,
+            Image image) {
+        decorations.put(stringcast(id), new Entry(description, image));
+        // Recompute the maximums since this might be a replacement
+        recomputeMaximums();
+    }
+
+    /**
+     * Registers a field decoration using the specified id. An image id of an
+     * image located in the default JFaceResources image registry is supplied.
+     * The image will not be created until the decoration is requested.
+     *
+     * @param id
+     *            the String id used to identify and access the decoration.
+     * @param description
+     *            the String description to be used in the decoration, or
+     *            <code>null</code> if the decoration has no description. *
+     * @param imageId
+     *            the id of the image in the JFaceResources image registry that
+     *            is used for this decorator
+     */
+    public void registerFieldDecoration(String id, String description,
+            String imageId) {
+        decorations.put(stringcast(id), new Entry(description, imageId, JFaceResources
+                .getImageRegistry()));
+        // Recompute the maximums as this could be a replacement of a previous
+        // image.
+        recomputeMaximums();
+    }
+
+    /**
+     * Registers a field decoration using the specified id. An image id and an
+     * image registry are supplied. The image will not be created until the
+     * decoration is requested.
+     *
+     * @param id
+     *            the String id used to identify and access the decoration.
+     * @param description
+     *            the String description to be used in the decoration, or
+     *            <code>null</code> if the decoration has no description. *
+     * @param imageId
+     *            the id of the image in the supplied image registry that is
+     *            used for this decorator
+     * @param imageRegistry
+     *            the registry used to obtain the image
+     */
+    public void registerFieldDecoration(String id, String description,
+            String imageId, ImageRegistry imageRegistry) {
+        decorations.put(stringcast(id), new Entry(description, imageId, imageRegistry));
+        // Recompute the maximums since this could be a replacement
+        recomputeMaximums();
+    }
+
+    /**
+     * Unregisters the field decoration with the specified id. No lifecycle
+     * management is performed on the decoration's image. This message has no
+     * effect if no field decoration with the specified id was previously
+     * registered.
+     * </p>
+     * <p>
+     * This method need not be called if the registered decoration's image is
+     * managed in an image registry. In that case, leaving the decoration in the
+     * registry will do no harm since the image will remain valid and will be
+     * properly disposed when the application is shut down. This method should
+     * be used in cases where the caller intends to dispose of the image
+     * referred to by the decoration, or otherwise determines that the
+     * decoration should no longer be used.
+     *
+     * @param id
+     *            the String id of the decoration to be unregistered.
+     */
+    public void unregisterFieldDecoration(String id) {
+        decorations.remove(stringcast(id));
+        recomputeMaximums();
+    }
+
+    /**
+     * Returns the field decoration registered by the specified id .
+     *
+     * @param id
+     *            the String id used to access the decoration.
+     * @return the FieldDecoration with the specified id, or <code>null</code>
+     *         if there is no decoration with the specified id.
+     */
+    public FieldDecoration getFieldDecoration(String id) {
+        Object entry = decorations.get(stringcast(id));
+        if (entry is null) {
+            return null;
+        }
+        return (cast(Entry) entry).getDecoration();
+
+    }
+
+    /*
+     * The maximum decoration width and height must be recomputed. Typically
+     * called in response to adding, removing, or replacing a decoration.
+     */
+    private void recomputeMaximums() {
+        Iterator entries = decorations.values().iterator();
+
+        maxDecorationHeight = 0;
+        maxDecorationWidth = 0;
+        while (entries.hasNext()) {
+            Image image = (cast(Entry)entries.next()).getDecoration().getImage();
+            if (image !is null) {
+                maxDecorationHeight = Math.max(maxDecorationHeight, image.getBounds().height);
+                maxDecorationWidth = Math.max(maxDecorationWidth, image.getBounds().width);
+            }
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/IContentProposal.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.fieldassist.IContentProposal;
+
+import java.lang.all;
+
+/**
+ * IContentProposal describes a content proposal to be shown. It consists of the
+ * content that will be provided if the proposal is accepted, an optional label
+ * used to describe the content to the user, and an optional description that
+ * further elaborates the meaning of the proposal.
+ *
+ * @since 3.2
+ */
+public interface IContentProposal {
+    /**
+     * Return the content represented by this proposal.
+     *
+     * @return the String content represented by this proposal.
+     */
+    public String getContent();
+
+    /**
+     * Return the integer position within the contents that the cursor should be
+     * placed after the proposal is accepted.
+     *
+     * @return the zero-based index position within the contents where the
+     *         cursor should be placed after the proposal is accepted.
+     */
+    public int getCursorPosition();
+
+    /**
+     * Return the label used to describe this proposal.
+     *
+     * @return the String label used to display the proposal. If
+     *         <code>null</code>, then the content will be displayed as the
+     *         label.
+     */
+    public String getLabel();
+
+    /**
+     * Return a description that describes this proposal.
+     *
+     * @return the String label used to further the proposal. If
+     *         <code>null</code>, then no description will be displayed.
+     */
+    public String getDescription();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/IContentProposalListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.fieldassist.IContentProposalListener;
+
+import java.lang.all;
+
+import org.eclipse.jface.fieldassist.IContentProposal;
+
+/**
+ * This interface is used to listen to notifications from a
+ * {@link ContentProposalAdapter}.
+ *
+ * @since 3.2
+ */
+public interface IContentProposalListener {
+    /**
+     * A content proposal has been accepted.
+     *
+     * @param proposal
+     *            the accepted content proposal
+     */
+    public void proposalAccepted(IContentProposal proposal);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/IContentProposalListener2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.fieldassist.IContentProposalListener2;
+
+import org.eclipse.jface.fieldassist.ContentProposalAdapter;
+
+import java.lang.all;
+
+/**
+ * This interface is used to listen to additional notifications from a
+ * {@link ContentProposalAdapter}.
+ *
+ * @since 3.3
+ */
+public interface IContentProposalListener2 {
+    /**
+     * A content proposal popup has been opened for content proposal assistance.
+     *
+     * @param adapter
+     *            the ContentProposalAdapter which is providing content proposal
+     *            behavior to a control
+     */
+    public void proposalPopupOpened(ContentProposalAdapter adapter);
+
+    /**
+     * A content proposal popup has been closed.
+     *
+     * @param adapter
+     *            the ContentProposalAdapter which is providing content proposal
+     *            behavior to a control
+     */
+    public void proposalPopupClosed(ContentProposalAdapter adapter);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/IContentProposalProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.fieldassist.IContentProposalProvider;
+
+import org.eclipse.jface.fieldassist.IContentProposal;
+
+import java.lang.all;
+
+/**
+ * IContentProposalProvider provides an array of IContentProposals that are
+ * appropriate for a textual dialog field, given the field's current content and
+ * the current cursor position.
+ *
+ * @since 3.2
+ */
+public interface IContentProposalProvider {
+
+    /**
+     * Return an array of Objects representing the valid content proposals for a
+     * field.
+     *
+     * @param contents
+     *            the current contents of the text field
+     * @param position
+     *            the current position of the cursor in the contents
+     *
+     * @return the array of {@link IContentProposal} that represent valid
+     *         proposals for the field.
+     */
+    IContentProposal[] getProposals(String contents, int position);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/IControlContentAdapter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.fieldassist.IControlContentAdapter;
+
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * This interface is used to set and retrieve text content from an arbitrary
+ * control. Clients are expected to implement this interface when defining a
+ * {@link ContentProposalAdapter}, in order to specify how to retrieve and set
+ * the contents of the control being adapted.
+ *
+ * @since 3.2
+ */
+public interface IControlContentAdapter {
+    /**
+     * Set the contents of the specified control to the specified text. Must not
+     * be <code>null</code>.
+     *
+     * @param control
+     *            the control whose contents are to be set (replaced).
+     * @param contents
+     *            the String specifying the new control content.
+     * @param cursorPosition
+     *            the zero-based index representing the desired cursor position
+     *            in the control's contents after the contents are set.
+     */
+    public void setControlContents(Control control, String contents,
+            int cursorPosition);
+
+    /**
+     * Insert the specified contents into the control's current contents. Must
+     * not be <code>null</code>.
+     *
+     * @param control
+     *            the control whose contents are to be altered.
+     * @param contents
+     *            the String to be inserted into the control contents.
+     * @param cursorPosition
+     *            the zero-based index representing the desired cursor position
+     *            within the inserted contents after the insertion is made.
+     */
+    public void insertControlContents(Control control, String contents,
+            int cursorPosition);
+
+    /**
+     * Get the text contents of the control.
+     *
+     * @param control
+     *            the control whose contents are to be retrieved.
+     * @return the String contents of the control.
+     */
+    public String getControlContents(Control control);
+
+    /**
+     * Get the current cursor position in the control. The position is specified
+     * as a zero-based index into the string. Valid ranges are from 0 to N,
+     * where N is the size of the contents string. A value of N indicates that
+     * the cursor is at the end of the contents.
+     *
+     * @param control
+     *            the control whose position is to be retrieved.
+     * @return the zero-based index representing the cursor position in the
+     *         control's contents.
+     */
+    public int getCursorPosition(Control control);
+
+    /**
+     * Get the bounds (in pixels) of the insertion point for the control
+     * content. This is a rectangle, in coordinates relative to the control,
+     * where the insertion point is displayed. If the implementer does not have
+     * an insertion point, or cannot determine the location of the insertion
+     * point, it is appropriate to return the bounds of the entire control. This
+     * value may be used to position a content proposal popup.
+     *
+     * @param control
+     *            the control whose offset is to be retrieved.
+     * @return the pixel width representing the distance between the edge of the
+     *         control and the insertion point.
+     */
+    public Rectangle getInsertionBounds(Control control);
+
+    /**
+     * Set the current cursor position in the control. The position is specified
+     * as a zero-based index into the string. Valid ranges are from 0 to N,
+     * where N is the size of the contents string. A value of N indicates that
+     * the cursor is at the end of the contents.
+     *
+     * @param control
+     *            the control whose cursor position is to be set.
+     * @param index
+     *            the zero-based index representing the cursor position in the
+     *            control's contents.
+     */
+    public void setCursorPosition(Control control, int index);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/IControlContentAdapter2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.fieldassist.IControlContentAdapter2;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * This interface is used by a {@link ContentProposalAdapter} in order to
+ * retrieve and set the selection range in a control.
+ * 
+ * @since 3.4
+ */
+public interface IControlContentAdapter2 {
+    /**
+     * Get the current selection range in the control. The x coordinate of the
+     * returned point is the position of the first selected character and the y
+     * coordinate of the returned point is the position of the last selected
+     * character. The positions are specified as a zero-based index into the
+     * string. Valid ranges are from 0 to N, where N is the size of the contents
+     * string. A value of N indicates that the last character is in the
+     * selection.
+     * 
+     * @param control
+     *            the control whose position is to be retrieved.
+     * @return a point representing the selection start and end
+     */
+    public Point getSelection(Control control);
+
+    /**
+     * Set the current selection range in the control. The x coordinate of the
+     * provided point is the position of the first selected character and the y
+     * coordinate of the point is the position of the last selected character.
+     * The positions are specified as a zero-based index into the string. Valid
+     * ranges are from 0 to N, where N is the size of the contents string. A
+     * value of N indicates that the last character is in the selection. If the
+     * x and y coordinates are the same, then there is no selection.
+     * 
+     * @param control
+     *            the control whose position is to be retrieved.
+     * @param range
+     *            a point representing the selection start and end
+     */
+    public void setSelection(Control control, Point range);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/IControlCreator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.jface.fieldassist.IControlCreator;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+import java.lang.all;
+
+/**
+ * This interface is used to create a control with a specific parent and style
+ * bits. It is used by {@link DecoratedField} to create the control to be
+ * decorated. Clients are expected to implement this interface in order to
+ * create a particular kind of control for decoration.
+ *
+ * @since 3.2
+ * @deprecated As of 3.3, clients should use {@link ControlDecoration} instead
+ *             of {@link DecoratedField}.
+ *
+ */
+public interface IControlCreator {
+    /**
+     * Create a control with the specified parent and style bits.
+     *
+     * @param parent
+     *            the parent of the control
+     * @param style
+     *            the style of the control
+     *
+     * @return the Control that was created.
+     */
+    public Control createControl(Composite parent, int style);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/SimpleContentProposalProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ *     Amir Kouchekinia <amir@pyrus.us> - bug 200762
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.fieldassist.SimpleContentProposalProvider;
+
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+import org.eclipse.jface.fieldassist.IContentProposal;
+
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * SimpleContentProposalProvider is a class designed to map a static list of
+ * Strings to content proposals.
+ *
+ * @see IContentProposalProvider
+ * @since 3.2
+ *
+ */
+public class SimpleContentProposalProvider : IContentProposalProvider {
+
+    /*
+     * The proposals provided.
+     */
+    private String[] proposals;
+
+    /*
+     * The proposals mapped to IContentProposal. Cached for speed in the case
+     * where filtering is not used.
+     */
+    private IContentProposal[] contentProposals;
+
+    /*
+     * Boolean that tracks whether filtering is used.
+     */
+    private bool filterProposals = false;
+
+    /**
+     * Construct a SimpleContentProposalProvider whose content proposals are
+     * always the specified array of Objects.
+     *
+     * @param proposals
+     *            the array of Strings to be returned whenever proposals are
+     *            requested.
+     */
+    public this(String[] proposals) {
+        //super();
+        this.proposals = proposals;
+    }
+
+    /**
+     * Return an array of Objects representing the valid content proposals for a
+     * field.
+     *
+     * @param contents
+     *            the current contents of the field (only consulted if filtering
+     *            is set to <code>true</code>)
+     * @param position
+     *            the current cursor position within the field (ignored)
+     * @return the array of Objects that represent valid proposals for the field
+     *         given its current content.
+     */
+    public IContentProposal[] getProposals(String contents, int position) {
+        if (filterProposals) {
+            ArrayList list = new ArrayList();
+            for (int i = 0; i < proposals.length; i++) {
+                if (proposals[i].length >= contents.length
+                        && proposals[i].substring(0, contents.length)
+                                .equalsIgnoreCase(contents)) {
+                    list.add(cast(Object)makeContentProposal(proposals[i]));
+                }
+            }
+            return arraycast!(IContentProposal)(list.toArray());
+        }
+        if (contentProposals is null) {
+            contentProposals = new IContentProposal[proposals.length];
+            for (int i = 0; i < proposals.length; i++) {
+                contentProposals[i] = makeContentProposal(proposals[i]);
+            }
+        }
+        return contentProposals;
+    }
+
+    /**
+     * Set the Strings to be used as content proposals.
+     *
+     * @param items
+     *            the array of Strings to be used as proposals.
+     */
+    public void setProposals(String[] items) {
+        this.proposals = items;
+        contentProposals = null;
+    }
+
+    /**
+     * Set the bool that controls whether proposals are filtered according to
+     * the current field content.
+     *
+     * @param filterProposals
+     *            <code>true</code> if the proposals should be filtered to
+     *            show only those that match the current contents of the field,
+     *            and <code>false</code> if the proposals should remain the
+     *            same, ignoring the field content.
+     * @since 3.3
+     */
+    public void setFiltering(bool filterProposals) {
+        this.filterProposals = filterProposals;
+        // Clear any cached proposals.
+        contentProposals = null;
+    }
+
+    /*
+     * Make an IContentProposal for showing the specified String.
+     */
+    private IContentProposal makeContentProposal( String proposal) {
+        return new class IContentProposal {
+            String proposal_;
+            this(){proposal_=proposal;}
+            public String getContent() {
+                return proposal_;
+            }
+
+            public String getDescription() {
+                return null;
+            }
+
+            public String getLabel() {
+                return null;
+            }
+
+            public int getCursorPosition() {
+                return proposal_.length;
+            }
+        };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/TextContentAdapter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.fieldassist.TextContentAdapter;
+
+import org.eclipse.jface.fieldassist.IControlContentAdapter;
+import org.eclipse.jface.fieldassist.IControlContentAdapter2;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * An {@link IControlContentAdapter} for SWT Text controls. This is a
+ * convenience class for easily creating a {@link ContentProposalAdapter} for
+ * text fields.
+ *
+ * @since 3.2
+ */
+public class TextContentAdapter : IControlContentAdapter,
+        IControlContentAdapter2 {
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.taskassistance.IControlContentAdapter#getControlContents(org.eclipse.swt.widgets.Control)
+     */
+    public String getControlContents(Control control) {
+        return (cast(Text) control).getText();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.fieldassist.IControlContentAdapter#setControlContents(org.eclipse.swt.widgets.Control,
+     *      java.lang.String, int)
+     */
+    public void setControlContents(Control control, String text,
+            int cursorPosition) {
+        (cast(Text) control).setText(text);
+        (cast(Text) control).setSelection(cursorPosition, cursorPosition);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.fieldassist.IControlContentAdapter#insertControlContents(org.eclipse.swt.widgets.Control,
+     *      java.lang.String, int)
+     */
+    public void insertControlContents(Control control, String text,
+            int cursorPosition) {
+        Point selection = (cast(Text) control).getSelection();
+        (cast(Text) control).insert(text);
+        // Insert will leave the cursor at the end of the inserted text. If this
+        // is not what we wanted, reset the selection.
+        if (cursorPosition < text.length) {
+            (cast(Text) control).setSelection(selection.x + cursorPosition,
+                    selection.x + cursorPosition);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.fieldassist.IControlContentAdapter#getCursorPosition(org.eclipse.swt.widgets.Control)
+     */
+    public int getCursorPosition(Control control) {
+        return (cast(Text) control).getCaretPosition();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.fieldassist.IControlContentAdapter#getInsertionBounds(org.eclipse.swt.widgets.Control)
+     */
+    public Rectangle getInsertionBounds(Control control) {
+        Text text = cast(Text) control;
+        Point caretOrigin = text.getCaretLocation();
+        // We fudge the y pixels due to problems with getCaretLocation
+        // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=52520
+        return new Rectangle(caretOrigin.x + text.getClientArea().x,
+                caretOrigin.y + text.getClientArea().y + 3, 1, text.getLineHeight());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.fieldassist.IControlContentAdapter#setCursorPosition(org.eclipse.swt.widgets.Control,
+     *      int)
+     */
+    public void setCursorPosition(Control control, int position) {
+        (cast(Text) control).setSelection(new Point(position, position));
+    }
+
+    /**
+     * @see org.eclipse.jface.fieldassist.IControlContentAdapter2#getSelection(org.eclipse.swt.widgets.Control)
+     *
+     * @since 3.4
+     */
+    public Point getSelection(Control control) {
+        return (cast(Text) control).getSelection();
+    }
+
+    /**
+     * @see org.eclipse.jface.fieldassist.IControlContentAdapter2#setSelection(org.eclipse.swt.widgets.Control,
+     *      org.eclipse.swt.graphics.Point)
+     *
+     * @since 3.4
+     */
+    public void setSelection(Control control, Point range) {
+        (cast(Text) control).setSelection(range);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/fieldassist/TextControlCreator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.jface.fieldassist.TextControlCreator;
+
+import org.eclipse.jface.fieldassist.IControlCreator;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+
+import java.lang.all;
+
+/**
+ * An {@link IControlCreator} for SWT Text controls. This is a convenience class
+ * for creating text controls to be supplied to a decorated field.
+ *
+ * @since 3.2
+ * @deprecated As of 3.3, clients should use {@link ControlDecoration} instead
+ *             of {@link DecoratedField}.
+ *
+ */
+public class TextControlCreator : IControlCreator {
+
+    public Control createControl(Composite parent, int style) {
+        return new Text(parent, style);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/internal/ConfigureColumnsDialog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,405 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.ConfigureColumnsDialog;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.window.IShellProvider;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * NON-API - This class is internal and will be moved to another package in 3.5.
+ *
+ */
+public class ConfigureColumnsDialog : Dialog {
+
+    private Control targetControl;
+    private ColumnObject[] columnObjects;
+    private Table table;
+    private Button upButton;
+    private Button downButton;
+    private Text text;
+    private bool moveableColumnsFound;
+
+    class ColumnObject {
+        Item column;
+        int index;
+        String name;
+        Image image;
+        bool visible;
+        int width;
+        bool moveable;
+        bool resizable;
+
+        this(Item column, int index, String text, Image image,
+                int width, bool moveable, bool resizable, bool visible) {
+            this.column = column;
+            this.index = index;
+            this.name = text;
+            this.image = image;
+            this.width = width;
+            this.moveable = moveable;
+            this.resizable = resizable;
+            this.visible = visible;
+        }
+    }
+
+    /**
+     * NON-API - This class is internal and will be moved to another package in
+     * 3.5. Creates a new dialog for configuring columns of the given column
+     * viewer. The column viewer must have an underlying {@link Tree} or {@link
+     * Table}, other controls are not supported.
+     *
+     * @param shellProvider
+     * @param table
+     */
+    public this(IShellProvider shellProvider, Table table) {
+        this(shellProvider, cast(Control) table);
+    }
+
+    /**
+     * NON-API - This class is internal and will be moved to another package in
+     * 3.5. Creates a new dialog for configuring columns of the given column
+     * viewer. The column viewer must have an underlying {@link Tree} or {@link
+     * Table}, other controls are not supported.
+     *
+     * @param shellProvider
+     * @param tree
+     */
+    public this(IShellProvider shellProvider, Tree tree) {
+        this(shellProvider, cast(Control) tree);
+    }
+
+    /**
+     * @param shellProvider
+     * @param control
+     */
+    private this(IShellProvider shellProvider, Control control) {
+        super(shellProvider);
+        this.targetControl = control;
+        this.moveableColumnsFound = createColumnObjects();
+    }
+
+    protected bool isResizable() {
+        return true;
+    }
+
+    public void create() {
+        super.create();
+        getShell().setText(
+                JFaceResources.getString("ConfigureColumnsDialog_Title")); //$NON-NLS-1$
+    }
+
+    protected void initializeBounds() {
+        super.initializeBounds();
+        table.setSelection(0);
+        handleSelectionChanged(0);
+    }
+
+    /**
+     * Returns true if any of the columns is moveable (can be reordered).
+     */
+    private bool createColumnObjects() {
+        bool result = true;
+        Item[] columns = getViewerColumns();
+        ColumnObject[] cObjects = new ColumnObject[columns.length];
+        for (int i = 0; i < columns.length; i++) {
+            Item c = columns[i];
+            bool moveable = getMoveable(c);
+            result = result && moveable;
+            cObjects[i] = new ColumnObject(c, i, getColumnName(c),
+                    getColumnImage(c), getColumnWidth(c), moveable,
+                    getResizable(c), true);
+        }
+        int[] columnOrder = getColumnOrder();
+        columnObjects = new ColumnObject[columns.length];
+        for (int i = 0; i < columnOrder.length; i++) {
+            columnObjects[i] = cObjects[columnOrder[i]];
+        }
+        return result;
+    }
+
+    /**
+     * @param c
+     * @return
+     */
+    private Image getColumnImage(Item item) {
+        if (null !is cast(TableColumn)item ) {
+            return (cast(TableColumn) item).getImage();
+        } else if (null !is cast(TreeColumn)item ) {
+            return (cast(TreeColumn) item).getImage();
+        }
+        return null;
+    }
+
+    /**
+     * @return
+     */
+    private int[] getColumnOrder() {
+        if (null !is cast(Table)targetControl ) {
+            return (cast(Table) targetControl).getColumnOrder();
+        } else if (null !is cast(Tree)targetControl ) {
+            return (cast(Tree) targetControl).getColumnOrder();
+        }
+        return new int[0];
+    }
+
+    /**
+     * @param c
+     * @return
+     */
+    private bool getMoveable(Item item) {
+        if (null !is cast(TableColumn)item ) {
+            return (cast(TableColumn) item).getMoveable();
+        } else if (null !is cast(TreeColumn)item ) {
+            return (cast(TreeColumn) item).getMoveable();
+        }
+        return false;
+    }
+
+    /**
+     * @param c
+     * @return
+     */
+    private bool getResizable(Item item) {
+        if (null !is cast(TableColumn)item ) {
+            return (cast(TableColumn) item).getResizable();
+        } else if (null !is cast(TreeColumn)item ) {
+            return (cast(TreeColumn) item).getResizable();
+        }
+        return false;
+    }
+
+    protected Control createDialogArea(Composite parent) {
+        Composite composite = cast(Composite) super.createDialogArea(parent);
+
+        table = new Table(composite, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL
+                | SWT.H_SCROLL /*
+                                                     * | SWT.CHECK
+                                                     */);
+        for (int i = 0; i < columnObjects.length; i++) {
+            TableItem tableItem = new TableItem(table, SWT.NONE);
+            tableItem.setText(columnObjects[i].name);
+            tableItem.setImage(columnObjects[i].image);
+            tableItem.setData(columnObjects[i]);
+        }
+
+        GridDataFactory.defaultsFor(table)
+                .span(1, moveableColumnsFound ? 3 : 1).applyTo(table);
+
+        if (moveableColumnsFound) {
+            upButton = new Button(composite, SWT.PUSH);
+            upButton.setText(JFaceResources
+                    .getString("ConfigureColumnsDialog_up")); //$NON-NLS-1$
+            upButton.addListener(SWT.Selection, new class Listener {
+                public void handleEvent(Event event) {
+                    handleMove(table, true);
+                }
+            });
+            setButtonLayoutData(upButton);
+            downButton = new Button(composite, SWT.PUSH);
+            downButton.setText(JFaceResources
+                    .getString("ConfigureColumnsDialog_down")); //$NON-NLS-1$
+            downButton.addListener(SWT.Selection, new class Listener {
+                public void handleEvent(Event event) {
+                    handleMove(table, false);
+                }
+            });
+            setButtonLayoutData(downButton);
+
+            // filler label
+            createLabel(composite, ""); //$NON-NLS-1$
+        }
+
+        Composite widthComposite = new Composite(composite, SWT.NONE);
+        createLabel(widthComposite, JFaceResources
+                .getString("ConfigureColumnsDialog_WidthOfSelectedColumn")); //$NON-NLS-1$
+
+        text = new Text(widthComposite, SWT.SINGLE | SWT.BORDER);
+        // see #initializeBounds
+        text.setText(Integer.toString(1000));
+
+        GridLayoutFactory.fillDefaults().numColumns(2).applyTo(widthComposite);
+
+        int numColumns = moveableColumnsFound ? 2 : 1;
+
+        GridDataFactory.defaultsFor(widthComposite).grab(false, false).span(
+                numColumns, 1).applyTo(widthComposite);
+
+        GridLayoutFactory.swtDefaults().numColumns(numColumns).applyTo(
+                composite);
+
+        table.addListener(SWT.Selection, new class Listener {
+            public void handleEvent(Event event) {
+                handleSelectionChanged(table.indexOf(cast(TableItem) event.item));
+            }
+        });
+        text.addListener(SWT.Modify, new class Listener {
+            public void handleEvent(Event event) {
+                ColumnObject columnObject = columnObjects[table
+                        .getSelectionIndex()];
+                if (!columnObject.resizable) {
+                    return;
+                }
+                try {
+                    int width = Integer.parseInt(text.getText());
+                    columnObject.width = width;
+                } catch (NumberFormatException ex) {
+                    // ignore for now
+                }
+            }
+        });
+
+        Dialog.applyDialogFont(composite);
+
+        return composite;
+    }
+
+    /**
+     * @param table
+     * @param up
+     */
+    protected void handleMove(Table table, bool up) {
+        int index = table.getSelectionIndex();
+        int newIndex = index + (up ? -1 : 1);
+        if (index < 0 || index >= table.getItemCount()) {
+            return;
+        }
+        ColumnObject columnObject = columnObjects[index];
+        columnObjects[index] = columnObjects[newIndex];
+        columnObjects[newIndex] = columnObject;
+        table.getItem(index).dispose();
+        TableItem newItem = new TableItem(table, SWT.NONE, newIndex);
+        newItem.setText(columnObject.name);
+        newItem.setImage(columnObject.image);
+        newItem.setData(columnObject);
+        table.setSelection(newIndex);
+        handleSelectionChanged(newIndex);
+    }
+
+    private void createLabel(Composite composite, String string) {
+        Label label = new Label(composite, SWT.NONE);
+        label.setText(string);
+    }
+
+    /**
+     * @param item
+     * @return
+     */
+    private String getColumnName(Item item) {
+        String result = ""; //$NON-NLS-1$
+        if (null !is cast(TableColumn)item ) {
+            result = (cast(TableColumn) item).getText();
+            if (result.trim().equals("")) { //$NON-NLS-1$
+                result = (cast(TableColumn) item).getToolTipText();
+            }
+        } else if (null !is cast(TreeColumn)item ) {
+            result = (cast(TreeColumn) item).getText();
+            if (result.trim().equals("")) { //$NON-NLS-1$
+                result = (cast(TreeColumn) item).getToolTipText();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @param item
+     * @return
+     */
+    private int getColumnWidth(Item item) {
+        if (null !is cast(TableColumn)item ) {
+            return (cast(TableColumn) item).getWidth();
+        } else if (null !is cast(TreeColumn)item ) {
+            return (cast(TreeColumn) item).getWidth();
+        }
+        return 0;
+    }
+
+    /**
+     * @return
+     */
+    private Item[] getViewerColumns() {
+        if (null !is cast(Table)targetControl ) {
+            return (cast(Table) targetControl).getColumns();
+        } else if (null !is cast(Tree)targetControl ) {
+            return (cast(Tree) targetControl).getColumns();
+        }
+        return new Item[0];
+    }
+
+    private void handleSelectionChanged(int index) {
+        ColumnObject c = columnObjects[index];
+        text.setText(Integer.toString(c.width));
+        text.setEnabled(c.resizable);
+        if (moveableColumnsFound) {
+            upButton.setEnabled(c.moveable && index > 0);
+            downButton.setEnabled(c.moveable
+                    && index + 1 < table.getItemCount());
+        }
+    }
+
+    protected void okPressed() {
+        int[] columnOrder = new int[columnObjects.length];
+        for (int i = 0; i < columnObjects.length; i++) {
+            ColumnObject columnObject = columnObjects[i];
+            columnOrder[i] = columnObject.index;
+            setColumnWidth(columnObject.column, columnObject.width);
+        }
+        setColumnOrder(columnOrder);
+        super.okPressed();
+    }
+
+    /**
+     * @param column
+     * @param width
+     */
+    private void setColumnWidth(Item item, int width) {
+        if (null !is cast(TableColumn)item ) {
+            (cast(TableColumn) item).setWidth(width);
+        } else if (null !is cast(TreeColumn)item ) {
+            (cast(TreeColumn) item).setWidth(width);
+        }
+    }
+
+    /**
+     * @param columnOrder
+     */
+    private void setColumnOrder(int[] order) {
+        if (null !is cast(Table)targetControl ) {
+            (cast(Table) targetControl).setColumnOrder(order);
+        } else if (null !is cast(Tree)targetControl ) {
+            (cast(Tree) targetControl).setColumnOrder(order);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/internal/InternalPolicy.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.internal.InternalPolicy;
+
+import java.lang.all;
+import java.util.Map;
+
+
+/**
+ * Internal class used for non-API debug flags.
+ *
+ * @since 3.3
+ */
+public class InternalPolicy {
+
+    /**
+     * (NON-API) A flag to indicate whether reentrant viewer calls should always be
+     * logged. If false, only the first reentrant call will cause a log entry.
+     *
+     * @since 3.3
+     */
+    public static bool DEBUG_LOG_REENTRANT_VIEWER_CALLS = false;
+
+    /**
+     * (NON-API) Instead of logging current conflicts they can be
+     * held here.  If there is a problem, they can be reported then.
+     */
+    public static Map currentConflicts = null;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/internal/provisional/action/CoolBarManager2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.provisional.action.CoolBarManager2;
+
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.internal.provisional.action.ICoolBarManager2;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.CoolBar;
+import org.eclipse.jface.action.CoolBarManager;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Extends <code>CoolBarManager</code> to implement <code>ICoolBarManager2</code>
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/UI team.
+ * </p>
+ *
+ * @since 3.2
+*/
+public class CoolBarManager2 : CoolBarManager, ICoolBarManager2 {
+
+    // delegate to super
+    public override void refresh(){
+        super.refresh();
+    }
+    public override void dispose(){
+        super.dispose();
+    }
+    public override void setItems(IContributionItem[] newItems){
+        super.setItems(newItems);
+    }
+    public override void resetItemOrder(){
+        super.resetItemOrder();
+    }
+
+    /**
+     * Creates a new cool bar manager with the default style. Equivalent to
+     * <code>CoolBarManager(SWT.NONE)</code>.
+     */
+    public this() {
+        super();
+    }
+
+    /**
+     * Creates a cool bar manager for an existing cool bar control. This
+     * manager becomes responsible for the control, and will dispose of it when
+     * the manager is disposed.
+     *
+     * @param coolBar
+     *            the cool bar control
+     */
+    public this(CoolBar coolBar) {
+        super(coolBar);
+    }
+
+    /**
+     * Creates a cool bar manager with the given SWT style. Calling <code>createControl</code>
+     * will create the cool bar control.
+     *
+     * @param style
+     *            the cool bar item style; see
+     *            {@link org.eclipse.swt.widgets.CoolBar CoolBar}for for valid
+     *            style bits
+     */
+    public this(int style) {
+       super(style);
+    }
+
+    /**
+     * Creates and returns this manager's cool bar control. Does not create a
+     * new control if one already exists.
+     *
+     * @param parent
+     *            the parent control
+     * @return the cool bar control
+     * @since 3.2
+     */
+    public Control createControl2(Composite parent) {
+        return createControl(parent);
+    }
+
+    /**
+     * Returns the control for this manager.
+     *
+     * @return the control, or <code>null</code> if none
+     * @since 3.2
+     */
+    public Control getControl2() {
+        return getControl();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/internal/provisional/action/ICoolBarManager2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.provisional.action.ICoolBarManager2;
+
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.ICoolBarManager;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Extends <code>ICoolBarManager</code> to allow clients to be decoupled
+ * from the actual kind of control used.
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/UI team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface ICoolBarManager2 : ICoolBarManager {
+
+    /**
+     * Creates and returns this manager's control. Does not create a
+     * new control if one already exists.
+     *
+     *
+     * @param parent
+     *            the parent control
+     * @return the control
+     * @since 3.2
+     */
+    public Control createControl2(Composite parent);
+
+    /**
+     * Returns the bar control for this manager.
+     *
+     * <p>
+     * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+     * part of a work in progress. There is a guarantee neither that this API will
+     * work nor that it will remain the same. Please do not use this API without
+     * consulting with the Platform/UI team.
+     * </p>
+     *
+     * @return the bar control, or <code>null</code> if none
+     * @since 3.2
+     */
+    public Control getControl2();
+
+    /**
+     * Synchronizes the visual order of the cool items in the control with this
+     * manager's internal data structures. This method should be called before
+     * requesting the order of the contribution items to ensure that the order
+     * is accurate.
+     * <p>
+     * Note that <code>update()</code> and <code>refresh()</code> are
+     * converses: <code>update()</code> changes the visual order to match the
+     * internal structures, and <code>refresh</code> changes the internal
+     * structures to match the visual order.
+     * </p>
+     *
+     * <p>
+     * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+     * part of a work in progress. There is a guarantee neither that this API will
+     * work nor that it will remain the same. Please do not use this API without
+     * consulting with the Platform/UI team.
+     * </p>
+     *
+     * @since 3.2
+     */
+    public void refresh();
+
+    /**
+     * Disposes the resources for this manager.
+     *
+     * <p>
+     * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+     * part of a work in progress. There is a guarantee neither that this API will
+     * work nor that it will remain the same. Please do not use this API without
+     * consulting with the Platform/UI team.
+     * </p>
+     *
+     * @since 3.2
+     */
+    public void dispose();
+
+    /**
+     * Restores the canonical order of this cool bar manager. The canonical
+     * order is the order in which the contribution items where added.
+     *
+     * <p>
+     * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+     * part of a work in progress. There is a guarantee neither that this API will
+     * work nor that it will remain the same. Please do not use this API without
+     * consulting with the Platform/UI team.
+     * </p>
+     *
+     * @since 3.2
+     */
+    public void resetItemOrder();
+
+    /**
+     * Replaces the current items with the given items.
+     * Forces an update.
+     *
+     * <p>
+     * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+     * part of a work in progress. There is a guarantee neither that this API will
+     * work nor that it will remain the same. Please do not use this API without
+     * consulting with the Platform/UI team.
+     * </p>
+     *
+     * @param newItems the items with which to replace the current items
+     * @since 3.2
+     */
+    public void setItems(IContributionItem[] newItems);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/internal/provisional/action/IToolBarContributionItem.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.provisional.action.IToolBarContributionItem;
+
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.IContributionManager;
+import org.eclipse.jface.action.IToolBarManager;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The intention of this interface is to provide in interface for
+ * ToolBarContributionItem so that the implementation can be replaced.
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/UI team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface IToolBarContributionItem : IContributionItem {
+
+    /**
+     * Returns the current height of the corresponding cool item.
+     *
+     * @return the current height
+     */
+    public int getCurrentHeight();
+
+    /**
+     * Returns the current width of the corresponding cool item.
+     *
+     * @return the current size
+     */
+    public int getCurrentWidth();
+
+    /**
+     * Returns the minimum number of tool items to show in the cool item.
+     *
+     * @return the minimum number of tool items to show, or <code>SHOW_ALL_ITEMS</code>
+     *         if a value was not set
+     * @see #setMinimumItemsToShow(int)
+     */
+    public int getMinimumItemsToShow();
+
+    /**
+     * Returns whether chevron support is enabled.
+     *
+     * @return <code>true</code> if chevron support is enabled, <code>false</code>
+     *         otherwise
+     */
+    public bool getUseChevron();
+
+    /**
+     * Sets the current height of the cool item. Update(SIZE) should be called
+     * to adjust the widget.
+     *
+     * @param currentHeight
+     *            the current height to set
+     */
+    public void setCurrentHeight(int currentHeight);
+
+    /**
+     * Sets the current width of the cool item. Update(SIZE) should be called
+     * to adjust the widget.
+     *
+     * @param currentWidth
+     *            the current width to set
+     */
+    public void setCurrentWidth(int currentWidth);
+
+    /**
+     * Sets the minimum number of tool items to show in the cool item. If this
+     * number is less than the total tool items, a chevron will appear and the
+     * hidden tool items appear in a drop down menu. By default, all the tool
+     * items are shown in the cool item.
+     *
+     * @param minimumItemsToShow
+     *            the minimum number of tool items to show.
+     * @see #getMinimumItemsToShow()
+     * @see #setUseChevron(bool)
+     */
+    public void setMinimumItemsToShow(int minimumItemsToShow);
+
+    /**
+     * Enables or disables chevron support for the cool item. By default,
+     * chevron support is enabled.
+     *
+     * @param value
+     *            <code>true</code> to enable chevron support, <code>false</code>
+     *            otherwise.
+     */
+    public void setUseChevron(bool value);
+
+    /**
+     * Returns the internal tool bar manager of the contribution item.
+     *
+     * @return the tool bar manager, or <code>null</code> if one is not
+     *         defined.
+     * @see IToolBarManager
+     */
+    public IToolBarManager getToolBarManager();
+
+    /**
+     * Returns the parent contribution manager, or <code>null</code> if this
+     * contribution item is not currently added to a contribution manager.
+     *
+     * @return the parent contribution manager, or <code>null</code>
+     *
+     * TODO may not need this, getToolBarManager may be enough.
+     */
+    public IContributionManager getParent();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/internal/provisional/action/IToolBarManager2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.provisional.action.IToolBarManager2;
+
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.jface.action.IContributionManagerOverrides;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.util.IPropertyChangeListener;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The <code>IToolBarManager2</code> extends <code>IToolBarManager</code> to
+ * allow clients to be isolated from the actual kind of SWT control used by the
+ * manager.
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/UI team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface IToolBarManager2 : IToolBarManager {
+
+    /**
+     * The property id for changes to the control's layout
+     */
+    public static final String PROP_LAYOUT = "PROP_LAYOUT"; //$NON-NLS-1$
+
+    /**
+     * Creates and returns this manager's toolbar control. Does not create a new
+     * control if one already exists.
+     *
+     * @param parent
+     *            the parent control
+     * @return the toolbar control
+     */
+    public ToolBar createControl(Composite parent);
+
+    /**
+     * Creates and returns this manager's control. Does not create a new control
+     * if one already exists.
+     *
+     * @param parent
+     *            the parent control
+     * @return the control
+     */
+    public Control createControl2(Composite parent);
+
+    /**
+     * Returns the toolbar control for this manager.
+     *
+     * @return the toolbar control, or <code>null</code> if none
+     */
+    public ToolBar getControl();
+
+    /**
+     * Returns the control for this manager.
+     *
+     * @return the control, or <code>null</code> if none
+     */
+    public Control getControl2();
+
+    /**
+     * Disposes the resources for this manager.
+     */
+    public void dispose();
+
+    /**
+     * Returns the item count of the control used by this manager.
+     *
+     * @return the number of items in the control
+     */
+    public int getItemCount();
+
+    /**
+     * Registers a property change listner with this manager.
+     *
+     * @param listener
+     */
+    public void addPropertyChangeListener(IPropertyChangeListener listener);
+
+    /**
+     * Removes a property change listner from this manager.
+     *
+     * @param listener
+     */
+    public void removePropertyChangeListener(IPropertyChangeListener listener);
+
+    /**
+     * Sets the overrides for this contribution manager
+     *
+     * @param newOverrides
+     *            the overrides for the items of this manager
+     */
+    public void setOverrides(IContributionManagerOverrides newOverrides);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/internal/provisional/action/ToolBarContributionItem2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.provisional.action.ToolBarContributionItem2;
+
+import org.eclipse.jface.internal.provisional.action.IToolBarContributionItem;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.IContributionManager;
+
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.ToolBarContributionItem;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Extends <code>ToolBarContributionItem</code> to implement <code>IToolBarContributionItem</code>.
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/UI team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public class ToolBarContributionItem2 : ToolBarContributionItem,
+        IToolBarContributionItem {
+
+    // delegate to super
+    public override int getCurrentHeight(){
+        return super.getCurrentHeight();
+    }
+    public override int getCurrentWidth(){
+        return super.getCurrentWidth();
+    }
+    public override int getMinimumItemsToShow(){
+        return super.getMinimumItemsToShow();
+    }
+    public override bool getUseChevron(){
+        return super.getUseChevron();
+    }
+    public override void setCurrentHeight(int currentHeight){
+        super.setCurrentHeight(currentHeight);
+    }
+    public override void setCurrentWidth(int currentWidth){
+        super.setCurrentWidth(currentWidth);
+    }
+    public override void setMinimumItemsToShow(int minimumItemsToShow){
+        super.setMinimumItemsToShow(minimumItemsToShow);
+    }
+    public override void setUseChevron(bool value){
+        super.setUseChevron(value);
+    }
+    public override IToolBarManager getToolBarManager(){
+        return super.getToolBarManager();
+    }
+    public override IContributionManager getParent(){
+        return super.getParent();
+    }
+
+
+    /**
+     *
+     */
+    public this() {
+        super();
+    }
+
+    /**
+     * @param toolBarManager
+     */
+    public this(IToolBarManager toolBarManager) {
+        super(toolBarManager);
+    }
+
+    /**
+     * @param toolBarManager
+     * @param id
+     */
+    public this(IToolBarManager toolBarManager, String id) {
+        super(toolBarManager, id);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/internal/provisional/action/ToolBarManager2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,192 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.internal.provisional.action.ToolBarManager2;
+
+import org.eclipse.jface.internal.provisional.action.IToolBarManager2;
+import org.eclipse.jface.action.IContributionManagerOverrides;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Extends <code>ToolBarManager</code> to implement <code>IToolBarManager2</code>.
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/UI team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public class ToolBarManager2 : ToolBarManager, IToolBarManager2 {
+
+    // delegate to super
+    public ToolBar createControl(Composite parent) {
+        return super.createControl(parent);
+    }
+    public ToolBar getControl() {
+        return super.getControl();
+    }
+    public void dispose() {
+        super.dispose();
+    }
+    public void setOverrides(IContributionManagerOverrides newOverrides) {
+        super.setOverrides(newOverrides);
+    }
+
+    /**
+     * A collection of objects listening to changes to this manager. This
+     * collection is <code>null</code> if there are no listeners.
+     */
+    private /+transient+/ ListenerList listenerList = null;
+
+    /**
+     * Creates a new tool bar manager with the default SWT button style. Use the
+     * <code>createControl</code> method to create the tool bar control.
+     */
+    public this() {
+        super();
+    }
+
+    /**
+     * Creates a tool bar manager with the given SWT button style. Use the
+     * <code>createControl</code> method to create the tool bar control.
+     *
+     * @param style
+     *            the tool bar item style
+     * @see org.eclipse.swt.widgets.ToolBar for valid style bits
+     */
+    public this(int style) {
+        super(style);
+    }
+
+    /**
+     * Creates a tool bar manager for an existing tool bar control. This manager
+     * becomes responsible for the control, and will dispose of it when the
+     * manager is disposed.
+     *
+     * @param toolbar
+     *            the tool bar control
+     */
+    public this(ToolBar toolbar) {
+        super(toolbar);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IToolBarManager2#createControl2(org.eclipse.swt.widgets.Composite)
+     */
+    public Control createControl2(Composite parent) {
+        return createControl(parent);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IToolBarManager2#getControl2()
+     */
+    public Control getControl2() {
+        return getControl();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IToolBarManager2#getItemCount()
+     */
+    public int getItemCount() {
+        ToolBar toolBar = getControl();
+        if (toolBar is null || toolBar.isDisposed()) {
+            return 0;
+        }
+        return toolBar.getItemCount();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IToolBarManager2#addPropertyChangeListener(org.eclipse.jface.util.IPropertyChangeListener)
+     */
+    public void addPropertyChangeListener(IPropertyChangeListener listener) {
+        if (listenerList is null) {
+            listenerList = new ListenerList(ListenerList.IDENTITY);
+        }
+
+        listenerList.add(cast(Object)listener);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.IToolBarManager2#removePropertyChangeListener(org.eclipse.jface.util.IPropertyChangeListener)
+     */
+    public void removePropertyChangeListener(IPropertyChangeListener listener) {
+        if (listenerList !is null) {
+            listenerList.remove(cast(Object)listener);
+
+            if (listenerList.isEmpty()) {
+                listenerList = null;
+            }
+        }
+    }
+
+    /**
+     * @return the listeners attached to this event manager.
+     * The listeners currently attached; may be empty, but never
+     * null.
+     *
+     */
+    protected final Object[] getListeners() {
+        final ListenerList list = listenerList;
+        if (list is null) {
+            return new Object[0];
+        }
+
+        return list.getListeners();
+    }
+
+    /*
+     * Notifies any property change listeners that a property has changed. Only
+     * listeners registered at the time this method is called are notified.
+     */
+    private void firePropertyChange(PropertyChangeEvent event) {
+        Object[] list = getListeners();
+        for (int i = 0; i < list.length; ++i) {
+            (cast(IPropertyChangeListener) list[i]).propertyChange(event);
+        }
+    }
+
+    /*
+     * Notifies any property change listeners that a property has changed. Only
+     * listeners registered at the time this method is called are notified. This
+     * method avoids creating an event object if there are no listeners
+     * registered, but calls firePropertyChange(PropertyChangeEvent) if there are.
+     */
+    private void firePropertyChange(String propertyName,
+            Object oldValue, Object newValue) {
+        if (listenerList !is null) {
+            firePropertyChange(new PropertyChangeEvent(this, propertyName,
+                    oldValue, newValue));
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.action.ToolBarManager#relayout(org.eclipse.swt.widgets.ToolBar, int, int)
+     */
+    protected override void relayout(ToolBar layoutBar, int oldCount, int newCount) {
+        super.relayout(layoutBar, oldCount, newCount);
+        firePropertyChange(PROP_LAYOUT, new Integer(oldCount), new Integer(newCount));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/layout/AbstractColumnLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,326 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 (original file org.eclipse.ui.texteditor.templates.ColumnLayout)
+ *     Tom Schindl <tom.schindl@bestsolution.at> - refactored to be widget independent (bug 171824)
+ *                                               - fix for bug 178280, 184342, 184045, 208014, 214532
+ *     Micah Hainline <micah_hainline@yahoo.com> - fix in bug: 208335
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.layout.AbstractColumnLayout;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Scrollable;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.util.Policy;
+import org.eclipse.jface.viewers.ColumnLayoutData;
+import org.eclipse.jface.viewers.ColumnPixelData;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.TableLayout;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The AbstractColumnLayout is a {@link Layout} used to set the size of a table
+ * in a consistent way even during a resize unlike a {@link TableLayout} which
+ * only sets initial sizes.
+ *
+ * <p>
+ * <b>You can only add the layout to a container whose only child is the
+ * table/tree control you want the layouts applied to.</b>
+ * </p>
+ *
+ * @since 3.4
+ */
+public abstract class AbstractColumnLayout : Layout {
+    private static int COLUMN_TRIM;
+
+    static const bool IS_GTK;
+
+    static const String LAYOUT_DATA;
+
+    static this() {
+        if ("win32".equals(SWT.getPlatform())) { //$NON-NLS-1$
+            COLUMN_TRIM = 4;
+        } else if ("carbon".equals(SWT.getPlatform())) { //$NON-NLS-1$
+            COLUMN_TRIM = 24;
+        } else {
+            COLUMN_TRIM = 3;
+        }
+        IS_GTK = "gtk".equals(SWT.getPlatform());//$NON-NLS-1$
+        LAYOUT_DATA = Policy.JFACE ~ ".LAYOUT_DATA"; //$NON-NLS-1$
+    }
+
+    private bool inupdateMode = false;
+
+    private bool relayout = true;
+
+    private Listener resizeListener;
+    private void init_resizeListener(){
+        resizeListener = new class Listener {
+
+            public void handleEvent(Event event) {
+                if( ! inupdateMode ) {
+                    updateColumnData(event.widget);
+                }
+            }
+
+        };
+    }
+    public this(){
+        init_resizeListener();
+    }
+
+    /**
+     * Adds a new column of data to this table layout.
+     *
+     * @param column
+     *            the column
+     *
+     * @param data
+     *            the column layout data
+     */
+    public void setColumnData(Widget column, ColumnLayoutData data) {
+
+        if (column.getData(LAYOUT_DATA) is null) {
+            column.addListener(SWT.Resize, resizeListener);
+        }
+
+        column.setData(LAYOUT_DATA, data);
+    }
+
+    /**
+     * Compute the size of the table or tree based on the ColumnLayoutData and
+     * the width and height hint.
+     *
+     * @param scrollable
+     *            the widget to compute
+     * @param wHint
+     *            the width hint
+     * @param hHint
+     *            the height hint
+     * @return Point where x is the width and y is the height
+     */
+    private Point computeTableTreeSize(Scrollable scrollable, int wHint,
+            int hHint) {
+        Point result = scrollable.computeSize(wHint, hHint);
+
+        int width = 0;
+        int size = getColumnCount(scrollable);
+        for (int i = 0; i < size; ++i) {
+            ColumnLayoutData layoutData = getLayoutData(scrollable, i);
+            if ( auto col = cast(ColumnPixelData)layoutData) {
+                width += col.width;
+                if (col.addTrim) {
+                    width += getColumnTrim();
+                }
+            } else if ( auto col = cast(ColumnWeightData)layoutData ) {
+                width += col.minimumWidth;
+            } else {
+                Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$
+            }
+        }
+        if (width > result.x)
+            result.x = width;
+
+        return result;
+    }
+
+    /**
+     * Layout the scrollable based on the supplied width and area. Only increase
+     * the size of the scrollable if increase is <code>true</code>.
+     *
+     * @param scrollable
+     * @param width
+     * @param area
+     * @param increase
+     */
+    private void layoutTableTree(Scrollable scrollable, int width,
+            Rectangle area, bool increase) {
+        int numberOfColumns = getColumnCount(scrollable);
+        int[] widths = new int[numberOfColumns];
+
+        int[] weightColumnIndices = new int[numberOfColumns];
+        int numberOfWeightColumns = 0;
+
+        int fixedWidth = 0;
+        int totalWeight = 0;
+
+        // First calc space occupied by fixed columns
+        for (int i = 0; i < numberOfColumns; i++) {
+            ColumnLayoutData col = getLayoutData(scrollable, i);
+            if ( auto cpd = cast(ColumnPixelData)col ) {
+                int pixels = cpd.width;
+                if (cpd.addTrim) {
+                    pixels += getColumnTrim();
+                }
+                widths[i] = pixels;
+                fixedWidth += pixels;
+            } else if ( auto cw = cast(ColumnWeightData) col ) {
+                weightColumnIndices[numberOfWeightColumns] = i;
+                numberOfWeightColumns++;
+                totalWeight += cw.weight;
+            } else {
+                Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$
+            }
+        }
+
+        bool recalculate;
+        do {
+            recalculate = false;
+            for (int i = 0; i < numberOfWeightColumns; i++) {
+                int colIndex = weightColumnIndices[i];
+                ColumnWeightData cw = cast(ColumnWeightData) getLayoutData(
+                        scrollable, colIndex);
+                final int minWidth = cw.minimumWidth;
+                final int allowedWidth = (width - fixedWidth) * cw.weight
+                        / totalWeight;
+                if (allowedWidth < minWidth) {
+                    /*
+                     * if the width assigned by weight is less than the minimum,
+                     * then treat this column as fixed, remove it from weight
+                     * calculations, and recalculate other weights.
+                     */
+                    numberOfWeightColumns--;
+                    totalWeight -= cw.weight;
+                    fixedWidth += minWidth;
+                    widths[colIndex] = minWidth;
+                    System.arraycopy(weightColumnIndices, i + 1,
+                            weightColumnIndices, i, numberOfWeightColumns - i);
+                    recalculate = true;
+                    break;
+                }
+                widths[colIndex] = allowedWidth;
+            }
+        } while (recalculate);
+
+        if (increase) {
+            scrollable.setSize(area.width, area.height);
+        }
+
+        inupdateMode = true;
+        setColumnWidths(scrollable, widths);
+        scrollable.update();
+        inupdateMode = false;
+
+        if (!increase) {
+            scrollable.setSize(area.width, area.height);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.swt.widgets.Layout#computeSize(org.eclipse.swt.widgets.Composite,
+     *      int, int, bool)
+     */
+    protected override Point computeSize(Composite composite, int wHint, int hHint,
+            bool flushCache) {
+        return computeTableTreeSize(getControl(composite), wHint, hHint);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite,
+     *      bool)
+     */
+    protected override void layout(Composite composite, bool flushCache) {
+        Rectangle area = composite.getClientArea();
+        Scrollable table = getControl(composite);
+        int tableWidth = table.getSize().x;
+        int trim = computeTrim(area, table, tableWidth);
+        int width = Math.max(0, area.width - trim);
+
+        if (width > 1)
+            layoutTableTree(table, width, area, tableWidth < area.width);
+
+        // For the first time we need to relayout because Scrollbars are not
+        // calculate appropriately
+        if (relayout) {
+            relayout = false;
+            composite.layout();
+        }
+    }
+
+    /**
+     * Compute the area required for trim.
+     *
+     * @param area
+     * @param scrollable
+     * @param currentWidth
+     * @return int
+     */
+    private int computeTrim(Rectangle area, Scrollable scrollable,
+            int currentWidth) {
+        int trim;
+
+        if (currentWidth > 1) {
+            trim = currentWidth - scrollable.getClientArea().width;
+        } else {
+            // initially, the table has no extend and no client area - use the
+            // border with
+            // plus some padding as educated guess
+            trim = 2 * scrollable.getBorderWidth() + 1;
+        }
+
+        return trim;
+    }
+
+    /**
+     * Get the control being laid out.
+     *
+     * @param composite
+     *            the composite with the layout
+     * @return {@link Scrollable}
+     */
+    Scrollable getControl(Composite composite) {
+        return cast(Scrollable) composite.getChildren()[0];
+    }
+
+    /**
+     * Get the number of columns for the receiver.
+     *
+     * @return the number of columns
+     */
+    abstract int getColumnCount(Scrollable tableTree);
+
+    /**
+     * Set the widths of the columns.
+     *
+     * @param widths
+     */
+    abstract void setColumnWidths(Scrollable tableTree, int[] widths);
+
+    abstract ColumnLayoutData getLayoutData(Scrollable tableTree,
+            int columnIndex);
+
+    abstract void updateColumnData(Widget column);
+
+    /**
+     * The number of extra pixels taken as horizontal trim by the table column.
+     * To ensure there are N pixels available for the content of the column,
+     * assign N+COLUMN_TRIM for the column width.
+     *
+     * @return the trim used by the columns
+     * @since 3.4
+     */
+    protected int getColumnTrim() {
+        return COLUMN_TRIM;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/layout/GridDataFactory.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,487 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ *     Stefan Xenos, IBM - initial implementation, bug 178888
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.layout.GridDataFactory;
+
+import org.eclipse.jface.layout.LayoutGenerator;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Control;
+
+import java.lang.all;
+import java.util.Set;
+import tango.core.Exception;
+
+/**
+ * This class provides a convienient shorthand for creating and initializing
+ * GridData. This offers several benefits over creating GridData normal way:
+ *
+ * <ul>
+ * <li>The same factory can be used many times to create several GridData instances</li>
+ * <li>The setters on GridDataFactory all return "this", allowing them to be chained</li>
+ * <li>GridDataFactory uses vector setters (it accepts Points), making it easy to
+ *     set X and Y values together</li>
+ * </ul>
+ *
+ * <p>
+ * GridDataFactory instances are created using one of the static methods on this class.
+ * </p>
+ *
+ * <p>
+ * Example usage:
+ * </p>
+ * <code><pre>
+ *
+ * ////////////////////////////////////////////////////////////
+ * // Example 1: Typical grid data for a non-wrapping label
+ *
+ *     // GridDataFactory version
+ *     GridDataFactory.fillDefaults().applyTo(myLabel);
+ *
+ *     // Equivalent SWT version
+ *     GridData labelData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL);
+ *     myLabel.setLayoutData(labelData);
+ *
+ * ///////////////////////////////////////////////////////////
+ * // Example 2: Typical grid data for a wrapping label
+ *
+ *     // GridDataFactory version
+ *     GridDataFactory.fillDefaults()
+ *          .align(SWT.FILL, SWT.CENTER)
+ *          .hint(150, SWT.DEFAULT)
+ *          .grab(true, false)
+ *          .applyTo(wrappingLabel);
+ *
+ *     // Equivalent SWT version
+ *     GridData wrappingLabelData = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_CENTER);
+ *     wrappingLabelData.minimumWidth = 1;
+ *     wrappingLabelData.widthHint = 150;
+ *     wrappingLabel.setLayoutData(wrappingLabelData);
+ *
+ * //////////////////////////////////////////////////////////////
+ * // Example 3: Typical grid data for a scrollable control (a list box, tree, table, etc.)
+ *
+ *     // GridDataFactory version
+ *     GridDataFactory.fillDefaults().grab(true, true).hint(150, 150).applyTo(listBox);
+ *
+ *     // Equivalent SWT version
+ *     GridData listBoxData = new GridData(GridData.FILL_BOTH);
+ *     listBoxData.widthHint = 150;
+ *     listBoxData.heightHint = 150;
+ *     listBoxData.minimumWidth = 1;
+ *     listBoxData.minimumHeight = 1;
+ *     listBox.setLayoutData(listBoxData);
+ *
+ * /////////////////////////////////////////////////////////////
+ * // Example 4: Typical grid data for a button
+ *
+ *     // GridDataFactory version
+ *     Point preferredSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, false);
+ *     Point hint = Geometry.max(LayoutConstants.getMinButtonSize(), preferredSize);
+ *     GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).hint(hint).applyTo(button);
+ *
+ *     // Equivalent SWT version
+ *     Point preferredSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, false);
+ *     Point hint = Geometry.max(LayoutConstants.getMinButtonSize(), preferredSize);
+ *     GridData buttonData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER);
+ *     buttonData.widthHint = hint.x;
+ *     buttonData.heightHint = hint.y;
+ *     button.setLayoutData(buttonData);
+ *
+ * /////////////////////////////////////////////////////////////
+ * // Example 5: Generated GridData
+ *
+ *     // Generates GridData a wrapping label that spans 2 columns
+ *     GridDataFactory.generate(wrappingLabel, 2, 1);
+ *
+ *     // Generates GridData for a listbox. and adjusts the preferred size to 300x400 pixels
+ *     GridDataFactory.defaultsFor(listBox).hint(300, 400).applyTo(listBox);
+ *
+ *     // Generates GridData equivalent to example 4
+ *     GridDataFactory.generate(button, 1, 1);
+ *
+ * </pre></code>
+ *
+ * @since 3.2
+ */
+public final class GridDataFactory {
+    private GridData data;
+
+    /**
+     * Creates a GridDataFactory that creates copes of the given GridData.
+     *
+     * @param d template GridData to copy
+     */
+    private this(GridData d) {
+        this.data = d;
+    }
+
+    /**
+     * Creates a new GridDataFactory initialized with the SWT defaults.
+     * This factory will generate GridData that is equivalent to
+     * "new GridData()".
+     *
+     * <p>
+     * Initial values are:
+     * </p>
+     *
+     * <ul>
+     * <li>align(SWT.BEGINNING, SWT.CENTER)</li>
+     * <li>exclude(false)</li>
+     * <li>grab(false, false)</li>
+     * <li>hint(SWT.DEFAULT, SWT.DEFAULT)</li>
+     * <li>indent(0,0)</li>
+     * <li>minSize(0,0)</li>
+     * <li>span(1,1)</li>
+     * </ul>
+     *
+     * @return a new GridDataFactory instance
+     * @see #fillDefaults()
+     */
+    public static GridDataFactory swtDefaults() {
+        return new GridDataFactory(new GridData());
+    }
+
+    /**
+     * Creates a new GridDataFactory that creates copies of the given GridData
+     * by default.
+     *
+     * @param data GridData to copy
+     * @return a new GridDataFactory that creates copies of the argument by default
+     */
+    public static GridDataFactory createFrom(GridData data) {
+        return new GridDataFactory(copyData(data));
+    }
+
+    /**
+     * Creates a GridDataFactory initialized with defaults that will cause
+     * the control to fill its cell. The minimum size is set to the smallest possible
+     * minimum size supported by SWT. Currently, the smallest supported minimum size
+     * is (1,1) so this is the default. If GridLayout ever adds support for grid data
+     * with no minimum size, this will be changed to 0,0 in the future.
+     *
+     * <p>
+     * Initial values are:
+     * </p>
+     *
+     * <ul>
+     * <li>align(SWT.FILL, SWT.FILL)</li>
+     * <li>exclude(false)</li>
+     * <li>grab(false, false)</li>
+     * <li>hint(SWT.DEFAULT, SWT.DEFAULT)</li>
+     * <li>indent(0,0)</li>
+     * <li>minSize(1,1)</li>
+     * <li>span(1,1)</li>
+     * </ul>
+     *
+     * @return a GridDataFactory that makes controls fill their grid by default
+     *
+     * @see #swtDefaults()
+     */
+    public static GridDataFactory fillDefaults() {
+        GridData data = new GridData();
+        data.minimumWidth = 1;
+        data.minimumHeight = 1;
+        data.horizontalAlignment = SWT.FILL;
+        data.verticalAlignment = SWT.FILL;
+
+        return new GridDataFactory(data);
+    }
+
+    /**
+     * Returns a GridDataFactory initialized with heuristicly generated defaults for the given control.
+     * To be precise, this method picks the default values that GridLayoutFactory.generateLayout
+     * would have assigned to the control. Does not attach GridData to the control. Callers must
+     * additionally call applyTo(theControl) if they wish to use the generated values.
+     *
+     * <p>
+     * This method is intended for situations where generateLayout is generating layout data
+     * for a particular control that is not quite right for the desired layout.
+     * This allows callers to start with the generated values and tweak one or two settings
+     * before applying the GridData to the control.
+     * </p>
+     *
+     * @see GridLayoutFactory#generateLayout(org.eclipse.swt.widgets.Composite)
+     * @param theControl
+     * @return a GridLayoutFactory initialized with defaults that GridLayoutFactory would have
+     * @since 3.3
+     */
+    public static GridDataFactory defaultsFor(Control theControl) {
+        return LayoutGenerator.defaultsFor(theControl);
+    }
+
+    /**
+     * Generates layout data to the given control, given the number of cells
+     * spanned by the control. Attaches a GridData to the control. This method
+     * allows generated layout data to be used with controls that span multiple cells.
+     * <p>
+     * The generated layout data is the same as what would be generated by
+     * GridLayoutFactory.generateLayout, except that the span is configurable
+     * </p>
+     *
+     * @see GridLayoutFactory#generateLayout(org.eclipse.swt.widgets.Composite)
+     * @param theControl
+     * @param hSpan number of columns spanned by the control
+     * @param vSpan number of rows spanned by the control
+     * @since 3.3
+     */
+    public static void generate(Control theControl, int hSpan, int vSpan) {
+        defaultsFor(theControl).span(hSpan, vSpan).applyTo(theControl);
+    }
+
+    /**
+     * Generates layout data to the given control, given the number of cells
+     * spanned by the control. Attaches GridData to the control. This method
+     * allows generated layout data to be used with controls that span multiple cells.
+     * <p>
+     * The generated layout data is the same as what would be generated by
+     * GridLayoutFactory.generateLayout, except that the span is configurable
+     * </p>
+     *
+     * @see GridLayoutFactory#generateLayout(org.eclipse.swt.widgets.Composite)
+     * @param theControl
+     * @param span The x coordinate indicates the number of
+     * columns spanned, and the y coordinate indicates the number of rows.
+     * @since 3.3
+     */
+    public static void generate(Control theControl, Point span) {
+        defaultsFor(theControl).span(span).applyTo(theControl);
+    }
+
+    /**
+     * Sets the GridData span. The span controls how many cells
+     * are filled by the control.
+     *
+     * @param hSpan number of columns spanned by the control
+     * @param vSpan number of rows spanned by the control
+     * @return this
+     */
+    public GridDataFactory span(int hSpan, int vSpan) {
+        data.horizontalSpan = hSpan;
+        data.verticalSpan = vSpan;
+        return this;
+    }
+
+    /**
+     * Sets the GridData span. The span controls how many cells
+     * are filled by the control.
+     *
+     * @param span the new span. The x coordinate indicates the number of
+     * columns spanned, and the y coordinate indicates the number of rows.
+     * @return this
+     */
+    public GridDataFactory span(Point span) {
+        data.horizontalSpan = span.x;
+        data.verticalSpan = span.y;
+        return this;
+    }
+
+    /**
+     * Sets the width and height hints. The width and height hints override
+     * the control's preferred size. If either hint is set to SWT.DEFAULT,
+     * the control's preferred size is used.
+     *
+     * @param xHint horizontal hint (pixels), or SWT.DEFAULT to use the control's preferred size
+     * @param yHint vertical hint (pixels), or SWT.DEFAULT to use the control's preferred size
+     * @return this
+     */
+    public GridDataFactory hint(int xHint, int yHint) {
+        data.widthHint = xHint;
+        data.heightHint = yHint;
+        return this;
+    }
+
+    /**
+     * Sets the width and height hints. The width and height hints override
+     * the control's preferred size. If either hint is set to SWT.DEFAULT,
+     * the control's preferred size is used.
+     *
+     * @param hint size (pixels) to be used instead of the control's preferred size. If
+     * the x or y values are set to SWT.DEFAULT, the control's computeSize() method will
+     * be used to obtain that dimension of the preferred size.
+     * @return this
+     */
+    public GridDataFactory hint(Point hint) {
+        data.widthHint = hint.x;
+        data.heightHint = hint.y;
+        return this;
+    }
+
+    /**
+     * Sets the alignment of the control within its cell.
+     *
+     * @param hAlign horizontal alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL.
+     * @param vAlign vertical alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL.
+     * @return this
+     */
+    public GridDataFactory align_(int hAlign, int vAlign) {
+        if (hAlign !is SWT.BEGINNING && hAlign !is SWT.CENTER && hAlign !is GridData.CENTER && hAlign !is SWT.END && hAlign !is GridData.END && hAlign !is SWT.FILL && hAlign !is SWT.LEFT && hAlign !is SWT.RIGHT) {
+            throw new IllegalArgumentException(null);
+        }
+        if (vAlign !is SWT.BEGINNING && vAlign !is SWT.CENTER && vAlign !is GridData.CENTER && vAlign !is SWT.END && vAlign !is GridData.END && vAlign !is SWT.FILL && vAlign !is SWT.TOP && vAlign !is SWT.BOTTOM) {
+            throw new IllegalArgumentException(null);
+        }
+        data.horizontalAlignment = hAlign;
+        data.verticalAlignment = vAlign;
+        return this;
+    }
+
+    /**
+     * Sets the indent of the control within the cell. Moves the position of the control
+     * by the given number of pixels. Positive values move toward the lower-right, negative
+     * values move toward the upper-left.
+     *
+     * @param hIndent distance to move to the right (negative values move left)
+     * @param vIndent distance to move down (negative values move up)
+     * @return this
+     */
+    public GridDataFactory indent(int hIndent, int vIndent) {
+        data.horizontalIndent = hIndent;
+        data.verticalIndent = vIndent;
+        return this;
+    }
+
+    /**
+     * Sets the indent of the control within the cell. Moves the position of the control
+     * by the given number of pixels. Positive values move toward the lower-right, negative
+     * values move toward the upper-left.
+     *
+     * @param indent offset to move the control
+     * @return this
+     */
+    public GridDataFactory indent(Point indent) {
+        data.horizontalIndent = indent.x;
+        data.verticalIndent = indent.y;
+        return this;
+    }
+
+    /**
+     * Determines whether extra horizontal or vertical space should be allocated to
+     * this control's column when the layout resizes. If any control in the column
+     * is set to grab horizontal then the whole column will grab horizontal space.
+     * If any control in the row is set to grab vertical then the whole row will grab
+     * vertical space.
+     *
+     * @param horizontal true if the control's column should grow horizontally
+     * @param vertical true if the control's row should grow vertically
+     * @return this
+     */
+    public GridDataFactory grab(bool horizontal, bool vertical) {
+        data.grabExcessHorizontalSpace = horizontal;
+        data.grabExcessVerticalSpace = vertical;
+        return this;
+    }
+
+    /**
+     * Sets the minimum size for the control. The control will not be permitted
+     * to shrink below this size. Note: GridLayout treats a minimum size of 0
+     * as an undocumented special value, so the smallest possible minimum size
+     * is a size of 1. A minimum size of SWT.DEFAULT indicates that the result
+     * of computeSize(int, int, bool) should be used as the control's minimum
+     * size.
+     *
+     *
+     * @param minX minimum a value of 1 or more is a horizontal size of the control (pixels).
+     *        SWT.DEFAULT indicates that the control's preferred size should be used. A size
+     *        of 0 has special semantics defined by GridLayout.
+     * @param minY minimum a value of 1 or more is a vertical size of the control (pixels). SWT.DEFAULT
+     *        indicates that the control's preferred size should be used. A size
+     *        of 0 has special semantics defined by GridLayout.
+     * @return this
+     */
+    public GridDataFactory minSize(int minX, int minY) {
+        data.minimumWidth = minX;
+        data.minimumHeight = minY;
+        return this;
+    }
+
+    /**
+     * Sets the minimum size for the control. The control will not be permitted
+     * to shrink below this size. Note: GridLayout treats a minimum size of 0
+     * as an undocumented special value, so the smallest possible minimum size
+     * is a size of 1. A minimum size of SWT.DEFAULT indicates that the result
+     * of computeSize(int, int, bool) should be used as the control's minimum
+     * size.
+     *
+     * @param min minimum size of the control
+     * @return this
+     */
+    public GridDataFactory minSize(Point min) {
+        data.minimumWidth = min.x;
+        data.minimumHeight = min.y;
+        return this;
+    }
+
+    /**
+     * Instructs the GridLayout to ignore this control when performing layouts.
+     *
+     * @param shouldExclude true iff the control should be excluded from layouts
+     * @return this
+     */
+    public GridDataFactory exclude(bool shouldExclude) {
+        data.exclude = shouldExclude;
+        return this;
+    }
+
+    /**
+     * Creates a new GridData instance. All attributes of the GridData instance
+     * will be initialized by the factory.
+     *
+     * @return a new GridData instance
+     */
+    public GridData create() {
+        return copyData(data);
+    }
+
+    /**
+     * Creates a copy of the reciever.
+     *
+     * @return a copy of the reciever
+     */
+    public GridDataFactory copy() {
+        return new GridDataFactory(create());
+    }
+
+    /**
+     * Returns a copy of the given GridData
+     *
+     * @param data GridData to copy
+     * @return a copy of the argument
+     */
+    public static GridData copyData(GridData data) {
+        GridData newData = new GridData(data.horizontalAlignment, data.verticalAlignment, data.grabExcessHorizontalSpace, data.grabExcessVerticalSpace, data.horizontalSpan,
+                data.verticalSpan);
+        newData.exclude = data.exclude;
+        newData.heightHint = data.heightHint;
+        newData.horizontalIndent = data.horizontalIndent;
+        newData.minimumHeight = data.minimumHeight;
+        newData.minimumWidth = data.minimumWidth;
+        newData.verticalIndent = data.verticalIndent;
+        newData.widthHint = data.widthHint;
+
+        return newData;
+    }
+
+    /**
+     * Sets the layout data on the given control. Creates a new GridData instance and
+     * assigns it to the control by calling control.setLayoutData.
+     *
+     * @param control control whose layout data will be initialized
+     */
+    public void applyTo(Control control) {
+        control.setLayoutData(create());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/layout/GridLayoutFactory.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,387 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ *     Stefan Xenos, IBM - initial implementation, bug 178888
+ *     Karsten Stoeckmann - bug 156982
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.layout.GridLayoutFactory;
+
+import org.eclipse.jface.layout.LayoutConstants;
+import org.eclipse.jface.layout.LayoutGenerator;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * GridLayoutFactory creates and initializes grid layouts. There are two ways to use GridLayoutFactory.
+ * Normally, it is used as a shorthand for writing "new GridLayout()" and initializing a bunch
+ * of fields. In this case the main benefit is a more concise syntax and the ability to create more
+ * than one identical GridLayout from the same factory. Changing a property of the factory will affect
+ * future layouts created by the factory, but has no effect on layouts that have already been created.
+ *
+ * <p>
+ * GridLayoutFactory can also generate grid data for all the controls in a layout. This is done with
+ * the generateLayout method. To use this feature:
+ * </p>
+ *
+ * <ol>
+ * <li>Create the composite</li>
+ * <li>Create all the controls in the composite</li>
+ * <li>Call generateLayout</li>
+ * </ol>
+ *
+ * <p>
+ * The order here is important. generateLayout must be called after all the child controls have
+ * been created. generateLayout will not change any layout data that has already been attached
+ * to a child control and it will not recurse into nested composites.
+ * </p>
+ *
+ * @since 3.2
+ */
+public final class GridLayoutFactory {
+
+    /**
+     * Template layout. The factory will create copies of this layout.
+     */
+    private GridLayout l;
+
+    /**
+     * Creates a new GridLayoutFactory that will create copies of the given layout.
+     *
+     * @param l layout to copy
+     */
+    private this(GridLayout l) {
+        this.l = l;
+    }
+
+    /**
+     * Creates a factory that creates copies of the given layout.
+     *
+     * @param l layout to copy
+     * @return a new GridLayoutFactory instance that creates copies of the given layout
+     */
+    public static GridLayoutFactory createFrom(GridLayout l) {
+        return new GridLayoutFactory(copyLayout(l));
+    }
+
+    /**
+     * Creates a copy of the reciever.
+     *
+     * @return a copy of the reciever
+     */
+    public GridLayoutFactory copy() {
+        return new GridLayoutFactory(create());
+    }
+
+    /**
+     * Creates a GridLayoutFactory that creates GridLayouts with the default SWT
+     * values.
+     *
+     * <p>
+     * Initial values are:
+     * </p>
+     *
+     * <ul>
+     * <li>numColumns(1)</li>
+     * <li>margins(5,5)</li>
+     * <li>extendedMargins(0,0,0,0)</li>
+     * <li>spacing(5,5)</li>
+     * <li>equalWidth(false)</li>
+     * </ul>
+     *
+     * @return a GridLayoutFactory that creates GridLayouts as though created with
+     * their default constructor
+     * @see #fillDefaults
+     */
+    public static GridLayoutFactory swtDefaults() {
+        return new GridLayoutFactory(new GridLayout());
+    }
+
+    /**
+     * Creates a GridLayoutFactory that creates GridLayouts with no margins and
+     * default dialog spacing.
+     *
+     * <p>
+     * Initial values are:
+     * </p>
+     *
+     * <ul>
+     * <li>numColumns(1)</li>
+     * <li>margins(0,0)</li>
+     * <li>extendedMargins(0,0,0,0)</li>
+     * <li>spacing(LayoutConstants.getSpacing())</li>
+     * <li>equalWidth(false)</li>
+     * </ul>
+     *
+     * @return a GridLayoutFactory that creates GridLayouts as though created with
+     * their default constructor
+     * @see #swtDefaults
+     */
+    public static GridLayoutFactory fillDefaults() {
+        GridLayout layout = new GridLayout();
+        layout.marginWidth = 0;
+        layout.marginHeight = 0;
+        Point defaultSpacing = LayoutConstants.getSpacing();
+        layout.horizontalSpacing = defaultSpacing.x;
+        layout.verticalSpacing = defaultSpacing.y;
+        return new GridLayoutFactory(layout);
+    }
+
+    /**
+     * Sets whether the columns should be forced to be equal width
+     *
+     * @param equal true iff the columns should be forced to be equal width
+     * @return this
+     */
+    public GridLayoutFactory equalWidth(bool equal) {
+        l.makeColumnsEqualWidth = equal;
+        return this;
+    }
+
+    /**
+     * Sets the spacing for layouts created with this factory. The spacing
+     * is the distance between cells within the layout.
+     *
+     * @param hSpacing horizontal spacing (pixels)
+     * @param vSpacing vertical spacing (pixels)
+     * @return this
+     * @see #margins(Point)
+     * @see #margins(int, int)
+     */
+    public GridLayoutFactory spacing(int hSpacing, int vSpacing) {
+        l.horizontalSpacing = hSpacing;
+        l.verticalSpacing = vSpacing;
+        return this;
+    }
+
+    /**
+     * Sets the spacing for layouts created with this factory. The spacing
+     * is the distance between cells within the layout.
+     *
+     * @param spacing space between controls in the layout (pixels)
+     * @return this
+     * @see #margins(Point)
+     * @see #margins(int, int)
+     */
+    public GridLayoutFactory spacing(Point spacing) {
+        l.horizontalSpacing = spacing.x;
+        l.verticalSpacing = spacing.y;
+        return this;
+    }
+
+    /**
+     * Sets the margins for layouts created with this factory. The margins
+     * are the distance between the outer cells and the edge of the layout.
+     *
+     * @param margins margin size (pixels)
+     * @return this
+     * @see #spacing(Point)
+     * @see #spacing(int, int)
+     */
+    public GridLayoutFactory margins(Point margins) {
+        l.marginWidth = margins.x;
+        l.marginHeight = margins.y;
+        return this;
+    }
+
+    /**
+     * Sets the margins for layouts created with this factory. The margins
+     * specify the number of pixels of horizontal and vertical margin that will
+     * be placed along the left/right and top/bottom edges of the layout. Note
+     * that thes margins will be added to the ones specified by
+     * {@link #extendedMargins(int, int, int, int)}.
+     *
+     * @param width
+     *            margin width (pixels)
+     * @param height
+     *            margin height (pixels)
+     * @return this
+     * @see #spacing(Point)
+     * * @see #spacing(int, int)
+     */
+    public GridLayoutFactory margins(int width, int height) {
+        l.marginWidth = width;
+        l.marginHeight = height;
+        return this;
+    }
+
+    /**
+     * Sets the margins for layouts created with this factory. The margins
+     * specify the number of pixels of horizontal and vertical margin that will
+     * be placed along the left, right, top, and bottom edges of the layout.
+     * Note that thes margins will be added to the ones specified by
+     * {@link #margins(int, int)}.
+     *
+     * @param left
+     *            left margin size (pixels)
+     * @param right
+     *            right margin size (pixels)
+     * @param top
+     *            top margin size (pixels)
+     * @param bottom
+     *            bottom margin size (pixels)
+     * @return this
+     * @see #spacing(Point)
+     * @see #spacing(int, int)
+     *
+     * @since 3.3
+     */
+    public GridLayoutFactory extendedMargins(int left, int right, int top, int bottom) {
+        l.marginLeft = left;
+        l.marginRight = right;
+        l.marginTop = top;
+        l.marginBottom = bottom;
+        return this;
+    }
+
+    /**
+     * Sets the margins for layouts created with this factory. The margins
+     * specify the number of pixels of horizontal and vertical margin that will
+     * be placed along the left, right, top, and bottom edges of the layout.
+     * Note that thes margins will be added to the ones specified by
+     * {@link #margins(int, int)}.
+     *
+     * <code><pre>
+     *     // Construct a GridLayout whose left, right, top, and bottom
+     *     // margin sizes are 10, 5, 0, and 15 respectively
+     *
+     *     Rectangle margins = Geometry.createDiffRectangle(10,5,0,15);
+     *     GridLayoutFactory.fillDefaults().extendedMargins(margins).applyTo(composite1);
+     * </pre></code>
+     *
+     * @param differenceRect rectangle which, when added to the client area of the
+     *        layout, returns the outer area of the layout. The x and y values of
+     *        the rectangle correspond to the position of the bounds of the
+     *        layout with respect to the client area. They should be negative.
+     *        The width and height correspond to the relative size of the bounds
+     *        of the layout with respect to the client area, and should be positive.
+     * @return this
+     * @see #spacing(Point)
+     * @see #spacing(int, int)
+     *
+     * @since 3.3
+     */
+    public GridLayoutFactory extendedMargins(Rectangle differenceRect) {
+        l.marginLeft = -differenceRect.x;
+        l.marginTop = -differenceRect.y;
+        l.marginBottom = differenceRect.y + differenceRect.height;
+        l.marginRight = differenceRect.x + differenceRect.width;
+        return this;
+    }
+
+    /**
+     * Sets the number of columns in the layout
+     *
+     * @param numColumns number of columns in the layout
+     * @return this
+     */
+    public GridLayoutFactory numColumns(int numColumns) {
+        l.numColumns = numColumns;
+        return this;
+    }
+
+    /**
+     * Creates a new GridLayout, and initializes it with values from the factory.
+     *
+     * @return a new initialized GridLayout.
+     * @see #applyTo
+     */
+    public GridLayout create() {
+        return copyLayout(l);
+    }
+
+    /**
+     * Creates a new GridLayout and attaches it to the given composite.
+     * Does not create the GridData of any of the controls in the composite.
+     *
+     * @param c composite whose layout will be set
+     * @see #generateLayout
+     * @see #create
+     * @see GridLayoutFactory
+     */
+    public void applyTo(Composite c) {
+        c.setLayout(copyLayout(l));
+    }
+
+    /**
+     * Copies the given GridLayout instance
+     *
+     * @param l layout to copy
+     * @return a new GridLayout
+     */
+    public static GridLayout copyLayout(GridLayout l) {
+        GridLayout result = new GridLayout(l.numColumns, l.makeColumnsEqualWidth);
+        result.horizontalSpacing = l.horizontalSpacing;
+        result.marginBottom = l.marginBottom;
+        result.marginHeight = l.marginHeight;
+        result.marginLeft = l.marginLeft;
+        result.marginRight = l.marginRight;
+        result.marginTop = l.marginTop;
+        result.marginWidth = l.marginWidth;
+        result.verticalSpacing = l.verticalSpacing;
+
+        return result;
+    }
+
+    /**
+     * Applies this layout to the given composite, and attaches default GridData
+     * to all immediate children that don't have one. The layout is generated using
+     * heuristics based on the widget types. In most cases, it will create exactly the same
+     * layout that would have been hardcoded by the programmer. In any situation
+     * where it does not produce the desired layout, the GridData for any child can be
+     * overridden by attaching the layout data before calling this method. In these cases,
+     * the special-case layout data can be hardcoded and the algorithm can supply defaults
+     * to the rest.
+     *
+     * <p>
+     * This must be called <b>AFTER</b> all of the child controls have been created and their
+     * layouts attached. This method will attach a layout to the given composite. If any new
+     * children are created after calling this method, their GridData must be created manually.
+     * The algorithm does not recurse into child composites. To generate all the layouts in
+     * a widget hierarchy, the method must be called bottom-up for each Composite.
+     * </p>
+     *
+     * <p>
+     * All controls are made to span a single cell. The algorithm tries to classify controls into one
+     * of the following categories:
+     * </p>
+     *
+     * <ul>
+     * <li>Pushbuttons: Set to a constant size large enough to fit their text and no smaller
+     * than the default button size.</li>
+     * <li>Wrapping with text (labels, read-only text boxes, etc.): override the preferred horizontal
+     *     size with the default wrapping point, fill horizontally, grab horizontal space, keep the
+     *     preferred vertical size</li>
+     * <li>Wrapping without text (toolbars, coolbars, etc.): fill align, don't grab, use the preferred size</li>
+     * <li>Horizontally scrolling controls (anything with horizontal scrollbars or where the user edits
+     *     text and can cursor through it from left-to-right): override the preferred horizontal size with
+     *     a constant, grab horizontal, fill horizontal.</li>
+     * <li>Vertically scrolling controls (anything with vertical scrollbars or where the user edits
+     *     text and can cursor through it up and down): override the preferred vertical size with a constant,
+     *     grab vertical, fill vertical</li>
+     * <li>Nested layouts: fill align both directions, grab along any dimension if the layout would
+     *     be able to expand along that dimension.</li>
+     * <li>Non-wrapping non-scrollable read-only text: fill horizontally, center vertically, default size, don't grab </li>
+     * <li>Non-wrapping non-scrollable non-text: fill both, default size, don't grab</li>
+     * </ul>
+     *
+     * @param c composite whose layout will be generated
+     */
+    public void generateLayout(Composite c) {
+        applyTo(c);
+        LayoutGenerator.generateLayout(c);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/layout/LayoutConstants.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.layout.LayoutConstants;
+
+import java.lang.all;
+
+
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.resource.JFaceResources;
+
+/**
+ * Contains various layout constants
+ *
+ * @since 3.2
+ */
+public final class LayoutConstants {
+    private static Point dialogMargins = null;
+    private static Point dialogSpacing = null;
+    private static Point minButtonSize = null;
+
+    private static void initializeConstants() {
+        if (dialogMargins !is null) {
+            return;
+        }
+
+        GC gc = new GC(Display.getCurrent());
+        gc.setFont(JFaceResources.getDialogFont());
+        FontMetrics fontMetrics = gc.getFontMetrics();
+
+        dialogMargins = new Point(Dialog.convertHorizontalDLUsToPixels(fontMetrics, IDialogConstants.HORIZONTAL_MARGIN),
+                Dialog.convertVerticalDLUsToPixels(fontMetrics, IDialogConstants.VERTICAL_MARGIN));
+
+        dialogSpacing = new Point(Dialog.convertHorizontalDLUsToPixels(fontMetrics, IDialogConstants.HORIZONTAL_SPACING),
+                Dialog.convertVerticalDLUsToPixels(fontMetrics, IDialogConstants.VERTICAL_SPACING));
+
+        minButtonSize  = new Point(Dialog.convertHorizontalDLUsToPixels(fontMetrics, IDialogConstants.BUTTON_WIDTH), 0);
+
+        gc.dispose();
+    }
+
+    /**
+     * Returns the default dialog margins, in pixels
+     *
+     * @return the default dialog margins, in pixels
+     */
+    public static final Point getMargins() {
+        initializeConstants();
+        return dialogMargins;
+    }
+
+    /**
+     * Returns the default dialog spacing, in pixels
+     *
+     * @return the default dialog spacing, in pixels
+     */
+    public static final Point getSpacing() {
+        initializeConstants();
+        return dialogSpacing;
+    }
+
+    /**
+     * Returns the default minimum button size, in pixels
+     *
+     * @return the default minimum button size, in pixels
+     */
+    public static final Point getMinButtonSize() {
+        initializeConstants();
+        return minButtonSize;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/layout/LayoutGenerator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,275 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.layout.LayoutGenerator;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.LayoutConstants;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Scrollable;
+import org.eclipse.jface.util.Geometry;
+
+import java.lang.all;
+import java.util.List;
+import java.util.Set;
+import tango.text.convert.Format;
+
+/* package */class LayoutGenerator {
+
+    /**
+     * Default size for controls with varying contents
+     */
+    private static const Point defaultSize;
+
+    /**
+     * Default wrapping size for wrapped labels
+     */
+    private static const int wrapSize = 350;
+
+    private static const GridDataFactory nonWrappingLabelData;
+
+    static this(){
+        defaultSize = new Point(150, 150);
+        nonWrappingLabelData = GridDataFactory.fillDefaults().align_(SWT.BEGINNING, SWT.CENTER).grab(false, false);
+    }
+
+    private static bool hasStyle(Control c, int style) {
+        return (c.getStyle() & style) !is 0;
+    }
+
+    /**
+     * Generates a GridLayout for the given composite by examining its child
+     * controls and attaching layout data to any immediate children that do not
+     * already have layout data.
+     *
+     * @param toGenerate
+     *            composite to generate a layout for
+     */
+    public static void generateLayout(Composite toGenerate) {
+        Control[] children = toGenerate.getChildren();
+
+        for (int i = 0; i < children.length; i++) {
+            Control control = children[i];
+
+            // Skip any children that already have layout data
+            if (control.getLayoutData() !is null) {
+                continue;
+            }
+
+            applyLayoutDataTo(control);
+        }
+    }
+
+    private static void applyLayoutDataTo(Control control) {
+        defaultsFor(control).applyTo(control);
+    }
+
+    /**
+     * Creates default factory for this control types:
+     * <ul>
+     *  <li>{@link Button} with {@link SWT#CHECK}</li>
+     *  <li>{@link Button}</li>
+     *  <li>{@link Composite}</li>
+     * </ul>
+     * @param control the control the factory is search for
+     * @return a default factory for the control
+     */
+    public static GridDataFactory defaultsFor(Control control) {
+        if ( auto button = cast(Button) control ) {
+
+            if (hasStyle(button, SWT.CHECK)) {
+                return nonWrappingLabelData.copy();
+            } else {
+                return GridDataFactory.fillDefaults().align_(SWT.FILL, SWT.CENTER).hint(Geometry.max(button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true), LayoutConstants.getMinButtonSize()));
+            }
+        }
+
+        if (auto scrollable = cast(Scrollable) control ) {
+
+            if ( auto composite = cast(Composite) scrollable ) {
+
+                Layout theLayout = composite.getLayout();
+                if ( cast(GridLayout) theLayout ) {
+                    bool growsHorizontally = false;
+                    bool growsVertically = false;
+
+                    Control[] children = composite.getChildren();
+                    for (int i = 0; i < children.length; i++) {
+                        Control child = children[i];
+
+                        GridData data = cast(GridData) child.getLayoutData();
+
+                        if (data !is null) {
+                            if (data.grabExcessHorizontalSpace) {
+                                growsHorizontally = true;
+                            }
+                            if (data.grabExcessVerticalSpace) {
+                                growsVertically = true;
+                            }
+                        }
+                    }
+
+                    return GridDataFactory.fillDefaults().grab(growsHorizontally, growsVertically);
+                }
+            }
+        }
+
+        bool wrapping = hasStyle(control, SWT.WRAP);
+
+        // Assume any control with the H_SCROLL or V_SCROLL flags are
+        // horizontally or vertically
+        // scrollable, respectively.
+        bool hScroll = hasStyle(control, SWT.H_SCROLL);
+        bool vScroll = hasStyle(control, SWT.V_SCROLL);
+
+        bool containsText = hasMethodSetText(control);//, "setText", [ ArrayWrapperString.classinfo ] ); //$NON-NLS-1$
+
+        // If the control has a setText method, an addModifyListener method, and
+        // does not have
+        // the SWT.READ_ONLY flag, assume it contains user-editable text.
+        bool userEditable = !hasStyle(control, SWT.READ_ONLY) && containsText && hasMethodAddModifyListener(control);//, "addModifyListener", [ ModifyListener.classinfo ]); //$NON-NLS-1$
+
+        // For controls containing user-editable text...
+        if (userEditable) {
+            if (hasStyle(control, SWT.MULTI)) {
+                vScroll = true;
+            }
+
+            if (!wrapping) {
+                hScroll = true;
+            }
+        }
+
+        // Compute the horizontal hint
+        int hHint = SWT.DEFAULT;
+        bool grabHorizontal = hScroll;
+
+        // For horizontally-scrollable controls, override their horizontal
+        // preferred size
+        // with a constant
+        if (hScroll) {
+            hHint = defaultSize.x;
+        } else {
+            // For wrapping controls, there are two cases.
+            // 1. For controls that contain text (like wrapping labels,
+            // read-only text boxes,
+            // etc.) override their preferred size with the preferred wrapping
+            // point and
+            // make them grab horizontal space.
+            // 2. For non-text controls (like wrapping toolbars), assume that
+            // their non-wrapped
+            // size is best.
+
+            if (wrapping) {
+                if (containsText) {
+                    hHint = wrapSize;
+                    grabHorizontal = true;
+                }
+            }
+        }
+
+        int vAlign = SWT.FILL;
+
+        // Heuristic for labels: Controls that contain non-wrapping read-only
+        // text should be
+        // center-aligned rather than fill-aligned
+        if (!vScroll && !wrapping && !userEditable && containsText) {
+            vAlign = SWT.CENTER;
+        }
+
+        return GridDataFactory.fillDefaults().grab(grabHorizontal, vScroll).align_(SWT.FILL, vAlign).hint(hHint, vScroll ? defaultSize.y : SWT.DEFAULT);
+    }
+
+    struct ControlInfo {
+        char[] name;
+        bool   hasSetText;
+        bool   hasAddModifierListener;
+    }
+    static ControlInfo[] controlInfo = [
+        { "org.eclipse.swt.custom.CBanner.CBanner", false, false },
+        { "org.eclipse.swt.custom.CCombo.CCombo", true, true },
+        { "org.eclipse.swt.custom.CLabel.CLabel", true, false },
+        { "org.eclipse.swt.custom.CTabFolder.CTabFolder", false, false },
+        { "org.eclipse.swt.custom.SashForm.SashForm", false, false },
+        { "org.eclipse.swt.custom.ScrolledComposite.ScrolledComposite", false, false },
+        { "org.eclipse.swt.custom.StyledText.StyledText", true, true },
+        { "org.eclipse.swt.custom.TableCursor.TableCursor", false, false },
+        { "org.eclipse.swt.custom.TableTree.TableTree", false, false },
+        { "org.eclipse.swt.custom.ViewForm.ViewForm", false, false },
+        { "org.eclipse.swt.opengl.GLCanvas.GLCanvas", false, false },
+        { "org.eclipse.swt.widgets.Button.Button", true, false },
+        { "org.eclipse.swt.widgets.Canvas.Canvas", false, false },
+        { "org.eclipse.swt.widgets.Combo.Combo", true, true },
+        { "org.eclipse.swt.widgets.Composite.Composite", false, false },
+        { "org.eclipse.swt.widgets.Control.Control", false, false },
+        { "org.eclipse.swt.widgets.CoolBar.CoolBar", false, false },
+        { "org.eclipse.swt.widgets.DateTime.DateTime", false, false },
+        { "org.eclipse.swt.widgets.Decorations.Decorations", true, false },
+        { "org.eclipse.swt.widgets.ExpandBar.ExpandBar", false, false },
+        { "org.eclipse.swt.widgets.Group.Group", true, false },
+        { "org.eclipse.swt.widgets.Label.Label", true, false },
+        { "org.eclipse.swt.widgets.Link.Link", true, false },
+        { "org.eclipse.swt.widgets.List.List", false, false },
+        { "org.eclipse.swt.widgets.ProgressBar.ProgressBar", false, false },
+        { "org.eclipse.swt.widgets.Sash.Sash", false, false },
+        { "org.eclipse.swt.widgets.Scale.Scale", false, false },
+        { "org.eclipse.swt.widgets.Scrollable.Scrollable", false, false },
+        { "org.eclipse.swt.widgets.Shell.Shell", true, false },
+        { "org.eclipse.swt.widgets.Slider.Slider", false, false },
+        { "org.eclipse.swt.widgets.Spinner.Spinner", false, true },
+        { "org.eclipse.swt.widgets.TabFolder.TabFolder", false, false },
+        { "org.eclipse.swt.widgets.Table.Table", false, false },
+        { "org.eclipse.swt.widgets.Text.Text", true, true },
+        { "org.eclipse.swt.widgets.ToolBar.ToolBar", false, false },
+        { "org.eclipse.swt.widgets.Tree.Tree", false, false },
+    ];
+    private static bool hasMethodSetText(Control control) {
+        char[] name = control.classinfo.name;
+        foreach( ci; controlInfo ){
+            if( ci.name == name ){
+                return ci.hasSetText;
+            }
+        }
+        throw new Exception( Format( "{}:{} Control was not found for reflection info: {}", __FILE__, __LINE__, name ));
+    }
+    private static bool hasMethodAddModifyListener(Control control) {
+        char[] name = control.classinfo.name;
+        foreach( ci; controlInfo ){
+            if( ci.name == name ){
+                return ci.hasAddModifierListener;
+            }
+        }
+        throw new Exception( Format( "{}:{} Control was not found for reflection info: {}", __FILE__, __LINE__, name ));
+    }
+//    private static bool hasMethod(Control control, String name, ClassInfo[] parameterTypes) {
+//        ClassInfo c = control.classinfo;
+//        implMissing(__FILE__,__LINE__);
+//        pragma(msg, "FIXME org.eclipse.jface.layout.LayoutGenerator hasMethod reflection" );
+//        return true;
+///+        try {
+//            return c.getMethod(name, parameterTypes) !is null;
+//        } catch (SecurityException e) {
+//            return false;
+//        } catch (NoSuchMethodException e) {
+//            return false;
+//        }+/
+//    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/layout/TableColumnLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * 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:
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                               - fix for bug 178280
+ *     IBM Corporation - API refactoring and general maintenance
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.layout.TableColumnLayout;
+
+import java.lang.all;
+
+import org.eclipse.jface.layout.AbstractColumnLayout;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Scrollable;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.jface.viewers.ColumnLayoutData;
+import org.eclipse.jface.viewers.ColumnPixelData;
+
+/**
+ * The TableColumnLayout is the {@link Layout} used to maintain
+ * {@link TableColumn} sizes in a {@link Table}.
+ *
+ * <p>
+ * <b>You can only add the {@link Layout} to a container whose <i>only</i>
+ * child is the {@link Table} control you want the {@link Layout} applied to.
+ * Don't assign the layout directly the {@link Table}</b>
+ * </p>
+ *
+ * @since 3.3
+ */
+public class TableColumnLayout : AbstractColumnLayout {
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.layout.AbstractColumnLayout#getColumnCount(org.eclipse.swt.widgets.Scrollable)
+     */
+    override int getColumnCount(Scrollable tableTree) {
+        return (cast(Table) tableTree).getColumnCount();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.layout.AbstractColumnLayout#setColumnWidths(org.eclipse.swt.widgets.Scrollable,
+     *      int[])
+     */
+    override void setColumnWidths(Scrollable tableTree, int[] widths) {
+        TableColumn[] columns = (cast(Table) tableTree).getColumns();
+        for (int i = 0; i < widths.length; i++) {
+            columns[i].setWidth(widths[i]);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.layout.AbstractColumnLayout#getLayoutData(int)
+     */
+    override ColumnLayoutData getLayoutData(Scrollable tableTree, int columnIndex) {
+        TableColumn column = (cast(Table) tableTree).getColumn(columnIndex);
+        return cast(ColumnLayoutData) column.getData(LAYOUT_DATA);
+    }
+
+    Composite getComposite(Widget column) {
+        return (cast(TableColumn) column).getParent().getParent();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.layout.AbstractColumnLayout#updateColumnData(org.eclipse.swt.widgets.Widget)
+     */
+    override void updateColumnData(Widget column) {
+        TableColumn tColumn = cast(TableColumn) column;
+        Table t = tColumn.getParent();
+
+        if( ! IS_GTK || t.getColumn(t.getColumnCount()-1) !is tColumn ){
+            tColumn.setData(LAYOUT_DATA,new ColumnPixelData(tColumn.getWidth()));
+            layout(t.getParent(), true);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/layout/TreeColumnLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * 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:
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                               - fix for bug 178280, 183999, 184609
+ *     IBM Corporation - API refactoring and general maintenance
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.layout.TreeColumnLayout;
+
+import org.eclipse.jface.layout.AbstractColumnLayout;
+
+import org.eclipse.swt.events.TreeEvent;
+import org.eclipse.swt.events.TreeListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Scrollable;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.jface.viewers.ColumnLayoutData;
+import org.eclipse.jface.viewers.ColumnPixelData;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The TreeColumnLayout is the {@link Layout} used to maintain {@link TreeColumn} sizes in a
+ * {@link Tree}.
+ *
+ * <p>
+ * <b>You can only add the {@link Layout} to a container whose <i>only</i>
+ * child is the {@link Tree} control you want the {@link Layout} applied to.
+ * Don't assign the layout directly the {@link Tree}</b>
+ * </p>
+ *
+ * @since 3.3
+ */
+public class TreeColumnLayout : AbstractColumnLayout {
+    private bool addListener = true;
+
+    private static class TreeLayoutListener : TreeListener {
+
+        public void treeCollapsed(TreeEvent e) {
+            update(cast(Tree) e.widget);
+        }
+
+        public void treeExpanded(TreeEvent e) {
+            update(cast(Tree) e.widget);
+        }
+
+        private void update(Tree tree) {
+            tree.getDisplay().asyncExec(new class(tree) Runnable {
+                Tree tree_;
+                this(Tree a){
+                    tree_=a;
+                }
+                public void run() {
+                    tree_.update();
+                    tree_.getParent().layout();
+                }
+
+            });
+        }
+
+    }
+
+    private static const TreeLayoutListener listener;
+
+    static this(){
+        listener = new TreeLayoutListener();
+    }
+
+    protected override void layout(Composite composite, bool flushCache) {
+        super.layout(composite, flushCache);
+        if( addListener ) {
+            addListener=false;
+            (cast(Tree)getControl(composite)).addTreeListener(listener);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.layout.AbstractColumnLayout#getColumnCount(org.eclipse.swt.widgets.Scrollable)
+     */
+    override int getColumnCount(Scrollable tree) {
+        return (cast(Tree) tree).getColumnCount();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.layout.AbstractColumnLayout#setColumnWidths(org.eclipse.swt.widgets.Scrollable, int[])
+     */
+    override void setColumnWidths(Scrollable tree, int[] widths) {
+        TreeColumn[] columns = (cast(Tree) tree).getColumns();
+        for (int i = 0; i < widths.length; i++) {
+            columns[i].setWidth(widths[i]);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.layout.AbstractColumnLayout#getLayoutData(org.eclipse.swt.widgets.Scrollable, int)
+     */
+    override ColumnLayoutData getLayoutData(Scrollable tableTree, int columnIndex) {
+        TreeColumn column = (cast(Tree) tableTree).getColumn(columnIndex);
+        return cast(ColumnLayoutData) column.getData(LAYOUT_DATA);
+    }
+
+    override void updateColumnData(Widget column) {
+        TreeColumn tColumn = cast(TreeColumn) column;
+        Tree t = tColumn.getParent();
+
+        if( ! IS_GTK || t.getColumn(t.getColumnCount()-1) !is tColumn ){
+            tColumn.setData(LAYOUT_DATA,new ColumnPixelData(tColumn.getWidth()));
+            layout(t.getParent(), true);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/menus/AbstractTrimWidget.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.menus.AbstractTrimWidget;
+
+import org.eclipse.jface.menus.IWidget;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.CoolBar;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.ToolBar;
+
+import java.lang.all;
+
+/**
+ * This extension to the {@link IWidget} interface allows clients adding
+ * elements to the trim to receive notifications if the User moves the widget to
+ * another trim area.
+ * <p>
+ * This class is intended to be the base for any trim contributions.
+ * </p>
+ * @since 3.2
+ *
+ */
+public abstract class AbstractTrimWidget : IWidget {
+    /**
+     * This method is called to initially construct the widget and is also
+     * called whenever the widget's composite has been moved to a trim area on a
+     * different side of the workbench. It is the client's responsibility to
+     * control the life-cycle of the Control it manages.
+     * <p>
+     * For example: If the implementation is constructing a {@link ToolBar} and
+     * the orientation were to change from horizontal to vertical it would have
+     * to <code>dispose</code> its old ToolBar and create a new one with the
+     * correct orientation.
+     * </p>
+     * <p>
+     * The sides can be one of:
+     * <ul>
+     * <li>{@link SWT#TOP}</li>
+     * <li>{@link SWT#BOTTOM}</li>
+     * <li>{@link SWT#LEFT}</li>
+     * <li>{@link SWT#RIGHT}</li>
+     * </ul>
+     * </p>
+     * <p>
+     *
+     * @param parent
+     *            The parent to (re)create the widget under
+     *
+     * @param oldSide
+     *            The previous side ({@link SWT#DEFAULT} on the initial fill)
+     * @param newSide
+     *            The current side
+     */
+    public abstract void fill(Composite parent, int oldSide, int newSide);
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.menus.IWidget#dispose()
+     */
+    public abstract void dispose();
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.menus.IWidget#fill(org.eclipse.swt.widgets.Composite)
+     */
+    public void fill(Composite parent) {
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.menus.IWidget#fill(org.eclipse.swt.widgets.Menu, int)
+     */
+    public void fill(Menu parent, int index) {
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.menus.IWidget#fill(org.eclipse.swt.widgets.ToolBar, int)
+     */
+    public void fill(ToolBar parent, int index) {
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.menus.IWidget#fill(org.eclipse.swt.widgets.CoolBar, int)
+     */
+    public void fill(CoolBar parent, int index) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/menus/IMenuStateIds.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.menus.IMenuStateIds;
+
+import org.eclipse.core.commands.INamedHandleStateIds;
+// import org.eclipse.jface.commands.RadioState;
+// import org.eclipse.jface.commands.ToggleState;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * State identifiers that should be understood by items and renderers of items.
+ * The state is associated with the command, and then interpreted by the menu
+ * renderer.
+ * </p>
+ * <p>
+ * Clients may implement or extend this class.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface IMenuStateIds : INamedHandleStateIds {
+
+    /**
+     * The state id used for indicating the widget style of a command presented
+     * in the menus and tool bars. This state must be an instance of
+     * {@link ToggleState} or {@link RadioState}.
+     */
+    public static String STYLE = "STYLE"; //$NON-NLS-1$
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/menus/IWidget.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.menus.IWidget;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.CoolBar;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.ToolBar;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * Provides a hook by which third-party code can contribute SWT widgets to a
+ * menu, tool bar or status line. This can be used, for example, to add a combo
+ * box to the status line, or a "Location" bar to the tool bar.
+ * </p>
+ * <p>
+ * It is possible for fill and dispose to be called multiple times for a single
+ * instance of <code>IWidget</code>.
+ * </p>
+ * <p>
+ * Clients may implement, but must not extend.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface IWidget {
+
+    /**
+     * Disposes of the underlying widgets. This can be called when the widget is
+     * becoming hidden.
+     */
+    public void dispose();
+
+    /**
+     * Fills the given composite control with controls representing this widget.
+     *
+     * @param parent
+     *            the parent control
+     */
+    public void fill(Composite parent);
+
+    /**
+     * Fills the given menu with controls representing this widget.
+     *
+     * @param parent
+     *            the parent menu
+     * @param index
+     *            the index where the controls are inserted, or <code>-1</code>
+     *            to insert at the end
+     */
+    public void fill(Menu parent, int index);
+
+    /**
+     * Fills the given tool bar with controls representing this contribution
+     * item.
+     *
+     * @param parent
+     *            the parent tool bar
+     * @param index
+     *            the index where the controls are inserted, or <code>-1</code>
+     *            to insert at the end
+     */
+    public void fill(ToolBar parent, int index);
+
+    /**
+     * Fills the given cool bar with controls representing this contribution
+     * item.
+     *
+     * @param parent
+     *            the parent cool bar
+     * @param index
+     *            the index where the controls are inserted, or <code>-1</code>
+     *            to insert at the end
+     */
+    public void fill(CoolBar parent, int index);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/menus/TextState.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.menus.TextState;
+
+import org.eclipse.core.commands.INamedHandleStateIds;
+import org.eclipse.jface.commands.PersistentState;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * <p>
+ * A piece of state carrying a single {@link String}.
+ * </p>
+ * <p>
+ * If this state is registered using {@link INamedHandleStateIds#NAME} or
+ * {@link INamedHandleStateIds#DESCRIPTION}, then this allows the handler to
+ * communicate a textual change for a given command. This is typically used by
+ * graphical applications to allow more specific text to be displayed in the
+ * menus. For example, "Undo" might become "Undo Typing" through the use of a
+ * {@link TextState}.
+ * </p>
+ * <p>
+ * Clients may instantiate this class, but must not extend.
+ * </p>
+ *
+ * @since 3.2
+ * @see INamedHandleStateIds
+ */
+public class TextState : PersistentState {
+
+    public override final void load(IPreferenceStore store,
+            String preferenceKey) {
+        String value = store.getString(preferenceKey);
+        setValue(stringcast(value));
+    }
+
+    public override final void save(IPreferenceStore store,
+            String preferenceKey) {
+        Object value = getValue();
+        if ( cast(ArrayWrapperString)value ) {
+            store.setValue(preferenceKey, stringcast(value));
+        }
+    }
+
+    public override void setValue(Object value) {
+        if (!( cast(ArrayWrapperString)value )) {
+            throw new IllegalArgumentException(
+                    "TextState takes a String as a value"); //$NON-NLS-1$
+        }
+
+        super.setValue(value);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/operation/AccumulatingProgressMonitor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,283 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.operation.AccumulatingProgressMonitor;
+
+
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IProgressMonitorWithBlocking;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ProgressMonitorWrapper;
+import org.eclipse.jface.dialogs.Dialog;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A progress monitor that accumulates <code>worked</code> and <code>subtask</code>
+ * calls in the following way by wrapping a standard progress monitor:
+ * <ul>
+ * <li> When a <code>worked</code> or <code>subtask</code> call occurs the first time,
+ *      the progress monitor posts a runnable into the asynchronous SWT event queue.
+ * </li>
+ * <li> Subsequent calls to <code>worked</code> or <code>subtask</code> do not post
+ *      a new runnable as long as a previous runnable still exists in the SWT event
+ *      queue. In this case, the progress monitor just updates the internal state of
+ *      the runnable that waits in the SWT event queue for its execution. If no runnable
+ *      exists, a new one is created and posted into the event queue.
+ * </ul>
+ * <p>
+ * This class is internal to the framework; clients outside JFace should not
+ * use this class.
+ * </p>
+ */
+/* package */class AccumulatingProgressMonitor : ProgressMonitorWrapper {
+
+    /**
+     * The display.
+     */
+    private Display display;
+
+    /**
+     * The collector, or <code>null</code> if none.
+     */
+    private Collector collector;
+
+    private String currentTask = ""; //$NON-NLS-1$
+
+    private class Collector : Runnable {
+        private String subTask_;
+
+        private double worked_;
+
+        private IProgressMonitor monitor;
+
+        /**
+         * Create a new collector.
+         * @param subTask
+         * @param work
+         * @param monitor
+         */
+        public this(String subTask_, double work, IProgressMonitor monitor) {
+            this.subTask_ = subTask_;
+            this.worked_ = work;
+            this.monitor = monitor;
+        }
+
+        /**
+         * Add worked to the work.
+         * @param workedIncrement
+         */
+        public void worked(double workedIncrement) {
+            this.worked_ = this.worked_ + workedIncrement;
+        }
+
+        /**
+         * Set the subTask name.
+         * @param subTaskName
+         */
+        public void subTask(String subTaskName) {
+            this.subTask_ = subTaskName;
+        }
+
+        /**
+         * Run the collector.
+         */
+        public void run() {
+            clearCollector(this);
+            if (subTask_ !is null) {
+                monitor.subTask(subTask_);
+            }
+            if (worked_ > 0) {
+                monitor.internalWorked(worked_);
+            }
+        }
+    }
+
+    /**
+     * Creates an accumulating progress monitor wrapping the given one
+     * that uses the given display.
+     *
+     * @param monitor the actual progress monitor to be wrapped
+     * @param display the SWT display used to forward the calls
+     *  to the wrapped progress monitor
+     */
+    public this(IProgressMonitor monitor, Display display) {
+        super(monitor);
+        Assert.isNotNull(display);
+        this.display = display;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IProgressMonitor.
+     */
+    public override void beginTask(String name, int totalWork) {
+        synchronized (this) {
+            collector = null;
+        }
+        display.asyncExec(new class(name,totalWork) Runnable {
+            String name_;
+            int totalWork_;
+            this(String a, int b){
+                name_=a;
+                totalWork_=b;
+            }
+            public void run() {
+                currentTask = name_;
+                getWrappedProgressMonitor().beginTask(name_, totalWork_);
+            }
+        });
+    }
+
+    /**
+     * Clears the collector object used to accumulate work and subtask calls
+     * if it matches the given one.
+     * @param collectorToClear
+     */
+    private synchronized void clearCollector(Collector collectorToClear) {
+        // Check if the accumulator is still using the given collector.
+        // If not, don't clear it.
+        if (this.collector is collectorToClear) {
+            this.collector = null;
+        }
+    }
+
+    /**
+     *  Creates a collector object to accumulate work and subtask calls.
+     * @param subTask
+     * @param work
+     */
+    private void createCollector(String subTask, double work) {
+        collector = new Collector(subTask, work, getWrappedProgressMonitor());
+        display.asyncExec(collector);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IProgressMonitor.
+     */
+    public override void done() {
+        synchronized (this) {
+            collector = null;
+        }
+        display.asyncExec(new class Runnable {
+            public void run() {
+                getWrappedProgressMonitor().done();
+            }
+        });
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IProgressMonitor.
+     */
+    public override synchronized void internalWorked(double work) {
+        if (collector is null) {
+            createCollector(null, work);
+        } else {
+            collector.worked(work);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IProgressMonitor.
+     */
+    public override void setTaskName(String name) {
+        synchronized (this) {
+            collector = null;
+        }
+        display.asyncExec(new class(name) Runnable {
+            String name_;
+            this(String a){
+                name_=a;
+            }
+            public void run() {
+                currentTask = name_;
+                getWrappedProgressMonitor().setTaskName(name_);
+            }
+        });
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IProgressMonitor.
+     */
+    public override synchronized void subTask(String name) {
+        if (collector is null) {
+            createCollector(name, 0);
+        } else {
+            collector.subTask(name);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IProgressMonitor.
+     */
+    public override synchronized void worked(int work) {
+        internalWorked(work);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.ProgressMonitorWrapper#clearBlocked()
+     */
+    public override void clearBlocked() {
+
+        //If this is a monitor that can report blocking do so.
+        //Don't bother with a collector as this should only ever
+        //happen once and prevent any more progress.
+        IProgressMonitor pm = getWrappedProgressMonitor();
+        if (!(cast(IProgressMonitorWithBlocking)pm )) {
+            return;
+        }
+
+        display.asyncExec(new class(pm) Runnable {
+            IProgressMonitor pm_;
+            this(IProgressMonitor a){ pm_=a; }
+            /* (non-Javadoc)
+             * @see java.lang.Runnable#run()
+             */
+            public void run() {
+                (cast(IProgressMonitorWithBlocking) pm_).clearBlocked();
+                Dialog.getBlockedHandler().clearBlocked();
+            }
+        });
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setBlocked(org.eclipse.core.runtime.IStatus)
+     */
+    public override void setBlocked(IStatus reason) {
+        //If this is a monitor that can report blocking do so.
+        //Don't bother with a collector as this should only ever
+        //happen once and prevent any more progress.
+        IProgressMonitor pm = getWrappedProgressMonitor();
+        if (!(cast(IProgressMonitorWithBlocking)pm )) {
+            return;
+        }
+
+        display.asyncExec(new class(pm,reason) Runnable {
+            IProgressMonitor pm_;
+            IStatus reason_;
+            this(IProgressMonitor a,IStatus b){
+                pm_=a;
+                reason_=b;
+            }
+            /* (non-Javadoc)
+             * @see java.lang.Runnable#run()
+             */
+            public void run() {
+                (cast(IProgressMonitorWithBlocking) pm_).setBlocked(reason_);
+                //Do not give a shell as we want it to block until it opens.
+                Dialog.getBlockedHandler().showBlocked(pm_, reason_, currentTask);
+            }
+        });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/operation/IRunnableContext.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.operation.IRunnableContext;
+
+import org.eclipse.jface.operation.IRunnableWithProgress;
+
+import java.lang.all;
+
+/**
+ * Interface for UI components which can execute a long-running operation
+ * in the form of an <code>IRunnableWithProgress</code>.
+ * The context is responsible for displaying a progress indicator and Cancel
+ * button to the end user while the operation is in progress; the context
+ * supplies a progress monitor to be used from code running inside the operation.
+ * Note that an <code>IRunnableContext</code> is not a runnable itself.
+ * <p>
+ * For examples of UI components which implement this interface,
+ * see <code>ApplicationWindow</code>, <code>ProgressMonitorDialog</code>,
+ * and <code>WizardDialog</code>.
+ * </p>
+ *
+ * @see IRunnableWithProgress
+ * @see org.eclipse.jface.window.ApplicationWindow
+ * @see org.eclipse.jface.dialogs.ProgressMonitorDialog
+ * @see org.eclipse.jface.wizard.WizardDialog
+ */
+public interface IRunnableContext {
+    /**
+     * <p>
+     * Runs the given <code>IRunnableWithProgress</code> in this context.
+     * For example, if this is a <code>ProgressMonitorDialog</code> then the runnable
+     * is run using this dialog's progress monitor.
+     * </p>
+     * <p>
+     * If <code>fork</code> is <code>false</code>, the current thread is used
+     * to run the runnable. Note that if <code>fork</code> is <code>true</code>,
+     * it is unspecified whether or not this method blocks until the runnable
+     * has been run. Implementers should document whether the runnable is run
+     * synchronously (blocking) or asynchronously (non-blocking), or if no
+     * assumption can be made about the blocking behaviour.
+     * </p>
+     *
+     * @param fork <code>true</code> if the runnable should be run in a separate thread,
+     *  and <code>false</code> to run in the same thread
+     * @param cancelable <code>true</code> to enable the cancelation, and
+     *  <code>false</code> to make the operation uncancellable
+     * @param runnable the runnable to run
+     *
+     * @exception InvocationTargetException wraps any exception or error which occurs
+     *  while running the runnable
+     * @exception InterruptedException propagated by the context if the runnable
+     *  acknowledges cancelation by throwing this exception.  This should not be thrown
+     *  if cancelable is <code>false</code>.
+     */
+    public void run(bool fork, bool cancelable,
+            IRunnableWithProgress runnable);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/operation/IRunnableWithProgress.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.operation.IRunnableWithProgress;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import java.lang.all;
+
+import tango.core.Tuple;
+import tango.core.Traits;
+
+/**
+ * The <code>IRunnableWithProgress</code> interface should be implemented by any
+ * class whose instances are intended to be executed as a long-running operation.
+ * Long-running operations are typically presented at the UI via a modal dialog
+ * showing a progress indicator and a Cancel button.
+ * The class must define a <code>run</code> method that takes a progress monitor.
+ * The <code>run</code> method is usually not invoked directly, but rather by
+ * passing the <code>IRunnableWithProgress</code> to the <code>run</code> method of
+ * an <code>IRunnableContext</code>, which provides the UI for the progress monitor
+ * and Cancel button.
+ *
+ * @see IRunnableContext
+ */
+public interface IRunnableWithProgress {
+    /**
+     * Runs this operation.  Progress should be reported to the given progress monitor.
+     * This method is usually invoked by an <code>IRunnableContext</code>'s <code>run</code> method,
+     * which supplies the progress monitor.
+     * A request to cancel the operation should be honored and acknowledged
+     * by throwing <code>InterruptedException</code>.
+     *
+     * @param monitor the progress monitor to use to display progress and receive
+     *   requests for cancelation
+     * @exception InvocationTargetException if the run method must propagate a checked exception,
+     *  it should wrap it inside an <code>InvocationTargetException</code>; runtime exceptions are automatically
+     *  wrapped in an <code>InvocationTargetException</code> by the calling context
+     * @exception InterruptedException if the operation detects a request to cancel,
+     *  using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing
+     *  <code>InterruptedException</code>
+     *
+     * @see IRunnableContext#run
+     */
+    public void run(IProgressMonitor monitor);
+}
+
+class _DgIRunnableWithProgressT(Dg,T...) : IRunnableWithProgress {
+
+    alias ParameterTupleOf!(Dg) DgArgs;
+    static assert( is(DgArgs == Tuple!(T))
+                || is(DgArgs == Tuple!(IProgressMonitor,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 run( IProgressMonitor pm ){
+        static if( is( typeof(dg(pm,t)))){
+            dg(pm,t);
+        }
+        else static if( is( typeof(dg(t)))){
+            dg(t);
+        }
+        else{
+            static assert( false, "Delegate type is incorrect for arguments supplied");
+        }
+    }
+}
+
+_DgIRunnableWithProgressT!(Dg,T) dgIRunnableWithProgress(Dg,T...)( Dg dg, T args ){
+    return new _DgIRunnableWithProgressT!(Dg,T)(dg,args);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/operation/IThreadListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.operation.IThreadListener;
+
+import java.lang.all;
+import java.lang.JThread;
+
+/**
+ * A thread listener is an object that is interested in receiving notifications
+ * of thread changes.  For example, a thread listener can be used to notify a
+ * runnable of the thread that will execute it, allowing the runnable to transfer
+ * thread-local state from the calling thread before control passes to the new thread.
+ *
+ * @since 3.1
+ */
+public interface IThreadListener {
+    /**
+     * Notification that a thread change is occurring.
+     *
+     * @param thread The new thread
+     */
+    public void threadChange(JThread thread);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/operation/ModalContext.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,502 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.operation.ModalContext;
+
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.ProgressMonitorWrapper;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.util.Policy;
+
+import org.eclipse.jface.operation.IThreadListener;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.operation.AccumulatingProgressMonitor;
+
+import java.lang.all;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Set;
+import java.lang.JThread;
+import tango.io.Stdout;
+
+/**
+ * Utility class for supporting modal operations. The runnable passed to the
+ * <code>run</code> method is executed in a separate thread, depending on the
+ * value of the passed fork argument. If the runnable is executed in a separate
+ * thread then the current thread either waits until the new thread ends or, if
+ * the current thread is the UI thread, it polls the SWT event queue and
+ * dispatches each event.
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ModalContext {
+
+    /**
+     * Indicated whether ModalContext is in debug mode; <code>false</code> by
+     * default.
+     */
+    private static bool debug_ = true;
+
+    /**
+     * The number of nested modal runs, or 0 if not inside a modal run. This is
+     * global state.
+     */
+    private static int modalLevel = 0;
+
+    /**
+     * Indicates whether operations should be run in a separate thread. Defaults
+     * to true. For internal debugging use, set to false to run operations in
+     * the calling thread.
+     */
+    private static bool runInSeparateThread = true;
+
+    /**
+     * Thread which runs the modal context.
+     */
+    private static class ModalContextThread : JThread {
+        /**
+         * The operation to be run.
+         */
+        private IRunnableWithProgress runnable;
+
+        /**
+         * The exception thrown by the operation starter.
+         */
+        private Exception throwable;
+
+        /**
+         * The progress monitor used for progress and cancelation.
+         */
+        private IProgressMonitor progressMonitor;
+
+        /**
+         * The display used for event dispatching.
+         */
+        private Display display;
+
+        /**
+         * Indicates whether to continue event queue dispatching.
+         */
+        private /+volatile+/ bool continueEventDispatching = true;
+
+        /**
+         * The thread that forked this modal context thread.
+         *
+         * @since 3.1
+         */
+        private JThread callingThread;
+
+        /**
+         * Creates a new modal context.
+         *
+         * @param operation
+         *            the runnable to run
+         * @param monitor
+         *            the progress monitor to use to display progress and
+         *            receive requests for cancelation
+         * @param display
+         *            the display to be used to read and dispatch events
+         */
+        private this(IRunnableWithProgress operation,
+                IProgressMonitor monitor, Display display) {
+            super(); //$NON-NLS-1$
+            Assert.isTrue(monitor !is null && display !is null);
+            runnable = operation;
+            progressMonitor = new AccumulatingProgressMonitor(monitor, display);
+            this.display = display;
+            this.callingThread = JThread.currentThread();
+        }
+
+        /*
+         * (non-Javadoc) Method declared on Thread.
+         */
+        public override void run() {
+            try {
+                if (runnable !is null) {
+                    runnable.run(progressMonitor);
+                }
+            /+
+            } catch (InvocationTargetException e) {
+                throwable = e;
+            } catch (InterruptedException e) {
+                throwable = e;
+            } catch (RuntimeException e) {
+                throwable = e;
+            } catch (ThreadDeath e) {
+                // Make sure to propagate ThreadDeath, or threads will never
+                // fully terminate
+                throw e;
+            +/
+            } catch (/+Error+/Exception e) {
+                throwable = e;
+            } finally {
+                // notify the operation of change of thread of control
+                if ( auto tl = cast(IThreadListener)runnable ) {
+                    auto exception =
+                        invokeThreadListener(tl, callingThread);
+
+                    //Forward it if we don't already have one
+                    if(exception !is null && throwable is null)
+                        throwable = exception;
+                }
+
+                // Make sure that all events in the asynchronous event queue
+                // are dispatched.
+                display.syncExec(dgRunnable( {
+                    // do nothing
+                } ));
+
+                // Stop event dispatching
+                continueEventDispatching = false;
+
+                // Force the event loop to return from sleep () so that
+                // it stops event dispatching.
+                display.asyncExec(null);
+            }
+        }
+
+        /**
+         * Processes events or waits until this modal context thread terminates.
+         */
+        public void block() {
+            if (display is Display.getCurrent()) {
+                while (continueEventDispatching) {
+                    // Run the event loop. Handle any uncaught exceptions caused
+                    // by UI events.
+                    try {
+                        if (!display.readAndDispatch()) {
+                            display.sleep();
+                        }
+                    }
+                    /+
+                    // ThreadDeath is a normal error when the thread is dying.
+                    // We must
+                    // propagate it in order for it to properly terminate.
+                    catch (ThreadDeath e) {
+                        throw (e);
+                    }
+                    +/
+                    // For all other exceptions, log the problem.
+                    catch (Exception e) {
+                        Policy
+                                .getLog()
+                                .log(
+                                        new Status(
+                                                IStatus.ERROR,
+                                                Policy.JFACE,
+                                                "Unhandled event loop exception during blocked modal context.",//$NON-NLS-1$
+                                                e));
+                    }
+                }
+            } else {
+                try {
+                    join();
+                } catch (Exception e) {
+                    throwable = e;
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns whether the first progress monitor is the same as, or a wrapper
+     * around, the second progress monitor.
+     *
+     * @param monitor1
+     *            the first progress monitor
+     * @param monitor2
+     *            the second progress monitor
+     * @return <code>true</code> if the first is the same as, or a wrapper
+     *         around, the second
+     * @see ProgressMonitorWrapper
+     */
+    public static bool canProgressMonitorBeUsed(IProgressMonitor monitor1,
+            IProgressMonitor monitor2) {
+        if (monitor1 is monitor2) {
+            return true;
+        }
+
+        while ( cast(ProgressMonitorWrapper)monitor1 ) {
+            monitor1 = (cast(ProgressMonitorWrapper)monitor1)
+                    .getWrappedProgressMonitor();
+            if (monitor1 is monitor2) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks with the given progress monitor and throws
+     * <code>InterruptedException</code> if it has been canceled.
+     * <p>
+     * Code in a long-running operation should call this method regularly so
+     * that a request to cancel will be honored.
+     * </p>
+     * <p>
+     * Convenience for:
+     *
+     * <pre>
+     * if (monitor.isCanceled())
+     *  throw new InterruptedException();
+     * </pre>
+     *
+     * </p>
+     *
+     * @param monitor
+     *            the progress monitor
+     * @exception InterruptedException
+     *                if cancelling the operation has been requested
+     * @see IProgressMonitor#isCanceled()
+     */
+    public static void checkCanceled(IProgressMonitor monitor) {
+        if (monitor.isCanceled()) {
+            throw new InterruptedException();
+        }
+    }
+
+    /**
+     * Returns the currently active modal context thread, or null if no modal
+     * context is active.
+     */
+    private static ModalContextThread getCurrentModalContextThread() {
+        JThread t = JThread.currentThread();
+        if ( auto r = cast(ModalContextThread)t ) {
+            return r;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the modal nesting level.
+     * <p>
+     * The modal nesting level increases by one each time the
+     * <code>ModalContext.run</code> method is called within the dynamic scope
+     * of another call to <code>ModalContext.run</code>.
+     * </p>
+     *
+     * @return the modal nesting level, or <code>0</code> if this method is
+     *         called outside the dynamic scope of any invocation of
+     *         <code>ModalContext.run</code>
+     */
+    public static int getModalLevel() {
+        return modalLevel;
+    }
+
+    /**
+     * Returns whether the given thread is running a modal context.
+     *
+     * @param thread
+     *            The thread to be checked
+     * @return <code>true</code> if the given thread is running a modal
+     *         context, <code>false</code> if not
+     */
+    public static bool isModalContextThread(JThread thread) {
+        return (cast(ModalContextThread)thread) !is null;
+    }
+
+    /**
+     * Runs the given runnable in a modal context, passing it a progress
+     * monitor.
+     * <p>
+     * The modal nesting level is increased by one from the perspective of the
+     * given runnable.
+     * </p>
+     * <p>
+     * If the supplied operation implements <code>IThreadListener</code>, it
+     * will be notified of any thread changes required to execute the operation.
+     * Specifically, the operation will be notified of the thread that will call
+     * its <code>run</code> method before it is called, and will be notified
+     * of the change of control back to the thread calling this method when the
+     * operation completes. These thread change notifications give the operation
+     * an opportunity to transfer any thread-local state to the execution thread
+     * before control is transferred to the new thread.
+     * </p>
+     *
+     * @param operation
+     *            the runnable to run
+     * @param fork
+     *            <code>true</code> if the runnable should run in a separate
+     *            thread, and <code>false</code> if in the same thread
+     * @param monitor
+     *            the progress monitor to use to display progress and receive
+     *            requests for cancelation
+     * @param display
+     *            the display to be used to read and dispatch events
+     * @exception InvocationTargetException
+     *                if the run method must propagate a checked exception, it
+     *                should wrap it inside an
+     *                <code>InvocationTargetException</code>; runtime
+     *                exceptions and errors are automatically wrapped in an
+     *                <code>InvocationTargetException</code> by this method
+     * @exception InterruptedException
+     *                if the operation detects a request to cancel, using
+     *                <code>IProgressMonitor.isCanceled()</code>, it should
+     *                exit by throwing <code>InterruptedException</code>;
+     *                this method propagates the exception
+     */
+    public static void run(IRunnableWithProgress operation, bool fork,
+            IProgressMonitor monitor, Display display) {
+        Assert.isTrue(operation !is null && monitor !is null);
+
+        modalLevel++;
+        try {
+            if (monitor !is null) {
+                monitor.setCanceled(false);
+            }
+            // Is the runnable supposed to be execute in the same thread.
+            if (!fork || !runInSeparateThread) {
+                runInCurrentThread(operation, monitor);
+            } else {
+                ModalContextThread t = getCurrentModalContextThread();
+                if (t !is null) {
+                    Assert.isTrue(canProgressMonitorBeUsed(monitor,
+                            t.progressMonitor));
+                    runInCurrentThread(operation, monitor);
+                } else {
+                    t = new ModalContextThread(operation, monitor, display);
+                    Exception listenerException = null;
+                    if ( auto tl = cast(IThreadListener)operation ) {
+                        listenerException = invokeThreadListener(tl, t);
+                    }
+
+                    if(listenerException is null){
+                        t.start();
+                        t.block();
+                    }
+                    else {
+                        if(t.throwable is null)
+                            t.throwable = listenerException;
+                    }
+                    Exception throwable = t.throwable;
+                    if (throwable !is null) {
+                        if (debug_
+                                && !(cast(InterruptedException)throwable )
+                                && !(cast(OperationCanceledException)throwable )) {
+                            Stderr.formatln("Exception in modal context operation:"); //$NON-NLS-1$
+                            ExceptionPrintStackTrace(throwable);
+                            Stderr.formatln("Called from:"); //$NON-NLS-1$
+                            // Don't create the InvocationTargetException on the
+                            // throwable,
+                            // otherwise it will print its stack trace (from the
+                            // other thread).
+                            ExceptionPrintStackTrace( new InvocationTargetException(null));
+                        }
+                        if (cast(InvocationTargetException)throwable ) {
+                            throw cast(InvocationTargetException) throwable;
+                        } else if (cast(InterruptedException)throwable ) {
+                            throw cast(InterruptedException) throwable;
+                        } else if (cast(OperationCanceledException)throwable ) {
+                            // See 1GAN3L5: ITPUI:WIN2000 - ModalContext
+                            // converts OperationCancelException into
+                            // InvocationTargetException
+                            throw new InterruptedException(throwable
+                                    .msg);
+                        } else {
+                            throw new InvocationTargetException(throwable);
+                        }
+                    }
+                }
+            }
+        } finally {
+            modalLevel--;
+        }
+    }
+
+    /**
+     * Invoke the ThreadListener if there are any errors or RuntimeExceptions
+     * return them.
+     *
+     * @param listener
+     * @param switchingThread
+     *            the {@link Thread} being switched to
+     */
+    static Exception invokeThreadListener(IThreadListener listener,
+            JThread switchingThread) {
+        try {
+            listener.threadChange(switchingThread);
+//         } catch (ThreadDeath e) {
+            // Make sure to propagate ThreadDeath, or threads will never
+            // fully terminate
+//             throw e;
+//         } catch (Error e) {
+//             return e;
+        }catch (RuntimeException e) {
+            return e;
+        }
+        return null;
+    }
+
+    /**
+     * Run a runnable. Convert all thrown exceptions to either
+     * InterruptedException or InvocationTargetException
+     */
+    private static void runInCurrentThread(IRunnableWithProgress runnable,
+            IProgressMonitor progressMonitor) {
+        try {
+            if (runnable !is null) {
+                runnable.run(progressMonitor);
+            }
+        } catch (InvocationTargetException e) {
+            throw e;
+        } catch (InterruptedException e) {
+            throw e;
+        } catch (OperationCanceledException e) {
+            throw new InterruptedException();
+        /+
+        } catch (ThreadDeath e) {
+            // Make sure to propagate ThreadDeath, or threads will never fully
+            // terminate
+            throw e;
+        +/
+        } catch (RuntimeException e) {
+            throw new InvocationTargetException(e);
+        } catch (/+Error+/Exception e) {
+            throw new InvocationTargetException(e);
+        }
+    }
+
+    /**
+     * Sets whether ModalContext is running in debug mode.
+     *
+     * @param debugMode
+     *            <code>true</code> for debug mode, and <code>false</code>
+     *            for normal mode (the default)
+     */
+    public static void setDebugMode(bool debugMode) {
+        debug_ = debugMode;
+    }
+
+    /**
+     * Sets whether ModalContext may process events (by calling
+     * <code>Display.readAndDispatch()</code>) while running operations. By
+     * default, ModalContext will process events while running operations. Use
+     * this method to disallow event processing temporarily.
+     *
+     * @param allowReadAndDispatch
+     *            <code>true</code> (the default) if events may be processed
+     *            while running an operation, <code>false</code> if
+     *            Display.readAndDispatch() should not be called from
+     *            ModalContext.
+     * @since 3.2
+     */
+    public static void setAllowReadAndDispatch(bool allowReadAndDispatch) {
+        // use a separate thread if and only if it is OK to spin the event loop
+        runInSeparateThread = allowReadAndDispatch;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/BooleanFieldEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,262 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.BooleanFieldEditor;
+
+import org.eclipse.jface.preference.FieldEditor;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A field editor for a bool type preference.
+ */
+public class BooleanFieldEditor : FieldEditor {
+
+    /**
+     * Style constant (value <code>0</code>) indicating the default
+     * layout where the field editor's check box appears to the left
+     * of the label.
+     */
+    public static const int DEFAULT = 0;
+
+    /**
+     * Style constant (value <code>1</code>) indicating a layout
+     * where the field editor's label appears on the left
+     * with a check box on the right.
+     */
+    public static const int SEPARATE_LABEL = 1;
+
+    /**
+     * Style bits. Either <code>DEFAULT</code> or
+     * <code>SEPARATE_LABEL</code>.
+     */
+    private int style;
+
+    /**
+     * The previously selected, or "before", value.
+     */
+    private bool wasSelected;
+
+    /**
+     * The checkbox control, or <code>null</code> if none.
+     */
+    private Button checkBox = null;
+
+    /**
+     * Creates a new bool field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a bool field editor in the given style.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param style the style, either <code>DEFAULT</code> or
+     *   <code>SEPARATE_LABEL</code>
+     * @param parent the parent of the field editor's control
+     * @see #DEFAULT
+     * @see #SEPARATE_LABEL
+     */
+    public this(String name, String labelText, int style,
+            Composite parent) {
+        init(name, labelText);
+        this.style = style;
+        createControl(parent);
+    }
+
+    /**
+     * Creates a bool field editor in the default style.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param label the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String label, Composite parent) {
+        this(name, label, DEFAULT, parent);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void adjustForNumColumns(int numColumns) {
+        if (style is SEPARATE_LABEL) {
+            numColumns--;
+        }
+        (cast(GridData) checkBox.getLayoutData()).horizontalSpan = numColumns;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doFillIntoGrid(Composite parent, int numColumns) {
+        String text = getLabelText();
+        switch (style) {
+        case SEPARATE_LABEL:
+            getLabelControl(parent);
+            numColumns--;
+            text = null;
+        default:
+            checkBox = getChangeControl(parent);
+            GridData gd = new GridData();
+            gd.horizontalSpan = numColumns;
+            checkBox.setLayoutData(gd);
+            if (text !is null) {
+                checkBox.setText(text);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     * Loads the value from the preference store and sets it to
+     * the check box.
+     */
+    protected override void doLoad() {
+        if (checkBox !is null) {
+            bool value = getPreferenceStore()
+                    .getBoolean(getPreferenceName());
+            checkBox.setSelection(value);
+            wasSelected = value;
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     * Loads the default value from the preference store and sets it to
+     * the check box.
+     */
+    protected override void doLoadDefault() {
+        if (checkBox !is null) {
+            bool value = getPreferenceStore().getDefaultBoolean(
+                    getPreferenceName());
+            checkBox.setSelection(value);
+            wasSelected = value;
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doStore() {
+        getPreferenceStore().setValue(getPreferenceName(),
+                checkBox.getSelection());
+    }
+
+    /**
+     * Returns this field editor's current value.
+     *
+     * @return the value
+     */
+    public bool getBooleanValue() {
+        return checkBox.getSelection();
+    }
+
+    /**
+     * Returns the change button for this field editor.
+     * @param parent The Composite to create the receiver in.
+     *
+     * @return the change button
+     */
+    protected Button getChangeControl(Composite parent) {
+        if (checkBox is null) {
+            checkBox = new Button(parent, SWT.CHECK | SWT.LEFT);
+            checkBox.setFont(parent.getFont());
+            checkBox.addSelectionListener(new class SelectionAdapter {
+                public void widgetSelected(SelectionEvent e) {
+                    bool isSelected = checkBox.getSelection();
+                    valueChanged(wasSelected, isSelected);
+                    wasSelected = isSelected;
+                }
+            });
+            checkBox.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    checkBox = null;
+                }
+            });
+        } else {
+            checkParent(checkBox, parent);
+        }
+        return checkBox;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public override int getNumberOfControls() {
+        switch (style) {
+        case SEPARATE_LABEL:
+            return 2;
+        default:
+            return 1;
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public override void setFocus() {
+        if (checkBox !is null) {
+            checkBox.setFocus();
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public override void setLabelText(String text) {
+        super.setLabelText(text);
+        Label label = getLabelControl();
+        if (label is null && checkBox !is null) {
+            checkBox.setText(text);
+        }
+    }
+
+    /**
+     * Informs this field editor's listener, if it has one, about a change
+     * to the value (<code>VALUE</code> property) provided that the old and
+     * new values are different.
+     *
+     * @param oldValue the old value
+     * @param newValue the new value
+     */
+    protected void valueChanged(bool oldValue, bool newValue) {
+        setPresentsDefaultValue(false);
+        if (oldValue !is newValue) {
+            fireStateChanged(VALUE, oldValue, newValue);
+        }
+    }
+
+    /*
+     * @see FieldEditor.setEnabled
+     */
+    public override void setEnabled(bool enabled, Composite parent) {
+        //Only call super if there is a label already
+        if (style is SEPARATE_LABEL) {
+            super.setEnabled(enabled, parent);
+        }
+        getChangeControl(parent).setEnabled(enabled);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/BooleanPropertyAction.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.preference.BooleanPropertyAction;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The BooleanPropertyAction is an action that set the values of a
+ * bool property in the preference store.
+ */
+
+public class BooleanPropertyAction : Action {
+
+    private IPreferenceStore preferenceStore;
+
+    private String property;
+
+    /**
+     * Create a new instance of the receiver.
+     * @param title The displayable name of the action.
+     * @param preferenceStore The preference store to propogate changes to
+     * @param property The property that is being updated
+     * @throws IllegalArgumentException Thrown if preferenceStore or
+     * property are <code>null</code>.
+     */
+    public this(String title,
+            IPreferenceStore preferenceStore, String property) {
+        super(title, AS_CHECK_BOX);
+
+        if (preferenceStore is null || property is null) {
+            throw new IllegalArgumentException(null);
+        }
+
+        this.preferenceStore = preferenceStore;
+        this.property = property;
+        final String finalProprety = property;
+
+        preferenceStore
+                .addPropertyChangeListener(new class IPropertyChangeListener {
+                    public void propertyChange(PropertyChangeEvent event) {
+                        if (finalProprety.equals(event.getProperty())) {
+                            setChecked(cast(bool)(Boolean.TRUE == event.getNewValue() ));
+                        }
+                    }
+                });
+
+        setChecked(preferenceStore.getBoolean(property));
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see org.eclipse.jface.action.IAction#run()
+     */
+    public override void run() {
+        preferenceStore.setValue(property, isChecked());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/ColorFieldEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.preference.ColorFieldEditor;
+
+import org.eclipse.jface.preference.FieldEditor;
+import org.eclipse.jface.preference.ColorSelector;
+import org.eclipse.jface.preference.PreferenceConverter;
+
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A field editor for a color type preference.
+ */
+public class ColorFieldEditor : FieldEditor {
+
+    /**
+     * The color selector, or <code>null</code> if none.
+     */
+    private ColorSelector colorSelector;
+
+    /**
+     * Creates a new color field editor
+     */
+    protected this() {
+        //No default behavior
+    }
+
+    /**
+     * Creates a color field editor.
+     *
+     * @param name
+     *            the name of the preference this field editor works on
+     * @param labelText
+     *            the label text of the field editor
+     * @param parent
+     *            the parent of the field editor's control
+     */
+    public this(String name, String labelText, Composite parent) {
+        super(name, labelText, parent);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on FieldEditor.
+     */
+    protected override void adjustForNumColumns(int numColumns) {
+        (cast(GridData) colorSelector.getButton().getLayoutData()).horizontalSpan = numColumns - 1;
+    }
+
+    /**
+     * Computes the size of the color image displayed on the button.
+     * <p>
+     * This is an internal method and should not be called by clients.
+     * </p>
+     *
+     * @param window
+     *            the window to create a GC on for calculation.
+     * @return Point The image size
+     *
+     */
+    protected Point computeImageSize(Control window) {
+        // Make the image height as high as a corresponding character. This
+        // makes sure that the button has the same size as a "normal" text
+        // button.
+        GC gc = new GC(window);
+        Font f = JFaceResources.getFontRegistry().get(
+                JFaceResources.DEFAULT_FONT);
+        gc.setFont(f);
+        int height = gc.getFontMetrics().getHeight();
+        gc.dispose();
+        Point p = new Point(height * 3 - 6, height);
+        return p;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.preference.FieldEditor#doFillIntoGrid(org.eclipse.swt.widgets.Composite, int)
+     */
+    protected override void doFillIntoGrid(Composite parent, int numColumns) {
+        Control control = getLabelControl(parent);
+        GridData gd = new GridData();
+        gd.horizontalSpan = numColumns - 1;
+        control.setLayoutData(gd);
+
+        Button colorButton = getChangeControl(parent);
+        colorButton.setLayoutData(new GridData());
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.preference.FieldEditor#doLoad()
+     */
+    protected override void doLoad() {
+        if (colorSelector is null) {
+            return;
+        }
+        colorSelector.setColorValue(PreferenceConverter.getColor(
+                getPreferenceStore(), getPreferenceName()));
+    }
+
+    /*
+     * (non-Javadoc) Method declared on FieldEditor.
+     */
+    protected override void doLoadDefault() {
+        if (colorSelector is null) {
+            return;
+        }
+        colorSelector.setColorValue(PreferenceConverter.getDefaultColor(
+                getPreferenceStore(), getPreferenceName()));
+    }
+
+    /*
+     * (non-Javadoc) Method declared on FieldEditor.
+     */
+    protected override void doStore() {
+        PreferenceConverter.setValue(getPreferenceStore(), getPreferenceName(),
+                colorSelector.getColorValue());
+    }
+
+    /**
+     * Get the color selector used by the receiver.
+     *
+     * @return ColorSelector/
+     */
+    public ColorSelector getColorSelector() {
+        return colorSelector;
+    }
+
+    /**
+     * Returns the change button for this field editor.
+     *
+     * @param parent
+     *            The control to create the button in if required.
+     * @return the change button
+     */
+    protected Button getChangeControl(Composite parent) {
+        if (colorSelector is null) {
+            colorSelector = new ColorSelector(parent);
+            colorSelector.addListener(new class IPropertyChangeListener {
+                // forward the property change of the color selector
+                public void propertyChange(PropertyChangeEvent event) {
+                    this.outer.fireValueChanged(event.getProperty(),
+                            event.getOldValue(), event.getNewValue());
+                    setPresentsDefaultValue(false);
+                }
+            });
+
+        } else {
+            checkParent(colorSelector.getButton(), parent);
+        }
+        return colorSelector.getButton();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on FieldEditor.
+     */
+    public override int getNumberOfControls() {
+        return 2;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.preference.FieldEditor#setEnabled(bool,
+     *      org.eclipse.swt.widgets.Composite)
+     */
+    public override void setEnabled(bool enabled, Composite parent) {
+        super.setEnabled(enabled, parent);
+        getChangeControl(parent).setEnabled(enabled);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/ColorSelector.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.preference.ColorSelector;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.accessibility.AccessibleAdapter;
+import org.eclipse.swt.accessibility.AccessibleEvent;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.ColorDialog;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.commands.common.EventManager;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The <code>ColorSelector</code> is a wrapper for a button that displays a
+ * selected <code>Color</code> and allows the user to change the selection.
+ */
+public class ColorSelector : EventManager {
+    /**
+     * Property name that signifies the selected color of this
+     * <code>ColorSelector</code> has changed.
+     *
+     * @since 3.0
+     */
+    public static const String PROP_COLORCHANGE = "colorValue"; //$NON-NLS-1$
+
+    private Button fButton;
+
+    private Color fColor;
+
+    private RGB fColorValue;
+
+    private Point fExtent;
+
+    private Image fImage;
+
+    /**
+     * Create a new instance of the reciever and the button that it wrappers in
+     * the supplied parent <code>Composite</code>.
+     *
+     * @param parent
+     *            The parent of the button.
+     */
+    public this(Composite parent) {
+        fButton = new Button(parent, SWT.PUSH);
+        fExtent = computeImageSize(parent);
+        fImage = new Image(parent.getDisplay(), fExtent.x, fExtent.y);
+        GC gc = new GC(fImage);
+        gc.setBackground(fButton.getBackground());
+        gc.fillRectangle(0, 0, fExtent.x, fExtent.y);
+        gc.dispose();
+        fButton.setImage(fImage);
+        fButton.addSelectionListener(new class SelectionAdapter {
+            public void widgetSelected(SelectionEvent event) {
+                open();
+            }
+        });
+        fButton.addDisposeListener(new class DisposeListener {
+            public void widgetDisposed(DisposeEvent event) {
+                if (fImage !is null) {
+                    fImage.dispose();
+                    fImage = null;
+                }
+                if (fColor !is null) {
+                    fColor.dispose();
+                    fColor = null;
+                }
+            }
+        });
+        fButton.getAccessible().addAccessibleListener(new class AccessibleAdapter {
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.swt.accessibility.AccessibleAdapter#getName(org.eclipse.swt.accessibility.AccessibleEvent)
+             */
+            public void getName(AccessibleEvent e) {
+                e.result = JFaceResources.getString("ColorSelector.Name"); //$NON-NLS-1$
+            }
+        });
+    }
+
+    /**
+     * Adds a property change listener to this <code>ColorSelector</code>.
+     * Events are fired when the color in the control changes via the user
+     * clicking an selecting a new one in the color dialog. No event is fired in
+     * the case where <code>setColorValue(RGB)</code> is invoked.
+     *
+     * @param listener
+     *            a property change listener
+     * @since 3.0
+     */
+    public void addListener(IPropertyChangeListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Compute the size of the image to be displayed.
+     *
+     * @param window -
+     *            the window used to calculate
+     * @return <code>Point</code>
+     */
+    private Point computeImageSize(Control window) {
+        GC gc = new GC(window);
+        Font f = JFaceResources.getFontRegistry().get(
+                JFaceResources.DIALOG_FONT);
+        gc.setFont(f);
+        int height = gc.getFontMetrics().getHeight();
+        gc.dispose();
+        Point p = new Point(height * 3 - 6, height);
+        return p;
+    }
+
+    /**
+     * Get the button control being wrappered by the selector.
+     *
+     * @return <code>Button</code>
+     */
+    public Button getButton() {
+        return fButton;
+    }
+
+    /**
+     * Return the currently displayed color.
+     *
+     * @return <code>RGB</code>
+     */
+    public RGB getColorValue() {
+        return fColorValue;
+    }
+
+    /**
+     * Removes the given listener from this <code>ColorSelector</code>. Has
+     * no affect if the listener is not registered.
+     *
+     * @param listener
+     *            a property change listener
+     * @since 3.0
+     */
+    public void removeListener(IPropertyChangeListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Set the current color value and update the control.
+     *
+     * @param rgb
+     *            The new color.
+     */
+    public void setColorValue(RGB rgb) {
+        fColorValue = rgb;
+        updateColorImage();
+    }
+
+    /**
+     * Set whether or not the button is enabled.
+     *
+     * @param state
+     *            the enabled state.
+     */
+    public void setEnabled(bool state) {
+        getButton().setEnabled(state);
+    }
+
+    /**
+     * Update the image being displayed on the button using the current color
+     * setting.
+     */
+    protected void updateColorImage() {
+        Display display = fButton.getDisplay();
+        GC gc = new GC(fImage);
+        gc.setForeground(display.getSystemColor(SWT.COLOR_BLACK));
+        gc.drawRectangle(0, 2, fExtent.x - 1, fExtent.y - 4);
+        if (fColor !is null) {
+            fColor.dispose();
+        }
+        fColor = new Color(display, fColorValue);
+        gc.setBackground(fColor);
+        gc.fillRectangle(1, 3, fExtent.x - 2, fExtent.y - 5);
+        gc.dispose();
+        fButton.setImage(fImage);
+    }
+
+    /**
+     * Activate the editor for this selector. This causes the color selection
+     * dialog to appear and wait for user input.
+     *
+     * @since 3.2
+     */
+    public void open() {
+        ColorDialog colorDialog = new ColorDialog(fButton.getShell());
+        colorDialog.setRGB(fColorValue);
+        RGB newColor = colorDialog.open();
+        if (newColor !is null) {
+            RGB oldValue = fColorValue;
+            fColorValue = newColor;
+            final Object[] finalListeners = getListeners();
+            if (finalListeners.length > 0) {
+                PropertyChangeEvent pEvent = new PropertyChangeEvent(
+                        this, PROP_COLORCHANGE, oldValue, newColor);
+                for (int i = 0; i < finalListeners.length; ++i) {
+                    IPropertyChangeListener listener = cast(IPropertyChangeListener) finalListeners[i];
+                    listener.propertyChange(pEvent);
+                }
+            }
+            updateColorImage();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/ComboFieldEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,226 @@
+/*******************************************************************************
+ * 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
+ *     Remy Chi Jian Suen <remy.suen@gmail.com> - Bug 214392 missing implementation of ComboFieldEditor.setEnabled
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.ComboFieldEditor;
+
+import org.eclipse.jface.preference.FieldEditor;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A field editor for a combo box that allows the drop-down selection of one of
+ * a list of items.
+ *
+ * @since 3.3
+ */
+public class ComboFieldEditor : FieldEditor {
+
+    /**
+     * The <code>Combo</code> widget.
+     */
+    private Combo fCombo;
+
+    /**
+     * The value (not the name) of the currently selected item in the Combo widget.
+     */
+    private String fValue;
+
+    /**
+     * The names (labels) and underlying values to populate the combo widget.  These should be
+     * arranged as: { {name1, value1}, {name2, value2}, ...}
+     */
+    private String[][] fEntryNamesAndValues;
+
+    /**
+     * Create the combo box field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param entryNamesAndValues the names (labels) and underlying values to populate the combo widget.  These should be
+     * arranged as: { {name1, value1}, {name2, value2}, ...}
+     * @param parent the parent composite
+     */
+    public this(String name, String labelText, String[][] entryNamesAndValues, Composite parent) {
+        init(name, labelText);
+        Assert.isTrue(checkArray(entryNamesAndValues));
+        fEntryNamesAndValues = entryNamesAndValues;
+        createControl(parent);
+    }
+
+    /**
+     * Checks whether given <code>String[][]</code> is of "type"
+     * <code>String[][2]</code>.
+     *
+     * @return <code>true</code> if it is ok, and <code>false</code> otherwise
+     */
+    private bool checkArray(String[][] table) {
+        if (table is null) {
+            return false;
+        }
+        for (int i = 0; i < table.length; i++) {
+            String[] array = table[i];
+            if (array is null || array.length !is 2) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.preference.FieldEditor#adjustForNumColumns(int)
+     */
+    protected override void adjustForNumColumns(int numColumns) {
+        if (numColumns > 1) {
+            Control control = getLabelControl();
+            int left = numColumns;
+            if (control !is null) {
+                (cast(GridData)control.getLayoutData()).horizontalSpan = 1;
+                left = left - 1;
+            }
+            (cast(GridData)fCombo.getLayoutData()).horizontalSpan = left;
+        } else {
+            Control control = getLabelControl();
+            if (control !is null) {
+                (cast(GridData)control.getLayoutData()).horizontalSpan = 1;
+            }
+            (cast(GridData)fCombo.getLayoutData()).horizontalSpan = 1;
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.preference.FieldEditor#doFillIntoGrid(org.eclipse.swt.widgets.Composite, int)
+     */
+    protected override void doFillIntoGrid(Composite parent, int numColumns) {
+        int comboC = 1;
+        if (numColumns > 1) {
+            comboC = numColumns - 1;
+        }
+        Control control = getLabelControl(parent);
+        GridData gd = new GridData();
+        gd.horizontalSpan = 1;
+        control.setLayoutData(gd);
+        control = getComboBoxControl(parent);
+        gd = new GridData();
+        gd.horizontalSpan = comboC;
+        gd.horizontalAlignment = GridData.FILL;
+        control.setLayoutData(gd);
+        control.setFont(parent.getFont());
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.preference.FieldEditor#doLoad()
+     */
+    protected override void doLoad() {
+        updateComboForValue(getPreferenceStore().getString(getPreferenceName()));
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.preference.FieldEditor#doLoadDefault()
+     */
+    protected override void doLoadDefault() {
+        updateComboForValue(getPreferenceStore().getDefaultString(getPreferenceName()));
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.preference.FieldEditor#doStore()
+     */
+    protected override void doStore() {
+        if (fValue is null) {
+            getPreferenceStore().setToDefault(getPreferenceName());
+            return;
+        }
+        getPreferenceStore().setValue(getPreferenceName(), fValue);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.preference.FieldEditor#getNumberOfControls()
+     */
+    public override int getNumberOfControls() {
+        return 2;
+    }
+
+    /*
+     * Lazily create and return the Combo control.
+     */
+    private Combo getComboBoxControl(Composite parent) {
+        if (fCombo is null) {
+            fCombo = new Combo(parent, SWT.READ_ONLY);
+            fCombo.setFont(parent.getFont());
+            for (int i = 0; i < fEntryNamesAndValues.length; i++) {
+                fCombo.add(fEntryNamesAndValues[i][0], i);
+            }
+
+            fCombo.addSelectionListener(new class SelectionAdapter {
+                public void widgetSelected(SelectionEvent evt) {
+                    String oldValue = fValue;
+                    String name = fCombo.getText();
+                    fValue = getValueForName(name);
+                    setPresentsDefaultValue(false);
+                    fireValueChanged(VALUE, stringcast(oldValue), stringcast(fValue));
+                }
+            });
+        }
+        return fCombo;
+    }
+
+    /*
+     * Given the name (label) of an entry, return the corresponding value.
+     */
+    private String getValueForName(String name) {
+        for (int i = 0; i < fEntryNamesAndValues.length; i++) {
+            String[] entry = fEntryNamesAndValues[i];
+            if (name.equals(entry[0])) {
+                return entry[1];
+            }
+        }
+        return fEntryNamesAndValues[0][0];
+    }
+
+    /*
+     * Set the name in the combo widget to match the specified value.
+     */
+    private void updateComboForValue(String value) {
+        fValue = value;
+        for (int i = 0; i < fEntryNamesAndValues.length; i++) {
+            if (value.equals(fEntryNamesAndValues[i][1])) {
+                fCombo.setText(fEntryNamesAndValues[i][0]);
+                return;
+            }
+        }
+        if (fEntryNamesAndValues.length > 0) {
+            fValue = fEntryNamesAndValues[0][1];
+            fCombo.setText(fEntryNamesAndValues[0][0]);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.jface.preference.FieldEditor#setEnabled(bool,
+     *      org.eclipse.swt.widgets.Composite)
+     */
+    public void setEnabled(bool enabled, Composite parent) {
+        super.setEnabled(enabled, parent);
+        getComboBoxControl(parent).setEnabled(enabled);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/DirectoryFieldEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.DirectoryFieldEditor;
+
+import org.eclipse.jface.preference.StringButtonFieldEditor;
+// import java.io.File;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+import tango.io.FilePath;
+static import tango.io.Path;
+import tango.io.FileSystem;
+
+/**
+ * A field editor for a directory path type preference. A standard directory
+ * dialog appears when the user presses the change button.
+ */
+public class DirectoryFieldEditor : StringButtonFieldEditor {
+    /**
+     * Creates a new directory field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a directory field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText, Composite parent) {
+        init(name, labelText);
+        setErrorMessage(JFaceResources
+                .getString("DirectoryFieldEditor.errorMessage"));//$NON-NLS-1$
+        setChangeButtonText(JFaceResources.getString("openBrowse"));//$NON-NLS-1$
+        setValidateStrategy(VALIDATE_ON_FOCUS_LOST);
+        createControl(parent);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StringButtonFieldEditor.
+     * Opens the directory chooser dialog and returns the selected directory.
+     */
+    protected override String changePressed() {
+        auto f = new FilePath(tango.io.Path.standard(getTextControl().getText()));
+        if (!f.exists()) {
+            f = cast(FilePath)null;
+        }
+        auto d = getDirectory(f);
+        if (d is null) {
+            return null;
+        }
+
+        return FileSystem.toAbsolute( d ).native.toString;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StringFieldEditor.
+     * Checks whether the text input field contains a valid directory.
+     */
+    protected override bool doCheckState() {
+        String fileName = getTextControl().getText();
+        fileName = fileName.trim();
+        if (fileName.length is 0 && isEmptyStringAllowed()) {
+            return true;
+        }
+        auto file = new FilePath(tango.io.Path.standard(fileName));
+        return file.exists() && file.isFolder();
+    }
+
+    /**
+     * Helper that opens the directory chooser dialog.
+     * @param startingDirectory The directory the dialog will open in.
+     * @return File File or <code>null</code>.
+     *
+     */
+    private FilePath getDirectory(FilePath startingDirectory) {
+
+        DirectoryDialog fileDialog = new DirectoryDialog(getShell(), SWT.OPEN);
+        if (startingDirectory !is null) {
+            fileDialog.setFilterPath(startingDirectory.path);
+        }
+        String dir = fileDialog.open();
+        if (dir !is null) {
+            dir = dir.trim();
+            if (dir.length > 0) {
+                return new FilePath(tango.io.Path.standard(dir));
+            }
+        }
+
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/FieldEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,732 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.FieldEditor;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.dialogs.DialogPage;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Abstract base class for all field editors.
+ * <p>
+ * A field editor presents the value of a preference to the end
+ * user. The value is loaded from a preference store; if
+ * modified by the end user, the value is validated and eventually
+ * stored back to the preference store. A field editor reports
+ * an event when the value, or the validity of the value, changes.
+ * </p>
+ * <p>
+ * Field editors should be used in conjunction with a field
+ * editor preference page (<code>FieldEditorPreferencePage</code>)
+ * which coordinates everything and provides the message line
+ * which display messages emanating from the editor.
+ * </p>
+ * <p>
+ * This package contains ready-to-use field editors for various
+ * types of preferences:
+ * <ul>
+ *   <li><code>BooleanFieldEditor</code> - booleans</li>
+ *   <li><code>IntegerFieldEditor</code> - integers</li>
+ *   <li><code>StringFieldEditor</code> - text strings</li>
+ *   <li><code>RadioGroupFieldEditor</code> - enumerations</li>
+ *   <li><code>ColorFieldEditor</code> - RGB colors</li>
+ *   <li><code>FontFieldEditor</code> - fonts</li>
+ *   <li><code>DirectoryFieldEditor</code> - directories</li>
+ *   <li><code>FileFieldEditor</code> - files</li>
+ *   <li><code>PathEditor</code> - paths</li>
+ * </ul>
+ * </p>
+ */
+public abstract class FieldEditor {
+
+    /**
+     * Property name constant (value <code>"field_editor_is_valid"</code>)
+     * to signal a change in the validity of the value of this field editor.
+     */
+    public static const String IS_VALID = "field_editor_is_valid";//$NON-NLS-1$
+
+    /**
+     * Property name constant (value <code>"field_editor_value"</code>)
+     * to signal a change in the value of this field editor.
+     */
+    public static const String VALUE = "field_editor_value";//$NON-NLS-1$
+
+    /**
+     * Gap between label and control.
+     */
+    protected static const int HORIZONTAL_GAP = 8;
+
+    /**
+     * The preference store, or <code>null</code> if none.
+     */
+    private IPreferenceStore preferenceStore = null;
+
+    /**
+     * The name of the preference displayed in this field editor.
+     */
+    private String preferenceName;
+
+    /**
+     * Indicates whether the default value is currently displayed,
+     * initially <code>false</code>.
+     */
+    private bool isDefaultPresented = false;
+
+    /**
+     * The label's text.
+     */
+    private String labelText;
+
+    /**
+     * The label control.
+     */
+    private Label label;
+
+    /**
+     * Listener, or <code>null</code> if none
+     */
+    private IPropertyChangeListener propertyChangeListener;
+
+    /**
+     * The page containing this field editor
+     */
+    private DialogPage page;
+
+    /**
+     * Creates a new field editor.
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a new field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    protected this(String name, String labelText, Composite parent) {
+        init(name, labelText);
+        createControl(parent);
+    }
+
+    /**
+     * Adjusts the horizontal span of this field editor's basic controls.
+     * <p>
+     * Subclasses must implement this method to adjust the horizontal span
+     * of controls so they appear correct in the given number of columns.
+     * </p>
+     * <p>
+     * The number of columns will always be equal to or greater than the
+     * value returned by this editor's <code>getNumberOfControls</code> method.
+     *
+     * @param numColumns the number of columns
+     */
+    protected abstract void adjustForNumColumns(int numColumns);
+    package void adjustForNumColumns_package(int numColumns){
+        adjustForNumColumns(numColumns);
+    }
+
+    /**
+     * Applies a font.
+     * <p>
+     * The default implementation of this framework method
+     * does nothing. Subclasses should override this method
+     * if they want to change the font of the SWT control to
+     * a value different than the standard dialog font.
+     * </p>
+     */
+    protected void applyFont() {
+    }
+    package void applyFont_package() {
+        applyFont();
+    }
+
+    /**
+     * Checks if the given parent is the current parent of the
+     * supplied control; throws an (unchecked) exception if they
+     * are not correctly related.
+     *
+     * @param control the control
+     * @param parent the parent control
+     */
+    protected void checkParent(Control control, Composite parent) {
+        Assert.isTrue(control.getParent() is parent, "Different parents");//$NON-NLS-1$
+    }
+
+    /**
+     * Clears the error message from the message line.
+     */
+    protected void clearErrorMessage() {
+        if (page !is null) {
+            page.setErrorMessage(null);
+        }
+    }
+
+    /**
+     * Clears the normal message from the message line.
+     */
+    protected void clearMessage() {
+        if (page !is null) {
+            page.setMessage(null);
+        }
+    }
+
+    /**
+     * Returns the number of pixels corresponding to the
+     * given number of horizontal dialog units.
+     * <p>
+     * Clients may call this framework method, but should not override it.
+     * </p>
+     *
+     * @param control the control being sized
+     * @param dlus the number of horizontal dialog units
+     * @return the number of pixels
+     */
+    protected int convertHorizontalDLUsToPixels(Control control, int dlus) {
+        GC gc = new GC(control);
+        gc.setFont(control.getFont());
+        int averageWidth = gc.getFontMetrics().getAverageCharWidth();
+        gc.dispose();
+
+        double horizontalDialogUnitSize = averageWidth * 0.25;
+
+        return cast(int) Math.round(dlus * horizontalDialogUnitSize);
+    }
+
+    /**
+     * Returns the number of pixels corresponding to the
+     * given number of vertical dialog units.
+     * <p>
+     * Clients may call this framework method, but should not override it.
+     * </p>
+     *
+     * @param control the control being sized
+     * @param dlus the number of vertical dialog units
+     * @return the number of pixels
+     */
+    protected int convertVerticalDLUsToPixels(Control control, int dlus) {
+        GC gc = new GC(control);
+        gc.setFont(control.getFont());
+        int height = gc.getFontMetrics().getHeight();
+        gc.dispose();
+
+        double verticalDialogUnitSize = height * 0.125;
+
+        return cast(int) Math.round(dlus * verticalDialogUnitSize);
+    }
+
+    /**
+     * Creates this field editor's main control containing all of its
+     * basic controls.
+     *
+     * @param parent the parent control
+     */
+    protected void createControl(Composite parent) {
+        GridLayout layout = new GridLayout();
+        layout.numColumns = getNumberOfControls();
+        layout.marginWidth = 0;
+        layout.marginHeight = 0;
+        layout.horizontalSpacing = HORIZONTAL_GAP;
+        parent.setLayout(layout);
+        doFillIntoGrid(parent, layout.numColumns);
+    }
+
+    /**
+     * Disposes the SWT resources used by this field editor.
+     */
+    public void dispose() {
+        // nothing to dispose
+    }
+
+    /**
+     * Fills this field editor's basic controls into the given parent.
+     * <p>
+     * Subclasses must implement this method to create the controls
+     * for this field editor.
+     * </p>
+     *
+     * @param parent the composite used as a parent for the basic controls;
+     *  the parent's layout must be a <code>GridLayout</code>
+     * @param numColumns the number of columns
+     */
+    protected abstract void doFillIntoGrid(Composite parent, int numColumns);
+
+    /**
+     * Initializes this field editor with the preference value from
+     * the preference store.
+     * <p>
+     * Subclasses must implement this method to properly initialize
+     * the field editor.
+     * </p>
+     */
+    protected abstract void doLoad();
+
+    /**
+     * Initializes this field editor with the default preference value from
+     * the preference store.
+     * <p>
+     * Subclasses must implement this method to properly initialize
+     * the field editor.
+     * </p>
+     */
+    protected abstract void doLoadDefault();
+
+    /**
+     * Stores the preference value from this field editor into
+     * the preference store.
+     * <p>
+     * Subclasses must implement this method to save the entered value
+     * into the preference store.
+     * </p>
+     */
+    protected abstract void doStore();
+
+    /**
+     * Fills this field editor's basic controls into the given parent.
+     *
+     * @param parent the composite used as a parent for the basic controls;
+     *  the parent's layout must be a <code>GridLayout</code>
+     * @param numColumns the number of columns
+     */
+    public void fillIntoGrid(Composite parent, int numColumns) {
+        Assert.isTrue(numColumns >= getNumberOfControls());
+        Assert.isTrue(null !is cast(GridLayout)parent.getLayout() );
+        doFillIntoGrid(parent, numColumns);
+    }
+
+    /**
+     * Informs this field editor's listener, if it has one, about a change to
+     * one of this field editor's bool-valued properties. Does nothing
+     * if the old and new values are the same.
+     *
+     * @param property the field editor property name,
+     *   such as <code>VALUE</code> or <code>IS_VALID</code>
+     * @param oldValue the old value
+     * @param newValue the new value
+     */
+    protected void fireStateChanged(String property, bool oldValue,
+            bool newValue) {
+        if (oldValue is newValue) {
+            return;
+        }
+        fireValueChanged(property, oldValue ? Boolean.TRUE : Boolean.FALSE, newValue ? Boolean.TRUE : Boolean.FALSE);
+    }
+
+    /**
+     * Informs this field editor's listener, if it has one, about a change to
+     * one of this field editor's properties.
+     *
+     * @param property the field editor property name,
+     *   such as <code>VALUE</code> or <code>IS_VALID</code>
+     * @param oldValue the old value object, or <code>null</code>
+     * @param newValue the new value, or <code>null</code>
+     */
+    protected void fireValueChanged(String property, Object oldValue,
+            Object newValue) {
+        if (propertyChangeListener is null) {
+            return;
+        }
+        propertyChangeListener.propertyChange(new PropertyChangeEvent(this,
+                property, oldValue, newValue));
+    }
+
+    /**
+     * Returns the symbolic font name used by this field editor.
+     *
+     * @return the symbolic font name
+     */
+    public String getFieldEditorFontName() {
+        return JFaceResources.DIALOG_FONT;
+    }
+
+    /**
+     * Returns the label control.
+     *
+     * @return the label control, or <code>null</code>
+     *  if no label control has been created
+     */
+    protected Label getLabelControl() {
+        return label;
+    }
+
+    /**
+     * Returns this field editor's label component.
+     * <p>
+     * The label is created if it does not already exist
+     * </p>
+     *
+     * @param parent the parent
+     * @return the label control
+     */
+    public Label getLabelControl(Composite parent) {
+        if (label is null) {
+            label = new Label(parent, SWT.LEFT);
+            label.setFont(parent.getFont());
+            String text = getLabelText();
+            if (text !is null) {
+                label.setText(text);
+            }
+            label.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    label = null;
+                }
+            });
+        } else {
+            checkParent(label, parent);
+        }
+        return label;
+    }
+
+    /**
+     * Returns this field editor's label text.
+     *
+     * @return the label text
+     */
+    public String getLabelText() {
+        return labelText;
+    }
+
+    /**
+     * Returns the number of basic controls this field editor consists of.
+     *
+     * @return the number of controls
+     */
+    public abstract int getNumberOfControls();
+
+    /**
+     * Returns the name of the preference this field editor operates on.
+     *
+     * @return the name of the preference
+     */
+    public String getPreferenceName() {
+        return preferenceName;
+    }
+
+    /**
+     * Returns the preference page in which this field editor
+     * appears.
+     *
+     * @return the preference page, or <code>null</code> if none
+     * @deprecated use #getPage()
+     */
+    protected PreferencePage getPreferencePage() {
+        if(page !is null && cast(PreferencePage)page ) {
+            return cast(PreferencePage) page;
+        }
+        return null;
+    }
+
+    /**
+     * Return the DialogPage that the receiver is sending
+     * updates to.
+     *
+     * @return DialogPage or <code>null</code> if it
+     * has not been set.
+     *
+     * @since 3.1
+     */
+    protected DialogPage getPage(){
+        return page;
+    }
+
+    /**
+     * Returns the preference store used by this field editor.
+     *
+     * @return the preference store, or <code>null</code> if none
+     * @see #setPreferenceStore
+     */
+    public IPreferenceStore getPreferenceStore() {
+        return preferenceStore;
+    }
+
+    /**
+     * Initialize the field editor with the given preference name and label.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param text the label text of the field editor
+     */
+    protected void init(String name, String text) {
+        Assert.isNotNull(name);
+        Assert.isNotNull(text);
+        preferenceName = name;
+        this.labelText = text;
+    }
+
+    /**
+     * Returns whether this field editor contains a valid value.
+     * <p>
+     * The default implementation of this framework method
+     * returns <code>true</code>. Subclasses wishing to perform
+     * validation should override both this method and
+     * <code>refreshValidState</code>.
+     * </p>
+     *
+     * @return <code>true</code> if the field value is valid,
+     *   and <code>false</code> if invalid
+     * @see #refreshValidState()
+     */
+    public bool isValid() {
+        return true;
+    }
+
+    /**
+     * Initializes this field editor with the preference value from
+     * the preference store.
+     */
+    public void load() {
+        if (preferenceStore !is null) {
+            isDefaultPresented = false;
+            doLoad();
+            refreshValidState();
+        }
+    }
+
+    /**
+     * Initializes this field editor with the default preference value
+     * from the preference store.
+     */
+    public void loadDefault() {
+        if (preferenceStore !is null) {
+            isDefaultPresented = true;
+            doLoadDefault();
+            refreshValidState();
+        }
+    }
+
+    /**
+     * Returns whether this field editor currently presents the
+     * default value for its preference.
+     *
+     * @return <code>true</code> if the default value is presented,
+     *   and <code>false</code> otherwise
+     */
+    public bool presentsDefaultValue() {
+        return isDefaultPresented;
+    }
+
+    /**
+     * Refreshes this field editor's valid state after a value change
+     * and fires an <code>IS_VALID</code> property change event if
+     * warranted.
+     * <p>
+     * The default implementation of this framework method does
+     * nothing. Subclasses wishing to perform validation should override
+     * both this method and <code>isValid</code>.
+     * </p>
+     *
+     * @see #isValid
+     */
+    protected void refreshValidState() {
+    }
+
+    /**
+     * Sets the focus to this field editor.
+     * <p>
+     * The default implementation of this framework method
+     * does nothing. Subclasses may reimplement.
+     * </p>
+     */
+    public void setFocus() {
+        // do nothing;
+    }
+
+    /**
+     * Sets this field editor's label text.
+     * The label is typically presented to the left of the entry field.
+     *
+     * @param text the label text
+     */
+    public void setLabelText(String text) {
+        Assert.isNotNull(text);
+        labelText = text;
+        if (label !is null) {
+            label.setText(text);
+        }
+    }
+
+    /**
+     * Sets the name of the preference this field editor operates on.
+     * <p>
+     * The ability to change this allows the same field editor object
+     * to be reused for different preferences.
+     * </p>
+     * <p>
+     * For example: <p>
+     * <pre>
+     *  ...
+     *  editor.setPreferenceName("font");
+     *  editor.load();
+     * </pre>
+     * </p>
+     *
+     * @param name the name of the preference
+     */
+    public void setPreferenceName(String name) {
+        preferenceName = name;
+    }
+
+    /**
+     * Sets the preference page in which this field editor
+     * appears.
+     *
+     * @param preferencePage the preference page, or <code>null</code> if none
+     * @deprecated use #setPage(DialogPage)
+     */
+    public void setPreferencePage(PreferencePage preferencePage) {
+        setPage(preferencePage);
+    }
+
+
+    /**
+     * Set the page to be the receiver.
+     * @param dialogPage
+     *
+     * @since 3.1
+     */
+    public void setPage(DialogPage dialogPage) {
+        page = dialogPage;
+
+    }
+
+    /**
+     * Sets the preference store used by this field editor.
+     *
+     * @param store the preference store, or <code>null</code> if none
+     * @see #getPreferenceStore
+     */
+    public void setPreferenceStore(IPreferenceStore store) {
+        preferenceStore = store;
+    }
+
+    /**
+     * Sets whether this field editor is presenting the default value.
+     *
+     * @param booleanValue <code>true</code> if the default value is being presented,
+     *  and <code>false</code> otherwise
+     */
+    protected void setPresentsDefaultValue(bool booleanValue) {
+        isDefaultPresented = booleanValue;
+    }
+    package void setPresentsDefaultValue_package(bool booleanValue) {
+        setPresentsDefaultValue(booleanValue);
+    }
+
+    /**
+     * Sets or removes the property change listener for this field editor.
+     * <p>
+     * Note that field editors can support only a single listener.
+     * </p>
+     *
+     * @param listener a property change listener, or <code>null</code>
+     *  to remove
+     */
+    public void setPropertyChangeListener(IPropertyChangeListener listener) {
+        propertyChangeListener = listener;
+    }
+
+    /**
+     * Shows the given error message in the page for this
+     * field editor if it has one.
+     *
+     * @param msg the error message
+     */
+    protected void showErrorMessage(String msg) {
+        if (page !is null) {
+            page.setErrorMessage(msg);
+        }
+    }
+
+    /**
+     * Shows the given message in the page for this
+     * field editor if it has one.
+     *
+     * @param msg the message
+     */
+    protected void showMessage(String msg) {
+        if (page !is null) {
+            page.setErrorMessage(msg);
+        }
+    }
+
+    /**
+     * Stores this field editor's value back into the preference store.
+     */
+    public void store() {
+        if (preferenceStore is null) {
+            return;
+        }
+
+        if (isDefaultPresented) {
+            preferenceStore.setToDefault(preferenceName);
+        } else {
+            doStore();
+        }
+    }
+
+    /**
+     * Set the GridData on button to be one that is spaced for the
+     * current font.
+     * @param button the button the data is being set on.
+     */
+
+    protected void setButtonLayoutData(Button button) {
+
+        GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+
+        // Compute and store a font metric
+        GC gc = new GC(button);
+        gc.setFont(button.getFont());
+        FontMetrics fontMetrics = gc.getFontMetrics();
+        gc.dispose();
+
+        int widthHint = org.eclipse.jface.dialogs.Dialog.Dialog
+                .convertVerticalDLUsToPixels(fontMetrics,
+                        IDialogConstants.BUTTON_WIDTH);
+        data.widthHint = Math.max(widthHint, button.computeSize(SWT.DEFAULT,
+                SWT.DEFAULT, true).x);
+        button.setLayoutData(data);
+    }
+
+    /**
+     * Set whether or not the controls in the field editor
+     * are enabled.
+     * @param enabled The enabled state.
+     * @param parent The parent of the controls in the group.
+     *  Used to create the controls if required.
+     */
+    public void setEnabled(bool enabled, Composite parent) {
+        getLabelControl(parent).setEnabled(enabled);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/FieldEditorPreferencePage.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,390 @@
+/*******************************************************************************
+ * 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
+ *     Chris Tilt (chris@tilts.net) - Bug 38547 - [Preferences] Changing preferences
+ *          ignored after "Restore defaults" pressed.
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.FieldEditorPreferencePage;
+
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.jface.preference.FieldEditor;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+
+// import java.util.Iterator;
+// import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A special abstract preference page to host field editors.
+ * <p>
+ * Subclasses must implement the <code>createFieldEditors</code> method
+ * and should override <code>createLayout</code> if a special layout of the field
+ * editors is needed.
+ * </p>
+ */
+public abstract class FieldEditorPreferencePage : PreferencePage,
+        IPropertyChangeListener {
+
+    /**
+     * Layout constant (value <code>0</code>) indicating that
+     * each field editor is handled as a single component.
+     */
+    public static const int FLAT = 0;
+
+    /**
+     * Layout constant (value <code>1</code>) indicating that
+     * the field editors' basic controls are put into a grid layout.
+     */
+    public static const int GRID = 1;
+
+    /**
+     * The vertical spacing used by layout styles <code>FLAT</code>
+     * and <code>GRID</code>.
+     */
+    protected static const int VERTICAL_SPACING = 10;
+
+    /**
+     * The margin width used by layout styles <code>FLAT</code>
+     * and <code>GRID</code>.
+     */
+    protected static const int MARGIN_WIDTH = 0;
+
+    /**
+     * The margin height used by layout styles <code>FLAT</code>
+     * and <code>GRID</code>.
+     */
+    protected static const int MARGIN_HEIGHT = 0;
+
+    /**
+     * The field editors, or <code>null</code> if not created yet.
+     */
+    private List fields = null;
+
+    /**
+     * The layout style; either <code>FLAT</code> or <code>GRID</code>.
+     */
+    private int style;
+
+    /**
+     * The first invalid field editor, or <code>null</code>
+     * if all field editors are valid.
+     */
+    private FieldEditor invalidFieldEditor = null;
+
+    /**
+     * The parent composite for field editors
+     */
+    private Composite fieldEditorParent;
+
+    /**
+     * Create a new instance of the reciever.
+     */
+    public this() {
+        this(FLAT);
+    }
+
+    /**
+     * Creates a new field editor preference page with the given style,
+     * an empty title, and no image.
+     *
+     * @param style either <code>GRID</code> or <code>FLAT</code>
+     */
+    protected this(int style) {
+        super();
+        this.style = style;
+    }
+
+    /**
+     * Creates a new field editor preference page with the given title
+     * and style, but no image.
+     *
+     * @param title the title of this preference page
+     * @param style either <code>GRID</code> or <code>FLAT</code>
+     */
+    protected this(String title, int style) {
+        super(title);
+        this.style = style;
+    }
+
+    /**
+     * Creates a new field editor preference page with the given title,
+     * image, and style.
+     *
+     * @param title the title of this preference page
+     * @param image the image for this preference page, or
+     *   <code>null</code> if none
+     * @param style either <code>GRID</code> or <code>FLAT</code>
+     */
+    protected this(String title, ImageDescriptor image,
+            int style) {
+        super(title, image);
+        this.style = style;
+    }
+
+    /**
+     * Adds the given field editor to this page.
+     *
+     * @param editor the field editor
+     */
+    protected void addField(FieldEditor editor) {
+        if (fields is null) {
+            fields = new ArrayList();
+        }
+        fields.add(editor);
+    }
+
+    /**
+     * Adjust the layout of the field editors so that
+     * they are properly aligned.
+     */
+    protected void adjustGridLayout() {
+        int numColumns = calcNumberOfColumns();
+        (cast(GridLayout) fieldEditorParent.getLayout()).numColumns = numColumns;
+        if (fields !is null) {
+            for (int i = 0; i < fields.size(); i++) {
+                FieldEditor fieldEditor = cast(FieldEditor) fields.get(i);
+                fieldEditor.adjustForNumColumns_package(numColumns);
+            }
+        }
+    }
+
+    /**
+     * Applys the font to the field editors managed by this page.
+     */
+    protected void applyFont() {
+        if (fields !is null) {
+            Iterator e = fields.iterator();
+            while (e.hasNext()) {
+                FieldEditor pe = cast(FieldEditor) e.next();
+                pe.applyFont_package();
+            }
+        }
+    }
+
+    /**
+     * Calculates the number of columns needed to host all field editors.
+     *
+     * @return the number of columns
+     */
+    private int calcNumberOfColumns() {
+        int result = 0;
+        if (fields !is null) {
+            Iterator e = fields.iterator();
+            while (e.hasNext()) {
+                FieldEditor pe = cast(FieldEditor) e.next();
+                result = Math.max(result, pe.getNumberOfControls());
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Recomputes the page's error state by calling <code>isValid</code> for
+     * every field editor.
+     */
+    protected void checkState() {
+        bool valid = true;
+        invalidFieldEditor = null;
+        // The state can only be set to true if all
+        // field editors contain a valid value. So we must check them all
+        if (fields !is null) {
+            int size = fields.size();
+            for (int i = 0; i < size; i++) {
+                FieldEditor editor = cast(FieldEditor) fields.get(i);
+                valid = valid && editor.isValid();
+                if (!valid) {
+                    invalidFieldEditor = editor;
+                    break;
+                }
+            }
+        }
+        setValid(valid);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on PreferencePage.
+     */
+    protected override Control createContents(Composite parent) {
+        fieldEditorParent = new Composite(parent, SWT.NULL);
+        GridLayout layout = new GridLayout();
+        layout.numColumns = 1;
+        layout.marginHeight = 0;
+        layout.marginWidth = 0;
+        fieldEditorParent.setLayout(layout);
+        fieldEditorParent.setFont(parent.getFont());
+
+        createFieldEditors();
+
+        if (style is GRID) {
+            adjustGridLayout();
+        }
+
+        initialize();
+        checkState();
+        return fieldEditorParent;
+    }
+
+    /**
+     * Creates the page's field editors.
+     * <p>
+     * The default implementation of this framework method
+     * does nothing. Subclass must implement this method to
+     * create the field editors.
+     * </p>
+     * <p>
+     * Subclasses should call <code>getFieldEditorParent</code>
+     * to obtain the parent control for each field editor.
+     * This same parent should not be used for more than
+     * one editor as the parent may change for each field
+     * editor depending on the layout style of the page
+     * </p>
+     */
+    protected abstract void createFieldEditors();
+
+    /**
+     * The field editor preference page implementation of an <code>IDialogPage</code>
+     * method disposes of this page's controls and images.
+     * Subclasses may override to release their own allocated SWT
+     * resources, but must call <code>super.dispose</code>.
+     */
+    public override void dispose() {
+        super.dispose();
+        if (fields !is null) {
+            Iterator e = fields.iterator();
+            while (e.hasNext()) {
+                FieldEditor pe = cast(FieldEditor) e.next();
+                pe.setPage(null);
+                pe.setPropertyChangeListener(null);
+                pe.setPreferenceStore(null);
+            }
+        }
+    }
+
+    /**
+     * Returns a parent composite for a field editor.
+     * <p>
+     * This value must not be cached since a new parent
+     * may be created each time this method called. Thus
+     * this method must be called each time a field editor
+     * is constructed.
+     * </p>
+     *
+     * @return a parent
+     */
+    protected Composite getFieldEditorParent() {
+        if (style is FLAT) {
+            // Create a new parent for each field editor
+            Composite parent = new Composite(fieldEditorParent, SWT.NULL);
+            parent.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+            return parent;
+        }
+        // Just return the parent
+        return fieldEditorParent;
+    }
+
+    /**
+     * Initializes all field editors.
+     */
+    protected void initialize() {
+        if (fields !is null) {
+            Iterator e = fields.iterator();
+            while (e.hasNext()) {
+                FieldEditor pe = cast(FieldEditor) e.next();
+                pe.setPage(this);
+                pe.setPropertyChangeListener(this);
+                pe.setPreferenceStore(getPreferenceStore());
+                pe.load();
+            }
+        }
+    }
+
+    /**
+     * The field editor preference page implementation of a <code>PreferencePage</code>
+     * method loads all the field editors with their default values.
+     */
+    protected override void performDefaults() {
+        if (fields !is null) {
+            Iterator e = fields.iterator();
+            while (e.hasNext()) {
+                FieldEditor pe = cast(FieldEditor) e.next();
+                pe.loadDefault();
+            }
+        }
+        // Force a recalculation of my error state.
+        checkState();
+        super.performDefaults();
+    }
+
+    /**
+     * The field editor preference page implementation of this
+     * <code>PreferencePage</code> method saves all field editors by
+     * calling <code>FieldEditor.store</code>. Note that this method
+     * does not save the preference store itself; it just stores the
+     * values back into the preference store.
+     *
+     * @see FieldEditor#store()
+     */
+    public override bool performOk() {
+        if (fields !is null) {
+            Iterator e = fields.iterator();
+            while (e.hasNext()) {
+                FieldEditor pe = cast(FieldEditor) e.next();
+                pe.store();
+                pe.setPresentsDefaultValue_package(false);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * The field editor preference page implementation of this <code>IPreferencePage</code>
+     * (and <code>IPropertyChangeListener</code>) method intercepts <code>IS_VALID</code>
+     * events but passes other events on to its superclass.
+     */
+    public void propertyChange(PropertyChangeEvent event) {
+
+        if (event.getProperty().equals(FieldEditor.IS_VALID)) {
+            bool newValue = (cast(Boolean) event.getNewValue()).booleanValue();
+            // If the new value is true then we must check all field editors.
+            // If it is false, then the page is invalid in any case.
+            if (newValue) {
+                checkState();
+            } else {
+                invalidFieldEditor = cast(FieldEditor) event.getSource();
+                setValid(newValue);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialog.
+     */
+    public override void setVisible(bool visible) {
+        super.setVisible(visible);
+        if (visible && invalidFieldEditor !is null) {
+            invalidFieldEditor.setFocus();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/FileFieldEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.preference.FileFieldEditor;
+
+import org.eclipse.jface.preference.StringButtonFieldEditor;
+// import java.io.File;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.List;
+import java.util.Set;
+import tango.io.FilePath;
+static import tango.io.Path;
+import tango.io.FileSystem;
+
+/**
+ * A field editor for a file path type preference. A standard file
+ * dialog appears when the user presses the change button.
+ */
+public class FileFieldEditor : StringButtonFieldEditor {
+
+    /**
+     * List of legal file extension suffixes, or <code>null</code>
+     * for system defaults.
+     */
+    private String[] extensions = null;
+
+    /**
+     * Indicates whether the path must be absolute;
+     * <code>false</code> by default.
+     */
+    private bool enforceAbsolute = false;
+
+    /**
+     * Creates a new file field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a file field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText, Composite parent) {
+        this(name, labelText, false, parent);
+    }
+    
+    /**
+     * Creates a file field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param enforceAbsolute <code>true</code> if the file path
+     *  must be absolute, and <code>false</code> otherwise
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText, bool enforceAbsolute, Composite parent) {
+        this(name, labelText, enforceAbsolute, VALIDATE_ON_FOCUS_LOST, parent);
+    }
+    /**
+     * Creates a file field editor.
+     * 
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param enforceAbsolute <code>true</code> if the file path
+     *  must be absolute, and <code>false</code> otherwise
+     * @param validationStrategy either {@link StringButtonFieldEditor#VALIDATE_ON_KEY_STROKE}
+     *  to perform on the fly checking, or {@link StringButtonFieldEditor#VALIDATE_ON_FOCUS_LOST}
+     *  (the default) to perform validation only after the text has been typed in
+     * @param parent the parent of the field editor's control.
+     * @since 3.4
+     * @see StringButtonFieldEditor#VALIDATE_ON_KEY_STROKE
+     * @see StringButtonFieldEditor#VALIDATE_ON_FOCUS_LOST
+     */
+    public this(String name, String labelText,
+            bool enforceAbsolute, int validationStrategy, Composite parent) {
+        init(name, labelText);
+        this.enforceAbsolute = enforceAbsolute;
+        setErrorMessage(JFaceResources
+                .getString("FileFieldEditor.errorMessage"));//$NON-NLS-1$
+        setChangeButtonText(JFaceResources.getString("openBrowse"));//$NON-NLS-1$
+        setValidateStrategy(validationStrategy);
+        createControl(parent);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StringButtonFieldEditor.
+     * Opens the file chooser dialog and returns the selected file.
+     */
+    protected override String changePressed() {
+        auto f = new FilePath(tango.io.Path.standard(getTextControl().getText()));
+        if (!f.exists()) {
+            f = cast(FilePath)null;
+        }
+        auto d = getFile(f);
+        if (d is null) {
+            return null;
+        }
+
+        return FileSystem.toAbsolute( d ).native.toString();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StringFieldEditor.
+     * Checks whether the text input field specifies an existing file.
+     */
+    protected override bool checkState() {
+
+        String msg = null;
+
+        String path = getTextControl().getText();
+        if (path !is null) {
+            path = path.trim();
+        } else {
+            path = "";//$NON-NLS-1$
+        }
+        if (path.length is 0) {
+            if (!isEmptyStringAllowed()) {
+                msg = getErrorMessage();
+            }
+        } else {
+            auto file = new FilePath(tango.io.Path.standard(path));
+            if (/+file.isFile()+/ file.exists && !file.isFolder ) {
+                if (enforceAbsolute && !file.isAbsolute()) {
+                    msg = JFaceResources
+                            .getString("FileFieldEditor.errorMessage2");//$NON-NLS-1$
+                }
+            } else {
+                msg = getErrorMessage();
+            }
+        }
+
+        if (msg !is null) { // error
+            showErrorMessage(msg);
+            return false;
+        }
+
+        // OK!
+        clearErrorMessage();
+        return true;
+    }
+
+    /**
+     * Helper to open the file chooser dialog.
+     * @param startingDirectory the directory to open the dialog on.
+     * @return File The File the user selected or <code>null</code> if they
+     * do not.
+     */
+    private FilePath getFile(FilePath startingDirectory) {
+
+        FileDialog dialog = new FileDialog(getShell(), SWT.OPEN);
+        if (startingDirectory !is null) {
+            dialog.setFileName(startingDirectory.path());
+        }
+        if (extensions !is null) {
+            dialog.setFilterExtensions(extensions);
+        }
+        String file = dialog.open();
+        if (file !is null) {
+            file = file.trim();
+            if (file.length > 0) {
+                return new FilePath(tango.io.Path.standard(file));
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Sets this file field editor's file extension filter.
+     *
+     * @param extensions a list of file extension, or <code>null</code>
+     * to set the filter to the system's default value
+     */
+    public void setFileExtensions(String[] extensions) {
+        this.extensions = extensions;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/FontFieldEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,428 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.FontFieldEditor;
+
+import org.eclipse.jface.preference.FieldEditor;
+import org.eclipse.jface.preference.PreferenceConverter;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.FontDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.StringConverter;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A field editor for a font type preference.
+ */
+public class FontFieldEditor : FieldEditor {
+
+    /**
+     * The change font button, or <code>null</code> if none
+     * (before creation and after disposal).
+     */
+    private Button changeFontButton = null;
+
+    /**
+     * The text for the change font button, or <code>null</code>
+     * if missing.
+     */
+    private String changeButtonText;
+
+    /**
+     * The text for the preview, or <code>null</code> if no preview is desired
+     */
+    private String previewText;
+
+    /**
+     * Font data for the chosen font button, or <code>null</code> if none.
+     */
+    private FontData[] chosenFont;
+
+    /**
+     * The label that displays the selected font, or <code>null</code> if none.
+     */
+    private Label valueControl;
+
+    /**
+     * The previewer, or <code>null</code> if none.
+     */
+    private DefaultPreviewer previewer;
+
+    /**
+     * Internal font previewer implementation.
+     */
+    private static class DefaultPreviewer {
+        private Text text;
+
+        private String string;
+
+        private Font font;
+
+        /**
+         * Constructor for the previewer.
+         * @param s
+         * @param parent
+         */
+        public this(String s, Composite parent) {
+            string = s;
+            text = new Text(parent, SWT.READ_ONLY | SWT.BORDER);
+            text.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent e) {
+                    if (font !is null) {
+                        font.dispose();
+                    }
+                }
+            });
+            if (string !is null) {
+                text.setText(string);
+            }
+        }
+
+        /**
+         * @return the control the previewer is using
+         */
+        public Control getControl() {
+            return text;
+        }
+
+        /**
+         * Set the font to display with
+         * @param fontData
+         */
+        public void setFont(FontData[] fontData) {
+            if (font !is null) {
+                font.dispose();
+            }
+            font = new Font(text.getDisplay(), fontData);
+            text.setFont(font);
+        }
+
+        /**
+         * @return the preferred size of the previewer.
+         */
+        public int getPreferredExtent() {
+            return 40;
+        }
+    }
+
+    /**
+     * Creates a new font field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a font field editor with an optional preview area.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param previewAreaText the text used for the preview window. If it is
+     * <code>null</code> there will be no preview area,
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText,
+            String previewAreaText, Composite parent) {
+        init(name, labelText);
+        previewText = previewAreaText;
+        changeButtonText = JFaceResources.getString("openChange"); //$NON-NLS-1$
+        createControl(parent);
+
+    }
+
+    /**
+     * Creates a font field editor without a preview.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText, Composite parent) {
+        this(name, labelText, null, parent);
+
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void adjustForNumColumns(int numColumns) {
+
+        GridData data = new GridData();
+        if (valueControl.getLayoutData() !is null) {
+            data = cast(GridData) valueControl.getLayoutData();
+        }
+
+        data.horizontalSpan = numColumns - getNumberOfControls() + 1;
+        valueControl.setLayoutData(data);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void applyFont() {
+        if (chosenFont !is null && previewer !is null) {
+            previewer.setFont(chosenFont);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doFillIntoGrid(Composite parent, int numColumns) {
+        getLabelControl(parent);
+
+        valueControl = getValueControl(parent);
+
+        GridData gd = new GridData(GridData.FILL_HORIZONTAL
+                | GridData.GRAB_HORIZONTAL);
+        gd.horizontalSpan = numColumns - getNumberOfControls() + 1;
+        valueControl.setLayoutData(gd);
+        if (previewText !is null) {
+            previewer = new DefaultPreviewer(previewText, parent);
+            gd = new GridData(GridData.FILL_HORIZONTAL);
+            gd.heightHint = previewer.getPreferredExtent();
+            gd.widthHint = previewer.getPreferredExtent();
+            previewer.getControl().setLayoutData(gd);
+        }
+
+        changeFontButton = getChangeControl(parent);
+        gd = new GridData();
+        int widthHint = convertHorizontalDLUsToPixels(changeFontButton,
+                IDialogConstants.BUTTON_WIDTH);
+        gd.widthHint = Math.max(widthHint, changeFontButton.computeSize(
+                SWT.DEFAULT, SWT.DEFAULT, true).x);
+        changeFontButton.setLayoutData(gd);
+
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doLoad() {
+        if (changeFontButton is null) {
+            return;
+        }
+        updateFont(PreferenceConverter.getFontDataArray(getPreferenceStore(),
+                getPreferenceName()));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doLoadDefault() {
+        if (changeFontButton is null) {
+            return;
+        }
+        updateFont(PreferenceConverter.getDefaultFontDataArray(
+                getPreferenceStore(), getPreferenceName()));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doStore() {
+        if (chosenFont !is null) {
+            PreferenceConverter.setValue(getPreferenceStore(),
+                    getPreferenceName(), chosenFont);
+        }
+    }
+
+    /**
+     * Returns the change button for this field editor.
+     *
+     * @param parent The Composite to create the button in if required.
+     * @return the change button
+     */
+    protected Button getChangeControl(Composite parent) {
+        if (changeFontButton is null) {
+            changeFontButton = new Button(parent, SWT.PUSH);
+            if (changeButtonText !is null) {
+                changeFontButton.setText(changeButtonText);
+            }
+            changeFontButton.addSelectionListener(new class SelectionAdapter {
+                public void widgetSelected(SelectionEvent event) {
+                    FontDialog fontDialog = new FontDialog(changeFontButton
+                            .getShell());
+                    if (chosenFont !is null) {
+                        fontDialog.setFontList(chosenFont);
+                    }
+                    FontData font = fontDialog.open();
+                    if (font !is null) {
+                        FontData[] oldFont = chosenFont;
+                        if (oldFont is null) {
+                            oldFont = JFaceResources.getDefaultFont()
+                                    .getFontData();
+                        }
+                        setPresentsDefaultValue(false);
+                        FontData[] newData = new FontData[1];
+                        newData[0] = font;
+                        updateFont(newData);
+                        fireValueChanged(VALUE, oldFont[0], font);
+                    }
+
+                }
+            });
+            changeFontButton.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    changeFontButton = null;
+                }
+            });
+            changeFontButton.setFont(parent.getFont());
+            setButtonLayoutData(changeFontButton);
+        } else {
+            checkParent(changeFontButton, parent);
+        }
+        return changeFontButton;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public override int getNumberOfControls() {
+        if (previewer is null) {
+            return 3;
+        }
+
+        return 4;
+    }
+
+    /**
+     * Returns the preferred preview height.
+     *
+     * @return the height, or <code>-1</code> if no previewer
+     *  is installed
+     */
+    public int getPreferredPreviewHeight() {
+        if (previewer is null) {
+            return -1;
+        }
+        return previewer.getPreferredExtent();
+    }
+
+    /**
+     * Returns the preview control for this field editor.
+     *
+     * @return the preview control
+     */
+    public Control getPreviewControl() {
+        if (previewer is null) {
+            return null;
+        }
+
+        return previewer.getControl();
+    }
+
+    /**
+     * Returns the value control for this field editor. The value control
+     * displays the currently selected font name.
+     * @param parent The Composite to create the viewer in if required
+     * @return the value control
+     */
+    protected Label getValueControl(Composite parent) {
+        if (valueControl is null) {
+            valueControl = new Label(parent, SWT.LEFT);
+            valueControl.setFont(parent.getFont());
+            valueControl.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    valueControl = null;
+                }
+            });
+        } else {
+            checkParent(valueControl, parent);
+        }
+        return valueControl;
+    }
+
+    /**
+     * Sets the text of the change button.
+     *
+     * @param text the new text
+     */
+    public void setChangeButtonText(String text) {
+        Assert.isNotNull(text);
+        changeButtonText = text;
+        if (changeFontButton !is null) {
+            changeFontButton.setText(text);
+        }
+    }
+
+    /**
+     * Updates the change font button and the previewer to reflect the
+     * newly selected font.
+     * @param font The FontData[] to update with.
+     */
+    private void updateFont(FontData font[]) {
+        FontData[] bestFont = JFaceResources.getFontRegistry().filterData(
+                font, valueControl.getDisplay());
+
+        //if we have nothing valid do as best we can
+        if (bestFont is null) {
+            bestFont = getDefaultFontData();
+        }
+
+        //Now cache this value in the receiver
+        this.chosenFont = bestFont;
+
+        if (valueControl !is null) {
+            valueControl.setText(StringConverter.asString(chosenFont[0]));
+        }
+        if (previewer !is null) {
+            previewer.setFont(bestFont);
+        }
+    }
+
+    /**
+     * Store the default preference for the field
+     * being edited
+     */
+    protected void setToDefault() {
+        FontData[] defaultFontData = PreferenceConverter
+                .getDefaultFontDataArray(getPreferenceStore(),
+                        getPreferenceName());
+        PreferenceConverter.setValue(getPreferenceStore(), getPreferenceName(),
+                defaultFontData);
+    }
+
+    /**
+     * Get the system default font data.
+     * @return FontData[]
+     */
+    private FontData[] getDefaultFontData() {
+        return valueControl.getDisplay().getSystemFont().getFontData();
+    }
+
+    /*
+     * @see FieldEditor.setEnabled(bool,Composite).
+     */
+    public override void setEnabled(bool enabled, Composite parent) {
+        super.setEnabled(enabled, parent);
+        getChangeControl(parent).setEnabled(enabled);
+        getValueControl(parent).setEnabled(enabled);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/IPersistentPreferenceStore.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.IPersistentPreferenceStore;
+
+import java.lang.all;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+
+/**
+ * IPersistentPreferenceStore is a preference store that can
+ * be saved.
+ */
+public interface IPersistentPreferenceStore : IPreferenceStore {
+
+    /**
+     * Saves the non-default-valued preferences known to this preference
+     * store to the file from which they were originally loaded.
+     *
+     * @exception java.io.IOException if there is a problem saving this store
+     */
+    public void save();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/IPreferenceNode.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.IPreferenceNode;
+
+import org.eclipse.jface.preference.IPreferencePage;
+
+import org.eclipse.swt.graphics.Image;
+import java.lang.all;
+
+/**
+ * Interface to a node in a preference dialog.
+ * A preference node maintains a label and image used to display the
+ * node in a preference dialog (usually in the form of a tree),
+ * as well as the preference page this node stands for.
+ *
+ * The node may use lazy creation for its page
+ *
+ * Note that all preference nodes must be dispose their resources.
+ * The node must dispose the page managed by this node, and any SWT resources
+ * allocated by this node (Images, Fonts, etc).
+ * However the node itself may be reused.
+ */
+public interface IPreferenceNode {
+    /**
+     * Adds the given preference node as a subnode of this
+     * preference node.
+     *
+     * @param node the node to add
+     */
+    public void add(IPreferenceNode node);
+
+    /**
+     * Creates the preference page for this node.
+     */
+    public void createPage();
+
+    /**
+     * Release the page managed by this node, and any SWT resources
+     * held onto by this node (Images, Fonts, etc).
+     *
+     * Note that nodes are reused so this is not a call to dispose the
+     * node itself.
+     */
+    public void disposeResources();
+
+    /**
+     * Returns the subnode of this contribution node with the given node id.
+     *
+     * @param id the preference node id
+     * @return the subnode, or <code>null</code> if none
+     */
+    public IPreferenceNode findSubNode(String id);
+
+    /**
+     * Returns the id of this contribution node.
+     * This id identifies a contribution node relative to its parent.
+     *
+     * @return the node id
+     */
+    public String getId();
+
+    /**
+     * Returns the image used to present this node in a preference dialog.
+     *
+     * @return the image for this node, or <code>null</code>
+     *   if there is no image for this node
+     */
+    public Image getLabelImage();
+
+    /**
+     * Returns the text label used to present this node in a preference dialog.
+     *
+     * @return the text label for this node, or <code>null</code>
+     *   if there is no label for this node
+     */
+    public String getLabelText();
+
+    /**
+     * Returns the preference page for this node.
+     *
+     * @return the preference page
+     */
+    public IPreferencePage getPage();
+
+    /**
+     * Returns an iterator over the subnodes (immediate children)
+     * of this contribution node.
+     *
+     * @return an IPreferenceNode array containing the child nodes
+     */
+    public IPreferenceNode[] getSubNodes();
+
+    /**
+     * Removes the subnode of this preference node with the given node id.
+     *
+     * @param id the subnode id
+     * @return the removed subnode, or <code>null</code> if none
+     */
+    public IPreferenceNode remove(String id);
+
+    /**
+     * Removes the given preference node from the list of subnodes
+     * (immediate children) of this node.
+     *
+     * @param node the node to remove
+     * @return <code>true</code> if the node was removed,
+     *  and <code>false</code> otherwise
+     */
+    public bool remove(IPreferenceNode node);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/IPreferencePage.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.IPreferencePage;
+
+import java.lang.all;
+
+import org.eclipse.jface.preference.IPreferencePageContainer;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.jface.dialogs.IDialogPage;
+
+/**
+ * An interface for a preference page. This interface
+ * is used primarily by the page's container
+ */
+public interface IPreferencePage : IDialogPage {
+
+    /**
+     * Computes a size for this page's UI component.
+     *
+     * @return the size of the preference page encoded as
+     *   <code>new Point(width,height)</code>, or
+     *   <code>(0,0)</code> if the page doesn't currently have any UI component
+     */
+    public Point computeSize();
+
+    /**
+     * Returns whether this dialog page is in a valid state.
+     *
+     * @return <code>true</code> if the page is in a valid state,
+     *   and <code>false</code> if invalid
+     */
+    public bool isValid();
+
+    /**
+     * Checks whether it is alright to leave this page.
+     *
+     * @return <code>false</code> to abort page flipping and the
+     *  have the current page remain visible, and <code>true</code>
+     *  to allow the page flip
+     */
+    public bool okToLeave();
+
+    /**
+     * Notifies that the container of this preference page has been canceled.
+     *
+     * @return <code>false</code> to abort the container's cancel
+     *  procedure and <code>true</code> to allow the cancel to happen
+     */
+    public bool performCancel();
+
+    /**
+     * Notifies that the OK button of this page's container has been pressed.
+     *
+     * @return <code>false</code> to abort the container's OK
+     *  processing and <code>true</code> to allow the OK to happen
+     */
+    public bool performOk();
+
+    /**
+     * Sets or clears the container of this page.
+     *
+     * @param preferencePageContainer the preference page container, or <code>null</code>
+     */
+    public void setContainer(IPreferencePageContainer preferencePageContainer);
+
+    /**
+     * Sets the size of this page's UI component.
+     *
+     * @param size the size of the preference page encoded as
+     *   <code>new Point(width,height)</code>
+     */
+    public void setSize(Point size);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/IPreferencePageContainer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.IPreferencePageContainer;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import java.lang.all;
+
+/**
+ * An interface used by a preference page to talk to
+ * its dialog.
+ */
+public interface IPreferencePageContainer {
+    /**
+     * Returns the preference store.
+     *
+     * @return the preference store, or <code>null</code> if none
+     */
+    public IPreferenceStore getPreferenceStore();
+
+    /**
+     * Adjusts the enable state of the OK
+     * button to reflect the state of the currently active
+     * page in this container.
+     * <p>
+     * This method is called by the container itself
+     * when its preference page changes and may be called
+     * by the page at other times to force a button state
+     * update.
+     * </p>
+     */
+    public void updateButtons();
+
+    /**
+     * Updates the message (or error message) shown in the message line to
+     * reflect the state of the currently active page in this container.
+     * <p>
+     * This method is called by the container itself
+     * when its preference page changes and may be called
+     * by the page at other times to force a message
+     * update.
+     * </p>
+     */
+    public void updateMessage();
+
+    /**
+     * Updates the title to reflect the state of the
+     * currently active page in this container.
+     * <p>
+     * This method is called by the container itself
+     * when its page changes and may be called
+     * by the page at other times to force a title
+     * update.
+     * </p>
+     */
+    public void updateTitle();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/IPreferenceStore.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,588 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.IPreferenceStore;
+
+import org.eclipse.jface.util.IPropertyChangeListener;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The <code>IPreferenceStore</code> interface represents a table mapping
+ * named preferences to values. If there is no value for a given name,
+ * then that preferences's default value is returned; and if there is no
+ * default value for that preference, then a default-default value is returned.
+ * The default-default values for the primitive types are as follows:
+ * <ul>
+ *  <li><code>bool</code> = <code>false</code></li>
+ *  <li><code>double</code> = <code>0.0</code></li>
+ *  <li><code>float</code> = <code>0.0f</code></li>
+ *  <li><code>int</code> = <code>0</code></li>
+ *  <li><code>long</code> = <code>0</code></li>
+ *  <li><code>String</code> = <code>""</code> (the empty string)</li>
+ * </ul>
+ * <p>
+ * Thus a preference store maintains two values for each of a set of
+ * names: a current value and a default value.
+ * The typical usage is to establish the defaults for all known preferences
+ * and then restore previously stored values for preferences whose values
+ * were different from their defaults. After the current values of
+ * the preferences have been modified, it is a simple matter to write
+ * out only those preferences whose values are different from their defaults.
+ * This two-tiered approach to saving and restoring preference setting
+ * minimized the number of preferences that need to be persisted; indeed,
+ * the normal starting state does not require storing any preferences
+ * at all.
+ * </p>
+ * <p>
+ * A property change event is reported whenever a preferences current
+ * value actually changes (whether through <code>setValue</code>,
+ * <code>setToDefault</code>, or other unspecified means). Note, however,
+ * that manipulating default values (with <code>setDefault</code>)
+ * does not cause such events to be reported.
+ * </p>
+ * <p>
+ * Clients who need a preference store may implement this interface or
+ * instantiate the standard implementation <code>PreferenceStore</code>.
+ * </p>
+ *
+ * @see PreferenceStore
+ */
+public interface IPreferenceStore {
+
+    /**
+     * The default-default value for bool preferences (<code>false</code>).
+     */
+    public static const bool BOOLEAN_DEFAULT_DEFAULT = false;
+
+    /**
+     * The default-default value for double preferences (<code>0.0</code>).
+     */
+    public static const double DOUBLE_DEFAULT_DEFAULT = 0.0;
+
+    /**
+     * The default-default value for float preferences (<code>0.0f</code>).
+     */
+    public static const float FLOAT_DEFAULT_DEFAULT = 0.0f;
+
+    /**
+     * The default-default value for int preferences (<code>0</code>).
+     */
+    public static const int INT_DEFAULT_DEFAULT = 0;
+
+    /**
+     * The default-default value for long preferences (<code>0L</code>).
+     */
+    public static const long LONG_DEFAULT_DEFAULT = 0L;
+
+    /**
+     * The default-default value for String preferences (<code>""</code>).
+     */
+    public static const String STRING_DEFAULT_DEFAULT = ""; //$NON-NLS-1$
+
+    /**
+     * The string representation used for <code>true</code> (<code>"true"</code>).
+     */
+    public static const String TRUE = "true"; //$NON-NLS-1$
+
+    /**
+     * The string representation used for <code>false</code> (<code>"false"</code>).
+     */
+    public static const String FALSE = "false"; //$NON-NLS-1$
+
+    /**
+     * <p>
+     * Adds a property change listener to this preference store.
+     * </p>
+     * <p>
+     * <b>Note</b> The types of the oldValue and newValue of the
+     * generated PropertyChangeEvent are determined by whether
+     * or not the typed API in IPreferenceStore was called.
+     * If values are changed via setValue(name,type) the
+     * values in the PropertyChangedEvent will be of that type.
+     * If they are set using a non typed API (i.e. #setToDefault
+     * or using the OSGI Preferences) the values will be unconverted
+     * Strings.
+     * </p>
+     * <p>
+     * A listener will be called in the same Thread
+     * that it is invoked in. Any Thread dependant listeners (such as
+     * those who update an SWT widget) will need to update in the
+     * correct Thread. In the case of an SWT update you can update
+     * using Display#syncExec(Runnable) or Display#asyncExec(Runnable).
+     * </p>
+     * <p>
+     * Likewise any application that updates an IPreferenceStore
+     * from a Thread other than the UI Thread should be aware of
+     * any listeners that require an update in the UI Thread.
+     * </p>
+     *
+     * @param listener a property change listener
+     * @see org.eclipse.jface.util.PropertyChangeEvent
+     * @see #setToDefault(String)
+     * @see #setValue(String, bool)
+     * @see #setValue(String, double)
+     * @see #setValue(String, float)
+     * @see #setValue(String, int)
+     * @see #setValue(String, long)
+     * @see #setValue(String, String)
+     */
+    public void addPropertyChangeListener(IPropertyChangeListener listener);
+
+    /**
+     * Returns whether the named preference is known to this preference
+     * store.
+     *
+     * @param name the name of the preference
+     * @return <code>true</code> if either a current value or a default
+     *  value is known for the named preference, and <code>false</code> otherwise
+     */
+    public bool contains(String name);
+
+    /**
+     * Fires a property change event corresponding to a change to the
+     * current value of the preference with the given name.
+     * <p>
+     * This method is provided on this interface to simplify the implementation
+     * of decorators. There is normally no need to call this method since
+     * <code>setValue</code> and <code>setToDefault</code> report such
+     * events in due course. Implementations should funnel all preference
+     * changes through this method.
+     * </p>
+     *
+     * @param name the name of the preference, to be used as the property
+     *  in the event object
+     * @param oldValue the old value
+     * @param newValue the new value
+     */
+    public void firePropertyChangeEvent(String name, Object oldValue,
+            Object newValue);
+
+    /**
+     * Returns the current value of the bool-valued preference with the
+     * given name.
+     * Returns the default-default value (<code>false</code>) if there
+     * is no preference with the given name, or if the current value
+     * cannot be treated as a bool.
+     *
+     * @param name the name of the preference
+     * @return the bool-valued preference
+     */
+    public bool getBoolean(String name);
+
+    /**
+     * Returns the default value for the bool-valued preference
+     * with the given name.
+     * Returns the default-default value (<code>false</code>) if there
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as a bool.
+     *
+     * @param name the name of the preference
+     * @return the default value of the named preference
+     */
+    public bool getDefaultBoolean(String name);
+
+    /**
+     * Returns the default value for the double-valued preference
+     * with the given name.
+     * Returns the default-default value (<code>0.0</code>) if there
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as a double.
+     *
+     * @param name the name of the preference
+     * @return the default value of the named preference
+     */
+    public double getDefaultDouble(String name);
+
+    /**
+     * Returns the default value for the float-valued preference
+     * with the given name.
+     * Returns the default-default value (<code>0.0f</code>) if there
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as a float.
+     *
+     * @param name the name of the preference
+     * @return the default value of the named preference
+     */
+    public float getDefaultFloat(String name);
+
+    /**
+     * Returns the default value for the integer-valued preference
+     * with the given name.
+     * Returns the default-default value (<code>0</code>) if there
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as an integer.
+     *
+     * @param name the name of the preference
+     * @return the default value of the named preference
+     */
+    public int getDefaultInt(String name);
+
+    /**
+     * Returns the default value for the long-valued preference
+     * with the given name.
+     * Returns the default-default value (<code>0L</code>) if there
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as a long.
+     *
+     * @param name the name of the preference
+     * @return the default value of the named preference
+     */
+    public long getDefaultLong(String name);
+
+    /**
+     * Returns the default value for the string-valued preference
+     * with the given name.
+     * Returns the default-default value (the empty string <code>""</code>)
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as a string.
+     *
+     * @param name the name of the preference
+     * @return the default value of the named preference
+     */
+    public String getDefaultString(String name);
+
+    /**
+     * Returns the current value of the double-valued preference with the
+     * given name.
+     * Returns the default-default value (<code>0.0</code>) if there
+     * is no preference with the given name, or if the current value
+     * cannot be treated as a double.
+     *
+     * @param name the name of the preference
+     * @return the double-valued preference
+     */
+    public double getDouble(String name);
+
+    /**
+     * Returns the current value of the float-valued preference with the
+     * given name.
+     * Returns the default-default value (<code>0.0f</code>) if there
+     * is no preference with the given name, or if the current value
+     * cannot be treated as a float.
+     *
+     * @param name the name of the preference
+     * @return the float-valued preference
+     */
+    public float getFloat(String name);
+
+    /**
+     * Returns the current value of the integer-valued preference with the
+     * given name.
+     * Returns the default-default value (<code>0</code>) if there
+     * is no preference with the given name, or if the current value
+     * cannot be treated as an integter.
+     *
+     * @param name the name of the preference
+     * @return the int-valued preference
+     */
+    public int getInt(String name);
+
+    /**
+     * Returns the current value of the long-valued preference with the
+     * given name.
+     * Returns the default-default value (<code>0L</code>) if there
+     * is no preference with the given name, or if the current value
+     * cannot be treated as a long.
+     *
+     * @param name the name of the preference
+     * @return the long-valued preference
+     */
+    public long getLong(String name);
+
+    /**
+     * Returns the current value of the string-valued preference with the
+     * given name.
+     * Returns the default-default value (the empty string <code>""</code>)
+     * if there is no preference with the given name, or if the current value
+     * cannot be treated as a string.
+     *
+     * @param name the name of the preference
+     * @return the string-valued preference
+     */
+    public String getString(String name);
+
+    /**
+     * Returns whether the current value of the preference with the given name
+     * has the default value.
+     *
+     * @param name the name of the preference
+     * @return <code>true</code> if the preference has a known default value
+     * and its current value is the same, and <code>false</code> otherwise
+     * (including the case where the preference is unknown to this store)
+     */
+    public bool isDefault(String name);
+
+    /**
+     * Returns whether the current values in this property store
+     * require saving.
+     *
+     * @return <code>true</code> if at least one of values of
+     *  the preferences known to this store has changed and
+     *  requires saving, and <code>false</code> otherwise.
+     */
+    public bool needsSaving();
+
+    /**
+     * Sets the current value of the preference with the given name to
+     * the given string value without sending a property change.
+     * <p>
+     * This method does not fire a property change event and
+     * should only be used for setting internal preferences
+     * that are not meant to be processed by listeners.
+     * Normal clients should instead call #setValue.
+     * </p>
+     *
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     */
+    public void putValue(String name, String value);
+
+    /**
+     * Removes the given listener from this preference store.
+     * Has no affect if the listener is not registered.
+     *
+     * @param listener a property change listener
+     */
+    public void removePropertyChangeListener(IPropertyChangeListener listener);
+
+    /**
+     * Sets the default value for the double-valued preference with the
+     * given name.
+     * <p>
+     * Note that the current value of the preference is affected if
+     * the preference's current value was its old default value, in which
+     * case it changes to the new default value. If the preference's current
+     * is different from its old default value, its current value is
+     * unaffected. No property change events are reported by changing default
+     * values.
+     * </p>
+     *
+     * @param name the name of the preference
+     * @param value the new default value for the preference
+     */
+    public void setDefault(String name, double value);
+
+    /**
+     * Sets the default value for the float-valued preference with the
+     * given name.
+     * <p>
+     * Note that the current value of the preference is affected if
+     * the preference's current value was its old default value, in which
+     * case it changes to the new default value. If the preference's current
+     * is different from its old default value, its current value is
+     * unaffected. No property change events are reported by changing default
+     * values.
+     * </p>
+     *
+     * @param name the name of the preference
+     * @param value the new default value for the preference
+     */
+    public void setDefault(String name, float value);
+
+    /**
+     * Sets the default value for the integer-valued preference with the
+     * given name.
+     * <p>
+     * Note that the current value of the preference is affected if
+     * the preference's current value was its old default value, in which
+     * case it changes to the new default value. If the preference's current
+     * is different from its old default value, its current value is
+     * unaffected. No property change events are reported by changing default
+     * values.
+     * </p>
+     *
+     * @param name the name of the preference
+     * @param value the new default value for the preference
+     */
+    public void setDefault(String name, int value);
+
+    /**
+     * Sets the default value for the long-valued preference with the
+     * given name.
+     * <p>
+     * Note that the current value of the preference is affected if
+     * the preference's current value was its old default value, in which
+     * case it changes to the new default value. If the preference's current
+     * is different from its old default value, its current value is
+     * unaffected. No property change events are reported by changing default
+     * values.
+     * </p>
+     *
+     * @param name the name of the preference
+     * @param value the new default value for the preference
+     */
+    public void setDefault(String name, long value);
+
+    /**
+     * Sets the default value for the string-valued preference with the
+     * given name.
+     * <p>
+     * Note that the current value of the preference is affected if
+     * the preference's current value was its old default value, in which
+     * case it changes to the new default value. If the preference's current
+     * is different from its old default value, its current value is
+     * unaffected. No property change events are reported by changing default
+     * values.
+     * </p>
+     *
+     * @param name the name of the preference
+     * @param defaultObject the new default value for the preference
+     */
+    public void setDefault(String name, String defaultObject);
+
+    /**
+     * Sets the default value for the bool-valued preference with the
+     * given name.
+     * <p>
+     * Note that the current value of the preference is affected if
+     * the preference's current value was its old default value, in which
+     * case it changes to the new default value. If the preference's current
+     * is different from its old default value, its current value is
+     * unaffected. No property change events are reported by changing default
+     * values.
+     * </p>
+     *
+     * @param name the name of the preference
+     * @param value the new default value for the preference
+     */
+    public void setDefault(String name, bool value);
+
+    /**
+     * Sets the current value of the preference with the given name back
+     * to its default value.
+     * <p>
+     * Note that the preferred way of re-initializing a preference to the
+     * appropriate default value is to call <code>setToDefault</code>.
+     * This is implemented by removing the named value from the store,
+     * thereby exposing the default value.
+     * </p>
+     *
+     * @param name the name of the preference
+     */
+    public void setToDefault(String name);
+
+    /**
+     * Sets the current value of the double-valued preference with the
+     * given name.
+     * <p>
+     * A property change event is reported if the current value of the
+     * preference actually changes from its previous value. In the event
+     * object, the property name is the name of the preference, and the
+     * old and new values are wrapped as objects.
+     * </p>
+     * <p>
+     * Note that the preferred way of re-initializing a preference to its
+     * default value is to call <code>setToDefault</code>.
+     * </p>
+     *
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     */
+    public void setValue(String name, double value);
+
+    /**
+     * Sets the current value of the float-valued preference with the
+     * given name.
+     * <p>
+     * A property change event is reported if the current value of the
+     * preference actually changes from its previous value. In the event
+     * object, the property name is the name of the preference, and the
+     * old and new values are wrapped as objects.
+     * </p>
+     * <p>
+     * Note that the preferred way of re-initializing a preference to its
+     * default value is to call <code>setToDefault</code>.
+     * </p>
+     *
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     */
+    public void setValue(String name, float value);
+
+    /**
+     * Sets the current value of the integer-valued preference with the
+     * given name.
+     * <p>
+     * A property change event is reported if the current value of the
+     * preference actually changes from its previous value. In the event
+     * object, the property name is the name of the preference, and the
+     * old and new values are wrapped as objects.
+     * </p>
+     * <p>
+     * Note that the preferred way of re-initializing a preference to its
+     * default value is to call <code>setToDefault</code>.
+     * </p>
+     *
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     */
+    public void setValue(String name, int value);
+
+    /**
+     * Sets the current value of the long-valued preference with the
+     * given name.
+     * <p>
+     * A property change event is reported if the current value of the
+     * preference actually changes from its previous value. In the event
+     * object, the property name is the name of the preference, and the
+     * old and new values are wrapped as objects.
+     * </p>
+     * <p>
+     * Note that the preferred way of re-initializing a preference to its
+     * default value is to call <code>setToDefault</code>.
+     * </p>
+     *
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     */
+    public void setValue(String name, long value);
+
+    /**
+     * Sets the current value of the string-valued preference with the
+     * given name.
+     * <p>
+     * A property change event is reported if the current value of the
+     * preference actually changes from its previous value. In the event
+     * object, the property name is the name of the preference, and the
+     * old and new values are wrapped as objects.
+     * </p>
+     * <p>
+     * Note that the preferred way of re-initializing a preference to its
+     * default value is to call <code>setToDefault</code>.
+     * </p>
+     *
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     */
+    public void setValue(String name, String value);
+
+    /**
+     * Sets the current value of the bool-valued preference with the
+     * given name.
+     * <p>
+     * A property change event is reported if the current value of the
+     * preference actually changes from its previous value. In the event
+     * object, the property name is the name of the preference, and the
+     * old and new values are wrapped as objects.
+     * </p>
+     * <p>
+     * Note that the preferred way of re-initializing a preference to its
+     * default value is to call <code>setToDefault</code>.
+     * </p>
+     *
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     */
+    public void setValue(String name, bool value);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/IntegerFieldEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * 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
+ *     <sgandon@nds.com> - Fix for bug 109389 - IntegerFieldEditor
+ *     does not fire property change all the time
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.IntegerFieldEditor;
+
+import org.eclipse.jface.preference.StringFieldEditor;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+import tango.util.Convert;
+
+/**
+ * A field editor for an integer type preference.
+ */
+public class IntegerFieldEditor : StringFieldEditor {
+    private int minValidValue = 0;
+
+    private int maxValidValue = int.max;
+
+    private static const int DEFAULT_TEXT_LIMIT = 10;
+
+    /**
+     * Creates a new integer field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates an integer field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText, Composite parent) {
+        this(name, labelText, parent, DEFAULT_TEXT_LIMIT);
+    }
+
+    /**
+     * Creates an integer field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     * @param textLimit the maximum number of characters in the text.
+     */
+    public this(String name, String labelText, Composite parent,
+            int textLimit) {
+        init(name, labelText);
+        setTextLimit(textLimit);
+        setEmptyStringAllowed(false);
+        setErrorMessage(JFaceResources
+                .getString("IntegerFieldEditor.errorMessage"));//$NON-NLS-1$
+        createControl(parent);
+    }
+
+    /**
+     * Sets the range of valid values for this field.
+     *
+     * @param min the minimum allowed value (inclusive)
+     * @param max the maximum allowed value (inclusive)
+     */
+    public void setValidRange(int min, int max) {
+        minValidValue = min;
+        maxValidValue = max;
+        setErrorMessage(JFaceResources.format(
+                "IntegerFieldEditor.errorMessageRange", //$NON-NLS-1$
+                min, max ));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StringFieldEditor.
+     * Checks whether the entered String is a valid integer or not.
+     */
+    protected override bool checkState() {
+
+        Text text = getTextControl();
+
+        if (text is null) {
+            return false;
+        }
+
+        String numberString = text.getText();
+        try {
+            int number = Integer.valueOf(numberString).intValue();
+            if (number >= minValidValue && number <= maxValidValue) {
+                clearErrorMessage();
+                return true;
+            }
+
+            showErrorMessage();
+            return false;
+
+        } catch (NumberFormatException e1) {
+            showErrorMessage();
+        }
+
+        return false;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doLoad() {
+        Text text = getTextControl();
+        if (text !is null) {
+            int value = getPreferenceStore().getInt(getPreferenceName());
+            text.setText( tango.text.convert.Integer.toString(value));//$NON-NLS-1$
+            oldValue = to!(String)( value ); //$NON-NLS-1$
+        }
+
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doLoadDefault() {
+        Text text = getTextControl();
+        if (text !is null) {
+            int value = getPreferenceStore().getDefaultInt(getPreferenceName());
+            text.setText(tango.text.convert.Integer.toString( value));//$NON-NLS-1$
+        }
+        valueChanged();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doStore() {
+        Text text = getTextControl();
+        if (text !is null) {
+            Integer i = new Integer(text.getText());
+            getPreferenceStore().setValue(getPreferenceName(), i.intValue());
+        }
+    }
+
+    /**
+     * Returns this field editor's current value as an integer.
+     *
+     * @return the value
+     * @exception NumberFormatException if the <code>String</code> does not
+     *   contain a parsable integer
+     */
+    public int getIntValue() {
+        return (new Integer(getStringValue())).intValue();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/JFacePreferences.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.preference.JFacePreferences;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ *
+ * JFacePreferences is a class used to administer the preferences used by JFace
+ * objects.
+ */
+public final class JFacePreferences {
+
+    /**
+     * Identifier for the Error Color
+     */
+    public static const String ERROR_COLOR = "ERROR_COLOR"; //$NON-NLS-1$
+
+    /**
+     * Identifier for the Hyperlink Color
+     */
+    public static const String HYPERLINK_COLOR = "HYPERLINK_COLOR"; //$NON-NLS-1$
+
+    /**
+     * Identifier for the Active Hyperlink Colour
+     */
+    public static const String ACTIVE_HYPERLINK_COLOR = "ACTIVE_HYPERLINK_COLOR"; //$NON-NLS-1$
+
+    /**
+     * Identifier for the color used to show extra informations in labels, as a
+     * qualified name. For example in 'Foo.txt - myproject/bar', the qualifier
+     * is '- myproject/bar'.
+     * 
+     * @since 3.4
+     */
+    public static final String QUALIFIER_COLOR = "QUALIFIER_COLOR"; //$NON-NLS-1$
+
+    /**
+     * Identifier for the color used to show label decorations For example in
+     * 'Foo.txt [1.16]', the decoration is '[1.16]'.
+     * 
+     * @since 3.4
+     */
+    public static final String DECORATIONS_COLOR = "DECORATIONS_COLOR"; //$NON-NLS-1$
+
+    /**
+     * Identifier for the color used to counter informations For example in
+     * 'Foo.txt (2 matches)', the counter information is '(2 matches)'.
+     * 
+     * @since 3.4
+     */
+    public static final String COUNTER_COLOR = "COUNTER_COLOR"; //$NON-NLS-1$
+
+    
+    /**
+     * Identifier for the color used for the background of content assist
+     * popup dialogs.
+     * 
+     * @since 3.4
+     */
+    public static final String CONTENT_ASSIST_BACKGROUND_COLOR = "CONTENT_ASSIST_BACKGROUND_COLOR"; //$NON-NLS-1$
+
+    /**
+     * Identifier for the color used for the foreground of content assist
+     * popup dialogs.
+     * 
+     * @since 3.4
+     */
+    public static final String CONTENT_ASSIST_FOREGROUND_COLOR = "CONTENT_ASSIST_FOREGROUND_COLOR"; //$NON-NLS-1$
+
+
+
+    private static IPreferenceStore preferenceStore;
+
+    /**
+     * Prevent construction.
+     */
+    private this() {
+    }
+
+    /**
+     * Return the preference store for the receiver.
+     * 
+     * @return IPreferenceStore or null
+     */
+    public static IPreferenceStore getPreferenceStore() {
+        return preferenceStore;
+    }
+
+    /**
+     * Set the preference store for the receiver.
+     * 
+     * @param store
+     *            IPreferenceStore
+     */
+    public static void setPreferenceStore(IPreferenceStore store) {
+        preferenceStore = store;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/ListEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,455 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.ListEditor;
+
+import org.eclipse.jface.preference.FieldEditor;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * An abstract field editor that manages a list of input values.
+ * The editor displays a list containing the values, buttons for
+ * adding and removing values, and Up and Down buttons to adjust
+ * the order of elements in the list.
+ * <p>
+ * Subclasses must implement the <code>parseString</code>,
+ * <code>createList</code>, and <code>getNewInputObject</code>
+ * framework methods.
+ * </p>
+ */
+public abstract class ListEditor : FieldEditor {
+
+    /**
+     * The list widget; <code>null</code> if none
+     * (before creation or after disposal).
+     */
+    private List list;
+
+    /**
+     * The button box containing the Add, Remove, Up, and Down buttons;
+     * <code>null</code> if none (before creation or after disposal).
+     */
+    private Composite buttonBox;
+
+    /**
+     * The Add button.
+     */
+    private Button addButton;
+
+    /**
+     * The Remove button.
+     */
+    private Button removeButton;
+
+    /**
+     * The Up button.
+     */
+    private Button upButton;
+
+    /**
+     * The Down button.
+     */
+    private Button downButton;
+
+    /**
+     * The selection listener.
+     */
+    private SelectionListener selectionListener;
+
+    /**
+     * Creates a new list field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a list field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    protected this(String name, String labelText, Composite parent) {
+        init(name, labelText);
+        createControl(parent);
+    }
+
+    /**
+     * Notifies that the Add button has been pressed.
+     */
+    private void addPressed() {
+        setPresentsDefaultValue(false);
+        String input = getNewInputObject();
+
+        if (input !is null) {
+            int index = list.getSelectionIndex();
+            if (index >= 0) {
+                list.add(input, index + 1);
+            } else {
+                list.add(input, 0);
+            }
+            selectionChanged();
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void adjustForNumColumns(int numColumns) {
+        Control control = getLabelControl();
+        (cast(GridData) control.getLayoutData()).horizontalSpan = numColumns;
+        (cast(GridData) list.getLayoutData()).horizontalSpan = numColumns - 1;
+    }
+
+    /**
+     * Creates the Add, Remove, Up, and Down button in the given button box.
+     *
+     * @param box the box for the buttons
+     */
+    private void createButtons(Composite box) {
+        addButton = createPushButton(box, "ListEditor.add");//$NON-NLS-1$
+        removeButton = createPushButton(box, "ListEditor.remove");//$NON-NLS-1$
+        upButton = createPushButton(box, "ListEditor.up");//$NON-NLS-1$
+        downButton = createPushButton(box, "ListEditor.down");//$NON-NLS-1$
+    }
+
+    /**
+     * Combines the given list of items into a single string.
+     * This method is the converse of <code>parseString</code>.
+     * <p>
+     * Subclasses must implement this method.
+     * </p>
+     *
+     * @param items the list of items
+     * @return the combined string
+     * @see #parseString
+     */
+    protected abstract String createList(String[] items);
+
+    /**
+     * Helper method to create a push button.
+     *
+     * @param parent the parent control
+     * @param key the resource name used to supply the button's label text
+     * @return Button
+     */
+    private Button createPushButton(Composite parent, String key) {
+        Button button = new Button(parent, SWT.PUSH);
+        button.setText(JFaceResources.getString(key));
+        button.setFont(parent.getFont());
+        GridData data = new GridData(GridData.FILL_HORIZONTAL);
+        int widthHint = convertHorizontalDLUsToPixels(button,
+                IDialogConstants.BUTTON_WIDTH);
+        data.widthHint = Math.max(widthHint, button.computeSize(SWT.DEFAULT,
+                SWT.DEFAULT, true).x);
+        button.setLayoutData(data);
+        button.addSelectionListener(getSelectionListener());
+        return button;
+    }
+
+    /**
+     * Creates a selection listener.
+     */
+    public void createSelectionListener() {
+        selectionListener = new class SelectionAdapter {
+            public void widgetSelected(SelectionEvent event) {
+                Widget widget = event.widget;
+                if (widget is addButton) {
+                    addPressed();
+                } else if (widget is removeButton) {
+                    removePressed();
+                } else if (widget is upButton) {
+                    upPressed();
+                } else if (widget is downButton) {
+                    downPressed();
+                } else if (widget is list) {
+                    selectionChanged();
+                }
+            }
+        };
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doFillIntoGrid(Composite parent, int numColumns) {
+        Control control = getLabelControl(parent);
+        GridData gd = new GridData();
+        gd.horizontalSpan = numColumns;
+        control.setLayoutData(gd);
+
+        list = getListControl(parent);
+        gd = new GridData(GridData.FILL_HORIZONTAL);
+        gd.verticalAlignment = GridData.FILL;
+        gd.horizontalSpan = numColumns - 1;
+        gd.grabExcessHorizontalSpace = true;
+        list.setLayoutData(gd);
+
+        buttonBox = getButtonBoxControl(parent);
+        gd = new GridData();
+        gd.verticalAlignment = GridData.BEGINNING;
+        buttonBox.setLayoutData(gd);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doLoad() {
+        if (list !is null) {
+            String s = getPreferenceStore().getString(getPreferenceName());
+            String[] array = parseString(s);
+            for (int i = 0; i < array.length; i++) {
+                list.add(array[i]);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doLoadDefault() {
+        if (list !is null) {
+            list.removeAll();
+            String s = getPreferenceStore().getDefaultString(
+                    getPreferenceName());
+            String[] array = parseString(s);
+            for (int i = 0; i < array.length; i++) {
+                list.add(array[i]);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doStore() {
+        String s = createList(list.getItems());
+        if (s !is null) {
+            getPreferenceStore().setValue(getPreferenceName(), s);
+        }
+    }
+
+    /**
+     * Notifies that the Down button has been pressed.
+     */
+    private void downPressed() {
+        swap(false);
+    }
+
+    /**
+     * Returns this field editor's button box containing the Add, Remove,
+     * Up, and Down button.
+     *
+     * @param parent the parent control
+     * @return the button box
+     */
+    public Composite getButtonBoxControl(Composite parent) {
+        if (buttonBox is null) {
+            buttonBox = new Composite(parent, SWT.NULL);
+            GridLayout layout = new GridLayout();
+            layout.marginWidth = 0;
+            buttonBox.setLayout(layout);
+            createButtons(buttonBox);
+            buttonBox.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    addButton = null;
+                    removeButton = null;
+                    upButton = null;
+                    downButton = null;
+                    buttonBox = null;
+                }
+            });
+
+        } else {
+            checkParent(buttonBox, parent);
+        }
+
+        selectionChanged();
+        return buttonBox;
+    }
+
+    /**
+     * Returns this field editor's list control.
+     *
+     * @param parent the parent control
+     * @return the list control
+     */
+    public List getListControl(Composite parent) {
+        if (list is null) {
+            list = new List(parent, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL
+                    | SWT.H_SCROLL);
+            list.setFont(parent.getFont());
+            list.addSelectionListener(getSelectionListener());
+            list.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    list = null;
+                }
+            });
+        } else {
+            checkParent(list, parent);
+        }
+        return list;
+    }
+
+    /**
+     * Creates and returns a new item for the list.
+     * <p>
+     * Subclasses must implement this method.
+     * </p>
+     *
+     * @return a new item
+     */
+    protected abstract String getNewInputObject();
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public override int getNumberOfControls() {
+        return 2;
+    }
+
+    /**
+     * Returns this field editor's selection listener.
+     * The listener is created if nessessary.
+     *
+     * @return the selection listener
+     */
+    private SelectionListener getSelectionListener() {
+        if (selectionListener is null) {
+            createSelectionListener();
+        }
+        return selectionListener;
+    }
+
+    /**
+     * Returns this field editor's shell.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method.
+     * </p>
+     *
+     * @return the shell
+     */
+    protected Shell getShell() {
+        if (addButton is null) {
+            return null;
+        }
+        return addButton.getShell();
+    }
+
+    /**
+     * Splits the given string into a list of strings.
+     * This method is the converse of <code>createList</code>.
+     * <p>
+     * Subclasses must implement this method.
+     * </p>
+     *
+     * @param stringList the string
+     * @return an array of <code>String</code>
+     * @see #createList
+     */
+    protected abstract String[] parseString(String stringList);
+
+    /**
+     * Notifies that the Remove button has been pressed.
+     */
+    private void removePressed() {
+        setPresentsDefaultValue(false);
+        int index = list.getSelectionIndex();
+        if (index >= 0) {
+            list.remove(index);
+            selectionChanged();
+        }
+    }
+
+    /**
+     * Notifies that the list selection has changed.
+     */
+    private void selectionChanged() {
+
+        int index = list.getSelectionIndex();
+        int size = list.getItemCount();
+
+        removeButton.setEnabled(index >= 0);
+        upButton.setEnabled(size > 1 && index > 0);
+        downButton.setEnabled(size > 1 && index >= 0 && index < size - 1);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public override void setFocus() {
+        if (list !is null) {
+            list.setFocus();
+        }
+    }
+
+    /**
+     * Moves the currently selected item up or down.
+     *
+     * @param up <code>true</code> if the item should move up,
+     *  and <code>false</code> if it should move down
+     */
+    private void swap(bool up) {
+        setPresentsDefaultValue(false);
+        int index = list.getSelectionIndex();
+        int target = up ? index - 1 : index + 1;
+
+        if (index >= 0) {
+            String[] selection = list.getSelection();
+            Assert.isTrue(selection.length is 1);
+            list.remove(index);
+            list.add(selection[0], target);
+            list.setSelection(target);
+        }
+        selectionChanged();
+    }
+
+    /**
+     * Notifies that the Up button has been pressed.
+     */
+    private void upPressed() {
+        swap(true);
+    }
+
+    /*
+     * @see FieldEditor.setEnabled(bool,Composite).
+     */
+    public override void setEnabled(bool enabled, Composite parent) {
+        super.setEnabled(enabled, parent);
+        getListControl(parent).setEnabled(enabled);
+        addButton.setEnabled(enabled);
+        removeButton.setEnabled(enabled);
+        upButton.setEnabled(enabled);
+        downButton.setEnabled(enabled);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/PathEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.PathEditor;
+
+import org.eclipse.jface.preference.ListEditor;
+
+import tango.io.FilePath;
+static import tango.io.Path;
+import tango.io.model.IFile;
+
+// import java.util.ArrayList;
+// import java.util.StringTokenizer;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.DirectoryDialog;
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Set;
+import tango.text.Util;
+
+/**
+ * A field editor to edit directory paths.
+ */
+public class PathEditor : ListEditor {
+
+    /**
+     * The last path, or <code>null</code> if none.
+     */
+    private String lastPath;
+
+    /**
+     * The special label text for directory chooser,
+     * or <code>null</code> if none.
+     */
+    private String dirChooserLabelText;
+
+    /**
+     * Creates a new path field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a path field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param dirChooserLabelText the label text displayed for the directory chooser
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText,
+            String dirChooserLabelText, Composite parent) {
+        init(name, labelText);
+        this.dirChooserLabelText = dirChooserLabelText;
+        createControl(parent);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ListEditor.
+     * Creates a single string from the given array by separating each
+     * string with the appropriate OS-specific path separator.
+     */
+    protected override String createList(String[] items) {
+        StringBuffer path = new StringBuffer("");//$NON-NLS-1$
+
+        for (int i = 0; i < items.length; i++) {
+            path.append(items[i]);
+            path.append(FileConst.SystemPathString);
+        }
+        return path.toString();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ListEditor.
+     * Creates a new path element by means of a directory dialog.
+     */
+    protected override String getNewInputObject() {
+
+        DirectoryDialog dialog = new DirectoryDialog(getShell());
+        if (dirChooserLabelText !is null) {
+            dialog.setMessage(dirChooserLabelText);
+        }
+        if (lastPath !is null) {
+            if ((new FilePath(tango.io.Path.standard(lastPath))).exists()) {
+                dialog.setFilterPath(lastPath);
+            }
+        }
+        String dir = dialog.open();
+        if (dir !is null) {
+            dir = java.lang.all.trim(dir);
+            if (dir.length is 0) {
+                return null;
+            }
+            lastPath = dir;
+        }
+        return dir;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ListEditor.
+     */
+    protected override String[] parseString(String stringList) {
+        return tango.text.Util.delimit(stringList.dup, FileConst.SystemPathString
+                ~ "\n\r");//$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/PreferenceContentProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 org.eclipse.jface.preference.PreferenceContentProvider;
+
+import org.eclipse.jface.preference.IPreferenceNode;
+import org.eclipse.jface.preference.PreferenceManager;
+
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Provides a tree model for <code>PreferenceManager</code> content.
+ *
+ * @since 3.0
+ */
+public class PreferenceContentProvider : ITreeContentProvider {
+
+    private PreferenceManager manager;
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.IContentProvider#dispose()
+     */
+    public void dispose() {
+        manager = null;
+    }
+
+    /**
+     * Find the parent of the provided node.  Will search recursivly through the
+     * preference tree.
+     *
+     * @param parent the possible parent node.
+     * @param target the target child node.
+     * @return the parent node of the child node.
+     */
+    private IPreferenceNode findParent(IPreferenceNode parent,
+            IPreferenceNode target) {
+        if (parent.getId().equals(target.getId())) {
+            return null;
+        }
+
+        IPreferenceNode found = parent.findSubNode(target.getId());
+        if (found !is null) {
+            return parent;
+        }
+
+        IPreferenceNode[] children = parent.getSubNodes();
+
+        for (int i = 0; i < children.length; i++) {
+            found = findParent(children[i], target);
+            if (found !is null) {
+                return found;
+            }
+        }
+
+        return null;
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
+     */
+    public Object[] getChildren(Object parentElement) {//must be an instance of <code>IPreferenceNode</code>.
+        return arraycast!(Object)((cast(IPreferenceNode) parentElement).getSubNodes());
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
+     */
+    public Object[] getElements(Object inputElement) {// must be an instance of <code>PreferenceManager</code>.
+        return getChildren(cast(Object)(cast(PreferenceManager) inputElement).getRoot_package());
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
+     */
+    public Object getParent(Object element) {//must be an instance of <code>IPreferenceNode</code>.
+        IPreferenceNode targetNode = cast(IPreferenceNode) element;
+        IPreferenceNode root = manager.getRoot_package();
+        return cast(Object)findParent(root, targetNode);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
+     */
+    public bool hasChildren(Object element) {
+        return getChildren(element).length > 0;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
+     */
+    public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+        manager = cast(PreferenceManager) newInput;
+    }
+    /**
+     * Set the manager for the preferences.
+     * @param manager The manager to set.
+     *
+     * @since 3.1
+     */
+    protected void setManager(PreferenceManager manager) {
+        this.manager = manager;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/PreferenceConverter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,590 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.preference.PreferenceConverter;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+
+// import java.util.Arrays;
+// import java.util.StringTokenizer;
+
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.StringConverter;
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * A utility class for dealing with preferences whose values are
+ * common SWT objects (color, points, rectangles, and font data).
+ * The static methods on this class handle the conversion between
+ * the SWT objects and their string representations.
+ * <p>
+ * Usage:
+ * <pre>
+ * IPreferenceStore store = ...;
+ * PreferenceConverter.setValue(store, "bg", new RGB(127,127,127));
+ * ...
+ * RBG bgColor = PreferenceConverter.getValue(store, "bg");
+ * </pre>
+ * </p>
+ * <p>
+ * This class contains static methods and fields only and cannot
+ * be instantiated.
+ * </p>
+ * Note: touching this class has the side effect of creating a display (static initializer).
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public class PreferenceConverter {
+
+    /**
+     * The default-default value for point preferences
+     * (the origin, <code>(0,0)</code>).
+     */
+    public static const Point POINT_DEFAULT_DEFAULT;
+
+    /**
+     * The default-default value for rectangle preferences
+     * (the empty rectangle <code>(0,0,0,0)</code>).
+     */
+    public static const Rectangle RECTANGLE_DEFAULT_DEFAULT;
+
+    /**
+     * The default-default value for color preferences
+     * (black, <code>RGB(0,0,0)</code>).
+     */
+    public static const RGB COLOR_DEFAULT_DEFAULT;
+
+    private static const String ENTRY_SEPARATOR = ";"; //$NON-NLS-1$
+
+    /**
+     * The default-default value for <code>FontData[]</code> preferences.
+     */
+    private static bool FONTDATA_ARRAY_DEFAULT_DEFAULT_initialized = false;
+    private static FontData[] FONTDATA_ARRAY_DEFAULT_DEFAULT_;
+    public static FontData[] FONTDATA_ARRAY_DEFAULT_DEFAULT(){
+        if( !FONTDATA_ARRAY_DEFAULT_DEFAULT_initialized ){
+            synchronized( PreferenceConverter.classinfo ){
+                if( !FONTDATA_ARRAY_DEFAULT_DEFAULT_initialized ){
+                    Display display = Display.getCurrent();
+                    if (display is null) {
+                        display = Display.getDefault ();
+                    }
+
+                    FONTDATA_ARRAY_DEFAULT_DEFAULT_ = display.getSystemFont().getFontData();
+                    FONTDATA_ARRAY_DEFAULT_DEFAULT_initialized = true;
+                }
+            }
+        }
+        return FONTDATA_ARRAY_DEFAULT_DEFAULT_;
+    }
+
+    /**
+     * The default-default value for <code>FontData</code> preferences.
+     */
+    private static FontData FONTDATA_DEFAULT_DEFAULT_;
+    private static bool FONTDATA_DEFAULT_DEFAULT_initialized = false;
+    public static FontData FONTDATA_DEFAULT_DEFAULT(){
+        if( !FONTDATA_DEFAULT_DEFAULT_initialized ){
+            FONTDATA_ARRAY_DEFAULT_DEFAULT();
+            synchronized( PreferenceConverter.classinfo ){
+                if( !FONTDATA_DEFAULT_DEFAULT_initialized ){
+                    /**
+                    * The default-default value for <code>FontData</code> preferences.
+                    * This is left in for compatibility purposes. It is recommended that
+                    * FONTDATA_ARRAY_DEFAULT_DEFAULT is actually used.
+                    */
+
+                    FONTDATA_DEFAULT_DEFAULT_ = FONTDATA_ARRAY_DEFAULT_DEFAULT()[0];
+                    FONTDATA_DEFAULT_DEFAULT_initialized = true;
+                }
+            }
+        }
+        return FONTDATA_DEFAULT_DEFAULT_;
+    }
+
+    static this() {
+        POINT_DEFAULT_DEFAULT = new Point(0, 0);
+        RECTANGLE_DEFAULT_DEFAULT = new Rectangle(0,
+            0, 0, 0);
+        COLOR_DEFAULT_DEFAULT = new RGB(0, 0, 0);
+    }
+
+    /* (non-Javadoc)
+     * private constructor to prevent instantiation.
+     */
+    private this() {
+        //no-op
+    }
+
+    /**
+     * Helper method to construct a color from the given string.
+     * @param value the indentifier for the color
+     * @return RGB
+     */
+    private static RGB basicGetColor(String value) {
+
+        if (IPreferenceStore.STRING_DEFAULT_DEFAULT.equals(value)) {
+            return COLOR_DEFAULT_DEFAULT;
+        }
+
+        RGB color = StringConverter.asRGB(value, null);
+        if (color is null) {
+            return COLOR_DEFAULT_DEFAULT;
+        }
+        return color;
+    }
+
+    /**
+     * Helper method to construct a <code>FontData</code> from the given string.
+     * String is in the form FontData;FontData; in order that
+     * multiple FontDatas can be defined.
+     * @param value the identifier for the font
+     * @return FontData[]
+     *
+     * @since 3.0
+     */
+    public static FontData[] basicGetFontData(String value) {
+        if (IPreferenceStore.STRING_DEFAULT_DEFAULT.equals(value)) {
+            return FONTDATA_ARRAY_DEFAULT_DEFAULT;
+        }
+
+        //Read in all of them to get the value
+        auto tokens = tango.text.Util.delimit( value, ENTRY_SEPARATOR);
+        int numTokens = tokens.length;
+        FontData[] fontData = new FontData[numTokens];
+
+        for (int i = 0; i < numTokens; i++) {
+            try {
+                fontData[i] = new FontData(tokens[i]);
+            } catch (SWTException error) {
+                return FONTDATA_ARRAY_DEFAULT_DEFAULT;
+            } catch (IllegalArgumentException error) {
+                return FONTDATA_ARRAY_DEFAULT_DEFAULT;
+            }
+        }
+        return fontData;
+    }
+
+    /**
+     * Reads the supplied string and returns its corresponding
+     * FontData. If it cannot be read then the default FontData
+     * will be returned.
+     *
+     * @param fontDataValue the string value for the font data
+     * @return the font data
+     */
+    public static FontData[] readFontData(String fontDataValue) {
+        return basicGetFontData(fontDataValue);
+    }
+
+    /**
+     * Helper method to construct a point from the given string.
+     * @param value
+     * @return Point
+     */
+    private static Point basicGetPoint(String value) {
+        Point dp = new Point(POINT_DEFAULT_DEFAULT.x, POINT_DEFAULT_DEFAULT.y);
+        if (IPreferenceStore.STRING_DEFAULT_DEFAULT.equals(value)) {
+            return dp;
+        }
+        return StringConverter.asPoint(value, dp);
+    }
+
+    /**
+     *  Helper method to construct a rectangle from the given string.
+     * @param value
+     * @return Rectangle
+     */
+    private static Rectangle basicGetRectangle(String value) {
+        // We can't just return RECTANGLE_DEFAULT_DEFAULT because
+        // a rectangle object doesn't have value semantik.
+        Rectangle dr = new Rectangle(RECTANGLE_DEFAULT_DEFAULT.x,
+                RECTANGLE_DEFAULT_DEFAULT.y, RECTANGLE_DEFAULT_DEFAULT.width,
+                RECTANGLE_DEFAULT_DEFAULT.height);
+
+        if (IPreferenceStore.STRING_DEFAULT_DEFAULT.equals(value)) {
+            return dr;
+        }
+        return StringConverter.asRectangle(value, dr);
+    }
+
+    /**
+     * Returns the current value of the color-valued preference with the
+     * given name in the given preference store.
+     * Returns the default-default value (<code>COLOR_DEFAULT_DEFAULT</code>)
+     * if there is no preference with the given name, or if the current value
+     * cannot be treated as a color.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the color-valued preference
+     */
+    public static RGB getColor(IPreferenceStore store, String name) {
+        return basicGetColor(store.getString(name));
+    }
+
+    /**
+     * Returns the default value for the color-valued preference
+     * with the given name in the given preference store.
+     * Returns the default-default value (<code>COLOR_DEFAULT_DEFAULT</code>)
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as a color.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the default value of the preference
+     */
+    public static RGB getDefaultColor(IPreferenceStore store, String name) {
+        return basicGetColor(store.getDefaultString(name));
+    }
+
+    /**
+     * Returns the default value array for the font-valued preference
+     * with the given name in the given preference store.
+     * Returns the default-default value (<code>FONTDATA_ARRAY_DEFAULT_DEFAULT</code>)
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as font data.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the default value of the preference
+     */
+    public static FontData[] getDefaultFontDataArray(IPreferenceStore store,
+            String name) {
+        return basicGetFontData(store.getDefaultString(name));
+    }
+
+    /**
+     * Returns a single default value for the font-valued preference
+     * with the given name in the given preference store.
+     * Returns the default-default value (<code>FONTDATA_DEFAULT_DEFAULT</code>)
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as font data.
+     * This method is provided for backwards compatibility. It is
+     * recommended that <code>getDefaultFontDataArray</code> is
+     * used instead.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the default value of the preference
+     */
+    public static FontData getDefaultFontData(IPreferenceStore store,
+            String name) {
+        return getDefaultFontDataArray(store, name)[0];
+    }
+
+    /**
+     * Returns the default value for the point-valued preference
+     * with the given name in the given preference store.
+     * Returns the default-default value (<code>POINT_DEFAULT_DEFAULT</code>)
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as a point.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the default value of the preference
+     */
+    public static Point getDefaultPoint(IPreferenceStore store, String name) {
+        return basicGetPoint(store.getDefaultString(name));
+    }
+
+    /**
+     * Returns the default value for the rectangle-valued preference
+     * with the given name in the given preference store.
+     * Returns the default-default value (<code>RECTANGLE_DEFAULT_DEFAULT</code>)
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as a rectangle.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the default value of the preference
+     */
+    public static Rectangle getDefaultRectangle(IPreferenceStore store,
+            String name) {
+        return basicGetRectangle(store.getDefaultString(name));
+    }
+
+    /**
+     * Returns the current value of the font-valued preference with the
+     * given name in the given preference store.
+     * Returns the default-default value (<code>FONTDATA_ARRAY_DEFAULT_DEFAULT</code>)
+     * if there is no preference with the given name, or if the current value
+     * cannot be treated as font data.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the font-valued preference
+     */
+    public static FontData[] getFontDataArray(IPreferenceStore store,
+            String name) {
+        return basicGetFontData(store.getString(name));
+    }
+
+    /**
+     * Returns the current value of the first entry of the
+     * font-valued preference with the
+     * given name in the given preference store.
+     * Returns the default-default value (<code>FONTDATA_ARRAY_DEFAULT_DEFAULT</code>)
+     * if there is no preference with the given name, or if the current value
+     * cannot be treated as font data.
+     * This API is provided for backwards compatibility. It is
+     * recommended that <code>getFontDataArray</code> is used instead.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the font-valued preference
+     */
+    public static FontData getFontData(IPreferenceStore store, String name) {
+        return getFontDataArray(store, name)[0];
+    }
+
+    /**
+     * Returns the current value of the point-valued preference with the
+     * given name in the given preference store.
+     * Returns the default-default value (<code>POINT_DEFAULT_DEFAULT</code>)
+     * if there is no preference with the given name, or if the current value
+     * cannot be treated as a point.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the point-valued preference
+     */
+    public static Point getPoint(IPreferenceStore store, String name) {
+        return basicGetPoint(store.getString(name));
+    }
+
+    /**
+     * Returns the current value of the rectangle-valued preference with the
+     * given name in the given preference store.
+     * Returns the default-default value (<code>RECTANGLE_DEFAULT_DEFAULT</code>)
+     * if there is no preference with the given name, or if the current value
+     * cannot be treated as a rectangle.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the rectangle-valued preference
+     */
+    public static Rectangle getRectangle(IPreferenceStore store, String name) {
+        return basicGetRectangle(store.getString(name));
+    }
+
+    /**
+     * Sets the default value of the preference with the given name
+     * in the given preference store. As FontDatas are stored as
+     * arrays this method is only provided for backwards compatibility.
+     * Use <code>setDefault(IPreferenceStore, String, FontData[])</code>
+     * instead.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new default value of the preference
+     */
+    public static void setDefault(IPreferenceStore store, String name,
+            FontData value) {
+        FontData[] fontDatas = new FontData[1];
+        fontDatas[0] = value;
+        setDefault(store, name, fontDatas);
+    }
+
+    /**
+     * Sets the default value of the preference with the given name
+     * in the given preference store.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new default value of the preference
+     */
+    public static void setDefault(IPreferenceStore store, String name,
+            FontData[] value) {
+        store.setDefault(name, getStoredRepresentation(value));
+    }
+
+    /**
+     * Sets the default value of the preference with the given name
+     * in the given preference store.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new default value of the preference
+     */
+    public static void setDefault(IPreferenceStore store, String name,
+            Point value) {
+        store.setDefault(name, StringConverter.asString(value));
+    }
+
+    /**
+     * Sets the default value of the preference with the given name
+     * in the given preference store.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new default value of the preference
+     */
+    public static void setDefault(IPreferenceStore store, String name,
+            Rectangle value) {
+        store.setDefault(name, StringConverter.asString(value));
+    }
+
+    /**
+     * Sets the default value of the preference with the given name
+     * in the given preference store.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new default value of the preference
+     */
+    public static void setDefault(IPreferenceStore store, String name, RGB value) {
+        store.setDefault(name, StringConverter.asString(value));
+    }
+
+    /**
+     * Sets the current value of the preference with the given name
+     * in the given preference store.
+     * <p>
+     * Included for backwards compatibility.  This method is equivalent to
+     * </code>setValue(store, name, new FontData[]{value})</code>.
+     * </p>
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     */
+    public static void setValue(IPreferenceStore store, String name,
+            FontData value) {
+        setValue(store, name, [ value ]);
+    }
+
+    /**
+     * Sets the current value of the preference with the given name
+     * in the given preference store. This method also sets the corresponding
+     * key in the JFace font registry to the value and fires a
+     * property change event to listeners on the preference store.
+     *
+     * <p>
+     * Note that this API does not update any other settings that may
+     * be dependant upon it. Only the value in the preference store
+     * and in the font registry is updated.
+     * </p>
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     *
+     * @see #putValue(IPreferenceStore, String, FontData[])
+     */
+    public static void setValue(IPreferenceStore store, String name,
+            FontData[] value) {
+        FontData[] oldValue = getFontDataArray(store, name);
+        // see if the font has changed
+        if (!Arrays.equals(oldValue, value)) {
+            store.putValue(name, getStoredRepresentation(value));
+            JFaceResources.getFontRegistry().put(name, value);
+            store.firePropertyChangeEvent(name, new ArrayWrapperT!(FontData)(oldValue), new ArrayWrapperT!(FontData)(value));
+        }
+    }
+
+    /**
+     * Sets the current value of the preference with the given name
+     * in the given preference store. This method does not update
+     * the font registry or fire a property change event.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     *
+     * @see PreferenceConverter#setValue(IPreferenceStore, String, FontData[])
+     */
+    public static void putValue(IPreferenceStore store, String name,
+            FontData[] value) {
+        FontData[] oldValue = getFontDataArray(store, name);
+        // see if the font has changed
+        if (!Arrays.equals(oldValue, value)) {
+            store.putValue(name, getStoredRepresentation(value));
+        }
+    }
+
+    /**
+     * Returns the stored representation of the given array of FontData objects.
+     * The stored representation has the form FontData;FontData;
+     * Only includes the non-null entries.
+     *
+     * @param fontData the array of FontData objects
+     * @return the stored representation of the FontData objects
+     * @since 3.0
+     */
+    public static String getStoredRepresentation(FontData[] fontData) {
+        StringBuffer buffer = new StringBuffer();
+        for (int i = 0; i < fontData.length; i++) {
+            if (fontData[i] !is null) {
+                buffer.append(fontData[i].toString());
+                buffer.append(ENTRY_SEPARATOR);
+            }
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Sets the current value of the preference with the given name
+     * in the given preference store.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     */
+    public static void setValue(IPreferenceStore store, String name, Point value) {
+        Point oldValue = getPoint(store, name);
+        if (oldValue is null || !oldValue.opEquals(value)) {
+            store.putValue(name, StringConverter.asString(value));
+            store.firePropertyChangeEvent(name, oldValue, value);
+        }
+    }
+
+    /**
+     * Sets the current value of the preference with the given name
+     * in the given preference store.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     */
+    public static void setValue(IPreferenceStore store, String name,
+            Rectangle value) {
+        Rectangle oldValue = getRectangle(store, name);
+        if (oldValue is null || !oldValue.opEquals(value)) {
+            store.putValue(name, StringConverter.asString(value));
+            store.firePropertyChangeEvent(name, oldValue, value);
+        }
+    }
+
+    /**
+     * Sets the current value of the preference with the given name
+     * in the given preference store.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     */
+    public static void setValue(IPreferenceStore store, String name, RGB value) {
+        RGB oldValue = getColor(store, name);
+        if (oldValue is null || !oldValue.opEquals(value)) {
+            store.putValue(name, StringConverter.asString(value));
+            store.firePropertyChangeEvent(name, oldValue, value);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/PreferenceDialog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1589 @@
+/*******************************************************************************
+ * 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
+ *     Teddy Walker <teddy.walker@googlemail.com>
+ *      - Bug 188056 [Preferences] PreferencePages have to less indent in PreferenceDialog
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.PreferenceDialog;
+
+import org.eclipse.jface.preference.IPreferencePageContainer;
+import org.eclipse.jface.preference.IPreferencePage;
+import org.eclipse.jface.preference.IPreferenceNode;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.IPersistentPreferenceStore;
+import org.eclipse.jface.preference.PreferenceManager;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.jface.preference.PreferenceLabelProvider;
+import org.eclipse.jface.preference.PreferenceContentProvider;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.HelpEvent;
+import org.eclipse.swt.events.HelpListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Sash;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.DialogMessageArea;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.IPageChangeProvider;
+import org.eclipse.jface.dialogs.IPageChangedListener;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.PageChangedEvent;
+import org.eclipse.jface.dialogs.TrayDialog;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.Policy;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.util.SafeRunnable;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.viewers.ViewerFilter;
+
+import java.lang.all;
+import java.util.List;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A preference dialog is a hierarchical presentation of preference pages. Each
+ * page is represented by a node in the tree shown on the left hand side of the
+ * dialog; when a node is selected, the corresponding page is shown on the right
+ * hand side.
+ */
+public class PreferenceDialog : TrayDialog, IPreferencePageContainer, IPageChangeProvider {
+    /**
+     * Layout for the page container.
+     *
+     */
+    private class PageLayout : Layout {
+        public override Point computeSize(Composite composite, int wHint, int hHint, bool force) {
+            if (wHint !is SWT.DEFAULT && hHint !is SWT.DEFAULT) {
+                return new Point(wHint, hHint);
+            }
+            int x = minimumPageSize.x;
+            int y = minimumPageSize.y;
+            Control[] children = composite.getChildren();
+            for (int i = 0; i < children.length; i++) {
+                Point size = children[i].computeSize(SWT.DEFAULT, SWT.DEFAULT, force);
+                x = Math.max(x, size.x);
+                y = Math.max(y, size.y);
+            }
+
+            //As pages can implement thier own computeSize
+            //take it into account
+            if(currentPage !is null){
+                Point size = currentPage.computeSize();
+                x = Math.max(x, size.x);
+                y = Math.max(y, size.y);
+            }
+
+            if (wHint !is SWT.DEFAULT) {
+                x = wHint;
+            }
+            if (hHint !is SWT.DEFAULT) {
+                y = hHint;
+            }
+            return new Point(x, y);
+        }
+
+        public override void layout(Composite composite, bool force) {
+            Rectangle rect = composite.getClientArea();
+            Control[] children = composite.getChildren();
+            for (int i = 0; i < children.length; i++) {
+                children[i].setSize(rect.width, rect.height);
+            }
+        }
+    }
+
+    //The id of the last page that was selected
+    private static String lastPreferenceId = null;
+
+    //The last known tree width
+    private static int lastTreeWidth = 180;
+
+    /**
+     * Indentifier for the error image
+     */
+    public static const String PREF_DLG_IMG_TITLE_ERROR = DLG_IMG_MESSAGE_ERROR;
+
+    /**
+     * Title area fields
+     */
+    public static const String PREF_DLG_TITLE_IMG = "preference_dialog_title_image"; //$NON-NLS-1$
+
+    /**
+     * Return code used when dialog failed
+     */
+    protected static const int FAILED = 2;
+
+    /**
+     * The current preference page, or <code>null</code> if there is none.
+     */
+    private IPreferencePage currentPage;
+
+    private DialogMessageArea messageArea;
+
+    private Point lastShellSize;
+
+    private IPreferenceNode lastSuccessfulNode;
+
+    /**
+     * The minimum page size; 400 by 400 by default.
+     *
+     * @see #setMinimumPageSize(Point)
+     */
+    private Point minimumPageSize;
+
+    /**
+     * The OK button.
+     */
+    private Button okButton;
+
+    /**
+     * The Composite in which a page is shown.
+     */
+    private Composite pageContainer;
+
+    /**
+     * The preference manager.
+     */
+    private PreferenceManager preferenceManager;
+
+    /**
+     * Flag for the presence of the error message.
+     */
+    private bool showingError = false;
+
+    /**
+     * Preference store, initially <code>null</code> meaning none.
+     *
+     * @see #setPreferenceStore
+     */
+    private IPreferenceStore preferenceStore;
+
+    private Composite titleArea;
+
+    /**
+     * The tree viewer.
+     */
+    private TreeViewer treeViewer;
+
+    private ListenerList pageChangedListeners;
+
+    /**
+     *  Composite with a FormLayout to contain the title area
+     */
+    Composite formTitleComposite;
+
+    private ScrolledComposite scrolled;
+
+    /**
+     * Creates a new preference dialog under the control of the given preference
+     * manager.
+     *
+     * @param parentShell
+     *            the parent shell
+     * @param manager
+     *            the preference manager
+     */
+    public this(Shell parentShell, PreferenceManager manager) {
+        minimumPageSize = new Point(400, 400);
+        pageChangedListeners = new ListenerList();
+        super(parentShell);
+        preferenceManager = manager;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int)
+     */
+    protected override void buttonPressed(int buttonId) {
+        switch (buttonId) {
+        case IDialogConstants.OK_ID: {
+            okPressed();
+            return;
+        }
+        case IDialogConstants.CANCEL_ID: {
+            cancelPressed();
+            return;
+        }
+        case IDialogConstants.HELP_ID: {
+            helpPressed();
+            return;
+        }
+        default:
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.Dialog#cancelPressed()
+     */
+    protected override void cancelPressed() {
+        // Inform all pages that we are cancelling
+        Iterator nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER).iterator();
+        while (nodes.hasNext()) {
+            IPreferenceNode node = cast(IPreferenceNode) nodes.next();
+            if (getPage(node) !is null) {
+
+                // this_: strange workaround for compiler error with dmd 1.028 in run()
+                SafeRunnable.run(dgSafeRunnable((IPreferenceNode node_, PreferenceDialog this_) {
+                    if (!this_.getPage(node_).performCancel()) {
+                        return;
+                    }
+                }, cast(IPreferenceNode)node, this ));
+            }
+        }
+        setReturnCode(CANCEL);
+        close();
+    }
+
+    /**
+     * Clear the last selected node. This is so that we not chache the last
+     * selection in case of an error.
+     */
+    void clearSelectedNode() {
+        setSelectedNodePreference(null);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.window.Window#close()
+     */
+    public override bool close() {
+
+        //Do this is in a SafeRunnable as it may run client code
+        SafeRunnable runnable = new class SafeRunnable{
+            /* (non-Javadoc)
+             * @see org.eclipse.core.runtime.ISafeRunnable#run()
+             */
+            public void run() {
+                auto nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER);
+                for (int i = 0; i < nodes.size(); i++) {
+                    IPreferenceNode node = cast(IPreferenceNode) nodes.get(i);
+                    node.disposeResources();
+                }
+
+            }
+
+            /* (non-Javadoc)
+             * @see org.eclipse.jface.util.SafeRunnable#handleException(java.lang.Throwable)
+             */
+            public void handleException(Exception e) {
+                super.handleException(e);
+                clearSelectedNode();//Do not cache a node with problems
+            }
+        };
+
+        SafeRunner.run(runnable);
+
+        return super.close();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
+     */
+    protected override void configureShell(Shell newShell) {
+        super.configureShell(newShell);
+        newShell.setText(JFaceResources.getString("PreferenceDialog.title")); //$NON-NLS-1$
+        newShell.addShellListener(new class ShellAdapter {
+            public void shellActivated(ShellEvent e) {
+                if (lastShellSize is null) {
+                    lastShellSize = getShell().getSize();
+                }
+            }
+
+        });
+
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.window.Window#constrainShellSize()
+     */
+    protected override void constrainShellSize() {
+        super.constrainShellSize();
+        // record opening shell size
+        if (lastShellSize is null) {
+            lastShellSize = getShell().getSize();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
+     */
+    protected override void createButtonsForButtonBar(Composite parent) {
+        // create OK and Cancel buttons by default
+        okButton = createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
+        getShell().setDefaultButton(okButton);
+        createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.window.Window#createContents(org.eclipse.swt.widgets.Composite)
+     */
+    protected override Control createContents(Composite parent) {
+        Control[1] control;
+        BusyIndicator.showWhile(getShell().getDisplay(), new class(parent,control) Runnable {
+            Composite parent_;
+            Control[] control_;
+            this(Composite a,Control[] b){
+                parent_=a;
+                control_=b;
+            }
+            public void run() {
+                control_[0] = callSuperCreateContents(parent_);
+                // Add the first page
+                selectSavedItem();
+            }
+        });
+
+        return control[0];
+    }
+    private Control callSuperCreateContents( Composite c ){
+        return super.createContents( c );
+    }
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+     */
+    protected override Control createDialogArea(Composite parent) {
+        final Composite composite = cast(Composite) super.createDialogArea(parent);
+        GridLayout parentLayout = (cast(GridLayout) composite.getLayout());
+        parentLayout.numColumns = 4;
+        parentLayout.marginHeight = 0;
+        parentLayout.marginWidth = 0;
+        parentLayout.verticalSpacing = 0;
+        parentLayout.horizontalSpacing = 0;
+
+        composite.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
+
+        Control treeControl = createTreeAreaContents(composite);
+        createSash(composite,treeControl);
+
+        Label versep = new Label(composite, SWT.SEPARATOR | SWT.VERTICAL);
+        GridData verGd = new GridData(GridData.FILL_VERTICAL | GridData.GRAB_VERTICAL);
+
+        versep.setLayoutData(verGd);
+        versep.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true));
+
+        Composite pageAreaComposite = new Composite(composite, SWT.NONE);
+        pageAreaComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+        GridLayout layout = new GridLayout(1, true);
+        layout.marginHeight = 0;
+        layout.marginWidth = 0;
+        layout.verticalSpacing = 0;
+        pageAreaComposite.setLayout(layout);
+
+        formTitleComposite = new Composite(pageAreaComposite, SWT.NONE);
+        FormLayout titleLayout = new FormLayout();
+        titleLayout.marginWidth = 0;
+        titleLayout.marginHeight = 0;
+        formTitleComposite.setLayout(titleLayout);
+
+        GridData titleGridData = new GridData(GridData.FILL_HORIZONTAL);
+        titleGridData.horizontalIndent = IDialogConstants.HORIZONTAL_MARGIN;
+        formTitleComposite.setLayoutData(titleGridData);
+
+        // Build the title area and separator line
+        Composite titleComposite = new Composite(formTitleComposite, SWT.NONE);
+        layout = new GridLayout();
+        layout.marginBottom = 5;
+        layout.marginHeight = 0;
+        layout.marginWidth = 0;
+        layout.horizontalSpacing = 0;
+        titleComposite.setLayout(layout);
+
+        FormData titleFormData = new FormData();
+        titleFormData.top = new FormAttachment(0,0);
+        titleFormData.left = new FormAttachment(0,0);
+        titleFormData.right = new FormAttachment(100,0);
+        titleFormData.bottom = new FormAttachment(100,0);
+
+        titleComposite.setLayoutData(titleFormData);
+        createTitleArea(titleComposite);
+
+        Label separator = new Label(pageAreaComposite, SWT.HORIZONTAL | SWT.SEPARATOR);
+
+        separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));
+
+
+        // Build the Page container
+        pageContainer = createPageContainer(pageAreaComposite);
+        GridData pageContainerData = new GridData(GridData.FILL_BOTH);
+        pageContainerData.horizontalIndent = IDialogConstants.HORIZONTAL_MARGIN;
+        pageContainer.setLayoutData(pageContainerData);
+        // Build the separator line
+        Label bottomSeparator = new Label(parent, SWT.HORIZONTAL | SWT.SEPARATOR);
+        bottomSeparator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));
+        return composite;
+    }
+
+    /**
+     * Create the sash with right control on the right. Note
+     * that this method assumes GridData for the layout data
+     * of the rightControl.
+     * @param composite
+     * @param rightControl
+     * @return Sash
+     *
+     * @since 3.1
+     */
+    protected Sash createSash(Composite composite, Control rightControl) {
+        Sash sash = new Sash(composite, SWT.VERTICAL);
+        sash.setLayoutData(new GridData(GridData.FILL_VERTICAL));
+        sash.setBackground(composite.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
+        // the following listener resizes the tree control based on sash deltas.
+        // If necessary, it will also grow/shrink the dialog.
+        sash.addListener(SWT.Selection, new class(composite,rightControl,sash) Listener {
+            Composite composite_;
+            Control rightControl_;
+            Sash sash_;
+            this(Composite a,Control b,Sash c){
+                composite_=a;
+                rightControl_=b;
+                sash_=c;
+            }
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+             */
+            public void handleEvent(Event event) {
+                if (event.detail is SWT.DRAG) {
+                    return;
+                }
+                int shift = event.x - sash_.getBounds().x;
+                GridData data = cast(GridData) rightControl_.getLayoutData();
+                int newWidthHint = data.widthHint + shift;
+                if (newWidthHint < 20) {
+                    return;
+                }
+                Point computedSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
+                Point currentSize = getShell().getSize();
+                // if the dialog wasn't of a custom size we know we can shrink
+                // it if necessary based on sash movement.
+                bool customSize = !computedSize.opEquals(currentSize);
+                data.widthHint = newWidthHint;
+                setLastTreeWidth(newWidthHint);
+                composite_.layout(true);
+                // recompute based on new widget size
+                computedSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
+                // if the dialog was of a custom size then increase it only if
+                // necessary.
+                if (customSize) {
+                    computedSize.x = Math.max(computedSize.x, currentSize.x);
+                }
+                computedSize.y = Math.max(computedSize.y, currentSize.y);
+                if (computedSize.opEquals(currentSize)) {
+                    return;
+                }
+                setShellSize(computedSize.x, computedSize.y);
+                lastShellSize = getShell().getSize();
+            }
+        });
+        return sash;
+    }
+
+    /**
+     * Creates the inner page container.
+     *
+     * @param parent
+     * @return Composite
+     */
+    protected Composite createPageContainer(Composite parent) {
+
+        Composite outer = new Composite(parent, SWT.NONE);
+
+        GridData outerData = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL
+                | GridData.GRAB_VERTICAL);
+        outerData.horizontalIndent = IDialogConstants.HORIZONTAL_MARGIN;
+
+        outer.setLayout(new GridLayout());
+        outer.setLayoutData(outerData);
+
+        //Create an outer composite for spacing
+        scrolled = new ScrolledComposite(outer, SWT.V_SCROLL | SWT.H_SCROLL);
+
+        scrolled.setExpandHorizontal(true);
+        scrolled.setExpandVertical(true);
+
+        GridData scrolledData = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL
+                | GridData.GRAB_VERTICAL);
+
+        scrolled.setLayoutData(scrolledData);
+
+        Composite result = new Composite(scrolled, SWT.NONE);
+
+        GridData resultData = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL
+                | GridData.GRAB_VERTICAL);
+
+        result.setLayout(getPageLayout());
+        result.setLayoutData(resultData);
+
+        scrolled.setContent(result);
+
+        return result;
+    }
+
+    /**
+     * Return the layout for the composite that contains
+     * the pages.
+     * @return PageLayout
+     *
+     * @since 3.1
+     */
+    protected Layout getPageLayout() {
+        return new PageLayout();
+    }
+
+    /**
+     * Creates the wizard's title area.
+     *
+     * @param parent
+     *            the SWT parent for the title area composite.
+     * @return the created title area composite.
+     */
+    protected Composite createTitleArea(Composite parent) {
+        // Create the title area which will contain
+        // a title, message, and image.
+        int margins = 2;
+        titleArea = new Composite(parent, SWT.NONE);
+        FormLayout layout = new FormLayout();
+        layout.marginHeight = 0;
+        layout.marginWidth = margins;
+        titleArea.setLayout(layout);
+
+
+        GridData layoutData = new GridData(GridData.FILL_HORIZONTAL);
+        layoutData.verticalAlignment = SWT.TOP;
+        titleArea.setLayoutData(layoutData);
+
+        // Message label
+        messageArea = new DialogMessageArea();
+        messageArea.createContents(titleArea);
+
+        titleArea.addControlListener(new class ControlAdapter {
+            /* (non-Javadoc)
+             * @see org.eclipse.swt.events.ControlAdapter#controlResized(org.eclipse.swt.events.ControlEvent)
+             */
+            public void controlResized(ControlEvent e) {
+                updateMessage();
+            }
+        });
+
+        IPropertyChangeListener fontListener = new class IPropertyChangeListener {
+            public void propertyChange(PropertyChangeEvent event) {
+                if (JFaceResources.BANNER_FONT.equals(event.getProperty())) {
+                    updateMessage();
+                }
+                if (JFaceResources.DIALOG_FONT.equals(event.getProperty())) {
+                    updateMessage();
+                    Font dialogFont = JFaceResources.getDialogFont();
+                    updateTreeFont(dialogFont);
+                    Control[] children = (cast(Composite) buttonBar).getChildren();
+                    for (int i = 0; i < children.length; i++) {
+                        children[i].setFont(dialogFont);
+                    }
+                }
+            }
+        };
+
+        titleArea.addDisposeListener(new class(fontListener) DisposeListener {
+            IPropertyChangeListener fontListener_;
+            this(IPropertyChangeListener a){
+                fontListener_=a;
+            }
+            public void widgetDisposed(DisposeEvent event) {
+                JFaceResources.getFontRegistry().removeListener(fontListener_);
+            }
+        });
+        JFaceResources.getFontRegistry().addListener(fontListener);
+        messageArea.setTitleLayoutData(createMessageAreaData());
+        messageArea.setMessageLayoutData(createMessageAreaData());
+        return titleArea;
+    }
+
+    /**
+     * Create the layout data for the message area.
+     *
+     * @return FormData for the message area.
+     */
+    private FormData createMessageAreaData() {
+        FormData messageData = new FormData();
+        messageData.top = new FormAttachment(0);
+        messageData.bottom = new FormAttachment(100);
+        messageData.right = new FormAttachment(100);
+        messageData.left = new FormAttachment(0);
+        return messageData;
+    }
+
+    /**
+     * @param parent
+     *            the SWT parent for the tree area controls.
+     * @return the new <code>Control</code>.
+     * @since 3.0
+     */
+    protected Control createTreeAreaContents(Composite parent) {
+        // Build the tree an put it into the composite.
+        treeViewer = createTreeViewer(parent);
+        treeViewer.setInput(getPreferenceManager());
+        updateTreeFont(JFaceResources.getDialogFont());
+        layoutTreeAreaControl(treeViewer.getControl());
+        return treeViewer.getControl();
+    }
+
+    /**
+     * Create a new <code>TreeViewer</code>.
+     *
+     * @param parent
+     *            the parent <code>Composite</code>.
+     * @return the <code>TreeViewer</code>.
+     * @since 3.0
+     */
+    protected TreeViewer createTreeViewer(Composite parent) {
+        final TreeViewer viewer = new TreeViewer(parent, SWT.NONE);
+        addListeners(viewer);
+        viewer.setLabelProvider(new PreferenceLabelProvider());
+        viewer.setContentProvider(new PreferenceContentProvider());
+        return viewer;
+    }
+
+    /**
+     * Add the listeners to the tree viewer.
+     * @param viewer
+     *
+     * @since 3.1
+     */
+    protected void addListeners(TreeViewer viewer) {
+        viewer.addPostSelectionChangedListener(new class(viewer) ISelectionChangedListener {
+            TreeViewer viewer_;
+            this(TreeViewer a){
+                viewer_=a;
+            }
+            private void handleError() {
+                try {
+                    // remove the listener temporarily so that the events caused
+                    // by the error handling dont further cause error handling
+                    // to occur.
+                    viewer_.removePostSelectionChangedListener(this);
+                    showPageFlippingAbortDialog();
+                    selectCurrentPageAgain();
+                    clearSelectedNode();
+                } finally {
+                    viewer_.addPostSelectionChangedListener(this);
+                }
+            }
+
+            public void selectionChanged(SelectionChangedEvent event) {
+                Object selection = cast(Object) getSingleSelection(event.getSelection());
+                if (cast(IPreferenceNode)selection ) {
+                    BusyIndicator.showWhile(getShell().getDisplay(), new class(selection) Runnable {
+                        Object selection_;
+                        this(Object o){ selection_=o; }
+                        public void run() {
+                            if (!isCurrentPageValid()) {
+                                handleError();
+                            } else if (!showPage(cast(IPreferenceNode) selection_)) {
+                                // Page flipping wasn't successful
+                                handleError();
+                            } else {
+                                // Everything went well
+                                lastSuccessfulNode = cast(IPreferenceNode) selection_;
+                            }
+                        }
+                    });
+                }
+            }
+        });
+        (cast(Tree) viewer.getControl()).addSelectionListener(new class(viewer) SelectionAdapter {
+            TreeViewer viewer_;
+            this(TreeViewer a){
+                viewer_=a;
+            }
+            public void widgetDefaultSelected(SelectionEvent event) {
+                ISelection selection = viewer_.getSelection();
+                if (selection.isEmpty()) {
+                    return;
+                }
+                IPreferenceNode singleSelection = getSingleSelection(selection);
+                bool expanded = viewer_.getExpandedState(cast(Object)singleSelection);
+                viewer_.setExpandedState(cast(Object)singleSelection, !expanded);
+            }
+        });
+        //Register help listener on the tree to use context sensitive help
+        viewer.getControl().addHelpListener(new class HelpListener {
+            public void helpRequested(HelpEvent event) {
+                // call perform help on the current page
+                if (currentPage !is null) {
+                    currentPage.performHelp();
+                }
+            }
+        });
+    }
+
+    /**
+     * Find the <code>IPreferenceNode</code> that has data the same id as the
+     * supplied value.
+     *
+     * @param nodeId
+     *            the id to search for.
+     * @return <code>IPreferenceNode</code> or <code>null</code> if not
+     *         found.
+     */
+    protected IPreferenceNode findNodeMatching(String nodeId) {
+        List nodes = preferenceManager.getElements(PreferenceManager.POST_ORDER);
+        for (Iterator i = nodes.iterator(); i.hasNext();) {
+            IPreferenceNode node = cast(IPreferenceNode) i.next();
+            if (node.getId().equals(nodeId)) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Get the last known right side width.
+     *
+     * @return the width.
+     */
+    protected int getLastRightWidth() {
+        return lastTreeWidth;
+    }
+
+    /**
+     * Returns the preference mananger used by this preference dialog.
+     *
+     * @return the preference mananger
+     */
+    public PreferenceManager getPreferenceManager() {
+        return preferenceManager;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.preference.IPreferencePageContainer#getPreferenceStore()
+     */
+    public IPreferenceStore getPreferenceStore() {
+        return preferenceStore;
+    }
+
+    /**
+     * Get the name of the selected item preference
+     *
+     * @return String
+     */
+    protected String getSelectedNodePreference() {
+        return lastPreferenceId;
+    }
+
+    /**
+     * @param selection
+     *            the <code>ISelection</code> to examine.
+     * @return the first element, or null if empty.
+     */
+    protected IPreferenceNode getSingleSelection(ISelection selection) {
+        if (!selection.isEmpty()) {
+            IStructuredSelection structured = cast(IStructuredSelection) selection;
+            if (cast(IPreferenceNode)structured.getFirstElement() ) {
+                return cast(IPreferenceNode) structured.getFirstElement();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @return the <code>TreeViewer</code> for this dialog.
+     * @since 3.3
+     */
+    public TreeViewer getTreeViewer() {
+        return treeViewer;
+    }
+
+    /**
+     * Save the values specified in the pages.
+     * <p>
+     * The default implementation of this framework method saves all pages of
+     * type <code>PreferencePage</code> (if their store needs saving and is a
+     * <code>PreferenceStore</code>).
+     * </p>
+     * <p>
+     * Subclasses may override.
+     * </p>
+     */
+    protected void handleSave() {
+        Iterator nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER).iterator();
+        while (nodes.hasNext()) {
+            IPreferenceNode node = cast(IPreferenceNode) nodes.next();
+            IPreferencePage page = node.getPage();
+            if (cast(PreferencePage)page ) {
+                // Save now in case tbe workbench does not shutdown cleanly
+                IPreferenceStore store = (cast(PreferencePage) page).getPreferenceStore();
+                if (store !is null && store.needsSaving()
+                        && cast(IPersistentPreferenceStore)store ) {
+                    try {
+                        (cast(IPersistentPreferenceStore) store).save();
+                    } catch (IOException e) {
+                        String message =JFaceResources.format(
+                                "PreferenceDialog.saveErrorMessage", [ page.getTitle(), e.msg ]); //$NON-NLS-1$
+                        Policy.getStatusHandler().show(
+                                new Status(IStatus.ERROR, Policy.JFACE, message, e),
+                                JFaceResources.getString("PreferenceDialog.saveErrorTitle")); //$NON-NLS-1$
+
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Notifies that the window's close button was pressed, the close menu was
+     * selected, or the ESCAPE key pressed.
+     * <p>
+     * The default implementation of this framework method sets the window's
+     * return code to <code>CANCEL</code> and closes the window using
+     * <code>close</code>. Subclasses may extend or reimplement.
+     * </p>
+     */
+    protected override void handleShellCloseEvent() {
+        // handle the same as pressing cancel
+        cancelPressed();
+    }
+
+    /**
+     * Notifies of the pressing of the Help button.
+     * <p>
+     * The default implementation of this framework method calls
+     * <code>performHelp</code> on the currently active page.
+     * </p>
+     */
+    protected /+override+/ void helpPressed() {
+        if (currentPage !is null) {
+            currentPage.performHelp();
+        }
+    }
+
+    /**
+     * Returns whether the current page is valid.
+     *
+     * @return <code>false</code> if the current page is not valid, or or
+     *         <code>true</code> if the current page is valid or there is no
+     *         current page
+     */
+    protected bool isCurrentPageValid() {
+        if (currentPage is null) {
+            return true;
+        }
+        return currentPage.isValid();
+    }
+
+    /**
+     * @param control
+     *            the <code>Control</code> to lay out.
+     * @since 3.0
+     */
+    protected void layoutTreeAreaControl(Control control) {
+        GridData gd = new GridData(GridData.FILL_VERTICAL);
+        gd.widthHint = getLastRightWidth();
+        gd.verticalSpan = 1;
+        control.setLayoutData(gd);
+    }
+
+    /**
+     * The preference dialog implementation of this <code>Dialog</code>
+     * framework method sends <code>performOk</code> to all pages of the
+     * preference dialog, then calls <code>handleSave</code> on this dialog to
+     * save any state, and then calls <code>close</code> to close this dialog.
+     */
+    protected override void okPressed() {
+        SafeRunnable.run(new class SafeRunnable {
+            private bool errorOccurred;
+
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.core.runtime.ISafeRunnable#run()
+             */
+            public void run() {
+                getButton(IDialogConstants.OK_ID).setEnabled(false);
+                errorOccurred = false;
+                bool hasFailedOK = false;
+                try {
+                    // Notify all the pages and give them a chance to abort
+                    Iterator nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER)
+                            .iterator();
+                    while (nodes.hasNext()) {
+                        IPreferenceNode node = cast(IPreferenceNode) nodes.next();
+                        IPreferencePage page = node.getPage();
+                        if (page !is null) {
+                            if (!page.performOk()){
+                                hasFailedOK = true;
+                                return;
+                            }
+                        }
+                    }
+                } catch (Exception e) {
+                    handleException(e);
+                } finally {
+                    //Don't bother closing if the OK failed
+                    if(hasFailedOK){
+                        setReturnCode(FAILED);
+                        getButton(IDialogConstants.OK_ID).setEnabled(true);
+                        //return;
+                    }
+                    else{
+
+                        if (!errorOccurred) {
+                            //Give subclasses the choice to save the state of the
+                            //preference pages.
+                            handleSave();
+                        }
+                        setReturnCode(OK);
+                        close();
+                    }
+                }
+            }
+
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.core.runtime.ISafeRunnable#handleException(java.lang.Throwable)
+             */
+            public void handleException(Exception e) {
+                errorOccurred = true;
+
+                Policy.getLog().log(new Status(IStatus.ERROR, Policy.JFACE, 0, e.toString(), e));
+
+                clearSelectedNode();
+                String message = JFaceResources.getString("SafeRunnable.errorMessage"); //$NON-NLS-1$
+
+                Policy.getStatusHandler().show(
+                        new Status(IStatus.ERROR, Policy.JFACE, message, e),
+                        JFaceResources.getString("Error")); //$NON-NLS-1$
+
+            }
+        });
+    }
+
+    /**
+     * Selects the page determined by <code>lastSuccessfulNode</code> in the
+     * page hierarchy.
+     */
+    void selectCurrentPageAgain() {
+        if (lastSuccessfulNode is null) {
+            return;
+        }
+        getTreeViewer().setSelection(new StructuredSelection(cast(Object)lastSuccessfulNode));
+        currentPage.setVisible(true);
+    }
+
+    /**
+     * Selects the saved item in the tree of preference pages. If it cannot do
+     * this it saves the first one.
+     */
+    protected void selectSavedItem() {
+        IPreferenceNode node = findNodeMatching(getSelectedNodePreference());
+        if (node is null) {
+            IPreferenceNode[] nodes = preferenceManager.getRootSubNodes();
+            ViewerComparator comparator = getTreeViewer().getComparator();
+            if (comparator !is null) {
+                comparator.sort(null, arraycast!(Object)(nodes));
+            }
+            ViewerFilter[] filters = getTreeViewer().getFilters();
+            for (int i = 0; i < nodes.length; i++) {
+                IPreferenceNode selectedNode = nodes[i];
+                // See if it passes all filters
+                for (int j = 0; j < filters.length; j++) {
+                    if (!filters[j].select(this.treeViewer, cast(Object)preferenceManager
+                            .getRoot_package(), cast(Object)selectedNode)) {
+                        selectedNode = null;
+                        break;
+                    }
+                }
+                // if it passes all filters select it
+                if (selectedNode !is null) {
+                    node = selectedNode;
+                    break;
+                }
+            }
+        }
+        if (node !is null) {
+            getTreeViewer().setSelection(new StructuredSelection(cast(Object)node), true);
+            // Keep focus in tree. See bugs 2692, 2621, and 6775.
+            getTreeViewer().getControl().setFocus();
+        }
+    }
+
+    /**
+     * Display the given error message. The currently displayed message is saved
+     * and will be redisplayed when the error message is set to
+     * <code>null</code>.
+     *
+     * @param newErrorMessage
+     *            the errorMessage to display or <code>null</code>
+     */
+    public void setErrorMessage(String newErrorMessage) {
+        if (newErrorMessage is null) {
+            messageArea.clearErrorMessage();
+        } else {
+            messageArea.updateText(newErrorMessage, IMessageProvider.ERROR);
+        }
+    }
+
+    /**
+     * Save the last known tree width.
+     *
+     * @param width
+     *            the width.
+     */
+    private void setLastTreeWidth(int width) {
+        lastTreeWidth = width;
+    }
+
+    /**
+     * Set the message text. If the message line currently displays an error,
+     * the message is stored and will be shown after a call to clearErrorMessage
+     * <p>
+     * Shortcut for <code>setMessage(newMessage, NONE)</code>
+     * </p>
+     *
+     * @param newMessage
+     *            the message, or <code>null</code> to clear the message
+     */
+    public void setMessage(String newMessage) {
+        setMessage(newMessage, IMessageProvider.NONE);
+    }
+
+    /**
+     * Sets the message for this dialog with an indication of what type of
+     * message it is.
+     * <p>
+     * The valid message types are one of <code>NONE</code>,
+     * <code>INFORMATION</code>,<code>WARNING</code>, or
+     * <code>ERROR</code>.
+     * </p>
+     * <p>
+     * Note that for backward compatibility, a message of type
+     * <code>ERROR</code> is different than an error message (set using
+     * <code>setErrorMessage</code>). An error message overrides the current
+     * message until the error message is cleared. This method replaces the
+     * current message and does not affect the error message.
+     * </p>
+     *
+     * @param newMessage
+     *            the message, or <code>null</code> to clear the message
+     * @param newType
+     *            the message type
+     * @since 2.0
+     */
+    public void setMessage(String newMessage, int newType) {
+        messageArea.updateText(newMessage, newType);
+    }
+
+    /**
+     * Sets the minimum page size.
+     *
+     * @param minWidth
+     *            the minimum page width
+     * @param minHeight
+     *            the minimum page height
+     * @see #setMinimumPageSize(Point)
+     */
+    public void setMinimumPageSize(int minWidth, int minHeight) {
+        minimumPageSize.x = minWidth;
+        minimumPageSize.y = minHeight;
+    }
+
+    /**
+     * Sets the minimum page size.
+     *
+     * @param size
+     *            the page size encoded as <code>new Point(width,height)</code>
+     * @see #setMinimumPageSize(int,int)
+     */
+    public void setMinimumPageSize(Point size) {
+        minimumPageSize.x = size.x;
+        minimumPageSize.y = size.y;
+    }
+
+    /**
+     * Sets the preference store for this preference dialog.
+     *
+     * @param store
+     *            the preference store
+     * @see #getPreferenceStore
+     */
+    public void setPreferenceStore(IPreferenceStore store) {
+        Assert.isNotNull(cast(Object)store);
+        preferenceStore = store;
+    }
+
+    /**
+     * Save the currently selected node.
+     */
+    private void setSelectedNode() {
+        String storeValue = null;
+        IStructuredSelection selection = cast(IStructuredSelection) getTreeViewer().getSelection();
+        if (selection.size() is 1) {
+            IPreferenceNode node = cast(IPreferenceNode) selection.getFirstElement();
+            storeValue = node.getId();
+        }
+        setSelectedNodePreference(storeValue);
+    }
+
+    /**
+     * Sets the name of the selected item preference. Public equivalent to
+     * <code>setSelectedNodePreference</code>.
+     *
+     * @param pageId
+     *            The identifier for the page
+     * @since 3.0
+     */
+    public void setSelectedNode(String pageId) {
+        setSelectedNodePreference(pageId);
+    }
+
+    /**
+     * Sets the name of the selected item preference.
+     *
+     * @param pageId
+     *            The identifier for the page
+     */
+    protected void setSelectedNodePreference(String pageId) {
+        lastPreferenceId = pageId;
+    }
+
+    /**
+     * Changes the shell size to the given size, ensuring that it is no larger
+     * than the display bounds.
+     *
+     * @param width
+     *            the shell width
+     * @param height
+     *            the shell height
+     */
+    private void setShellSize(int width, int height) {
+        Rectangle preferred = getShell().getBounds();
+        preferred.width = width;
+        preferred.height = height;
+        getShell().setBounds(getConstrainedShellBounds(preferred));
+    }
+
+    /**
+     * Shows the preference page corresponding to the given preference node.
+     * Does nothing if that page is already current.
+     *
+     * @param node
+     *            the preference node, or <code>null</code> if none
+     * @return <code>true</code> if the page flip was successful, and
+     *         <code>false</code> is unsuccessful
+     */
+    protected bool showPage(IPreferenceNode node) {
+        if (node is null) {
+            return false;
+        }
+        // Create the page if nessessary
+        if (node.getPage() is null) {
+            createPage(node);
+        }
+        if (node.getPage() is null) {
+            return false;
+        }
+        IPreferencePage newPage = getPage(node);
+        if (newPage is currentPage) {
+            return true;
+        }
+        if (currentPage !is null) {
+            if (!currentPage.okToLeave()) {
+                return false;
+            }
+        }
+        IPreferencePage oldPage = currentPage;
+        currentPage = newPage;
+        // Set the new page's container
+        currentPage.setContainer(this);
+        // Ensure that the page control has been created
+        // (this allows lazy page control creation)
+        if (currentPage.getControl() is null) {
+            bool[1] failed; failed[0] = false;
+            SafeRunnable.run(new class(failed) ISafeRunnable {
+                bool[] failed_;
+                this(bool[] a){
+                    this.failed_=a;
+                }
+                public void handleException(Exception e) {
+                    this.failed_[0] = true;
+                }
+
+                public void run() {
+                    createPageControl(currentPage, pageContainer);
+                }
+            });
+            if (failed[0]) {
+                return false;
+            }
+            // the page is responsible for ensuring the created control is
+            // accessable
+            // via getControl.
+            Assert.isNotNull(currentPage.getControl());
+        }
+        // Force calculation of the page's description label because
+        // label can be wrapped.
+        Point[1] size;
+        Point failed = new Point(-1, -1);
+        SafeRunnable.run(new class(size,failed) ISafeRunnable {
+            Point[] size_;
+            Point failed_;
+            this(Point[] a,Point b){
+                size_=a;
+                failed_=b;
+            }
+            public void handleException(Exception e) {
+                size_[0] = failed_;
+            }
+
+            public void run() {
+                size_[0] = currentPage.computeSize();
+            }
+        });
+        if (size[0].opEquals(failed)) {
+            return false;
+        }
+        Point contentSize = size[0];
+        // Do we need resizing. Computation not needed if the
+        // first page is inserted since computing the dialog's
+        // size is done by calling dialog.open().
+        // Also prevent auto resize if the user has manually resized
+        Shell shell = getShell();
+        Point shellSize = shell.getSize();
+        if (oldPage !is null) {
+            Rectangle rect = pageContainer.getClientArea();
+            Point containerSize = new Point(rect.width, rect.height);
+            int hdiff = contentSize.x - containerSize.x;
+            int vdiff = contentSize.y - containerSize.y;
+            if ((hdiff > 0 || vdiff > 0) && shellSize.opEquals(lastShellSize)) {
+                    hdiff = Math.max(0, hdiff);
+                    vdiff = Math.max(0, vdiff);
+                    setShellSize(shellSize.x + hdiff, shellSize.y + vdiff);
+                    lastShellSize = shell.getSize();
+                    if (currentPage.getControl().getSize().x is 0) {
+                        currentPage.getControl().setSize(containerSize);
+                    }
+
+            } else {
+                currentPage.setSize(containerSize);
+            }
+        }
+
+        scrolled.setMinSize(contentSize);
+        // Ensure that all other pages are invisible
+        // (including ones that triggered an exception during
+        // their creation).
+        Control[] children = pageContainer.getChildren();
+        Control currentControl = currentPage.getControl();
+        for (int i = 0; i < children.length; i++) {
+            if (children[i] !is currentControl) {
+                children[i].setVisible(false);
+            }
+        }
+        // Make the new page visible
+        currentPage.setVisible(true);
+        if (oldPage !is null) {
+            oldPage.setVisible(false);
+        }
+        // update the dialog controls
+        update();
+        return true;
+    }
+
+    /**
+     * Create the page for the node.
+     * @param node
+     *
+     * @since 3.1
+     */
+    protected void createPage(IPreferenceNode node) {
+        node.createPage();
+    }
+
+    /**
+     * Get the page for the node.
+     * @param node
+     * @return IPreferencePage
+     *
+     * @since 3.1
+     */
+    protected IPreferencePage getPage(IPreferenceNode node) {
+        return node.getPage();
+    }
+
+    /**
+     * Shows the "Page Flipping abort" dialog.
+     */
+    void showPageFlippingAbortDialog() {
+        MessageDialog.openError(getShell(), JFaceResources
+                .getString("AbortPageFlippingDialog.title"), //$NON-NLS-1$
+                JFaceResources.getString("AbortPageFlippingDialog.message")); //$NON-NLS-1$
+    }
+
+    /**
+     * Updates this dialog's controls to reflect the current page.
+     */
+    protected void update() {
+        // Update the title bar
+        updateTitle();
+        // Update the message line
+        updateMessage();
+        // Update the buttons
+        updateButtons();
+        //Saved the selected node in the preferences
+        setSelectedNode();
+        firePageChanged(new PageChangedEvent(this, cast(Object)getCurrentPage()));
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.preference.IPreferencePageContainer#updateButtons()
+     */
+    public void updateButtons() {
+        okButton.setEnabled(isCurrentPageValid());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.preference.IPreferencePageContainer#updateMessage()
+     */
+    public void updateMessage() {
+        String message = null;
+        String errorMessage = null;
+        if(currentPage !is null){
+            message = currentPage.getMessage();
+            errorMessage = currentPage.getErrorMessage();
+        }
+        int messageType = IMessageProvider.NONE;
+        if (message !is null && cast(IMessageProvider)currentPage ) {
+            messageType = (cast(IMessageProvider) currentPage).getMessageType();
+        }
+
+        if (errorMessage is null){
+            if (showingError) {
+                // we were previously showing an error
+                showingError = false;
+            }
+        }
+        else {
+            message = errorMessage;
+            messageType = IMessageProvider.ERROR;
+            if (!showingError) {
+                // we were not previously showing an error
+                showingError = true;
+            }
+        }
+        messageArea.updateText(message,messageType);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.preference.IPreferencePageContainer#updateTitle()
+     */
+    public void updateTitle() {
+        if(currentPage is null) {
+            return;
+        }
+        messageArea.showTitle(currentPage.getTitle(), currentPage.getImage());
+    }
+
+    /**
+     * Update the tree to use the specified <code>Font</code>.
+     *
+     * @param dialogFont
+     *            the <code>Font</code> to use.
+     * @since 3.0
+     */
+    protected void updateTreeFont(Font dialogFont) {
+        getTreeViewer().getControl().setFont(dialogFont);
+    }
+
+    /**
+     * Returns the currentPage.
+     * @return IPreferencePage
+     * @since 3.1
+     */
+    protected IPreferencePage getCurrentPage() {
+        return currentPage;
+    }
+
+    /**
+     * Sets the current page.
+     * @param currentPage
+     *
+     * @since 3.1
+     */
+    protected void setCurrentPage(IPreferencePage currentPage) {
+        this.currentPage = currentPage;
+    }
+
+    /**
+     * Set the treeViewer.
+     * @param treeViewer
+     *
+     * @since 3.1
+     */
+    protected void setTreeViewer(TreeViewer treeViewer) {
+        this.treeViewer = treeViewer;
+    }
+
+    /**
+     * Get the composite that is showing the page.
+     *
+     * @return Composite.
+     *
+     * @since 3.1
+     */
+    protected Composite getPageContainer() {
+        return this.pageContainer;
+    }
+
+    /**
+     * Set the composite that is showing the page.
+     * @param pageContainer Composite
+     *
+     * @since 3.1
+     */
+    protected void setPageContainer(Composite pageContainer) {
+        this.pageContainer = pageContainer;
+    }
+    /**
+     * Create the page control for the supplied page.
+     *
+     * @param page - the preference page to be shown
+     * @param parent - the composite to parent the page
+     *
+     * @since 3.1
+     */
+    protected void createPageControl(IPreferencePage page, Composite parent) {
+        page.createControl(parent);
+    }
+
+    /**
+     * @see org.eclipse.jface.dialogs.IPageChangeProvider#getSelectedPage()
+     *
+     * @since 3.1
+     */
+    public Object getSelectedPage() {
+            return cast(Object)getCurrentPage();
+        }
+
+    /**
+     * @see org.eclipse.jface.dialogs.IPageChangeProvider#addPageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener)
+     * @since 3.1
+     */
+    public void addPageChangedListener(IPageChangedListener listener) {
+        pageChangedListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * @see org.eclipse.jface.dialogs.IPageChangeProvider#removePageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener)
+     * @since 3.1
+     */
+    public void removePageChangedListener(IPageChangedListener listener) {
+        pageChangedListeners.remove(cast(Object)listener);
+
+    }
+
+    /**
+     * Notifies any selection changed listeners that the selected page
+     * has changed.
+     * Only listeners registered at the time this method is called are notified.
+     *
+     * @param event a selection changed event
+     *
+     * @see IPageChangedListener#pageChanged
+     *
+     * @since 3.1
+     */
+    protected void firePageChanged(PageChangedEvent event) {
+        Object[] listeners = pageChangedListeners.getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            SafeRunnable.run(new class(event,cast(IPageChangedListener) listeners[i]) SafeRunnable {
+                PageChangedEvent event_;
+                IPageChangedListener l;
+                this(PageChangedEvent a,IPageChangedListener b){
+                    event_=a;
+                    l =b ;
+                }
+                public void run() {
+                    l.pageChanged(event_);
+                }
+            });
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.eclipse.jface.dialogs.Dialog#isResizable()
+     */
+    protected bool isResizable() {
+        return true;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/PreferenceLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 org.eclipse.jface.preference.PreferenceLabelProvider;
+
+import org.eclipse.jface.preference.IPreferenceNode;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.jface.viewers.LabelProvider;
+
+import java.lang.all;
+
+/**
+ * Provides labels for <code>IPreferenceNode</code> objects.
+ *
+ * @since 3.0
+ */
+public class PreferenceLabelProvider : LabelProvider {
+
+    /**
+     * @param element must be an instance of <code>IPreferenceNode</code>.
+     * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
+     */
+    public override String getText(Object element) {
+        return (cast(IPreferenceNode) element).getLabelText();
+    }
+
+    /**
+     * @param element must be an instance of <code>IPreferenceNode</code>.
+     * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object)
+     */
+    public override Image getImage(Object element) {
+        return (cast(IPreferenceNode) element).getLabelImage();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/PreferenceManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,276 @@
+/*******************************************************************************
+ * 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
+ *     Jan-Hendrik Diederich, Bredex GmbH - bug 201052
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.PreferenceManager;
+
+import org.eclipse.jface.preference.PreferenceNode;
+import org.eclipse.jface.preference.IPreferenceNode;
+
+// import java.util.List;
+// import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import tango.text.Util;
+
+/**
+ * A preference manager maintains a hierarchy of preference nodes and
+ * associated preference pages.
+ */
+public class PreferenceManager {
+    /**
+     * Pre-order traversal means visit the root first,
+     * then the children.
+     */
+    public static const int PRE_ORDER = 0;
+
+    /**
+     * Post-order means visit the children, and then the root.
+     */
+    public static const int POST_ORDER = 1;
+
+    /**
+     * The id of the root node.
+     */
+    private final static String ROOT_NODE_ID = ""; //$NON-NLS-1$
+
+    /**
+     * The root node.
+     * Note that the root node is a special internal node
+     * that is used to collect together all the nodes that
+     * have no parent; it is not given out to clients.
+     */
+    PreferenceNode root;
+
+    /**
+     * The path separator character.
+     */
+    String separator;
+
+    /**
+     * Creates a new preference manager.
+     */
+    public this() {
+        this('.', new PreferenceNode(ROOT_NODE_ID));
+    }
+
+    /**
+     * Creates a new preference manager with the given
+     * path separator.
+     *
+     * @param separatorChar
+     */
+    public this(char separatorChar) {
+        this(separatorChar, new PreferenceNode(ROOT_NODE_ID));
+    }
+
+    /**
+     * Creates a new preference manager with the given
+     * path separator and root node.
+     *
+     * @param separatorChar the separator character
+     * @param rootNode the root node.
+     *
+     * @since 3.4
+     */
+    public this(char separatorChar, PreferenceNode rootNode) {
+        separator = [ separatorChar ];
+        this.root = rootNode;
+    }
+
+    /**
+     * Adds the given preference node as a subnode of the
+     * node at the given path.
+     *
+     * @param path the path
+     * @param node the node to add
+     * @return <code>true</code> if the add was successful,
+     *  and <code>false</code> if there is no contribution at
+     *  the given path
+     */
+    public bool addTo(String path, IPreferenceNode node) {
+        IPreferenceNode target = find(path);
+        if (target is null) {
+            return false;
+        }
+        target.add(node);
+        return true;
+    }
+
+    /**
+     * Adds the given preference node as a subnode of the
+     * root.
+     *
+     * @param node the node to add, which must implement
+     *   <code>IPreferenceNode</code>
+     */
+    public void addToRoot(IPreferenceNode node) {
+        Assert.isNotNull(cast(Object)node);
+        root.add(node);
+    }
+
+    /**
+     * Recursively enumerates all nodes at or below the given node
+     * and adds them to the given list in the given order.
+     *
+     * @param node the starting node
+     * @param sequence a read-write list of preference nodes
+     *  (element type: <code>IPreferenceNode</code>)
+     *  in the given order
+     * @param order the traversal order, one of
+     *  <code>PRE_ORDER</code> and <code>POST_ORDER</code>
+     */
+    protected void buildSequence(IPreferenceNode node, List sequence, int order) {
+        if (order is PRE_ORDER) {
+            sequence.add(cast(Object)node);
+        }
+        IPreferenceNode[] subnodes = node.getSubNodes();
+        for (int i = 0; i < subnodes.length; i++) {
+            buildSequence(subnodes[i], sequence, order);
+        }
+        if (order is POST_ORDER) {
+            sequence.add(cast(Object)node);
+        }
+    }
+
+    /**
+     * Finds and returns the contribution node at the given path.
+     *
+     * @param path the path
+     * @return the node, or <code>null</code> if none
+     */
+    public IPreferenceNode find(String path) {
+       return find(path,root);
+    }
+
+    /**
+     * Finds and returns the preference node directly
+     * below the top at the given path.
+     *
+     * @param path the path
+     * @param top top at the given path
+     * @return the node, or <code>null</code> if none
+     *
+     * @since 3.1
+     */
+    protected IPreferenceNode find(String path,IPreferenceNode top){
+         Assert.isNotNull(path);
+         auto tokens = tango.text.Util.delimit( path, separator );
+         IPreferenceNode node = top;
+         foreach( id; tokens ){
+             node = node.findSubNode(id);
+             if (node is null) {
+                return null;
+            }
+         }
+         if (node is top) {
+            return null;
+        }
+         return node;
+    }
+
+    /**
+     * Returns all preference nodes managed by this
+     * manager.
+     *
+     * @param order the traversal order, one of
+     *  <code>PRE_ORDER</code> and <code>POST_ORDER</code>
+     * @return a list of preference nodes
+     *  (element type: <code>IPreferenceNode</code>)
+     *  in the given order
+     */
+    public List getElements(int order) {
+        Assert.isTrue(order is PRE_ORDER || order is POST_ORDER,
+                "invalid traversal order");//$NON-NLS-1$
+        ArrayList sequence = new ArrayList();
+        IPreferenceNode[] subnodes = getRoot().getSubNodes();
+        for (int i = 0; i < subnodes.length; i++) {
+            buildSequence(subnodes[i], sequence, order);
+        }
+        return sequence;
+    }
+
+    /**
+     * Returns the root node.
+     * Note that the root node is a special internal node
+     * that is used to collect together all the nodes that
+     * have no parent; it is not given out to clients.
+     *
+     * @return the root node
+     */
+    protected IPreferenceNode getRoot() {
+        return root;
+    }
+    package IPreferenceNode getRoot_package() {
+        return getRoot();
+    }
+
+    /**
+     * Returns the root level nodes of this preference manager.
+     *
+     * @return an array containing the root nodes
+     * @since 3.2
+     */
+    public final IPreferenceNode[] getRootSubNodes() {
+        return getRoot().getSubNodes();
+    }
+
+    /**
+     * Removes the preference node at the given path.
+     *
+     * @param path
+     *            the path
+     * @return the node that was removed, or <code>null</code> if there was no
+     *         node at the given path
+     */
+    public IPreferenceNode remove(String path) {
+        Assert.isNotNull(path);
+        int index = path.lastIndexOf(separator);
+        if (index is -1) {
+            return root.remove(path);
+        }
+        // Make sure that the last character in the string isn't the "."
+        Assert.isTrue(index < path.length - 1, "Path can not end with a dot");//$NON-NLS-1$
+        String parentPath = path.substring(0, index);
+        String id = path.substring(index + 1);
+        IPreferenceNode parentNode = find(parentPath);
+        if (parentNode is null) {
+            return null;
+        }
+        return parentNode.remove(id);
+    }
+
+    /**
+     * Removes the given prefreence node if it is managed by
+     * this contribution manager.
+     *
+     * @param node the node to remove
+     * @return <code>true</code> if the node was removed,
+     *  and <code>false</code> otherwise
+     */
+    public bool remove(IPreferenceNode node) {
+        Assert.isNotNull(cast(Object)node);
+
+        return root.remove(node);
+    }
+
+    /**
+     * Removes all contribution nodes known to this manager.
+     */
+    public void removeAll() {
+        root = new PreferenceNode("");//$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/PreferenceNode.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,290 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.preference.PreferenceNode;
+
+import org.eclipse.jface.preference.IPreferenceNode;
+import org.eclipse.jface.preference.IPreferencePage;
+
+
+// import java.util.List;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * A concrete implementation of a node in a preference dialog tree. This class
+ * also supports lazy creation of the node's preference page.
+ */
+public class PreferenceNode : IPreferenceNode {
+    /**
+     * Preference page, or <code>null</code> if not yet loaded.
+     */
+    private IPreferencePage page;
+
+    /**
+     * The list of subnodes (immediate children) of this node (element type:
+     * <code>IPreferenceNode</code>).
+     */
+    private List subNodes;
+
+    /**
+     * Name of a class that implements <code>IPreferencePage</code>, or
+     * <code>null</code> if none.
+     */
+    private String classname;
+
+    /**
+     * The id of this node.
+     */
+    private String id;
+
+    /**
+     * Text label for this node. Note that this field is only used prior to the
+     * creation of the preference page.
+     */
+    private String label;
+
+    /**
+     * Image descriptor for this node, or <code>null</code> if none.
+     */
+    private ImageDescriptor imageDescriptor;
+
+    /**
+     * Cached image, or <code>null</code> if none.
+     */
+    private Image image;
+
+    /**
+     * Creates a new preference node with the given id. The new node has no
+     * subnodes.
+     *
+     * @param id
+     *            the node id
+     */
+    public this(String id) {
+        Assert.isNotNull(id);
+        this.id = id;
+    }
+
+    /**
+     * Creates a preference node with the given id, label, and image, and
+     * lazily-loaded preference page. The preference node assumes (sole)
+     * responsibility for disposing of the image; this will happen when the node
+     * is disposed.
+     *
+     * @param id
+     *            the node id
+     * @param label
+     *            the label used to display the node in the preference dialog's
+     *            tree
+     * @param image
+     *            the image displayed left of the label in the preference
+     *            dialog's tree, or <code>null</code> if none
+     * @param className
+     *            the class name of the preference page; this class must
+     *            implement <code>IPreferencePage</code>
+     */
+    public this(String id, String label, ImageDescriptor image,
+            String className) {
+        this(id);
+        this.imageDescriptor = image;
+        Assert.isNotNull(label);
+        this.label = label;
+        this.classname = className;
+    }
+
+    /**
+     * Creates a preference node with the given id and preference page. The
+     * title of the preference page is used for the node label. The node will
+     * not have an image.
+     *
+     * @param id
+     *            the node id
+     * @param preferencePage
+     *            the preference page
+     */
+    public this(String id, IPreferencePage preferencePage) {
+        this(id);
+        Assert.isNotNull(cast(Object)preferencePage);
+        page = preferencePage;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public void add(IPreferenceNode node) {
+        if (subNodes is null) {
+            subNodes = new ArrayList();
+        }
+        subNodes.add(cast(Object)node);
+    }
+
+    /**
+     * Creates a new instance of the given class <code>className</code>.
+     *
+     * @param className
+     * @return new Object or <code>null</code> in case of failures.
+     */
+    private Object createObject(String className) {
+        Assert.isNotNull(className);
+//         try {
+            ClassInfo cl = ClassInfo.find(className);
+            if (cl !is null) {
+                return cl.create();
+            }
+//         } catch (ClassNotFoundException e) {
+//             return null;
+//         } catch (InstantiationException e) {
+//             return null;
+//         } catch (IllegalAccessException e) {
+//             return null;
+//         } catch (NoSuchMethodError e) {
+//             return null;
+//         }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public void createPage() {
+        page = cast(IPreferencePage) createObject(classname);
+        if (getLabelImage() !is null) {
+            page.setImageDescriptor(imageDescriptor);
+        }
+        page.setTitle(label);
+    }
+
+    /**
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public void disposeResources() {
+        if (image !is null) {
+            image.dispose();
+            image = null;
+        }
+        if (page !is null) {
+            page.dispose();
+            page = null;
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionNode.
+     */
+    public IPreferenceNode findSubNode(String id) {
+        Assert.isNotNull(id);
+        Assert.isTrue(id.length > 0);
+        if (subNodes is null) {
+            return null;
+        }
+        int size = subNodes.size();
+        for (int i = 0; i < size; i++) {
+            IPreferenceNode node = cast(IPreferenceNode) subNodes.get(i);
+            if (id.equals(node.getId())) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public String getId() {
+        return this.id;
+    }
+
+    /**
+     * Returns the image descriptor for this node.
+     *
+     * @return the image descriptor
+     */
+    protected ImageDescriptor getImageDescriptor() {
+        return imageDescriptor;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public Image getLabelImage() {
+        if (image is null && imageDescriptor !is null) {
+            image = imageDescriptor.createImage();
+        }
+        return image;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public String getLabelText() {
+        if (page !is null) {
+            return page.getTitle();
+        }
+        return label;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public IPreferencePage getPage() {
+        return page;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public IPreferenceNode[] getSubNodes() {
+        if (subNodes is null) {
+            return new IPreferenceNode[0];
+        }
+        return arraycast!(IPreferenceNode)( subNodes
+                .toArray());
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public IPreferenceNode remove(String id) {
+        IPreferenceNode node = findSubNode(id);
+        if (node !is null) {
+            remove(node);
+        }
+        return node;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public bool remove(IPreferenceNode node) {
+        if (subNodes is null) {
+            return false;
+        }
+        return subNodes.remove(cast(Object)node);
+    }
+
+    /**
+     * Set the current page to be newPage.
+     *
+     * @param newPage
+     */
+    public void setPage(IPreferencePage newPage) {
+        page = newPage;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/PreferencePage.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,673 @@
+/*******************************************************************************
+ * 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
+ *     Sebastian Davids <sdavids@gmx.de> - Fix for bug 38729 - [Preferences]
+ *           NPE PreferencePage isValid.
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.PreferencePage;
+
+import org.eclipse.jface.preference.IPreferencePage;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.IPreferencePageContainer;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.DialogPage;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IDialogPage;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Abstract base implementation for all preference page implementations.
+ * <p>
+ * Subclasses must implement the <code>createControl</code> framework
+ * method to supply the page's main control.
+ * </p>
+ * <p>
+ * Subclasses should extend the <code>doComputeSize</code> framework
+ * method to compute the size of the page's control.
+ * </p>
+ * <p>
+ * Subclasses may override the <code>performOk</code>, <code>performApply</code>,
+ * <code>performDefaults</code>, <code>performCancel</code>, and <code>performHelp</code>
+ * framework methods to react to the standard button events.
+ * </p>
+ * <p>
+ * Subclasses may call the <code>noDefaultAndApplyButton</code> framework
+ * method before the page's control has been created to suppress
+ * the standard Apply and Defaults buttons.
+ * </p>
+ */
+public abstract class PreferencePage : DialogPage,
+        IPreferencePage {
+    alias DialogPage.setMessage setMessage;
+
+    /**
+     * Preference store, or <code>null</code>.
+     */
+    private IPreferenceStore preferenceStore;
+
+    /**
+     * Valid state for this page; <code>true</code> by default.
+     *
+     * @see #isValid
+     */
+    private bool isValid_ = true;
+
+    /**
+     * Body of page.
+     */
+    private Control body_;
+
+    /**
+     * Whether this page has the standard Apply and Defaults buttons;
+     * <code>true</code> by default.
+     *
+     * @see #noDefaultAndApplyButton
+     */
+    private bool createDefaultAndApplyButton = true;
+
+    /**
+     * Standard Defaults button, or <code>null</code> if none.
+     * This button has id <code>DEFAULTS_ID</code>.
+     */
+    private Button defaultsButton = null;
+
+    /**
+     * The container this preference page belongs to; <code>null</code>
+     * if none.
+     */
+    private IPreferencePageContainer container = null;
+
+    /**
+     * Standard Apply button, or <code>null</code> if none.
+     * This button has id <code>APPLY_ID</code>.
+     */
+    private Button applyButton = null;
+
+    /**
+     * Description label.
+     *
+     * @see #createDescriptionLabel(Composite)
+     */
+    private Label descriptionLabel;
+
+    /**
+     * Caches size of page.
+     */
+    private Point size = null;
+
+
+    /**
+     * Creates a new preference page with an empty title and no image.
+     */
+    protected this() {
+        this(""); //$NON-NLS-1$
+    }
+
+    /**
+     * Creates a new preference page with the given title and no image.
+     *
+     * @param title the title of this preference page
+     */
+    protected this(String title) {
+        super(title);
+    }
+
+    /**
+     * Creates a new abstract preference page with the given title and image.
+     *
+     * @param title the title of this preference page
+     * @param image the image for this preference page,
+     *  or <code>null</code> if none
+     */
+    protected this(String title, ImageDescriptor image) {
+        super(title, image);
+    }
+
+    /**
+     * Computes the size for this page's UI control.
+     * <p>
+     * The default implementation of this <code>IPreferencePage</code>
+     * method returns the size set by <code>setSize</code>; if no size
+     * has been set, but the page has a UI control, the framework
+     * method <code>doComputeSize</code> is called to compute the size.
+     * </p>
+     *
+     * @return the size of the preference page encoded as
+     *   <code>new Point(width,height)</code>, or
+     *   <code>(0,0)</code> if the page doesn't currently have any UI component
+     */
+    public Point computeSize() {
+        if (size !is null) {
+            return size;
+        }
+        Control control = getControl();
+        if (control !is null) {
+            size = doComputeSize();
+            return size;
+        }
+        return new Point(0, 0);
+    }
+
+    /**
+     * Contributes additional buttons to the given composite.
+     * <p>
+     * The default implementation of this framework hook method does
+     * nothing. Subclasses should override this method to contribute buttons
+     * to this page's button bar. For each button a subclass contributes,
+     * it must also increase the parent's grid layout number of columns
+     * by one; that is,
+     * <pre>
+     * ((GridLayout) parent.getLayout()).numColumns++);
+     * </pre>
+     * </p>
+     *
+     * @param parent the button bar
+     */
+    protected void contributeButtons(Composite parent) {
+    }
+
+    /**
+     * Creates and returns the SWT control for the customized body
+     * of this preference page under the given parent composite.
+     * <p>
+     * This framework method must be implemented by concrete subclasses. Any
+     * subclass returning a <code>Composite</code> object whose <code>Layout</code>
+     * has default margins (for example, a <code>GridLayout</code>) are expected to
+     * set the margins of this <code>Layout</code> to 0 pixels.
+     * </p>
+     *
+     * @param parent the parent composite
+     * @return the new control
+     */
+    protected abstract Control createContents(Composite parent);
+
+    /**
+     * The <code>PreferencePage</code> implementation of this
+     * <code>IDialogPage</code> method creates a description label
+     * and button bar for the page. It calls <code>createContents</code>
+     * to create the custom contents of the page.
+     * <p>
+     * If a subclass that overrides this method creates a <code>Composite</code>
+     * that has a layout with default margins (for example, a <code>GridLayout</code>)
+     * it is expected to set the margins of this <code>Layout</code> to 0 pixels.
+     * @see IDialogPage#createControl(Composite)
+     */
+    public void createControl(Composite parent){
+
+        GridData gd;
+        Composite content = new Composite(parent, SWT.NONE);
+        setControl(content);
+        GridLayout layout = new GridLayout();
+        layout.marginWidth = 0;
+        layout.marginHeight = 0;
+        content.setLayout(layout);
+        //Apply the font on creation for backward compatibility
+        applyDialogFont(content);
+
+        // initialize the dialog units
+        initializeDialogUnits(content);
+
+        descriptionLabel = createDescriptionLabel(content);
+        if (descriptionLabel !is null) {
+            descriptionLabel.setLayoutData(new GridData(
+                    GridData.FILL_HORIZONTAL));
+        }
+
+        body_ = createContents(content);
+        if (body_ !is null) {
+            // null is not a valid return value but support graceful failure
+            body_.setLayoutData(new GridData(GridData.FILL_BOTH));
+        }
+
+        Composite buttonBar = new Composite(content, SWT.NONE);
+        layout = new GridLayout();
+        layout.numColumns = 0;
+        layout.marginHeight = 0;
+        layout.marginWidth = 0;
+        layout.makeColumnsEqualWidth = false;
+        buttonBar.setLayout(layout);
+
+        gd = new GridData(GridData.HORIZONTAL_ALIGN_END);
+
+        buttonBar.setLayoutData(gd);
+
+        contributeButtons(buttonBar);
+
+        if (createDefaultAndApplyButton) {
+            layout.numColumns = layout.numColumns + 2;
+            String[] labels = JFaceResources.getStrings([
+                    "defaults", "apply"]); //$NON-NLS-2$//$NON-NLS-1$
+            int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
+            defaultsButton = new Button(buttonBar, SWT.PUSH);
+            defaultsButton.setText(labels[0]);
+            Dialog.applyDialogFont(defaultsButton);
+            GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+            Point minButtonSize = defaultsButton.computeSize(SWT.DEFAULT,
+                    SWT.DEFAULT, true);
+            data.widthHint = Math.max(widthHint, minButtonSize.x);
+            defaultsButton.setLayoutData(data);
+            defaultsButton.addSelectionListener(new class SelectionAdapter {
+                public void widgetSelected(SelectionEvent e) {
+                    performDefaults();
+                }
+            });
+
+            applyButton = new Button(buttonBar, SWT.PUSH);
+            applyButton.setText(labels[1]);
+            Dialog.applyDialogFont(applyButton);
+            data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+            minButtonSize = applyButton.computeSize(SWT.DEFAULT, SWT.DEFAULT,
+                    true);
+            data.widthHint = Math.max(widthHint, minButtonSize.x);
+            applyButton.setLayoutData(data);
+            applyButton.addSelectionListener(new class SelectionAdapter {
+                public void widgetSelected(SelectionEvent e) {
+                    performApply();
+                }
+            });
+            applyButton.setEnabled(isValid());
+            applyDialogFont(buttonBar);
+        } else {
+            /* Check if there are any other buttons on the button bar.
+             * If not, throw away the button bar composite.  Otherwise
+             * there is an unusually large button bar.
+             */
+            if (buttonBar.getChildren().length < 1) {
+                buttonBar.dispose();
+            }
+        }
+    }
+
+
+
+    /**
+     * Apply the dialog font to the composite and it's children
+     * if it is set. Subclasses may override if they wish to
+     * set the font themselves.
+     * @param composite
+     */
+    protected void applyDialogFont(Composite composite) {
+        Dialog.applyDialogFont(composite);
+    }
+
+    /**
+     * Creates and returns an SWT label under the given composite.
+     *
+     * @param parent the parent composite
+     * @return the new label
+     */
+    protected Label createDescriptionLabel(Composite parent) {
+        Label result = null;
+        String description = getDescription();
+        if (description !is null) {
+            result = new Label(parent, SWT.WRAP);
+            result.setFont(parent.getFont());
+            result.setText(description);
+        }
+        return result;
+    }
+
+    /**
+     * Computes the size needed by this page's UI control.
+     * <p>
+     * All pages should override this method and set the appropriate sizes
+     * of their widgets, and then call <code>super.doComputeSize</code>.
+     * </p>
+     *
+     * @return the size of the preference page encoded as
+     *   <code>new Point(width,height)</code>
+     */
+    protected Point doComputeSize() {
+        if (descriptionLabel !is null && body_ !is null) {
+            Point bodySize = body_.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+            GridData gd = cast(GridData) descriptionLabel.getLayoutData();
+            gd.widthHint = bodySize.x;
+        }
+        return getControl().computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+    }
+
+    /**
+     * Returns the preference store of this preference page.
+     * <p>
+     * This is a framework hook method for subclasses to return a
+     * page-specific preference store. The default implementation
+     * returns <code>null</code>.
+     * </p>
+     *
+     * @return the preference store, or <code>null</code> if none
+     */
+    protected IPreferenceStore doGetPreferenceStore() {
+        return null;
+    }
+
+    /**
+     * Returns the container of this page.
+     *
+     * @return the preference page container, or <code>null</code> if this
+     *   page has yet to be added to a container
+     */
+    public IPreferencePageContainer getContainer() {
+        return container;
+    }
+
+    /**
+     * Returns the preference store of this preference page.
+     *
+     * @return the preference store , or <code>null</code> if none
+     */
+    public IPreferenceStore getPreferenceStore() {
+        if (preferenceStore is null) {
+            preferenceStore = doGetPreferenceStore();
+        }
+        if (preferenceStore !is null) {
+            return preferenceStore;
+        } else if (container !is null) {
+            return container.getPreferenceStore();
+        }
+        return null;
+    }
+
+    /**
+     * The preference page implementation of an <code>IPreferencePage</code>
+     * method returns whether this preference page is valid. Preference
+     * pages are considered valid by default; call <code>setValid(false)</code>
+     * to make a page invalid.
+     * @see IPreferencePage#isValid()
+     */
+    public bool isValid() {
+        return isValid_;
+    }
+
+    /**
+     * Suppresses creation of the standard Default and Apply buttons
+     * for this page.
+     * <p>
+     * Subclasses wishing a preference page without these buttons
+     * should call this framework method before the page's control
+     * has been created.
+     * </p>
+     */
+    protected void noDefaultAndApplyButton() {
+        createDefaultAndApplyButton = false;
+    }
+
+    /**
+     * The <code>PreferencePage</code> implementation of this
+     * <code>IPreferencePage</code> method returns <code>true</code>
+     * if the page is valid.
+     * @see IPreferencePage#okToLeave()
+     */
+    public bool okToLeave() {
+        return isValid();
+    }
+
+    /**
+     * Performs special processing when this page's Apply button has been pressed.
+     * <p>
+     * This is a framework hook method for sublcasses to do special things when
+     * the Apply button has been pressed.
+     * The default implementation of this framework method simply calls
+     * <code>performOk</code> to simulate the pressing of the page's OK button.
+     * </p>
+     *
+     * @see #performOk
+     */
+    protected void performApply() {
+        performOk();
+    }
+
+    /**
+     * The preference page implementation of an <code>IPreferencePage</code>
+     * method performs special processing when this page's Cancel button has
+     * been pressed.
+     * <p>
+     * This is a framework hook method for subclasses to do special things when
+     * the Cancel button has been pressed. The default implementation of this
+     * framework method does nothing and returns <code>true</code>.
+     * @see IPreferencePage#performCancel()
+     */
+    public bool performCancel() {
+        return true;
+    }
+
+    /**
+     * Performs special processing when this page's Defaults button has been pressed.
+     * <p>
+     * This is a framework hook method for subclasses to do special things when
+     * the Defaults button has been pressed.
+     * Subclasses may override, but should call <code>super.performDefaults</code>.
+     * </p>
+     */
+    protected void performDefaults() {
+        updateApplyButton();
+    }
+
+   
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.preference.IPreferencePage#performOk()
+     */
+    public bool performOk() {
+        return true;
+    }
+
+    
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.preference.IPreferencePage#setContainer(org.eclipse.jface.preference.IPreferencePageContainer)
+     */
+    public void setContainer(IPreferencePageContainer container) {
+        this.container = container;
+    }
+
+    /**
+     * Sets the preference store for this preference page.
+     * <p>
+     * If preferenceStore is set to null, getPreferenceStore
+     * will invoke doGetPreferenceStore the next time it is called.
+     * </p>
+     *
+     * @param store the preference store, or <code>null</code>
+     * @see #getPreferenceStore
+     */
+    public void setPreferenceStore(IPreferenceStore store) {
+        preferenceStore = store;
+    }
+
+   
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.preference.IPreferencePage#setSize(org.eclipse.swt.graphics.Point)
+     */
+    public void setSize(Point uiSize) {
+        Control control = getControl();
+        if (control !is null) {
+            control.setSize(uiSize);
+            size = uiSize;
+        }
+    }
+
+    /**
+     * The <code>PreferencePage</code> implementation of this <code>IDialogPage</code>
+     * method extends the <code>DialogPage</code> implementation to update
+     * the preference page container title. Subclasses may extend.
+     * @see IDialogPage#setTitle(String)
+     */
+    public override void setTitle(String title) {
+        super.setTitle(title);
+        if (getContainer() !is null) {
+            getContainer().updateTitle();
+        }
+    }
+
+    /**
+     * Sets whether this page is valid.
+     * The enable state of the container buttons and the
+     * apply button is updated when a page's valid state
+     * changes.
+     * <p>
+     *
+     * @param b the new valid state
+     */
+    public void setValid(bool b) {
+        bool oldValue = isValid_;
+        isValid_ = b;
+        if (oldValue !is isValid_) {
+            // update container state
+            if (getContainer() !is null) {
+                getContainer().updateButtons();
+            }
+            // update page state
+            updateApplyButton();
+        }
+    }
+
+   
+    /* (non-Javadoc)
+     * @see java.lang.Object#toString()
+     */
+    public override String toString() {
+        return getTitle();
+    }
+
+    /**
+     * Updates the enabled state of the Apply button to reflect whether
+     * this page is valid.
+     */
+    protected void updateApplyButton() {
+        if (applyButton !is null) {
+            applyButton.setEnabled(isValid());
+        }
+    }
+
+    /**
+     * Creates a composite with a highlighted Note entry and a message text.
+     * This is designed to take up the full width of the page.
+     *
+     * @param font the font to use
+     * @param composite the parent composite
+     * @param title the title of the note
+     * @param message the message for the note
+     * @return the composite for the note
+     */
+    protected Composite createNoteComposite(Font font, Composite composite,
+            String title, String message) {
+        Composite messageComposite = new Composite(composite, SWT.NONE);
+        GridLayout messageLayout = new GridLayout();
+        messageLayout.numColumns = 2;
+        messageLayout.marginWidth = 0;
+        messageLayout.marginHeight = 0;
+        messageComposite.setLayout(messageLayout);
+        messageComposite.setLayoutData(new GridData(
+                GridData.HORIZONTAL_ALIGN_FILL));
+        messageComposite.setFont(font);
+
+        final Label noteLabel = new Label(messageComposite, SWT.BOLD);
+        noteLabel.setText(title);
+        noteLabel.setFont(JFaceResources.getFontRegistry().getBold(
+                JFaceResources.DEFAULT_FONT));
+        noteLabel
+                .setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING));
+
+        final IPropertyChangeListener fontListener = new class IPropertyChangeListener {
+            public void propertyChange(PropertyChangeEvent event) {
+                if (JFaceResources.BANNER_FONT.equals(event.getProperty())) {
+                    noteLabel.setFont(JFaceResources
+                            .getFont(JFaceResources.BANNER_FONT));
+                }
+            }
+        };
+        JFaceResources.getFontRegistry().addListener(fontListener);
+        noteLabel.addDisposeListener(new class DisposeListener {
+            public void widgetDisposed(DisposeEvent event) {
+                JFaceResources.getFontRegistry().removeListener(fontListener);
+            }
+        });
+
+        Label messageLabel = new Label(messageComposite, SWT.WRAP);
+        messageLabel.setText(message);
+        messageLabel.setFont(font);
+        return messageComposite;
+    }
+
+    /**
+     * Returns the Apply button.
+     *
+     * @return the Apply button
+     */
+    protected Button getApplyButton() {
+        return applyButton;
+    }
+
+    /**
+     * Returns the Restore Defaults button.
+     *
+     * @return the Restore Defaults button
+     */
+    protected Button getDefaultsButton() {
+        return defaultsButton;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.dialogs.IDialogPage#performHelp()
+     */
+    public override void performHelp() {
+        getControl().notifyListeners(SWT.Help, new Event());
+    }
+
+    /**
+     * Apply the data to the receiver. By default do nothing.
+     * @param data
+     * @since 3.1
+     */
+    public void applyData(Object data) {
+
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.dialogs.DialogPage#setErrorMessage(java.lang.String)
+     */
+    public override void setErrorMessage(String newMessage) {
+        super.setErrorMessage(newMessage);
+        if (getContainer() !is null) {
+            getContainer().updateMessage();
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.dialogs.DialogPage#setMessage(java.lang.String, int)
+     */
+    public override void setMessage(String newMessage, int newType) {
+        super.setMessage(newMessage, newType);
+        if (getContainer() !is null) {
+            getContainer().updateMessage();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/PreferenceStore.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,979 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.preference.PreferenceStore;
+
+import org.eclipse.jface.preference.IPersistentPreferenceStore;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+// import java.io.FileInputStream;
+// import java.io.FileOutputStream;
+// import java.io.IOException;
+// import java.io.InputStream;
+// import java.io.OutputStream;
+// import java.io.PrintStream;
+// import java.io.PrintWriter;
+// import java.util.ArrayList;
+// import java.util.Enumeration;
+// import java.util.Properties;
+
+import org.eclipse.core.commands.common.EventManager;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.util.SafeRunnable;
+
+import java.lang.all;
+import java.util.Enumeration;
+import java.util.ArrayList;
+import java.util.Set;
+import tango.io.stream.Format;
+import tango.io.device.File;
+
+    import tango.io.model.IConduit;
+    import tango.text.Util;
+    public class Properties {
+
+        protected Properties defaults;
+
+        private String[ String ] map;
+
+        public this (){
+        }
+
+        public this ( Properties defaults ){
+            this.defaults = defaults;
+        }
+
+        public synchronized String setProperty( String key, String value ){
+            String res;
+            if( auto v = key in map ){
+                res = *v;
+            }
+            map[ key ] = value;
+            return res;
+        }
+
+        public synchronized void load( InputStream inStream ){
+            char[] line;
+            bool eof = false;
+            //tango.io.Stdout.Stdout.formatln( "properties put ..." );
+            void readLine(){
+                line.length = 0;
+                char[1] rdbuf;
+                int i = inStream.read( rdbuf );
+                while( i is 1 && rdbuf[0] != '\n' && rdbuf[0] != '\r' ){
+                    line ~= rdbuf[0];
+                    i = inStream.read( rdbuf );
+                }
+                eof = i !is 1;
+            }
+
+            //tango.io.Stdout.Stdout.formatln( "properties put {}", __LINE__ );
+            bool linecontinue = false;
+            bool iskeypart = true;
+            char[] key;
+            char[] value;
+    nextline:
+            while( !eof ){
+                //tango.io.Stdout.Stdout.formatln( "properties put {} startline", __LINE__ );
+                readLine();
+                line = tango.text.Util.trim( line );
+                if( line.length == 0 ){
+                    //tango.io.Stdout.Stdout.formatln( "properties put {} was 0 length", __LINE__ );
+                    continue;
+                }
+                if( line[0] == '#' ){
+                    //tango.io.Stdout.Stdout.formatln( "properties put {} was comment", __LINE__ );
+                    continue;
+                }
+                int pos = 0;
+                bool esc = false;
+                if( !linecontinue ){
+                    iskeypart = true;
+                    key = null;
+                    value = null;
+                }
+                else{
+                    linecontinue = false;
+                }
+                while( pos < line.length ){
+                    char c = line[pos];
+                    if( esc ){
+                        esc = false;
+                        switch( c ){
+                        case 't': c = '\t'; break;
+                        case 'r': c = '\r'; break;
+                        case 'n': c = '\n'; break;
+                        case '\\': c = '\\'; break;
+                        default:  break;
+                        }
+                    }
+                    else{
+                        if( c == '\\' ){
+                            if( pos == line.length -1 ){
+                                linecontinue = true;
+                                goto nextline;
+                            }
+                            esc = true;
+                            pos++;
+                            continue;
+                        }
+                        else if( iskeypart && c == '=' ){
+                            pos++;
+                            iskeypart = false;
+                            continue;
+                        }
+                    }
+                    pos++;
+                    if( iskeypart ){
+                        key ~= c;
+                    }
+                    else{
+                        value ~= c;
+                    }
+                }
+                if( iskeypart ){
+                    tango.io.Stdout.Stdout.formatln( "dejavu.util.Properties put cannot find '='." );
+                    continue;
+                }
+                key = tango.text.Util.trim( key );
+                value = tango.text.Util.trim(value);
+                //tango.io.Stdout.Stdout.formatln( "properties put {}=>{}", key, value );
+
+                map[ key.dup ] = value.dup;
+                //tango.io.Stdout.Stdout.formatln( "properties put {}", __LINE__ );
+            }
+        }
+
+        public synchronized void store( OutputStream ostr, String comments ){
+            void append(char[] s){
+                foreach( c; s ){
+                    switch( c ){
+                    case '\t': ostr.write( "\\t"  ); break;
+                    case '\n': ostr.write( "\\n"  ); break;
+                    case '\r': ostr.write( "\\r"  ); break;
+                    case '\\': ostr.write( "\\\\" ); break;
+                    case ':' : ostr.write( "\\:"  ); break;
+                    default: {
+                            char[1] b;
+                            b[0] = c;
+                            ostr.write( b ); break;
+                        }
+                    }
+                }
+            }
+            if( comments.length ){
+                bool lineStart = true;
+                for( int idx = 0; idx < comments.length; idx++ ){
+                    char[1] b;
+                    if( lineStart ){
+                        b[0] = '#';
+                        ostr.write(b);
+                    }
+                    append( comments[ idx .. idx+1 ] );
+                    lineStart = false;
+                    if( comments[idx] is '\n' ){
+                        lineStart = true;
+                    }
+                }
+                ostr.write( "\n" );
+            }
+            foreach( k, v; map ){
+                append(k);
+                append("=");
+                append(v);
+                ostr.write( "\n" );
+            }
+        }
+
+//         public synchronized void save( dejavu.io.OutputStream.OutputStream out_KEYWORDESCAPE, dejavu.lang.String.String comments ){
+//             implMissing( __FILE__, __LINE__ );
+//         }
+//
+//         public synchronized void loadFromXML( dejavu.io.InputStream.InputStream in_KEYWORDESCAPE ){
+//             implMissing( __FILE__, __LINE__ );
+//         }
+//
+//         public synchronized void storeToXML( dejavu.io.OutputStream.OutputStream os, dejavu.lang.String.String comment ){
+//             implMissing( __FILE__, __LINE__ );
+//         }
+//
+//         public synchronized void storeToXML( dejavu.io.OutputStream.OutputStream os, dejavu.lang.String.String comment, dejavu.lang.String.String encoding ){
+//             implMissing( __FILE__, __LINE__ );
+//         }
+
+        public String getProperty( String aKey ){
+            if( auto res = aKey in map ){
+                return *res;
+            }
+            if( defaults !is null ){
+                return defaults.getProperty( aKey );
+            }
+            return null;
+        }
+
+        public String get( String aKey ){
+            if( auto res = aKey in map ){
+                return *res;
+            }
+            return null;
+        }
+        public String put( String aKey, String aValue ){
+            if( auto pres = aKey in map ){
+                String res = *pres;
+                map[ aKey ] = aValue;
+                return res;
+            }
+            map[ aKey ] = aValue;
+            return null;
+        }
+        public String remove( String aKey ){
+            if( auto res = aKey in map ){
+                map.remove(aKey);
+                return *res;
+            }
+            return null;
+        }
+
+        public String getProperty( String key, String defaultValue ){
+            if( auto res = key in map ){
+                return *res;
+            }
+            return defaultValue;
+        }
+
+        public void list(FormatOutput!(char) print){
+            foreach( k, v; map ){
+                print( k )( '=' )( v ).newline;
+            }
+        }
+        public bool containsKey( String key ){
+            return ( key in map ) !is null;
+        }
+        public String[] propertyNames(){
+            String[] res = new String[ map.length ];
+            int idx;
+            foreach( key, val; map ){
+                res[idx] = key;
+                idx++;
+            }
+            return res;
+        }
+
+//         public dejavu.util.Enumeration.Enumeration propertyNames(){
+//             implMissing( __FILE__, __LINE__ );
+//             return null;
+//         }
+//
+//         public void list( dejavu.io.PrintStream.PrintStream out_KEYWORDESCAPE ){
+//             implMissing( __FILE__, __LINE__ );
+//         }
+//
+//         public void list( dejavu.io.PrintWriter.PrintWriter out_KEYWORDESCAPE ){
+//             implMissing( __FILE__, __LINE__ );
+//         }
+//
+//         public override char[] toUtf8(){
+//             return "";
+//         }
+
+    }
+
+
+
+/**
+ * A concrete preference store implementation based on an internal
+ * <code>java.util.Properties</code> object, with support for persisting the
+ * non-default preference values to files or streams.
+ * <p>
+ * This class was not designed to be subclassed.
+ * </p>
+ *
+ * @see IPreferenceStore
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class PreferenceStore : EventManager,
+        IPersistentPreferenceStore {
+
+    /**
+     * The mapping from preference name to preference value (represented as
+     * strings).
+     */
+    private Properties properties;
+
+    /**
+     * The mapping from preference name to default preference value (represented
+     * as strings); <code>null</code> if none.
+     */
+    private Properties defaultProperties;
+
+    /**
+     * Indicates whether a value as been changed by <code>setToDefault</code>
+     * or <code>setValue</code>; initially <code>false</code>.
+     */
+    private bool dirty = false;
+
+    /**
+     * The file name used by the <code>load</code> method to load a property
+     * file. This filename is used to save the properties file when
+     * <code>save</code> is called.
+     */
+    private String filename;
+
+    /**
+     * Creates an empty preference store.
+     * <p>
+     * Use the methods <code>load(InputStream)</code> and
+     * <code>save(InputStream)</code> to load and store this preference store.
+     * </p>
+     *
+     * @see #load(InputStream)
+     * @see #save(OutputStream, String)
+     */
+    public this() {
+        defaultProperties = new Properties();
+        properties = new Properties(defaultProperties);
+    }
+
+    /**
+     * Creates an empty preference store that loads from and saves to the a
+     * file.
+     * <p>
+     * Use the methods <code>load()</code> and <code>save()</code> to load
+     * and store this preference store.
+     * </p>
+     *
+     * @param filename
+     *            the file name
+     * @see #load()
+     * @see #save()
+     */
+    public this(String filename) {
+        this();
+        Assert.isNotNull(filename);
+        this.filename = filename;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void addPropertyChangeListener(IPropertyChangeListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public bool contains(String name) {
+        return (properties.containsKey(name) || defaultProperties
+                .containsKey(name));
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void firePropertyChangeEvent(String name, Object oldValue,
+            Object newValue) {
+        final Object[] finalListeners = getListeners();
+        // Do we need to fire an event.
+        if (finalListeners.length > 0
+                && (oldValue is null || !oldValue.opEquals(newValue))) {
+            final PropertyChangeEvent pe = new PropertyChangeEvent(this, name,
+                    oldValue, newValue);
+            for (int i = 0; i < finalListeners.length; ++i) {
+                SafeRunnable.run(new class(JFaceResources.getString("PreferenceStore.changeError"),cast(IPropertyChangeListener) finalListeners[i]) SafeRunnable { //$NON-NLS-1$
+                    IPropertyChangeListener l;
+                    this(char[] s,IPropertyChangeListener b){
+                        super(s);
+                        l = b;
+                    }
+                    public void run() {
+                        l.propertyChange(pe);
+                    }
+                });
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public bool getBoolean(String name) {
+        return getBoolean(properties, name);
+    }
+
+    /**
+     * Helper function: gets bool for a given name.
+     *
+     * @param p
+     * @param name
+     * @return bool
+     */
+    private bool getBoolean(Properties p, String name) {
+        String value = p !is null ? p.getProperty(name) : null;
+        if (value is null) {
+            return BOOLEAN_DEFAULT_DEFAULT;
+        }
+        if (value.equals(IPreferenceStore.TRUE)) {
+            return true;
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public bool getDefaultBoolean(String name) {
+        return getBoolean(defaultProperties, name);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public double getDefaultDouble(String name) {
+        return getDouble(defaultProperties, name);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public float getDefaultFloat(String name) {
+        return getFloat(defaultProperties, name);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public int getDefaultInt(String name) {
+        return getInt(defaultProperties, name);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public long getDefaultLong(String name) {
+        return getLong(defaultProperties, name);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public String getDefaultString(String name) {
+        return getString(defaultProperties, name);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public double getDouble(String name) {
+        return getDouble(properties, name);
+    }
+
+    /**
+     * Helper function: gets double for a given name.
+     *
+     * @param p
+     * @param name
+     * @return double
+     */
+    private double getDouble(Properties p, String name) {
+        String value = p !is null ? p.getProperty(name) : null;
+        if (value is null) {
+            return DOUBLE_DEFAULT_DEFAULT;
+        }
+        double ival = DOUBLE_DEFAULT_DEFAULT;
+        try {
+            ival = (new Double(value)).doubleValue();
+        } catch (NumberFormatException e) {
+        }
+        return ival;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public float getFloat(String name) {
+        return getFloat(properties, name);
+    }
+
+    /**
+     * Helper function: gets float for a given name.
+     *
+     * @param p
+     * @param name
+     * @return float
+     */
+    private float getFloat(Properties p, String name) {
+        String value = p !is null ? p.getProperty(name) : null;
+        if (value is null) {
+            return FLOAT_DEFAULT_DEFAULT;
+        }
+        float ival = FLOAT_DEFAULT_DEFAULT;
+        try {
+            ival = (new Float(value)).floatValue();
+        } catch (NumberFormatException e) {
+        }
+        return ival;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public int getInt(String name) {
+        return getInt(properties, name);
+    }
+
+    /**
+     * Helper function: gets int for a given name.
+     *
+     * @param p
+     * @param name
+     * @return int
+     */
+    private int getInt(Properties p, String name) {
+        String value = p !is null ? p.getProperty(name) : null;
+        if (value is null) {
+            return INT_DEFAULT_DEFAULT;
+        }
+        int ival = 0;
+        try {
+            ival = Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+        }
+        return ival;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public long getLong(String name) {
+        return getLong(properties, name);
+    }
+
+    /**
+     * Helper function: gets long for a given name.
+     *
+     * @param p
+     *            the properties storage (may be <code>null</code>)
+     * @param name
+     *            the name of the property
+     * @return the long or a default value of if:
+     *         <ul>
+     *         <li>properties storage is <code>null</code></li>
+     *         <li>property is not found</li>
+     *         <li>property value is not a number</li>
+     *         </ul>
+     * @see IPreferenceStore#LONG_DEFAULT_DEFAULT
+     */
+    private long getLong(Properties p, String name) {
+        String value = p !is null ? p.getProperty(name) : null;
+        if (value is null) {
+            return LONG_DEFAULT_DEFAULT;
+        }
+        long ival = LONG_DEFAULT_DEFAULT;
+        try {
+            ival = Long.parseLong(value);
+        } catch (NumberFormatException e) {
+        }
+        return ival;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public String getString(String name) {
+        return getString(properties, name);
+    }
+
+    /**
+     * Helper function: gets string for a given name.
+     *
+     * @param p
+     *            the properties storage (may be <code>null</code>)
+     * @param name
+     *            the name of the property
+     * @return the value or a default value of if:
+     *         <ul>
+     *         <li>properties storage is <code>null</code></li>
+     *         <li>property is not found</li>
+     *         <li>property value is not a number</li>
+     *         </ul>
+     * @see IPreferenceStore#STRING_DEFAULT_DEFAULT
+     */
+    private String getString(Properties p, String name) {
+        String value = p !is null ? p.getProperty(name) : null;
+        if (value is null) {
+            return STRING_DEFAULT_DEFAULT;
+        }
+        return value;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public bool isDefault(String name) {
+        return (!properties.containsKey(name) && defaultProperties
+                .containsKey(name));
+    }
+
+    /**
+     * Prints the contents of this preference store to the given print stream.
+     *
+     * @param out
+     *            the print stream
+     */
+    public void list(FormatOutput!(char) out_) {
+        properties.list(out_);
+    }
+
+//     /**
+//      * Prints the contents of this preference store to the given print writer.
+//      *
+//      * @param out
+//      *            the print writer
+//      */
+//     public void list(PrintWriter out_) {
+//         properties.list(out_);
+//     }
+
+    /**
+     * Loads this preference store from the file established in the constructor
+     * <code>PreferenceStore(java.lang.String)</code> (or by
+     * <code>setFileName</code>). Default preference values are not affected.
+     *
+     * @exception java.io.IOException
+     *                if there is a problem loading this store
+     */
+    public void load() {
+        if (filename is null) {
+            throw new IOException("File name not specified");//$NON-NLS-1$
+        }
+        File in_ = new File(filename, File.ReadExisting);
+        load(in_.input);
+        in_.close();
+    }
+
+    /**
+     * Loads this preference store from the given input stream. Default
+     * preference values are not affected.
+     *
+     * @param in
+     *            the input stream
+     * @exception java.io.IOException
+     *                if there is a problem loading this store
+     */
+    public void load(InputStream in_)  {
+        properties.load(in_);
+        dirty = false;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public bool needsSaving() {
+        return dirty;
+    }
+
+    /**
+     * Returns an enumeration of all preferences known to this store which have
+     * current values other than their default value.
+     *
+     * @return an array of preference names
+     */
+    public String[] preferenceNames() {
+        String[] list;
+        foreach( prop; properties.propertyNames() ){
+            list ~= prop;
+        }
+        return list;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void putValue(String name, String value) {
+        String oldValue = getString(name);
+        if (oldValue is null || !oldValue.equals(value)) {
+            setValue(properties, name, value);
+            dirty = true;
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void removePropertyChangeListener(IPropertyChangeListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Saves the non-default-valued preferences known to this preference store
+     * to the file from which they were originally loaded.
+     *
+     * @exception java.io.IOException
+     *                if there is a problem saving this store
+     */
+    public void save() {
+        if (filename is null) {
+            throw new IOException("File name not specified");//$NON-NLS-1$
+        }
+        File out_ = null;
+        try {
+            out_ = new File(filename,File.WriteCreate);
+            save(out_, null);
+        } finally {
+            if (out_ !is null) {
+                out_.close();
+            }
+        }
+    }
+
+    /**
+     * Saves this preference store to the given output stream. The given string
+     * is inserted as header information.
+     *
+     * @param out
+     *            the output stream
+     * @param header
+     *            the header
+     * @exception java.io.IOException
+     *                if there is a problem saving this store
+     */
+    public void save(OutputStream out_, String header) {
+        properties.store(out_, header);
+        dirty = false;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setDefault(String name, double value) {
+        setValue(defaultProperties, name, value);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setDefault(String name, float value) {
+        setValue(defaultProperties, name, value);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setDefault(String name, int value) {
+        setValue(defaultProperties, name, value);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setDefault(String name, long value) {
+        setValue(defaultProperties, name, value);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setDefault(String name, String value) {
+        setValue(defaultProperties, name, value);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setDefault(String name, bool value) {
+        setValue(defaultProperties, name, value);
+    }
+
+    /**
+     * Sets the name of the file used when loading and storing this preference
+     * store.
+     * <p>
+     * Afterward, the methods <code>load()</code> and <code>save()</code>
+     * can be used to load and store this preference store.
+     * </p>
+     *
+     * @param name
+     *            the file name
+     * @see #load()
+     * @see #save()
+     */
+    public void setFilename(String name) {
+        filename = name;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setToDefault(String name) {
+        String oldValue = properties.get(name);
+        properties.remove(name);
+        dirty = true;
+        String newValue;
+        if (defaultProperties !is null) {
+            newValue = defaultProperties.get(name);
+        }
+        firePropertyChangeEvent(name, stringcast(oldValue), stringcast(newValue));
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setValue(String name, double value) {
+        double oldValue = getDouble(name);
+        if (oldValue !is value) {
+            setValue(properties, name, value);
+            dirty = true;
+            firePropertyChangeEvent(name, new Double(oldValue), new Double(
+                    value));
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setValue(String name, float value) {
+        float oldValue = getFloat(name);
+        if (oldValue !is value) {
+            setValue(properties, name, value);
+            dirty = true;
+            firePropertyChangeEvent(name, new Float(oldValue), new Float(value));
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setValue(String name, int value) {
+        int oldValue = getInt(name);
+        if (oldValue !is value) {
+            setValue(properties, name, value);
+            dirty = true;
+            firePropertyChangeEvent(name, new Integer(oldValue), new Integer(
+                    value));
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setValue(String name, long value) {
+        long oldValue = getLong(name);
+        if (oldValue !is value) {
+            setValue(properties, name, value);
+            dirty = true;
+            firePropertyChangeEvent(name, new Long(oldValue), new Long(value));
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setValue(String name, String value) {
+        String oldValue = getString(name);
+        if (oldValue is null || !oldValue.equals(value)) {
+            setValue(properties, name, value);
+            dirty = true;
+            firePropertyChangeEvent(name, stringcast(oldValue), stringcast(value));
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setValue(String name, bool value) {
+        bool oldValue = getBoolean(name);
+        if (oldValue !is value) {
+            setValue(properties, name, value);
+            dirty = true;
+            firePropertyChangeEvent(name, oldValue ? Boolean.TRUE
+                    : Boolean.FALSE, value ? Boolean.TRUE : Boolean.FALSE);
+        }
+    }
+
+    /**
+     * Helper method: sets value for a given name.
+     *
+     * @param p
+     * @param name
+     * @param value
+     */
+    private void setValue(Properties p, String name, double value) {
+        Assert.isTrue(p !is null);
+        p.put(name, Double.toString(value));
+    }
+
+    /**
+     * Helper method: sets value for a given name.
+     *
+     * @param p
+     * @param name
+     * @param value
+     */
+    private void setValue(Properties p, String name, float value) {
+        Assert.isTrue(p !is null);
+        p.put(name, Float.toString(value));
+    }
+
+    /**
+     * Helper method: sets value for a given name.
+     *
+     * @param p
+     * @param name
+     * @param value
+     */
+    private void setValue(Properties p, String name, int value) {
+        Assert.isTrue(p !is null);
+        p.put(name, Integer.toString(value));
+    }
+
+    /**
+     * Helper method: sets the value for a given name.
+     *
+     * @param p
+     * @param name
+     * @param value
+     */
+    private void setValue(Properties p, String name, long value) {
+        Assert.isTrue(p !is null);
+        p.put(name, Long.toString(value));
+    }
+
+    /**
+     * Helper method: sets the value for a given name.
+     *
+     * @param p
+     * @param name
+     * @param value
+     */
+    private void setValue(Properties p, String name, String value) {
+        // SWT: allow null value
+        Assert.isTrue(p !is null /+&& value !is null+/);
+        p.put(name, value);
+    }
+
+    /**
+     * Helper method: sets the value for a given name.
+     *
+     * @param p
+     * @param name
+     * @param value
+     */
+    private void setValue(Properties p, String name, bool value) {
+        Assert.isTrue(p !is null);
+        p.put(name, value is true ? IPreferenceStore.TRUE
+                : IPreferenceStore.FALSE);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/RadioGroupFieldEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,357 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.RadioGroupFieldEditor;
+
+import org.eclipse.jface.preference.FieldEditor;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A field editor for an enumeration type preference.
+ * The choices are presented as a list of radio buttons.
+ */
+public class RadioGroupFieldEditor : FieldEditor {
+
+    /**
+     * List of radio button entries of the form [label,value].
+     */
+    private String[][] labelsAndValues;
+
+    /**
+     * Number of columns into which to arrange the radio buttons.
+     */
+    private int numColumns;
+
+    /**
+     * Indent used for the first column of the radion button matrix.
+     */
+    private int indent = HORIZONTAL_GAP;
+
+    /**
+     * The current value, or <code>null</code> if none.
+     */
+    private String value;
+
+    /**
+     * The box of radio buttons, or <code>null</code> if none
+     * (before creation and after disposal).
+     */
+    private Composite radioBox;
+
+    /**
+     * The radio buttons, or <code>null</code> if none
+     * (before creation and after disposal).
+     */
+    private Button[] radioButtons;
+
+    /**
+     * Whether to use a Group control.
+     */
+    private bool useGroup;
+
+    /**
+     * Creates a new radio group field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a radio group field editor.
+     * This constructor does not use a <code>Group</code> to contain the radio buttons.
+     * It is equivalent to using the following constructor with <code>false</code>
+     * for the <code>useGroup</code> argument.
+     * <p>
+     * Example usage:
+     * <pre>
+     *      RadioGroupFieldEditor editor= new RadioGroupFieldEditor(
+     *          "GeneralPage.DoubleClick", resName, 1,
+     *          new String[][] {
+     *              {"Open Browser", "open"},
+     *              {"Expand Tree", "expand"}
+     *          },
+     *          parent);
+     * </pre>
+     * </p>
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param numColumns the number of columns for the radio button presentation
+     * @param labelAndValues list of radio button [label, value] entries;
+     *  the value is returned when the radio button is selected
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText, int numColumns,
+            String[][] labelAndValues, Composite parent) {
+        this(name, labelText, numColumns, labelAndValues, parent, false);
+    }
+
+    /**
+     * Creates a radio group field editor.
+     * <p>
+     * Example usage:
+     * <pre>
+     *      RadioGroupFieldEditor editor= new RadioGroupFieldEditor(
+     *          "GeneralPage.DoubleClick", resName, 1,
+     *          new String[][] {
+     *              {"Open Browser", "open"},
+     *              {"Expand Tree", "expand"}
+     *          },
+     *          parent,
+     *          true);
+     * </pre>
+     * </p>
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param numColumns the number of columns for the radio button presentation
+     * @param labelAndValues list of radio button [label, value] entries;
+     *  the value is returned when the radio button is selected
+     * @param parent the parent of the field editor's control
+     * @param useGroup whether to use a Group control to contain the radio buttons
+     */
+    public this(String name, String labelText, int numColumns,
+            String[][] labelAndValues, Composite parent, bool useGroup) {
+        init(name, labelText);
+        Assert.isTrue(checkArray(labelAndValues));
+        this.labelsAndValues = labelAndValues;
+        this.numColumns = numColumns;
+        this.useGroup = useGroup;
+        createControl(parent);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void adjustForNumColumns(int numColumns) {
+        Control control = getLabelControl();
+        if (control !is null) {
+            (cast(GridData) control.getLayoutData()).horizontalSpan = numColumns;
+        }
+        (cast(GridData) radioBox.getLayoutData()).horizontalSpan = numColumns;
+    }
+
+    /**
+     * Checks whether given <code>String[][]</code> is of "type"
+     * <code>String[][2]</code>.
+     * @param table
+     *
+     * @return <code>true</code> if it is ok, and <code>false</code> otherwise
+     */
+    private bool checkArray(String[][] table) {
+        if (table is null) {
+            return false;
+        }
+        for (int i = 0; i < table.length; i++) {
+            String[] array = table[i];
+            if (array is null || array.length !is 2) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doFillIntoGrid(Composite parent, int numColumns) {
+        if (useGroup) {
+            Control control = getRadioBoxControl(parent);
+            GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+            control.setLayoutData(gd);
+        } else {
+            Control control = getLabelControl(parent);
+            GridData gd = new GridData();
+            gd.horizontalSpan = numColumns;
+            control.setLayoutData(gd);
+            control = getRadioBoxControl(parent);
+            gd = new GridData();
+            gd.horizontalSpan = numColumns;
+            gd.horizontalIndent = indent;
+            control.setLayoutData(gd);
+        }
+
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doLoad() {
+        updateValue(getPreferenceStore().getString(getPreferenceName()));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doLoadDefault() {
+        updateValue(getPreferenceStore().getDefaultString(getPreferenceName()));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doStore() {
+        if (value is null) {
+            getPreferenceStore().setToDefault(getPreferenceName());
+            return;
+        }
+
+        getPreferenceStore().setValue(getPreferenceName(), value);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public override int getNumberOfControls() {
+        return 1;
+    }
+
+    /**
+     * Returns this field editor's radio group control.
+     * @param parent The parent to create the radioBox in
+     * @return the radio group control
+     */
+    public Composite getRadioBoxControl(Composite parent) {
+        if (radioBox is null) {
+
+            Font font = parent.getFont();
+
+            if (useGroup) {
+                Group group = new Group(parent, SWT.NONE);
+                group.setFont(font);
+                String text = getLabelText();
+                if (text !is null) {
+                    group.setText(text);
+                }
+                radioBox = group;
+                GridLayout layout = new GridLayout();
+                layout.horizontalSpacing = HORIZONTAL_GAP;
+                layout.numColumns = numColumns;
+                radioBox.setLayout(layout);
+            } else {
+                radioBox = new Composite(parent, SWT.NONE);
+                GridLayout layout = new GridLayout();
+                layout.marginWidth = 0;
+                layout.marginHeight = 0;
+                layout.horizontalSpacing = HORIZONTAL_GAP;
+                layout.numColumns = numColumns;
+                radioBox.setLayout(layout);
+                radioBox.setFont(font);
+            }
+
+            radioButtons = new Button[labelsAndValues.length];
+            for (int i = 0; i < labelsAndValues.length; i++) {
+                Button radio = new Button(radioBox, SWT.RADIO | SWT.LEFT);
+                radioButtons[i] = radio;
+                String[] labelAndValue = labelsAndValues[i];
+                radio.setText(labelAndValue[0]);
+                radio.setData(stringcast(labelAndValue[1]));
+                radio.setFont(font);
+                radio.addSelectionListener(new class SelectionAdapter {
+                    public void widgetSelected(SelectionEvent event) {
+                        String oldValue = value;
+                        value = stringcast( event.widget.getData() );
+                        setPresentsDefaultValue(false);
+                        fireValueChanged(VALUE, stringcast(oldValue), stringcast(value));
+                    }
+                });
+            }
+            radioBox.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    radioBox = null;
+                    radioButtons = null;
+                }
+            });
+        } else {
+            checkParent(radioBox, parent);
+        }
+        return radioBox;
+    }
+
+    /**
+     * Sets the indent used for the first column of the radion button matrix.
+     *
+     * @param indent the indent (in pixels)
+     */
+    public void setIndent(int indent) {
+        if (indent < 0) {
+            this.indent = 0;
+        } else {
+            this.indent = indent;
+        }
+    }
+
+    /**
+     * Select the radio button that conforms to the given value.
+     *
+     * @param selectedValue the selected value
+     */
+    private void updateValue(String selectedValue) {
+        this.value = selectedValue;
+        if (radioButtons is null) {
+            return;
+        }
+
+        if (this.value !is null) {
+            bool found = false;
+            for (int i = 0; i < radioButtons.length; i++) {
+                Button radio = radioButtons[i];
+                bool selection = false;
+                if (stringcast( radio.getData()).equals(this.value)) {
+                    selection = true;
+                    found = true;
+                }
+                radio.setSelection(selection);
+            }
+            if (found) {
+                return;
+            }
+        }
+
+        // We weren't able to find the value. So we select the first
+        // radio button as a default.
+        if (radioButtons.length > 0) {
+            radioButtons[0].setSelection(true);
+            this.value = stringcast( radioButtons[0].getData());
+        }
+        return;
+    }
+
+    /*
+     * @see FieldEditor.setEnabled(bool,Composite).
+     */
+    public override void setEnabled(bool enabled, Composite parent) {
+        if (!useGroup) {
+            super.setEnabled(enabled, parent);
+        }
+        for (int i = 0; i < radioButtons.length; i++) {
+            radioButtons[i].setEnabled(enabled);
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/ScaleFieldEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,387 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 org.eclipse.jface.preference.ScaleFieldEditor;
+
+import org.eclipse.jface.preference.FieldEditor;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Scale;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A field editor for an integer type preference. This class may be used as is,
+ * or subclassed as required.
+ *
+ * @since 3.0
+ */
+public class ScaleFieldEditor : FieldEditor {
+
+    /**
+     * Value that will feed Scale.setIncrement(int).
+     */
+    private int incrementValue;
+
+    /**
+     * Value that will feed Scale.setMaximum(int).
+     */
+    private int maxValue;
+
+    /**
+     * Value that will feed Scale.setMinimum(int).
+     */
+    private int minValue;
+
+    /**
+     * Old integer value.
+     */
+    private int oldValue;
+
+    /**
+     * Value that will feed Scale.setPageIncrement(int).
+     */
+    private int pageIncrementValue;
+
+    /**
+     * The scale, or <code>null</code> if none.
+     */
+    protected Scale scale;
+
+    /**
+     * Creates a scale field editor.
+     *
+     * @param name
+     *            the name of the preference this field editor works on
+     * @param labelText
+     *            the label text of the field editor
+     * @param parent
+     *            the parent of the field editor's control
+     */
+    public this(String name, String labelText, Composite parent) {
+        super(name, labelText, parent);
+        setDefaultValues();
+    }
+
+    /**
+     * Creates a scale field editor with particular scale values.
+     *
+     * @param name
+     *            the name of the preference this field editor works on
+     * @param labelText
+     *            the label text of the field editor
+     * @param parent
+     *            the parent of the field editor's control
+     * @param min
+     *            the value used for Scale.setMinimum(int).
+     * @param max
+     *            the value used for Scale.setMaximum(int).
+     * @param increment
+     *            the value used for Scale.setIncrement(int).
+     * @param pageIncrement
+     *            the value used for Scale.setPageIncrement(int).
+     */
+    public this(String name, String labelText, Composite parent,
+            int min, int max, int increment, int pageIncrement) {
+        super(name, labelText, parent);
+        setValues(min, max, increment, pageIncrement);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.preference.FieldEditor#adjustForNumColumns(int)
+     */
+    protected override void adjustForNumColumns(int numColumns) {
+        (cast(GridData) scale.getLayoutData()).horizontalSpan = numColumns - 1;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.preference.FieldEditor#doFillIntoGrid(org.eclipse.swt.widgets.Composite,
+     *      int)
+     */
+    protected override void doFillIntoGrid(Composite parent, int numColumns) {
+        Control control = getLabelControl(parent);
+        GridData gd = new GridData();
+        control.setLayoutData(gd);
+
+        scale = getScaleControl(parent);
+        gd = new GridData(GridData.FILL_HORIZONTAL);
+        gd.verticalAlignment = GridData.FILL;
+        gd.horizontalSpan = numColumns - 1;
+        gd.grabExcessHorizontalSpace = true;
+        scale.setLayoutData(gd);
+        updateScale();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.preference.FieldEditor#doLoad()
+     */
+    protected override void doLoad() {
+        if (scale !is null) {
+            int value = getPreferenceStore().getInt(getPreferenceName());
+            scale.setSelection(value);
+            oldValue = value;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.preference.FieldEditor#doLoadDefault()
+     */
+    protected override void doLoadDefault() {
+        if (scale !is null) {
+            int value = getPreferenceStore().getDefaultInt(getPreferenceName());
+            scale.setSelection(value);
+        }
+        valueChanged();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.preference.FieldEditor#doStore()
+     */
+    protected override void doStore() {
+        getPreferenceStore()
+                .setValue(getPreferenceName(), scale.getSelection());
+    }
+
+    /**
+     * Returns the value that will be used for Scale.setIncrement(int).
+     *
+     * @return the value.
+     * @see org.eclipse.swt.widgets.Scale#setIncrement(int)
+     */
+    public int getIncrement() {
+        return incrementValue;
+    }
+
+    /**
+     * Returns the value that will be used for Scale.setMaximum(int).
+     *
+     * @return the value.
+     * @see org.eclipse.swt.widgets.Scale#setMaximum(int)
+     */
+    public int getMaximum() {
+        return maxValue;
+    }
+
+    /**
+     * Returns the value that will be used for Scale.setMinimum(int).
+     *
+     * @return the value.
+     * @see org.eclipse.swt.widgets.Scale#setMinimum(int)
+     */
+    public int getMinimum() {
+        return minValue;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.preference.FieldEditor#getNumberOfControls()
+     */
+    public override int getNumberOfControls() {
+        return 2;
+    }
+
+    /**
+     * Returns the value that will be used for Scale.setPageIncrement(int).
+     *
+     * @return the value.
+     * @see org.eclipse.swt.widgets.Scale#setPageIncrement(int)
+     */
+    public int getPageIncrement() {
+        return pageIncrementValue;
+    }
+
+    /**
+     * Returns this field editor's scale control.
+     *
+     * @return the scale control, or <code>null</code> if no scale field is
+     *         created yet
+     */
+    public Scale getScaleControl() {
+        return scale;
+    }
+
+    /**
+     * Returns this field editor's scale control. The control is created if it
+     * does not yet exist.
+     *
+     * @param parent
+     *            the parent
+     * @return the scale control
+     */
+    private Scale getScaleControl(Composite parent) {
+        if (scale is null) {
+            scale = new Scale(parent, SWT.HORIZONTAL);
+            scale.setFont(parent.getFont());
+            scale.addSelectionListener(new class SelectionAdapter {
+                public void widgetSelected(SelectionEvent event) {
+                    valueChanged();
+                }
+            });
+            scale.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    scale = null;
+                }
+            });
+        } else {
+            checkParent(scale, parent);
+        }
+        return scale;
+    }
+
+    /**
+     * Set default values for the various scale fields.  These defaults are:<br>
+     * <ul>
+     * <li>Minimum  = 0
+     * <li>Maximim = 10
+     * <li>Increment = 1
+     * <li>Page Increment = 1
+     * </ul>
+     */
+    private void setDefaultValues() {
+        setValues(0, 10, 1, 1);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.preference.FieldEditor#setFocus()
+     */
+    public override void setFocus() {
+        if (scale !is null && !scale.isDisposed()) {
+            scale.setFocus();
+        }
+    }
+
+    /**
+     * Set the value to be used for Scale.setIncrement(int) and update the
+     * scale.
+     *
+     * @param increment
+     *            a value greater than 0.
+     * @see org.eclipse.swt.widgets.Scale#setIncrement(int)
+     */
+    public void setIncrement(int increment) {
+        this.incrementValue = increment;
+        updateScale();
+    }
+
+    /**
+     * Set the value to be used for Scale.setMaximum(int) and update the
+     * scale.
+     *
+     * @param max
+     *            a value greater than 0.
+     * @see org.eclipse.swt.widgets.Scale#setMaximum(int)
+     */
+    public void setMaximum(int max) {
+        this.maxValue = max;
+        updateScale();
+    }
+
+    /**
+     * Set the value to be used for Scale.setMinumum(int) and update the
+     * scale.
+     *
+     * @param min
+     *            a value greater than 0.
+     * @see org.eclipse.swt.widgets.Scale#setMinimum(int)
+     */
+    public void setMinimum(int min) {
+        this.minValue = min;
+        updateScale();
+    }
+
+    /**
+     * Set the value to be used for Scale.setPageIncrement(int) and update the
+     * scale.
+     *
+     * @param pageIncrement
+     *            a value greater than 0.
+     * @see org.eclipse.swt.widgets.Scale#setPageIncrement(int)
+     */
+    public void setPageIncrement(int pageIncrement) {
+        this.pageIncrementValue = pageIncrement;
+        updateScale();
+    }
+
+    /**
+     * Set all Scale values.
+     *
+     * @param min
+     *            the value used for Scale.setMinimum(int).
+     * @param max
+     *            the value used for Scale.setMaximum(int).
+     * @param increment
+     *            the value used for Scale.setIncrement(int).
+     * @param pageIncrement
+     *            the value used for Scale.setPageIncrement(int).
+     */
+    private void setValues(int min, int max, int increment, int pageIncrement) {
+        this.incrementValue = increment;
+        this.maxValue = max;
+        this.minValue = min;
+        this.pageIncrementValue = pageIncrement;
+        updateScale();
+    }
+
+    /**
+     * Update the scale particulars with set values.
+     */
+    private void updateScale() {
+        if (scale !is null && !scale.isDisposed()) {
+            scale.setMinimum(getMinimum());
+            scale.setMaximum(getMaximum());
+            scale.setIncrement(getIncrement());
+            scale.setPageIncrement(getPageIncrement());
+        }
+    }
+
+    /**
+     * Informs this field editor's listener, if it has one, about a change to
+     * the value (<code>VALUE</code> property) provided that the old and new
+     * values are different.
+     * <p>
+     * This hook is <em>not</em> called when the scale is initialized (or
+     * reset to the default value) from the preference store.
+     * </p>
+     */
+    protected void valueChanged() {
+        setPresentsDefaultValue(false);
+
+        int newValue = scale.getSelection();
+        if (newValue !is oldValue) {
+            fireStateChanged(IS_VALID, false, true);
+            fireValueChanged(VALUE, new ValueWrapperInt(oldValue),
+                    new ValueWrapperInt(newValue));
+            oldValue = newValue;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/StringButtonFieldEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,186 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Thierry Lach - thierry.lach@bbdodetroit.com - Fix for Bug 37155
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.preference.StringButtonFieldEditor;
+
+import org.eclipse.jface.preference.StringFieldEditor;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * An abstract field editor for a string type preference that presents
+ * a string input field with a change button to its right to edit the
+ * input field's content. When the user presses the change button, the
+ * abstract framework method <code>changePressed()</code> gets called
+ * to compute a new string.
+ */
+public abstract class StringButtonFieldEditor : StringFieldEditor {
+
+    /**
+     * The change button, or <code>null</code> if none
+     * (before creation and after disposal).
+     */
+    private Button changeButton;
+
+    /**
+     * The text for the change button, or <code>null</code> if missing.
+     */
+    private String changeButtonText;
+
+    /**
+     * Creates a new string button field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a string button field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    protected this(String name, String labelText,
+            Composite parent) {
+        init(name, labelText);
+        createControl(parent);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void adjustForNumColumns(int numColumns) {
+        (cast(GridData) getTextControl().getLayoutData()).horizontalSpan = numColumns - 2;
+    }
+
+    /**
+     * Notifies that this field editor's change button has been pressed.
+     * <p>
+     * Subclasses must implement this method to provide a corresponding
+     * new string for the text field. If the returned value is <code>null</code>,
+     * the currently displayed value remains.
+     * </p>
+     *
+     * @return the new string to display, or <code>null</code> to leave the
+     *  old string showing
+     */
+    protected abstract String changePressed();
+
+    /* (non-Javadoc)
+     * Method declared on StringFieldEditor (and FieldEditor).
+     */
+    protected override void doFillIntoGrid(Composite parent, int numColumns) {
+        super.doFillIntoGrid(parent, numColumns - 1);
+        changeButton = getChangeControl(parent);
+        GridData gd = new GridData();
+        gd.horizontalAlignment = GridData.FILL;
+        int widthHint = convertHorizontalDLUsToPixels(changeButton,
+                IDialogConstants.BUTTON_WIDTH);
+        gd.widthHint = Math.max(widthHint, changeButton.computeSize(
+                SWT.DEFAULT, SWT.DEFAULT, true).x);
+        changeButton.setLayoutData(gd);
+    }
+
+    /**
+     * Get the change control. Create it in parent if required.
+     * @param parent
+     * @return Button
+     */
+    protected Button getChangeControl(Composite parent) {
+        if (changeButton is null) {
+            changeButton = new Button(parent, SWT.PUSH);
+            if (changeButtonText is null) {
+                changeButtonText = JFaceResources.getString("openChange"); //$NON-NLS-1$
+            }
+            changeButton.setText(changeButtonText);
+            changeButton.setFont(parent.getFont());
+            changeButton.addSelectionListener(new class SelectionAdapter {
+                public void widgetSelected(SelectionEvent evt) {
+                    String newValue = changePressed();
+                    if (newValue !is null) {
+                        setStringValue(newValue);
+                    }
+                }
+            });
+            changeButton.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    changeButton = null;
+                }
+            });
+        } else {
+            checkParent(changeButton, parent);
+        }
+        return changeButton;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public override int getNumberOfControls() {
+        return 3;
+    }
+
+    /**
+     * Returns this field editor's shell.
+     *
+     * @return the shell
+     */
+    protected Shell getShell() {
+        if (changeButton is null) {
+            return null;
+        }
+        return changeButton.getShell();
+    }
+
+    /**
+     * Sets the text of the change button.
+     *
+     * @param text the new text
+     */
+    public void setChangeButtonText(String text) {
+        Assert.isNotNull(text);
+        changeButtonText = text;
+        if (changeButton !is null) {
+            changeButton.setText(text);
+            Point prefSize = changeButton.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+            GridData data = cast(GridData)changeButton.getLayoutData();
+            data.widthHint = Math.max(SWT.DEFAULT, prefSize.x);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.preference.FieldEditor#setEnabled(bool, org.eclipse.swt.widgets.Composite)
+     */
+    public override void setEnabled(bool enabled, Composite parent) {
+        super.setEnabled(enabled, parent);
+        if (changeButton !is null) {
+            changeButton.setEnabled(enabled);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/preference/StringFieldEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,534 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.preference.StringFieldEditor;
+
+import org.eclipse.jface.preference.FieldEditor;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A field editor for a string type preference.
+ * <p>
+ * This class may be used as is, or subclassed as required.
+ * </p>
+ */
+public class StringFieldEditor : FieldEditor {
+
+    alias FieldEditor.showErrorMessage showErrorMessage;
+    /**
+     * Validation strategy constant (value <code>0</code>) indicating that
+     * the editor should perform validation after every key stroke.
+     *
+     * @see #setValidateStrategy
+     */
+    public static const int VALIDATE_ON_KEY_STROKE = 0;
+
+    /**
+     * Validation strategy constant (value <code>1</code>) indicating that
+     * the editor should perform validation only when the text widget
+     * loses focus.
+     *
+     * @see #setValidateStrategy
+     */
+    public static const int VALIDATE_ON_FOCUS_LOST = 1;
+
+    /**
+     * Text limit constant (value <code>-1</code>) indicating unlimited
+     * text limit and width.
+     */
+    public static int UNLIMITED = -1;
+
+    /**
+     * Cached valid state.
+     */
+    private bool isValid_;
+
+    /**
+     * Old text value.
+     * @since 3.4 this field is protected.
+     */
+    protected String oldValue;
+
+    /**
+     * The text field, or <code>null</code> if none.
+     */
+    Text textField;
+
+    /**
+     * Width of text field in characters; initially unlimited.
+     */
+    private int widthInChars;
+
+    /**
+     * Text limit of text field in characters; initially unlimited.
+     */
+    private int textLimit;
+
+    /**
+     * The error message, or <code>null</code> if none.
+     */
+    private String errorMessage;
+
+    /**
+     * Indicates whether the empty string is legal;
+     * <code>true</code> by default.
+     */
+    private bool emptyStringAllowed = true;
+
+    /**
+     * The validation strategy;
+     * <code>VALIDATE_ON_KEY_STROKE</code> by default.
+     */
+    private int validateStrategy;
+
+    /**
+     * Creates a new string field editor
+     */
+    protected this() {
+        widthInChars = UNLIMITED;
+        textLimit = UNLIMITED;
+        validateStrategy = VALIDATE_ON_KEY_STROKE;
+    }
+
+    /**
+     * Creates a string field editor.
+     * Use the method <code>setTextLimit</code> to limit the text.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param width the width of the text input field in characters,
+     *  or <code>UNLIMITED</code> for no limit
+     * @param strategy either <code>VALIDATE_ON_KEY_STROKE</code> to perform
+     *  on the fly checking (the default), or <code>VALIDATE_ON_FOCUS_LOST</code> to
+     *  perform validation only after the text has been typed in
+     * @param parent the parent of the field editor's control
+     * @since 2.0
+     */
+    public this(String name, String labelText, int width,
+            int strategy, Composite parent) {
+        this();
+        init(name, labelText);
+        widthInChars = width;
+        setValidateStrategy(strategy);
+        isValid_ = false;
+        errorMessage = JFaceResources
+                .getString("StringFieldEditor.errorMessage");//$NON-NLS-1$
+        createControl(parent);
+    }
+
+    /**
+     * Creates a string field editor.
+     * Use the method <code>setTextLimit</code> to limit the text.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param width the width of the text input field in characters,
+     *  or <code>UNLIMITED</code> for no limit
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText, int width,
+            Composite parent) {
+        this(name, labelText, width, VALIDATE_ON_KEY_STROKE, parent);
+    }
+
+    /**
+     * Creates a string field editor of unlimited width.
+     * Use the method <code>setTextLimit</code> to limit the text.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText, Composite parent) {
+        this(name, labelText, UNLIMITED, parent);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void adjustForNumColumns(int numColumns) {
+        GridData gd = cast(GridData) textField.getLayoutData();
+        gd.horizontalSpan = numColumns - 1;
+        // We only grab excess space if we have to
+        // If another field editor has more columns then
+        // we assume it is setting the width.
+        gd.grabExcessHorizontalSpace = gd.horizontalSpan is 1;
+    }
+
+    /**
+     * Checks whether the text input field contains a valid value or not.
+     *
+     * @return <code>true</code> if the field value is valid,
+     *   and <code>false</code> if invalid
+     */
+    protected bool checkState() {
+        bool result = false;
+        if (emptyStringAllowed) {
+            result = true;
+        }
+
+        if (textField is null) {
+            result = false;
+        }
+
+        String txt = textField.getText();
+
+        result = (txt.trim().length > 0) || emptyStringAllowed;
+
+        // call hook for subclasses
+        result = result && doCheckState();
+
+        if (result) {
+            clearErrorMessage();
+        } else {
+            showErrorMessage(errorMessage);
+        }
+
+        return result;
+    }
+
+    /**
+     * Hook for subclasses to do specific state checks.
+     * <p>
+     * The default implementation of this framework method does
+     * nothing and returns <code>true</code>.  Subclasses should
+     * override this method to specific state checks.
+     * </p>
+     *
+     * @return <code>true</code> if the field value is valid,
+     *   and <code>false</code> if invalid
+     */
+    protected bool doCheckState() {
+        return true;
+    }
+
+    /**
+     * Fills this field editor's basic controls into the given parent.
+     * <p>
+     * The string field implementation of this <code>FieldEditor</code>
+     * framework method contributes the text field. Subclasses may override
+     * but must call <code>super.doFillIntoGrid</code>.
+     * </p>
+     */
+    protected override void doFillIntoGrid(Composite parent, int numColumns) {
+        getLabelControl(parent);
+
+        textField = getTextControl(parent);
+        GridData gd = new GridData();
+        gd.horizontalSpan = numColumns - 1;
+        if (widthInChars !is UNLIMITED) {
+            GC gc = new GC(textField);
+            try {
+                Point extent = gc.textExtent("X");//$NON-NLS-1$
+                gd.widthHint = widthInChars * extent.x;
+            } finally {
+                gc.dispose();
+            }
+        } else {
+            gd.horizontalAlignment = GridData.FILL;
+            gd.grabExcessHorizontalSpace = true;
+        }
+        textField.setLayoutData(gd);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doLoad() {
+        if (textField !is null) {
+            String value = getPreferenceStore().getString(getPreferenceName());
+            textField.setText(value);
+            oldValue = value;
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doLoadDefault() {
+        if (textField !is null) {
+            String value = getPreferenceStore().getDefaultString(
+                    getPreferenceName());
+            textField.setText(value);
+        }
+        valueChanged();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void doStore() {
+        getPreferenceStore().setValue(getPreferenceName(), textField.getText());
+    }
+
+    /**
+     * Returns the error message that will be displayed when and if
+     * an error occurs.
+     *
+     * @return the error message, or <code>null</code> if none
+     */
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public override int getNumberOfControls() {
+        return 2;
+    }
+
+    /**
+     * Returns the field editor's value.
+     *
+     * @return the current value
+     */
+    public String getStringValue() {
+        if (textField !is null) {
+            return textField.getText();
+        }
+
+        return getPreferenceStore().getString(getPreferenceName());
+    }
+
+    /**
+     * Returns this field editor's text control.
+     *
+     * @return the text control, or <code>null</code> if no
+     * text field is created yet
+     */
+    protected Text getTextControl() {
+        return textField;
+    }
+
+    /**
+     * Returns this field editor's text control.
+     * <p>
+     * The control is created if it does not yet exist
+     * </p>
+     *
+     * @param parent the parent
+     * @return the text control
+     */
+    public Text getTextControl(Composite parent) {
+        if (textField is null) {
+            textField = new Text(parent, SWT.SINGLE | SWT.BORDER);
+            textField.setFont(parent.getFont());
+            switch (validateStrategy) {
+            case VALIDATE_ON_KEY_STROKE:
+                textField.addKeyListener(new class KeyAdapter {
+
+                    /* (non-Javadoc)
+                     * @see org.eclipse.swt.events.KeyAdapter#keyReleased(org.eclipse.swt.events.KeyEvent)
+                     */
+                    public void keyReleased(KeyEvent e) {
+                        valueChanged();
+                    }
+                });
+
+                break;
+            case VALIDATE_ON_FOCUS_LOST:
+                textField.addKeyListener(new class KeyAdapter {
+                    public void keyPressed(KeyEvent e) {
+                        clearErrorMessage();
+                    }
+                });
+                textField.addFocusListener(new class FocusAdapter {
+                    public void focusGained(FocusEvent e) {
+                        refreshValidState();
+                    }
+
+                    public void focusLost(FocusEvent e) {
+                        valueChanged();
+                        clearErrorMessage();
+                    }
+                });
+                break;
+            default:
+                Assert.isTrue(false, "Unknown validate strategy");//$NON-NLS-1$
+            }
+            textField.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    textField = null;
+                }
+            });
+            if (textLimit > 0) {//Only set limits above 0 - see SWT spec
+                textField.setTextLimit(textLimit);
+            }
+        } else {
+            checkParent(textField, parent);
+        }
+        return textField;
+    }
+
+    /**
+     * Returns whether an empty string is a valid value.
+     *
+     * @return <code>true</code> if an empty string is a valid value, and
+     *  <code>false</code> if an empty string is invalid
+     * @see #setEmptyStringAllowed
+     */
+    public bool isEmptyStringAllowed() {
+        return emptyStringAllowed;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public override bool isValid() {
+        return isValid_;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected override void refreshValidState() {
+        isValid_ = checkState();
+    }
+
+    /**
+     * Sets whether the empty string is a valid value or not.
+     *
+     * @param b <code>true</code> if the empty string is allowed,
+     *  and <code>false</code> if it is considered invalid
+     */
+    public void setEmptyStringAllowed(bool b) {
+        emptyStringAllowed = b;
+    }
+
+    /**
+     * Sets the error message that will be displayed when and if
+     * an error occurs.
+     *
+     * @param message the error message
+     */
+    public void setErrorMessage(String message) {
+        errorMessage = message;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public override void setFocus() {
+        if (textField !is null) {
+            textField.setFocus();
+        }
+    }
+
+    /**
+     * Sets this field editor's value.
+     *
+     * @param value the new value, or <code>null</code> meaning the empty string
+     */
+    public void setStringValue(String value) {
+        if (textField !is null) {
+            if (value is null) {
+                value = "";//$NON-NLS-1$
+            }
+            oldValue = textField.getText();
+            if (!oldValue.equals(value)) {
+                textField.setText(value);
+                valueChanged();
+            }
+        }
+    }
+
+    /**
+     * Sets this text field's text limit.
+     *
+     * @param limit the limit on the number of character in the text
+     *  input field, or <code>UNLIMITED</code> for no limit
+
+     */
+    public void setTextLimit(int limit) {
+        textLimit = limit;
+        if (textField !is null) {
+            textField.setTextLimit(limit);
+        }
+    }
+
+    /**
+     * Sets the strategy for validating the text.
+     * <p>
+     * Calling this method has no effect after <code>createPartControl</code>
+     * is called. Thus this method is really only useful for subclasses to call
+     * in their constructor. However, it has public visibility for backward
+     * compatibility.
+     * </p>
+     *
+     * @param value either <code>VALIDATE_ON_KEY_STROKE</code> to perform
+     *  on the fly checking (the default), or <code>VALIDATE_ON_FOCUS_LOST</code> to
+     *  perform validation only after the text has been typed in
+     */
+    public void setValidateStrategy(int value) {
+        Assert.isTrue(value is VALIDATE_ON_FOCUS_LOST
+                || value is VALIDATE_ON_KEY_STROKE);
+        validateStrategy = value;
+    }
+
+    /**
+     * Shows the error message set via <code>setErrorMessage</code>.
+     */
+    public void showErrorMessage() {
+        showErrorMessage(errorMessage);
+    }
+
+    /**
+     * Informs this field editor's listener, if it has one, about a change
+     * to the value (<code>VALUE</code> property) provided that the old and
+     * new values are different.
+     * <p>
+     * This hook is <em>not</em> called when the text is initialized
+     * (or reset to the default value) from the preference store.
+     * </p>
+     */
+    protected void valueChanged() {
+        setPresentsDefaultValue(false);
+        bool oldState = isValid_;
+        refreshValidState();
+
+        if (isValid_ !is oldState) {
+            fireStateChanged(IS_VALID, oldState, isValid_);
+        }
+
+        String newValue = textField.getText();
+        if (!newValue.equals(oldValue)) {
+            fireValueChanged(VALUE, stringcast(oldValue), stringcast(newValue));
+            oldValue = newValue;
+        }
+    }
+
+    /*
+     * @see FieldEditor.setEnabled(bool,Composite).
+     */
+    public override void setEnabled(bool enabled, Composite parent) {
+        super.setEnabled(enabled, parent);
+        getTextControl(parent).setEnabled(enabled);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/AbstractResourceManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.resource.AbstractResourceManager;
+
+import org.eclipse.jface.resource.ResourceManager;
+import org.eclipse.jface.resource.DeviceResourceDescriptor;
+
+
+import java.lang.all;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+/**
+ * Abstract implementation of ResourceManager. Maintains reference counts for all previously
+ * allocated SWT resources. Delegates to the abstract method allocate(...) the first time a resource
+ * is referenced and delegates to the abstract method deallocate(...) the last time a reference is
+ * removed.
+ *
+ * @since 3.1
+ */
+abstract class AbstractResourceManager : ResourceManager {
+
+    /**
+     * Map of ResourceDescriptor onto RefCount. (null when empty)
+     */
+    private HashMap map = null;
+
+    /**
+     * Holds a reference count for a previously-allocated resource
+     */
+    private static class RefCount {
+        Object resource;
+        int count = 1;
+
+        this(Object resource) {
+            this.resource = resource;
+        }
+    }
+
+    /**
+     * Called the first time a resource is requested. Should allocate and return a resource
+     * of the correct type.
+     *
+     * @since 3.1
+     *
+     * @param descriptor identifier for the resource to allocate
+     * @return the newly allocated resource
+     * @throws DeviceResourceException Thrown when allocation of an SWT device resource fails
+     */
+    protected abstract Object allocate(DeviceResourceDescriptor descriptor);
+
+    /**
+     * Called the last time a resource is dereferenced. Should release any resources reserved by
+     * allocate(...).
+     *
+     * @since 3.1
+     *
+     * @param resource resource being deallocated
+     * @param descriptor identifier for the resource
+     */
+    protected abstract void deallocate(Object resource, DeviceResourceDescriptor descriptor);
+
+    /* (non-Javadoc)
+     * @see ResourceManager#create(DeviceResourceDescriptor)
+     */
+    public override final Object create(DeviceResourceDescriptor descriptor){
+
+        // Lazily allocate the map
+        if (map is null) {
+            map = new HashMap();
+        }
+
+        // Get the current reference count
+        RefCount count = cast(RefCount)map.get(descriptor);
+        if (count !is null) {
+            // If this resource already exists, increment the reference count and return
+            // the existing resource.
+            count.count++;
+            return count.resource;
+        }
+
+        // Allocate and return a new resource (with ref count = 1)
+        Object resource = allocate(descriptor);
+
+        count = new RefCount(resource);
+        map.put(descriptor, count);
+
+        return resource;
+    }
+
+    /* (non-Javadoc)
+     * @see ResourceManager#destroy(DeviceResourceDescriptor)
+     */
+    public override final void destroy(DeviceResourceDescriptor descriptor) {
+        // If the map is empty (null) then there are no resources to dispose
+        if (map is null) {
+            return;
+        }
+
+        // Find the existing resource
+        RefCount count = cast(RefCount)map.get(descriptor);
+        if (count !is null) {
+            // If the resource exists, decrement the reference count.
+            count.count--;
+            if (count.count is 0) {
+                // If this was the last reference, deallocate it.
+                deallocate(count.resource, descriptor);
+                map.remove(descriptor);
+            }
+        }
+
+        // Null out the map when empty to save a small amount of memory
+        if (map.isEmpty()) {
+            map = null;
+        }
+    }
+
+    /**
+     * Deallocates any resources allocated by this registry that have not yet been
+     * deallocated.
+     *
+     * @since 3.1
+     */
+    public override void dispose() {
+        super.dispose();
+
+        if (map is null) {
+            return;
+        }
+
+        Collection entries = map.entrySet();
+
+        for (Iterator iter = entries.iterator(); iter.hasNext();) {
+            Map.Entry next = cast(Map.Entry) iter.next();
+
+            Object key = next.getKey();
+            RefCount val = cast(RefCount)next.getValue();
+
+            deallocate(val.resource, cast(DeviceResourceDescriptor)key);
+        }
+
+        map = null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.ResourceManager#find(org.eclipse.jface.resource.DeviceResourceDescriptor)
+     */
+    public override Object find(DeviceResourceDescriptor descriptor) {
+        if (map is null) {
+            return null;
+        }
+        RefCount refCount = cast(RefCount)map.get(descriptor);
+        if (refCount is null)
+            return null;
+        return refCount.resource;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/ArrayFontDescriptor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.resource.ArrayFontDescriptor;
+
+import java.lang.all;
+
+import org.eclipse.jface.resource.FontDescriptor;
+
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+
+/**
+ * Describes a Font using an array of FontData
+ *
+ * @since 3.1
+ */
+final class ArrayFontDescriptor : FontDescriptor {
+
+    private FontData[] data;
+    private Font originalFont = null;
+
+    /**
+     * Creates a font descriptor for a font with the given name, height,
+     * and style. These arguments are passed directly to the constructor
+     * of Font.
+     *
+     * @param data FontData describing the font to create
+     *
+     * @see org.eclipse.swt.graphics.Font#Font(org.eclipse.swt.graphics.Device, org.eclipse.swt.graphics.FontData)
+     * @since 3.1
+     */
+    public this(FontData[] data) {
+        this.data = data;
+    }
+
+    /**
+     * Creates a font descriptor that describes the given font.
+     *
+     * @param originalFont font to be described
+     *
+     * @see FontDescriptor#createFrom(org.eclipse.swt.graphics.Font)
+     * @since 3.1
+     */
+    public this(Font originalFont) {
+        this(originalFont.getFontData());
+        this.originalFont = originalFont;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.FontDescriptor#getFontData()
+     */
+    public override FontData[] getFontData() {
+        // Copy the original array to ensure that callers will not modify it
+        return copy(data);
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.FontDescriptor#createFont(org.eclipse.swt.graphics.Device)
+     */
+    public override Font createFont(Device device) {
+
+        // If this descriptor is an existing font, then we can return the original font
+        // if this is the same device.
+        if (originalFont !is null) {
+            // If we're allocating on the same device as the original font, return the original.
+            if (originalFont.getDevice() is device) {
+                return originalFont;
+            }
+        }
+
+        return new Font(device, data);
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public override int opEquals(Object obj) {
+        if ((obj.classinfo is ArrayFontDescriptor.classinfo)) {
+            ArrayFontDescriptor descr = cast(ArrayFontDescriptor)obj;
+
+            if (descr.originalFont !is originalFont) {
+                return false;
+            }
+
+            if (originalFont !is null) {
+                return true;
+            }
+
+            if (data.length !is descr.data.length) {
+                return false;
+            }
+
+            for (int i = 0; i < data.length; i++) {
+                FontData fd = data[i];
+                FontData fd2 = descr.data[i];
+
+                if (!fd.opEquals(fd2)) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    public override hash_t toHash() {
+        if (originalFont !is null) {
+            return originalFont.toHash();
+        }
+
+        int code = 0;
+
+        for (int i = 0; i < data.length; i++) {
+            FontData fd = data[i];
+            code += fd.toHash();
+        }
+        return code;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.FontDescriptor#destroyFont(org.eclipse.swt.graphics.Font)
+     */
+    public override void destroyFont(Font previouslyCreatedFont) {
+        if (previouslyCreatedFont is originalFont) {
+            return;
+        }
+        previouslyCreatedFont.dispose();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/ColorDescriptor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.resource.ColorDescriptor;
+
+import org.eclipse.jface.resource.DeviceResourceDescriptor;
+import org.eclipse.jface.resource.RGBColorDescriptor;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.RGB;
+
+import java.lang.all;
+
+/**
+ * Lightweight descriptor for an SWT color. Each ColorDescriptor will create a particular SWT
+ * Color on demand. This object will be compared so hashCode(...) and equals(...) must
+ * return meaningful values.
+ *
+ * @since 3.1
+ */
+public abstract class ColorDescriptor : DeviceResourceDescriptor {
+
+    /**
+     * Creates a ColorDescriptor from an existing Color, given the Device associated
+     * with the original Color. This is the usual way to convert a Color into
+     * a ColorDescriptor. Note that the returned ColorDescriptor depends on the
+     * original Color, and disposing the Color will invalidate the ColorDescriptor.
+     *
+     * @deprecated use {@link ColorDescriptor#createFrom(Color)}
+     *
+     * @since 3.1
+     *
+     * @param toCreate Color to convert into a ColorDescriptor.
+     * @param originalDevice this must be the same Device that was passed into the
+     * original Color's constructor.
+     * @return a newly created ColorDescriptor that describes the given Color.
+     */
+    public static ColorDescriptor createFrom(Color toCreate, Device originalDevice) {
+        return new RGBColorDescriptor(toCreate);
+    }
+
+    /**
+     * Creates a ColorDescriptor from an existing color.
+     *
+     * The returned ColorDescriptor depends on the original Color. Disposing
+     * the original colour while the color descriptor is still in use may cause
+     * SWT to throw a graphic disposed exception.
+     *
+     * @since 3.1
+     *
+     * @param toCreate Color to generate a ColorDescriptor from
+     * @return a newly created ColorDescriptor
+     */
+    public static ColorDescriptor createFrom(Color toCreate) {
+        return new RGBColorDescriptor(toCreate);
+    }
+
+    /**
+     * Returns a color descriptor for the given RGB values
+     * @since 3.1
+     *
+     * @param toCreate RGB values to create
+     * @return a new ColorDescriptor
+     */
+    public static ColorDescriptor createFrom(RGB toCreate) {
+        return new RGBColorDescriptor(toCreate);
+    }
+
+    /**
+     * Returns the Color described by this descriptor.
+     *
+     * @param device SWT device on which to allocate the Color
+     * @return a newly allocated SWT Color object (never null)
+     * @throws DeviceResourceException if unable to allocate the Color
+     */
+    public abstract Color createColor(Device device);
+
+    /**
+     * Undoes whatever was done by createColor.
+     *
+     * @since 3.1
+     *
+     * @param toDestroy a Color that was previously allocated by an equal ColorDescriptor
+     */
+    public abstract void destroyColor(Color toDestroy);
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.DeviceResourceDescriptor#createResource(org.eclipse.swt.graphics.Device)
+     */
+    public override final Object createResource(Device device){
+        return createColor(device);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.DeviceResourceDescriptor#destroyResource(java.lang.Object)
+     */
+    public override final void destroyResource(Object previouslyCreatedObject) {
+        destroyColor(cast(Color)previouslyCreatedObject);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/ColorRegistry.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,348 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.resource.ColorRegistry;
+
+import org.eclipse.jface.resource.ColorDescriptor;
+import org.eclipse.jface.resource.ResourceRegistry;
+import org.eclipse.jface.resource.RGBColorDescriptor;
+
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Collections;
+import java.util.Collection;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+/**
+ * A color registry maintains a mapping between symbolic color names and SWT
+ * <code>Color</code>s.
+ * <p>
+ * A color registry owns all of the <code>Color</code> objects registered with
+ * it, and automatically disposes of them when the SWT Display that creates the
+ * <code>Color</code>s is disposed. Because of this, clients do not need to
+ * (indeed, must not attempt to) dispose of <code>Color</code> objects
+ * themselves.
+ * </p>
+ * <p>
+ * Methods are provided for registering listeners that will be kept
+ * apprised of changes to list of registed colors.
+ * </p>
+ * <p>
+ * Clients may instantiate this class (it was not designed to be subclassed).
+ * </p>
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ColorRegistry : ResourceRegistry {
+
+    /**
+     * Default color value.  This is cyan (very unappetizing).
+     * @since 3.4
+     */
+    private static ColorDescriptor DEFAULT_COLOR;
+    private static void init_DEFAULT_COLOR () {
+        if( DEFAULT_COLOR is null ){
+            synchronized( ColorRegistry.classinfo ){
+                if( DEFAULT_COLOR is null ){
+                    DEFAULT_COLOR = new RGBColorDescriptor(new RGB(0, 255, 255));
+                }
+            }
+        }
+    }
+
+    /**
+     * This registries <code>Display</code>. All colors will be allocated using
+     * it.
+     */
+    protected Display display;
+
+    /**
+     * Collection of <code>Color</code> that are now stale to be disposed when
+     * it is safe to do so (i.e. on shutdown).
+     */
+    private List staleColors;
+
+    /**
+     * Table of known colors, keyed by symbolic color name (key type: <code>String</code>,
+     * value type: <code>org.eclipse.swt.graphics.Color</code>.
+     */
+    private Map stringToColor;
+
+    /**
+     * Table of known color data, keyed by symbolic color name (key type:
+     * <code>String</code>, value type: <code>org.eclipse.swt.graphics.RGB</code>).
+     */
+    private Map stringToRGB;
+
+    /**
+     * Runnable that cleans up the manager on disposal of the display.
+     */
+    protected Runnable displayRunnable;
+    private void init_displayRunnable(){
+        displayRunnable = new class Runnable {
+            public void run() {
+                clearCaches();
+            }
+        };
+    }
+
+    private final bool cleanOnDisplayDisposal;
+
+    /**
+     * Create a new instance of the receiver that is hooked to the current
+     * display.
+     *
+     * @see org.eclipse.swt.widgets.Display#getCurrent()
+     */
+    public this() {
+        this(Display.getCurrent(), true);
+    }
+
+    /**
+     * Create a new instance of the receiver.
+     *
+     * @param display the <code>Display</code> to hook into.
+     */
+    public this(Display display) {
+        this (display, true);
+    }
+
+    /**
+     * Create a new instance of the receiver.
+     *
+     * @param display the <code>Display</code> to hook into
+     * @param cleanOnDisplayDisposal
+     *            whether all fonts allocated by this <code>ColorRegistry</code>
+     *            should be disposed when the display is disposed
+     * @since 3.1
+     */
+    public this(Display display, bool cleanOnDisplayDisposal) {
+        init_DEFAULT_COLOR();
+        staleColors = new ArrayList();
+        stringToColor = new HashMap();
+        stringToRGB = new HashMap();
+        init_displayRunnable();
+        Assert.isNotNull(display);
+        this.display = display;
+        this.cleanOnDisplayDisposal = cleanOnDisplayDisposal;
+        if (cleanOnDisplayDisposal) {
+            hookDisplayDispose();
+        }
+    }
+
+    /**
+     * Create a new <code>Color</code> on the receivers <code>Display</code>.
+     *
+     * @param rgb the <code>RGB</code> data for the color.
+     * @return the new <code>Color</code> object.
+     *
+     * @since 3.1
+     */
+    private Color createColor(RGB rgb) {
+        if (this.display is null) {
+            Display display = Display.getCurrent();
+            if (display is null) {
+                throw new IllegalStateException();
+            }
+            this.display = display;
+            if (cleanOnDisplayDisposal) {
+                hookDisplayDispose();
+            }
+        }
+        return new Color(display, rgb);
+    }
+
+    /**
+     * Dispose of all of the <code>Color</code>s in this iterator.
+     *
+     * @param iterator over <code>Collection</code> of <code>Color</code>
+     */
+    private void disposeColors(Iterator iterator) {
+        while (iterator.hasNext()) {
+            Object next = iterator.next();
+            (cast(Color) next).dispose();
+        }
+    }
+
+    /**
+     * Returns the <code>color</code> associated with the given symbolic color
+     * name, or <code>null</code> if no such definition exists.
+     *
+     * @param symbolicName symbolic color name
+     * @return the <code>Color</code> or <code>null</code>
+     */
+    public Color get(String symbolicName) {
+
+        Assert.isNotNull(symbolicName);
+        Object result = stringToColor.get(stringcast(symbolicName));
+        if (result !is null) {
+            return cast(Color) result;
+        }
+
+        Color color = null;
+
+        result = stringToRGB.get(stringcast(symbolicName));
+        if (result is null) {
+            return null;
+        }
+
+        color = createColor( cast(RGB) result);
+
+        stringToColor.put(stringcast(symbolicName), color);
+
+        return color;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.ResourceRegistry#getKeySet()
+     */
+    public override Set getKeySet() {
+        return Collections.unmodifiableSet(stringToRGB.keySet());
+    }
+
+    /**
+     * Returns the color data associated with the given symbolic color name.
+     *
+     * @param symbolicName symbolic color name.
+     * @return the <code>RGB</code> data, or <code>null</code> if the symbolic name
+     * is not valid.
+     */
+    public RGB getRGB(String symbolicName) {
+        Assert.isNotNull(symbolicName);
+        return cast(RGB) stringToRGB.get(symbolicName);
+    }
+
+    /**
+     * Returns the color descriptor associated with the given symbolic color
+     * name. As of 3.4 if this color is not defined then an unspecified color
+     * is returned. Users that wish to ensure a reasonable default value should
+     * use {@link #getColorDescriptor(String, ColorDescriptor)} instead.
+     *
+     * @since 3.1
+     *
+     * @param symbolicName
+     * @return the color descriptor associated with the given symbolic color
+     *         name or an unspecified sentinel.
+     */
+    public ColorDescriptor getColorDescriptor(String symbolicName) {
+        return getColorDescriptor(symbolicName, DEFAULT_COLOR);
+    }
+
+    /**
+     * Returns the color descriptor associated with the given symbolic color
+     * name. If this name does not exist within the registry the supplied
+     * default value will be used.
+     *
+     * @param symbolicName
+     * @param defaultValue
+     * @return the color descriptor associated with the given symbolic color
+     *         name or the default
+     * @since 3.4
+     */
+    public ColorDescriptor getColorDescriptor(String symbolicName,
+            ColorDescriptor defaultValue) {
+        RGB rgb = getRGB(symbolicName);
+        if (rgb is null)
+            return defaultValue;
+        return ColorDescriptor.createFrom(rgb);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.resource.ResourceRegistry#clearCaches()
+     */
+    protected override void clearCaches() {
+        disposeColors(stringToColor.values().iterator());
+        disposeColors(staleColors.iterator());
+        stringToColor.clear();
+        staleColors.clear();
+        display = null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.ResourceRegistry#hasValueFor(java.lang.String)
+     */
+    public override bool hasValueFor(String colorKey) {
+        return stringToRGB.containsKey(colorKey);
+    }
+
+    /**
+     * Hook a dispose listener on the SWT display.
+     */
+    private void hookDisplayDispose() {
+        display.disposeExec(displayRunnable);
+    }
+
+    /**
+     * Adds (or replaces) a color to this color registry under the given
+     * symbolic name.
+     * <p>
+     * A property change event is reported whenever the mapping from a symbolic
+     * name to a color changes. The source of the event is this registry; the
+     * property name is the symbolic color name.
+     * </p>
+     *
+     * @param symbolicName the symbolic color name
+     * @param colorData an <code>RGB</code> object
+     */
+    public void put(String symbolicName, RGB colorData) {
+        put(symbolicName, colorData, true);
+    }
+
+    /**
+     * Adds (or replaces) a color to this color registry under the given
+     * symbolic name.
+     * <p>
+     * A property change event is reported whenever the mapping from a symbolic
+     * name to a color changes. The source of the event is this registry; the
+     * property name is the symbolic color name.
+     * </p>
+     *
+     * @param symbolicName the symbolic color name
+     * @param colorData an <code>RGB</code> object
+     * @param update - fire a color mapping changed if true. False if this
+     *            method is called from the get method as no setting has
+     *            changed.
+     */
+    private void put(String symbolicName, RGB colorData, bool update) {
+
+        Assert.isNotNull(symbolicName);
+        Assert.isNotNull(colorData);
+
+        RGB existing = cast(RGB)stringToRGB.get(symbolicName);
+        if (colorData.opEquals(existing)) {
+            return;
+        }
+
+        Color oldColor = cast(Color) stringToColor.remove(symbolicName);
+        stringToRGB.put(stringcast(symbolicName), colorData);
+        if (update) {
+            fireMappingChanged(symbolicName, existing, colorData);
+        }
+
+        if (oldColor !is null) {
+            staleColors.add(oldColor);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/CompositeImageDescriptor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.resource.CompositeImageDescriptor;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Abstract base class for image descriptors that synthesize an image from other
+ * images in order to simulate the effect of custom drawing. For example, this
+ * could be used to superimpose a red bar dexter symbol across an image to
+ * indicate that something was disallowed.
+ * <p>
+ * Subclasses must implement the <code>getSize</code> and <code>fill</code>
+ * methods. Little or no work happens until the image descriptor's image is
+ * actually requested by a call to <code>createImage</code> (or to
+ * <code>getImageData</code> directly).
+ * </p>
+ */
+public abstract class CompositeImageDescriptor : ImageDescriptor {
+
+    /**
+     * The image data for this composite image.
+     */
+    private ImageData imageData;
+
+    /**
+     * Constructs an uninitialized composite image.
+     */
+    protected this() {
+    }
+
+    /**
+     * Draw the composite images.
+     * <p>
+     * Subclasses must implement this framework method to paint images within
+     * the given bounds using one or more calls to the <code>drawImage</code>
+     * framework method.
+     * </p>
+     *
+     * @param width
+     *            the width
+     * @param height
+     *            the height
+     */
+    protected abstract void drawCompositeImage(int width, int height);
+
+    /**
+     * Draws the given source image data into this composite image at the given
+     * position.
+     * <p>
+     * Call this internal framework method to superimpose another image atop
+     * this composite image.
+     * </p>
+     *
+     * @param src
+     *            the source image data
+     * @param ox
+     *            the x position
+     * @param oy
+     *            the y position
+     */
+    final protected void drawImage(ImageData src, int ox, int oy) {
+        ImageData dst = imageData;
+        PaletteData srcPalette = src.palette;
+        ImageData srcMask = null;
+        int alphaMask = 0, alphaShift = 0;
+        if (src.maskData !is null) {
+            srcMask = src.getTransparencyMask ();
+            if (src.depth is 32) {
+                alphaMask = ~(srcPalette.redMask | srcPalette.greenMask | srcPalette.blueMask);
+                while (alphaMask !is 0 && ((alphaMask >>> alphaShift) & 1) is 0) alphaShift++;
+            }
+        }
+        for (int srcY = 0, dstY = srcY + oy; srcY < src.height; srcY++, dstY++) {
+            for (int srcX = 0, dstX = srcX + ox; srcX < src.width; srcX++, dstX++) {
+                if (!(0 <= dstX && dstX < dst.width && 0 <= dstY && dstY < dst.height)) continue;
+                int srcPixel = src.getPixel(srcX, srcY);
+                int srcAlpha = 255;
+                if (src.maskData !is null) {
+                    if (src.depth is 32) {
+                        srcAlpha = (srcPixel & alphaMask) >>> alphaShift;
+                        if (srcAlpha is 0) {
+                            srcAlpha = srcMask.getPixel(srcX, srcY) !is 0 ? 255 : 0;
+                        }
+                    } else {
+                        if (srcMask.getPixel(srcX, srcY) is 0) srcAlpha = 0;
+                    }
+                } else if (src.transparentPixel !is -1) {
+                    if (src.transparentPixel is srcPixel) srcAlpha = 0;
+                } else if (src.alpha !is -1) {
+                    srcAlpha = src.alpha;
+                } else if (src.alphaData !is null) {
+                    srcAlpha = src.getAlpha(srcX, srcY);
+                }
+                if (srcAlpha is 0) continue;
+                int srcRed, srcGreen, srcBlue;
+                if (srcPalette.isDirect) {
+                    srcRed = srcPixel & srcPalette.redMask;
+                    srcRed = (srcPalette.redShift < 0) ? srcRed >>> -srcPalette.redShift : srcRed << srcPalette.redShift;
+                    srcGreen = srcPixel & srcPalette.greenMask;
+                    srcGreen = (srcPalette.greenShift < 0) ? srcGreen >>> -srcPalette.greenShift : srcGreen << srcPalette.greenShift;
+                    srcBlue = srcPixel & srcPalette.blueMask;
+                    srcBlue = (srcPalette.blueShift < 0) ? srcBlue >>> -srcPalette.blueShift : srcBlue << srcPalette.blueShift;
+                } else {
+                    RGB rgb = srcPalette.getRGB(srcPixel);
+                    srcRed = rgb.red;
+                    srcGreen = rgb.green;
+                    srcBlue = rgb.blue;
+                }
+                int dstRed, dstGreen, dstBlue, dstAlpha;
+                if (srcAlpha is 255) {
+                    dstRed = srcRed;
+                    dstGreen = srcGreen;
+                    dstBlue= srcBlue;
+                    dstAlpha = srcAlpha;
+                } else {
+                    int dstPixel = dst.getPixel(dstX, dstY);
+                    dstAlpha = dst.getAlpha(dstX, dstY);
+                    dstRed = (dstPixel & 0xFF) >>> 0;
+                    dstGreen = (dstPixel & 0xFF00) >>> 8;
+                    dstBlue = (dstPixel & 0xFF0000) >>> 16;
+                    dstRed += (srcRed - dstRed) * srcAlpha / 255;
+                    dstGreen += (srcGreen - dstGreen) * srcAlpha / 255;
+                    dstBlue += (srcBlue - dstBlue) * srcAlpha / 255;
+                    dstAlpha += (srcAlpha - dstAlpha) * srcAlpha / 255;
+                }
+                dst.setPixel(dstX, dstY, ((dstRed & 0xFF) << 0) | ((dstGreen & 0xFF) << 8) | ((dstBlue & 0xFF) << 16));
+                dst.setAlpha(dstX, dstY, dstAlpha);
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on ImageDesciptor.
+     */
+    public override ImageData getImageData() {
+        Point size = getSize();
+
+        /* Create a 24 bit image data with alpha channel */
+        imageData = new ImageData(size.x, size.y, 24, new PaletteData(0xFF, 0xFF00, 0xFF0000));
+        imageData.alphaData = new byte[imageData.width * imageData.height];
+
+        drawCompositeImage(size.x, size.y);
+
+        /* Detect minimum transparency */
+        bool transparency = false;
+        byte[] alphaData = imageData.alphaData;
+        for (int i = 0; i < alphaData.length; i++) {
+            int alpha = alphaData[i] & 0xFF;
+            if (!(alpha is 0 || alpha is 255)) {
+                /* Full alpha channel transparency */
+                return imageData;
+            }
+            if (!transparency && alpha is 0) transparency = true;
+        }
+        if (transparency) {
+            /* Reduce to 1-bit alpha channel transparency */
+            PaletteData palette = new PaletteData([new RGB(0, 0, 0), new RGB(255, 255, 255)]);
+            ImageData mask = new ImageData(imageData.width, imageData.height, 1, palette);
+            for (int y = 0; y < mask.height; y++) {
+                for (int x = 0; x < mask.width; x++) {
+                    mask.setPixel(x, y, imageData.getAlpha(x, y) is 255 ? 1 : 0);
+                }
+            }
+        } else {
+            /* no transparency */
+            imageData.alphaData = null;
+        }
+        return imageData;
+    }
+
+
+    /**
+     * Return the transparent pixel for the receiver.
+     * <strong>NOTE</strong> This value is not currently in use in the
+     * default implementation.
+     * @return int
+     * @since 3.3
+     */
+    protected int getTransparentPixel() {
+        return 0;
+    }
+
+    /**
+     * Return the size of this composite image.
+     * <p>
+     * Subclasses must implement this framework method.
+     * </p>
+     *
+     * @return the x and y size of the image expressed as a point object
+     */
+    protected abstract Point getSize();
+
+    /**
+     * @param imageData The imageData to set.
+     * @since 3.3
+     */
+    protected void setImageData(ImageData imageData) {
+        this.imageData = imageData;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/DataFormatException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.resource.DataFormatException;
+
+import java.lang.all;
+
+/**
+ * An exception indicating that a string value could not be
+ * converted into the requested data type.
+ *
+ * @see StringConverter
+ */
+public class DataFormatException : IllegalArgumentException {
+
+    /**
+     * Generated serial version UID for this class.
+     * @since 3.1
+     */
+    private static const long serialVersionUID = 3544955467404031538L;
+
+    /**
+     * Creates a new exception.
+     */
+    public this() {
+        super("");
+    }
+
+    /**
+     * Creates a new exception.
+     *
+     * @param message the message
+     */
+    public this(String message) {
+        super(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/DerivedImageDescriptor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.resource.DerivedImageDescriptor;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.DeviceResourceException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.widgets.Display;
+
+import java.lang.all;
+
+/**
+ * An image descriptor which creates images based on another ImageDescriptor, but with
+ * additional SWT flags. Note that this is only intended for compatibility.
+ *
+ * @since 3.1
+ */
+final class DerivedImageDescriptor : ImageDescriptor {
+    alias ImageDescriptor.createImage createImage;
+
+    private ImageDescriptor original;
+    private int flags;
+
+    /**
+     * Create a new image descriptor
+     * @param original the original one
+     * @param swtFlags flags to be used when image is created {@link Image#Image(Device, Image, int)}
+     * @see SWT#IMAGE_COPY
+     * @see SWT#IMAGE_DISABLE
+     * @see SWT#IMAGE_GRAY
+     */
+    public this(ImageDescriptor original, int swtFlags) {
+        this.original = original;
+        flags = swtFlags;
+    }
+
+    public override Object createResource(Device device) {
+        try {
+            return internalCreateImage(device);
+        } catch (SWTException e) {
+            throw new DeviceResourceException(this, e);
+        }
+    }
+
+    public override Image createImage(Device device) {
+        return internalCreateImage(device);
+    }
+
+    public override hash_t toHash() {
+        return original.toHash() + flags;
+    }
+
+    public override int opEquals(Object arg0) {
+        if ( auto desc = cast(DerivedImageDescriptor)arg0 ) {
+            return desc.original is original && flags is desc.flags;
+        }
+
+        return false;
+    }
+
+    /**
+     * Creates a new Image on the given device. Note that we defined a new
+     * method rather than overloading createImage since this needs to be
+     * called by getImageData(), and we want to be absolutely certain not
+     * to cause infinite recursion if the base class gets refactored.
+     *
+     * @param device device to create the image on
+     * @return a newly allocated Image. Must be disposed by calling image.dispose().
+     */
+    private final Image internalCreateImage(Device device) {
+        Image originalImage = original.createImage(device);
+        Image result = new Image(device, originalImage, flags);
+        original.destroyResource(originalImage);
+        return result;
+    }
+
+    public override ImageData getImageData() {
+        Image image = internalCreateImage(Display.getCurrent());
+        ImageData result = image.getImageData();
+        image.dispose();
+        return result;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/DeviceResourceDescriptor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.resource.DeviceResourceDescriptor;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Device;
+
+/**
+ * Instances of this class can allocate and dispose SWT resources. Each
+ * instance describes a particular resource (such as a Color, Font, or Image)
+ * and can create and destroy that resource on demand. DeviceResourceDescriptors
+ * are managed by a ResourceRegistry.
+ *
+ * @see org.eclipse.jface.resource.ResourceManager
+ *
+ * @since 3.1
+ */
+public abstract class DeviceResourceDescriptor {
+    /**
+     * Creates the resource described by this descriptor
+     *
+     * @since 3.1
+     *
+     * @param device the Device on which to allocate the resource
+     * @return the newly allocated resource (not null)
+     * @throws DeviceResourceException if unable to allocate the resource
+     */
+    public abstract Object createResource(Device device);
+
+    /**
+     * Undoes everything that was done by a previous call to create(...), given
+     * the object that was returned by create(...).
+     *
+     * @since 3.1
+     *
+     * @param previouslyCreatedObject an object that was returned by an equal
+     * descriptor in a previous call to createResource(...).
+     */
+    public abstract void destroyResource(Object previouslyCreatedObject);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/DeviceResourceException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.resource.DeviceResourceException;
+
+import org.eclipse.jface.resource.DeviceResourceDescriptor;
+
+import java.lang.all;
+
+/**
+ * Thrown when allocation of an SWT device resource fails
+ *
+ * @since 3.1
+ */
+public class DeviceResourceException : RuntimeException {
+
+    private Exception cause;
+
+    /**
+     * All serializable objects should have a stable serialVersionUID
+     */
+    private static const long serialVersionUID = 11454598756198L;
+
+    /**
+     * Creates a DeviceResourceException indicating an error attempting to
+     * create a resource and an embedded low-level exception describing the cause
+     *
+     * @param missingResource
+     * @param cause cause of the exception (or null if none)
+     */
+    public this(DeviceResourceDescriptor missingResource, Exception cause) {
+        super("Unable to create resource " ~ missingResource.toString()); //$NON-NLS-1$
+        // don't pass the cause to super, to allow compilation against JCL Foundation (bug 80059)
+        this.cause = cause;
+    }
+
+    /**
+     * Creates a DeviceResourceException indicating an error attempting to
+     * create a resource
+     *
+     * @param missingResource
+     */
+    public this(DeviceResourceDescriptor missingResource) {
+        this(missingResource, null);
+    }
+
+    /**
+     * Returns the cause of this throwable or <code>null</code> if the
+     * cause is nonexistent or unknown.
+     *
+     * @return the cause or <code>null</code>
+     * @since 3.1
+     */
+    public override Exception getCause() {
+        return cause;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/DeviceResourceManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.resource.DeviceResourceManager;
+
+import org.eclipse.jface.resource.AbstractResourceManager;
+import org.eclipse.jface.resource.DeviceResourceDescriptor;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Image;
+
+import java.lang.all;
+
+/**
+ * Manages SWT resources for a particular device.
+ *
+ * <p>
+ * IMPORTANT: in most cases clients should use a <code>LocalResourceManager</code> instead of a
+ * <code>DeviceResourceManager</code>. To create a resource manager on a particular display,
+ * use <code>new LocalResourceManager(JFaceResources.getResources(myDisplay))</code>.
+ * <code>DeviceResourceManager</code> should only be used directly when managing
+ * resources for a device other than a Display (such as a printer).
+ * </p>
+ *
+ * @see LocalResourceManager
+ *
+ * @since 3.1
+ */
+public final class DeviceResourceManager : AbstractResourceManager {
+
+    private Device device;
+    private Image missingImage;
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.ResourceManager#getDevice()
+     */
+    public override Device getDevice() {
+        return device;
+    }
+
+    /**
+     * Creates a new registry for the given device.
+     *
+     * @param device device to manage
+     */
+    public this(Device device) {
+        this.device = device;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.AbstractResourceManager#allocate(org.eclipse.jface.resource.DeviceResourceDescriptor)
+     */
+    protected override Object allocate(DeviceResourceDescriptor descriptor){
+        return descriptor.createResource(device);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.AbstractResourceManager#deallocate(java.lang.Object, org.eclipse.jface.resource.DeviceResourceDescriptor)
+     */
+    protected override void deallocate(Object resource, DeviceResourceDescriptor descriptor) {
+        descriptor.destroyResource(resource);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.ResourceManager#getDefaultImage()
+     */
+    protected override Image getDefaultImage() {
+        if (missingImage is null) {
+            missingImage = ImageDescriptor.getMissingImageDescriptor().createImage();
+        }
+        return missingImage;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.AbstractResourceManager#dispose()
+     */
+    public override void dispose() {
+        super.dispose();
+        if (missingImage !is null) {
+            missingImage.dispose();
+            missingImage = null;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/FileImageDescriptor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.resource.FileImageDescriptor;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+
+// import java.io.BufferedInputStream;
+// import java.io.FileInputStream;
+// import java.io.FileNotFoundException;
+// import java.io.IOException;
+// import java.io.InputStream;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+// import org.eclipse.core.runtime.FileLocator;
+// import org.eclipse.core.runtime.Path;
+
+import java.lang.all;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+
+import tango.util.log.Trace;
+import tango.text.convert.Format;
+
+/**
+ * An image descriptor that loads its image information from a file.
+ */
+class FileImageDescriptor : ImageDescriptor {
+
+    /**
+     * The class whose resource directory contain the file, or <code>null</code>
+     * if none.
+     */
+//     private ClassInfo location;
+
+    /**
+     * The name of the file.
+     */
+    private String name;
+    private void[] importdata;
+
+    /**
+     * Creates a new file image descriptor. The file has the given file name and
+     * is located in the given class's resource directory. If the given class is
+     * <code>null</code>, the file name must be absolute.
+     * <p>
+     * Note that the file is not accessed until its <code>getImageDate</code>
+     * method is called.
+     * </p>
+     *
+     * @param clazz
+     *            class for resource directory, or <code>null</code>
+     * @param filename
+     *            the name of the file
+     */
+    this(ImportData importdata) {
+//         this.location = clazz;
+        this.name = importdata.name;
+        this.importdata = importdata.data;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Object.
+     */
+    public override int opEquals(Object o) {
+        if (!( cast(FileImageDescriptor)o )) {
+            return false;
+        }
+        FileImageDescriptor other = cast(FileImageDescriptor) o;
+//         if (location !is null) {
+//             if ( location.name != other.location.name ) {
+//                 return false;
+//             }
+//         } else {
+//             if (other.location !is null) {
+//                 return false;
+//             }
+//         }
+        return importdata == other.importdata;
+    }
+
+    /**
+     * @see org.eclipse.jface.resource.ImageDescriptor#getImageData() The
+     *      FileImageDescriptor implementation of this method is not used by
+     *      {@link ImageDescriptor#createImage(bool, Device)} as of version
+     *      3.4 so that the SWT OS optimised loading can be used.
+     */
+    public override ImageData getImageData() {
+        InputStream in_ = getStream();
+        ImageData result = null;
+        if (in_ !is null) {
+            try {
+                result = new ImageData(in_);
+            } catch (SWTException e) {
+                if (e.code !is SWT.ERROR_INVALID_IMAGE /+&& e.code !is SWT.ERROR_UNSUPPORTED_FORMAT+/) {
+                    Trace.formatln( "FileImageDescriptor getImageData SWTException for name={}", name );
+                    throw e;
+                    // fall through otherwise
+                }
+            } finally {
+                in_.close();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns a stream on the image contents. Returns null if a stream could
+     * not be opened.
+     *
+     * @return the buffered stream on the file or <code>null</code> if the
+     *         file cannot be found
+     */
+    private InputStream getStream() {
+        InputStream is_ = null;
+
+//         if (location !is null) {
+// Trace.formatln( "FileImageDescriptor getStream importdata.length={} name={}",importdata.length, name );
+            is_ = new ByteArrayInputStream(cast(byte[]) importdata);
+//             is_ = ClassInfoGetResourceAsStream( location, name);
+
+//         } else {
+//             try {
+//                 is_ = new FileInputStream(name);
+//             } catch (/+FileNotFoundException+/ IOException e) {
+//                 return null;
+//             }
+//         }
+        if (is_ is null) {
+            return null;
+        }
+        return new BufferedInputStream(is_);
+
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Object.
+     */
+    public override hash_t toHash() {
+        int code = java.lang.all.toHash(cast(char[])importdata/+name+/);
+//         if (location !is null) {
+//             code += location.toHash();
+//         }
+        return code;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Object.
+     */
+    /**
+     * The <code>FileImageDescriptor</code> implementation of this
+     * <code>Object</code> method returns a string representation of this
+     * object which is suitable only for debugging.
+     */
+    public override String toString() {
+        return Format("FileImageDescriptor(name={})", name );//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.resource.ImageDescriptor#createImage(bool,
+     *      org.eclipse.swt.graphics.Device)
+     */
+    public override Image createImage(bool returnMissingImageOnError, Device device) {
+        if( importdata.length is 0 ){
+            String path = getFilePath();
+            if (path is null )
+                return createDefaultImage(returnMissingImageOnError, device);
+            try {
+                return new Image(device, path);
+            } catch (SWTException exception) {
+                //if we fail try the default way using a stream
+            }
+        }
+        return super.createImage(returnMissingImageOnError, device);
+    }
+
+    /**
+     * Return default image if returnMissingImageOnError is true.
+     *
+     * @param device
+     * @return Image or <code>null</code>
+     */
+    private Image createDefaultImage(bool returnMissingImageOnError,
+            Device device) {
+        try {
+            if (returnMissingImageOnError)
+                return new Image(device, DEFAULT_IMAGE_DATA);
+        } catch (SWTException nextException) {
+            return null;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the filename for the ImageData.
+     *
+     * @return {@link String} or <code>null</code> if the file cannot be found
+     */
+    private String getFilePath() {
+
+//         if (location is null)
+//             return (new Path(name)).toOSString();
+//
+//         URL resource = location.getResource(name);
+//
+//         if (resource is null)
+//             return null;
+//         try {
+//             if (JFaceActivator.getBundleContext() is null) {// Stand-alone case
+//
+//                 return new Path(resource.getFile()).toOSString();
+//             }
+//             return new Path(FileLocator.toFileURL(resource).getPath()).toOSString();
+//         } catch (IOException e) {
+//             Policy.logException(e);
+            return null;
+//         }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/FontDescriptor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,302 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.resource.FontDescriptor;
+
+import org.eclipse.jface.resource.DeviceResourceDescriptor;
+import org.eclipse.jface.resource.ArrayFontDescriptor;
+
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.widgets.Display;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Lightweight descriptor for a font. Creates the described font on demand.
+ * Subclasses can implement different ways of describing a font. These objects
+ * will be compared, so hashCode(...) and equals(...) must return something
+ * meaningful.
+ *
+ * @since 3.1
+ */
+public abstract class FontDescriptor : DeviceResourceDescriptor {
+
+    /**
+     * Creates a FontDescriptor that describes an existing font. The resulting
+     * descriptor depends on the Font. Disposing the Font while the descriptor
+     * is still in use may throw a graphic disposed exception.
+     *
+     * @since 3.1
+     *
+     * @deprecated use {@link FontDescriptor#createFrom(Font)}
+     *
+     * @param font a font to describe
+     * @param originalDevice must be the same Device that was passed into
+     * the font's constructor when it was first created.
+     * @return a newly created FontDescriptor.
+     */
+    public static FontDescriptor createFrom(Font font, Device originalDevice) {
+        return new ArrayFontDescriptor(font);
+    }
+
+    /**
+     * Creates a FontDescriptor that describes an existing font. The resulting
+     * descriptor depends on the original Font, and disposing the original Font
+     * while the descriptor is still in use may cause SWT to throw a graphic
+     * disposed exception.
+     *
+     * @since 3.1
+     *
+     * @param font font to create
+     * @return a newly created FontDescriptor that describes the given font
+     */
+    public static FontDescriptor createFrom(Font font) {
+        return new ArrayFontDescriptor(font);
+    }
+
+    /**
+     * Creates a new FontDescriptor given the an array of FontData that describes
+     * the font.
+     *
+     * @since 3.1
+     *
+     * @param data an array of FontData that describes the font (will be passed into
+     * the Font's constructor)
+     * @return a FontDescriptor that describes the given font
+     */
+    public static FontDescriptor createFrom(FontData[] data) {
+        return new ArrayFontDescriptor(data);
+    }
+
+    /**
+     * Creates a new FontDescriptor given the associated FontData
+     *
+     * @param data FontData describing the font to create
+     * @return a newly created FontDescriptor
+     */
+    public static FontDescriptor createFrom(FontData data) {
+        return new ArrayFontDescriptor( [data] );
+    }
+
+    /**
+     * Creates a new FontDescriptor given an OS-specific font name, height, and style.
+     *
+     * @see Font#Font(org.eclipse.swt.graphics.Device, java.lang.String, int, int)
+     *
+     * @param name os-specific font name
+     * @param height height (pixels)
+     * @param style a bitwise combination of NORMAL, BOLD, ITALIC
+     * @return a new FontDescriptor
+     */
+    public static FontDescriptor createFrom(String name, int height, int style) {
+        return createFrom(new FontData(name, height, style));
+    }
+
+    /**
+     * Returns the set of FontData associated with this font. Modifying the elements
+     * in the returned array has no effect on the original FontDescriptor.
+     *
+     * @return the set of FontData associated with this font
+     * @since 3.3
+     */
+    public FontData[] getFontData() {
+        Font tempFont = createFont(Display.getCurrent());
+        FontData[] result = tempFont.getFontData();
+        destroyFont(tempFont);
+        return result;
+    }
+
+    /**
+     * Returns an array of FontData containing copies of the FontData
+     * from the original.
+     *
+     * @param original array to copy
+     * @return a deep copy of the original array
+     * @since 3.3
+     */
+    public static FontData[] copy(FontData[] original) {
+        FontData[] result = new FontData[original.length];
+        for (int i = 0; i < original.length; i++) {
+            FontData next = original[i];
+
+            result[i] = copy(next);
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns a copy of the original FontData
+     *
+     * @param next FontData to copy
+     * @return a copy of the given FontData
+     * @since 3.3
+     */
+    public static FontData copy(FontData next) {
+        FontData result = new FontData(next.getName(), next.getHeight(), next.getStyle());
+        result.setLocale(next.getLocale());
+        return result;
+    }
+
+    /**
+     * Returns a FontDescriptor that is equivalent to the reciever, but uses
+     * the given style bits.
+     *
+     * <p>Does not modify the reciever.</p>
+     *
+     * @param style a bitwise combination of SWT.NORMAL, SWT.ITALIC and SWT.BOLD
+     * @return a new FontDescriptor with the given style
+     *
+     * @since 3.3
+     */
+    public final FontDescriptor setStyle(int style) {
+        FontData[] data = getFontData();
+
+        for (int i = 0; i < data.length; i++) {
+            FontData next = data[i];
+
+            next.setStyle(style);
+        }
+
+        // Optimization: avoid holding onto extra instances by returning the reciever if
+        // if it is exactly the same as the result
+        FontDescriptor result = new ArrayFontDescriptor(data);
+        if (result.opEquals(this)) {
+            return this;
+        }
+
+        return result;
+    }
+
+    /**
+     * <p>Returns a FontDescriptor that is equivalent to the reciever, but
+     * has the given style bits, in addition to any styles the reciever already has.</p>
+     *
+     * <p>Does not modify the reciever.</p>
+     *
+     * @param style a bitwise combination of SWT.NORMAL, SWT.ITALIC and SWT.BOLD
+     * @return a new FontDescriptor with the given additional style bits
+     * @since 3.3
+     */
+    public final FontDescriptor withStyle(int style) {
+        FontData[] data = getFontData();
+
+        for (int i = 0; i < data.length; i++) {
+            FontData next = data[i];
+
+            next.setStyle(next.getStyle() | style);
+        }
+
+        // Optimization: avoid allocating extra instances by returning the reciever if
+        // if it is exactly the same as the result
+        FontDescriptor result = new ArrayFontDescriptor(data);
+        if (result.opEquals(this)) {
+            return this;
+        }
+
+        return result;
+    }
+
+    /**
+     * <p>Returns a new FontDescriptor that is equivalent to the reciever, but
+     * has the given height.</p>
+     *
+     * <p>Does not modify the reciever.</p>
+     *
+     * @param height a height, in points
+     * @return a new FontDescriptor with the height, in points
+     * @since 3.3
+     */
+    public final FontDescriptor setHeight(int height) {
+        FontData[] data = getFontData();
+
+        for (int i = 0; i < data.length; i++) {
+            FontData next = data[i];
+
+            next.setHeight(height);
+        }
+
+        // Optimization: avoid holding onto extra instances by returning the reciever if
+        // if it is exactly the same as the result
+        FontDescriptor result = new ArrayFontDescriptor(data);
+        if (result.opEquals(this)) {
+            return this;
+        }
+
+        return result;
+    }
+
+    /**
+     * <p>Returns a FontDescriptor that is equivalent to the reciever, but whose height
+     * is larger by the given number of points.</p>
+     *
+     * <p>Does not modify the reciever.</p>
+     *
+     * @param heightDelta a change in height, in points. Negative values will return smaller
+     * fonts.
+     * @return a FontDescriptor whose height differs from the reciever by the given number
+     * of points.
+     * @since 3.3
+     */
+    public final FontDescriptor increaseHeight(int heightDelta) {
+        if (heightDelta is 0) {
+            return this;
+        }
+        FontData[] data = getFontData();
+
+        for (int i = 0; i < data.length; i++) {
+            FontData next = data[i];
+
+            next.setHeight(next.getHeight() + heightDelta);
+        }
+
+        return new ArrayFontDescriptor(data);
+    }
+
+    /**
+     * Creates the Font described by this descriptor.
+     *
+     * @since 3.1
+     *
+     * @param device device on which to allocate the font
+     * @return a newly allocated Font (never null)
+     * @throws DeviceResourceException if unable to allocate the Font
+     */
+    public abstract Font createFont(Device device);
+
+    /**
+     * Deallocates anything that was allocated by createFont, given a font
+     * that was allocated by an equal FontDescriptor.
+     *
+     * @since 3.1
+     *
+     * @param previouslyCreatedFont previously allocated font
+     */
+    public abstract void destroyFont(Font previouslyCreatedFont);
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.DeviceResourceDescriptor#create(org.eclipse.swt.graphics.Device)
+     */
+    public override final Object createResource(Device device) {
+        return createFont(device);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.DeviceResourceDescriptor#destroy(java.lang.Object)
+     */
+    public override final void destroyResource(Object previouslyCreatedObject) {
+        destroyFont(cast(Font)previouslyCreatedObject);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/FontRegistry.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,880 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.resource.FontRegistry;
+
+import org.eclipse.jface.resource.ResourceRegistry;
+import org.eclipse.jface.resource.FontDescriptor;
+import org.eclipse.jface.resource.StringConverter;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.DataFormatException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.runtime.Assert;
+
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+import java.lang.all;
+import java.util.Collections;
+import java.util.Collection;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+version(Windows) import org.eclipse.swt.internal.win32.OS;
+
+/**
+ * A font registry maintains a mapping between symbolic font names
+ * and SWT fonts.
+ * <p>
+ * A font registry owns all of the font objects registered
+ * with it, and automatically disposes of them when the SWT Display
+ * that creates the fonts is disposed. Because of this, clients do
+ * not need to (indeed, must not attempt to) dispose of font
+ * objects themselves.
+ * </p>
+ * <p>
+ * A special constructor is provided for populating a font registry
+ * from a property files using the standard Java resource bundle mechanism.
+ * </p>
+ * <p>
+ * Methods are provided for registering listeners that will be kept
+ * apprised of changes to list of registed fonts.
+ * </p>
+ * <p>
+ * Clients may instantiate this class (it was not designed to be subclassed).
+ * </p>
+ *
+ * Since 3.0 this class extends ResourceRegistry.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class FontRegistry : ResourceRegistry {
+
+    /**
+     * FontRecord is a private helper class that holds onto a font
+     * and can be used to generate its bold and italic version.
+     */
+    private class FontRecord {
+
+        Font baseFont;
+
+        Font boldFont;
+
+        Font italicFont;
+
+        FontData[] baseData;
+
+        /**
+         * Create a new instance of the receiver based on the
+         * plain font and the data for it.
+         * @param plainFont The base looked up font.
+         * @param data The data used to look it up.
+         */
+        this(Font plainFont, FontData[] data) {
+            baseFont = plainFont;
+            baseData = data;
+        }
+
+        /**
+         * Dispose any of the fonts created for this record.
+         */
+        void dispose() {
+            baseFont.dispose();
+            if (boldFont !is null) {
+                boldFont.dispose();
+            }
+            if (italicFont !is null) {
+                italicFont.dispose();
+            }
+        }
+
+        /**
+         * Return the base Font.
+         * @return Font
+         */
+        public Font getBaseFont() {
+            return baseFont;
+        }
+
+        /**
+         * Return the bold Font. Create a bold version
+         * of the base font to get it.
+         * @return Font
+         */
+        public Font getBoldFont() {
+            if (boldFont !is null) {
+                return boldFont;
+            }
+
+            FontData[] boldData = getModifiedFontData(SWT.BOLD);
+            boldFont = new Font(Display.getCurrent(), boldData);
+            return boldFont;
+        }
+
+        /**
+         * Get a version of the base font data with the specified
+         * style.
+         * @param style the new style
+         * @return the font data with the style {@link FontData#FontData(String, int, int)}
+         * @see SWT#ITALIC
+         * @see SWT#NORMAL
+         * @see SWT#BOLD
+         * @todo Generated comment
+         */
+        private FontData[] getModifiedFontData(int style) {
+            FontData[] styleData = new FontData[baseData.length];
+            for (int i = 0; i < styleData.length; i++) {
+                FontData base = baseData[i];
+                styleData[i] = new FontData(base.getName(), base.getHeight(),
+                        base.getStyle() | style);
+            }
+
+            return styleData;
+        }
+
+        /**
+         * Return the italic Font. Create an italic version of the
+         * base font to get it.
+         * @return Font
+         */
+        public Font getItalicFont() {
+            if (italicFont !is null) {
+                return italicFont;
+            }
+
+            FontData[] italicData = getModifiedFontData(SWT.ITALIC);
+            italicFont = new Font(Display.getCurrent(), italicData);
+            return italicFont;
+        }
+
+        /**
+         * Add any fonts that were allocated for this record to the
+         * stale fonts. Anything that matches the default font will
+         * be skipped.
+         * @param defaultFont The system default.
+         */
+        void addAllocatedFontsToStale(Font defaultFont) {
+            //Return all of the fonts allocated by the receiver.
+            //if any of them are the defaultFont then don't bother.
+            if (defaultFont !is baseFont && baseFont !is null) {
+                staleFonts.add(baseFont);
+            }
+            if (defaultFont !is boldFont && boldFont !is null) {
+                staleFonts.add(boldFont);
+            }
+            if (defaultFont !is italicFont && italicFont !is null) {
+                staleFonts.add(italicFont);
+            }
+        }
+    }
+
+    /**
+     * Table of known fonts, keyed by symbolic font name
+     * (key type: <code>String</code>,
+     *  value type: <code>FontRecord</code>.
+     */
+    private Map stringToFontRecord;
+
+    /**
+     * Table of known font data, keyed by symbolic font name
+     * (key type: <code>String</code>,
+     *  value type: <code>org.eclipse.swt.graphics.FontData[]</code>).
+     */
+    private Map stringToFontData;
+
+    /**
+     * Collection of Fonts that are now stale to be disposed
+     * when it is safe to do so (i.e. on shutdown).
+     * @see List
+     */
+    private List staleFonts;
+
+    /**
+     * Runnable that cleans up the manager on disposal of the display.
+     */
+    protected Runnable displayRunnable;
+
+    private bool displayDisposeHooked;
+
+    private final bool cleanOnDisplayDisposal;
+
+    /**
+     * Creates an empty font registry.
+     * <p>
+     * There must be an SWT Display created in the current
+     * thread before calling this method.
+     * </p>
+     */
+    public this() {
+        this(Display.getCurrent(), true);
+    }
+
+    /**
+     * Creates a font registry and initializes its content from
+     * a property file.
+     * <p>
+     * There must be an SWT Display created in the current
+     * thread before calling this method.
+     * </p>
+     * <p>
+     * The OS name (retrieved using <code>System.getProperty("os.name")</code>)
+     * is converted to lowercase, purged of whitespace, and appended
+     * as suffix (separated by an underscore <code>'_'</code>) to the given
+     * location string to yield the base name of a resource bundle
+     * acceptable to <code>ResourceBundle.getBundle</code>.
+     * The standard Java resource bundle mechanism is then used to locate
+     * and open the appropriate properties file, taking into account
+     * locale specific variations.
+     * </p>
+     * <p>
+     * For example, on the Windows 2000 operating system the location string
+     * <code>"com.example.myapp.Fonts"</code> yields the base name
+     * <code>"com.example.myapp.Fonts_windows2000"</code>. For the US English locale,
+     * this further elaborates to the resource bundle name
+     * <code>"com.example.myapp.Fonts_windows2000_en_us"</code>.
+     * </p>
+     * <p>
+     * If no appropriate OS-specific resource bundle is found, the
+     * process is repeated using the location as the base bundle name.
+     * </p>
+     * <p>
+     * The property file contains entries that look like this:
+     * <pre>
+     *  textfont.0=MS Sans Serif-regular-10
+     *  textfont.1=Times New Roman-regular-10
+     *
+     *  titlefont.0=MS Sans Serif-regular-12
+     *  titlefont.1=Times New Roman-regular-12
+     * </pre>
+     * Each entry maps a symbolic font names (the font registry keys) with
+     * a "<code>.<it>n</it></code> suffix to standard font names
+     * on the right. The suffix indicated order of preference:
+     * "<code>.0</code>" indicates the first choice,
+     * "<code>.1</code>" indicates the second choice, and so on.
+     * </p>
+     * The following example shows how to use the font registry:
+     * <pre>
+     *  FontRegistry registry = new FontRegistry("com.example.myapp.fonts");
+     *  Font font = registry.get("textfont");
+     *  control.setFont(font);
+     *  ...
+     * </pre>
+     *
+     * @param location the name of the resource bundle
+     * @param loader the ClassLoader to use to find the resource bundle
+     * @exception MissingResourceException if the resource bundle cannot be found
+     * @since 2.1
+     */
+    public this(String location, /+ClassLoader+/Object loader){
+        initInstance();
+        Display display = Display.getCurrent();
+        Assert.isNotNull(display);
+        // FIXE: need to respect loader
+        //readResourceBundle(location, loader);
+        readResourceBundle(location);
+
+        cleanOnDisplayDisposal = true;
+        hookDisplayDispose(display);
+    }
+
+    private void initInstance(){
+        displayRunnable = new class Runnable {
+            public void run() {
+                clearCaches();
+            }
+        };
+        stringToFontRecord = new HashMap(7);
+        stringToFontData = new HashMap(7);
+        staleFonts = new ArrayList();
+    }
+
+    /**
+     * Load the FontRegistry using the ClassLoader from the PlatformUI
+     * plug-in
+     * @param location the location to read the resource bundle from
+     * @throws MissingResourceException Thrown if a resource is missing
+     */
+    public this(String location) {
+        // FIXE:
+        //  this(location, WorkbenchPlugin.getDefault().getDescriptor().getPluginClassLoader());
+        this(location, null);
+    }
+
+    /**
+     * Read the resource bundle at location. Look for a file with the
+     * extension _os_ws first, then _os then just the name.
+     * @param location - String - the location of the file.
+     */
+
+    private void readResourceBundle(String location) {
+//         String osname = System.getProperty("os.name").trim(); //$NON-NLS-1$
+//         String wsname = SWT.getPlatform();
+//         osname = StringConverter.removeWhiteSpaces(osname).toLowerCase();
+//         wsname = StringConverter.removeWhiteSpaces(wsname).toLowerCase();
+//         String OSLocation = location;
+//         String WSLocation = location;
+        ResourceBundle bundle = null;
+//         if (osname !is null) {
+//             OSLocation = location ~ "_" ~ osname; //$NON-NLS-1$
+//             if (wsname !is null) {
+//                 WSLocation = OSLocation ~ "_" ~ wsname; //$NON-NLS-1$
+//             }
+//         }
+        String errorLocation = "org.eclipse.jface.resource.FontRegistry properties";
+        const char[] prefix = "org.eclipse.jface.resource.jfacefonts_";
+        const char[] postfix = ".properties";
+        version( linux ){
+            ImportData propdata = getImportData!( prefix ~ "linux_gtk" ~ postfix );
+        }
+        else version( Windows ){
+            ImportData propdata;
+            if( OS.IsWin95 && OS.WIN32_VERSION >= OS.VERSION (4, 10 )){
+                propdata = getImportData!( prefix ~ "windows98" ~ postfix );
+            }
+            else if( OS.WIN32_VERSION >= OS.VERSION (5, 1 )){
+                propdata = getImportData!( prefix ~ "windowsxp" ~ postfix );
+            }
+            else if( OS.WIN32_VERSION >= OS.VERSION (5, 0)){
+                propdata = getImportData!( prefix ~ "windows2000" ~ postfix );
+            }
+            else if( OS.WIN32_VERSION >= OS.VERSION (4, 0)){
+                propdata = getImportData!( prefix ~ "windowsnt" ~ postfix );
+            }
+            else{
+                assert( false, "TODO: detect windows version" );
+            }
+        }
+        else {
+            static assert( false, "unknown plattform" );
+        }
+
+//         try {
+            bundle = ResourceBundle.getBundle(propdata);
+            readResourceBundle(bundle, errorLocation);
+//         } catch (MissingResourceException wsException) {
+//             try {
+//                 bundle = ResourceBundle.getBundle(OSLocation);
+//                 readResourceBundle(bundle, WSLocation);
+//             } catch (MissingResourceException osException) {
+//                 if (location !is OSLocation) {
+//                     bundle = ResourceBundle.getBundle(location);
+//                     readResourceBundle(bundle, WSLocation);
+//                 } else {
+//                     throw osException;
+//                 }
+//             }
+//         }
+    }
+
+    /**
+     * Creates an empty font registry.
+     *
+     * @param display the Display
+     */
+    public this(Display display) {
+        this(display, true);
+    }
+
+    /**
+     * Creates an empty font registry.
+     *
+     * @param display
+     *            the <code>Display</code>
+     * @param cleanOnDisplayDisposal
+     *            whether all fonts allocated by this <code>FontRegistry</code>
+     *            should be disposed when the display is disposed
+     * @since 3.1
+     */
+    public this(Display display, bool cleanOnDisplayDisposal) {
+        initInstance();
+        Assert.isNotNull(display);
+        this.cleanOnDisplayDisposal = cleanOnDisplayDisposal;
+        if (cleanOnDisplayDisposal) {
+            hookDisplayDispose(display);
+        }
+    }
+
+    /**
+     * Find the first valid fontData in the provided list. If none are valid
+     * return the first one regardless. If the list is empty return null. Return
+     * <code>null</code> if one cannot be found.
+     *
+     * @param fonts the font list
+     * @param display the display used
+     * @return the font data of the like describe above
+     *
+     * @deprecated use bestDataArray in order to support Motif multiple entry
+     *             fonts.
+     */
+    public FontData bestData(FontData[] fonts, Display display) {
+        for (int i = 0; i < fonts.length; i++) {
+            FontData fd = fonts[i];
+
+            if (fd is null) {
+                break;
+            }
+
+            FontData[] fixedFonts = display.getFontList(fd.getName(), false);
+            if (isFixedFont(fixedFonts, fd)) {
+                return fd;
+            }
+
+            FontData[] scalableFonts = display.getFontList(fd.getName(), true);
+            if (scalableFonts.length > 0) {
+                return fd;
+            }
+        }
+
+        //None of the provided datas are valid. Return the
+        //first one as it is at least the first choice.
+        if (fonts.length > 0) {
+            return fonts[0];
+        }
+
+        //Nothing specified
+        return null;
+    }
+
+    /**
+     * Find the first valid fontData in the provided list.
+     * If none are valid return the first one regardless.
+     * If the list is empty return <code>null</code>.
+     *
+     * @param fonts list of fonts
+     * @param display the display
+     * @return font data like described above
+     * @deprecated use filterData in order to preserve
+     * multiple entry fonts on Motif
+     */
+    public FontData[] bestDataArray(FontData[] fonts, Display display) {
+
+        FontData bestData = bestData(fonts, display);
+        if (bestData is null) {
+            return null;
+        }
+
+        FontData[] datas = new FontData[1];
+        datas[0] = bestData;
+        return datas;
+    }
+
+    /**
+     * Removes from the list all fonts that do not exist in this system.
+     * If none are valid, return the first irregardless.  If the list is
+     * empty return <code>null</code>.
+     *
+     * @param fonts the fonts to check
+     * @param display the display to check against
+     * @return the list of fonts that have been found on this system
+     * @since 3.1
+     */
+    public FontData [] filterData(FontData [] fonts, Display display) {
+        ArrayList good = new ArrayList(fonts.length);
+        for (int i = 0; i < fonts.length; i++) {
+            FontData fd = fonts[i];
+
+            if (fd is null) {
+                continue;
+            }
+
+            FontData[] fixedFonts = display.getFontList(fd.getName(), false);
+            if (isFixedFont(fixedFonts, fd)) {
+                good.add(fd);
+            }
+
+            FontData[] scalableFonts = display.getFontList(fd.getName(), true);
+            if (scalableFonts.length > 0) {
+                good.add(fd);
+            }
+        }
+
+
+        //None of the provided datas are valid. Return the
+        //first one as it is at least the first choice.
+        if (good.isEmpty() && fonts.length > 0) {
+            good.add(fonts[0]);
+        }
+        else if (fonts.length is 0) {
+            return null;
+        }
+
+        return arraycast!(FontData)(good.toArray());
+    }
+
+
+    /**
+     * Creates a new font with the given font datas or <code>null</code>
+     * if there is no data.
+     * @return FontRecord for the new Font or <code>null</code>.
+     */
+    private FontRecord createFont(String symbolicName, FontData[] fonts) {
+        Display display = Display.getCurrent();
+        if (display is null) {
+            return null;
+        }
+        if (cleanOnDisplayDisposal && !displayDisposeHooked) {
+            hookDisplayDispose(display);
+        }
+
+        FontData[] validData = filterData(fonts, display);
+        if (validData.length is 0) {
+            //Nothing specified
+            return null;
+        }
+
+        //Do not fire the update from creation as it is not a property change
+        put(symbolicName, validData, false);
+        Font newFont = new Font(display, validData);
+        return new FontRecord(newFont, validData);
+    }
+
+    /**
+     * Calculates the default font and returns the result.
+     * This method creates a font that must be disposed.
+     */
+    Font calculateDefaultFont() {
+        Display current = Display.getCurrent();
+        if (current is null) {
+            Shell shell = new Shell();
+            Font font = new Font(null, shell.getFont().getFontData());
+            shell.dispose();
+            return font;
+        }
+        return new Font(current, current.getSystemFont().getFontData());
+    }
+
+    /**
+     * Returns the default font data.  Creates it if necessary.
+     * @return Font
+     */
+    public Font defaultFont() {
+        return defaultFontRecord().getBaseFont();
+    }
+
+    /**
+     * Returns the font descriptor for the font with the given symbolic
+     * font name. Returns the default font if there is no special value
+     * associated with that name
+     *
+     * @param symbolicName symbolic font name
+     * @return the font descriptor (never null)
+     *
+     * @since 3.3
+     */
+    public FontDescriptor getDescriptor(String symbolicName) {
+        Assert.isTrue(symbolicName.length > 0);
+        return FontDescriptor.createFrom(getFontData(symbolicName));
+    }
+
+
+
+    /**
+     * Returns the default font record.
+     */
+    private FontRecord defaultFontRecord() {
+
+        FontRecord record = cast(FontRecord) stringToFontRecord
+                .get(JFaceResources.DEFAULT_FONT);
+        if (record is null) {
+            Font defaultFont = calculateDefaultFont();
+            record = createFont(JFaceResources.DEFAULT_FONT, defaultFont
+                    .getFontData());
+            defaultFont.dispose();
+            stringToFontRecord.put(stringcast(JFaceResources.DEFAULT_FONT), record);
+        }
+        return record;
+    }
+
+    /**
+     * Returns the default font data.  Creates it if necessary.
+     */
+    private FontData[] defaultFontData() {
+        return defaultFontRecord().baseData;
+    }
+
+    /**
+     * Returns the font data associated with the given symbolic font name.
+     * Returns the default font data if there is no special value associated
+     * with that name.
+     *
+     * @param symbolicName symbolic font name
+     * @return the font
+     */
+    public FontData[] getFontData(String symbolicName) {
+
+        Assert.isTrue(symbolicName.length > 0);
+        Object result = stringToFontData.get(symbolicName);
+        if (result is null) {
+            return defaultFontData();
+        }
+
+        return arrayFromObject!(FontData)(result);
+    }
+
+    /**
+     * Returns the font associated with the given symbolic font name.
+     * Returns the default font if there is no special value associated
+     * with that name.
+     *
+     * @param symbolicName symbolic font name
+     * @return the font
+     */
+    public Font get(String symbolicName) {
+
+        return getFontRecord(symbolicName).getBaseFont();
+    }
+
+    /**
+     * Returns the bold font associated with the given symbolic font name.
+     * Returns the bolded default font if there is no special value associated
+     * with that name.
+     *
+     * @param symbolicName symbolic font name
+     * @return the font
+     * @since 3.0
+     */
+    public Font getBold(String symbolicName) {
+
+        return getFontRecord(symbolicName).getBoldFont();
+    }
+
+    /**
+     * Returns the italic font associated with the given symbolic font name.
+     * Returns the italic default font if there is no special value associated
+     * with that name.
+     *
+     * @param symbolicName symbolic font name
+     * @return the font
+     * @since 3.0
+     */
+    public Font getItalic(String symbolicName) {
+
+        return getFontRecord(symbolicName).getItalicFont();
+    }
+
+    /**
+     * Return the font record for the key.
+     * @param symbolicName The key for the record.
+     * @return FontRecird
+     */
+    private FontRecord getFontRecord(String symbolicName) {
+        Assert.isNotNull(symbolicName);
+        auto result = stringToFontRecord.get(symbolicName);
+        if (result !is null) {
+            return cast(FontRecord) result;
+        }
+
+        result = stringToFontData.get(symbolicName);
+
+        FontRecord fontRecord;
+
+        if (result is null) {
+            fontRecord = defaultFontRecord();
+        } else {
+            fontRecord = createFont(symbolicName, arrayFromObject!(FontData)(result));
+        }
+
+        if (fontRecord is null) {
+            fontRecord = defaultFontRecord();
+        }
+
+        stringToFontRecord.put(symbolicName.dup, fontRecord);
+        return fontRecord;
+
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.ResourceRegistry#getKeySet()
+     */
+    public override Set getKeySet() {
+        return Collections.unmodifiableSet(stringToFontData.keySet());
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.ResourceRegistry#hasValueFor(java.lang.String)
+     */
+    public override bool hasValueFor(String fontKey) {
+        return stringToFontData.containsKey(fontKey);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.ResourceRegistry#clearCaches()
+     */
+    protected override void clearCaches() {
+
+        Iterator iterator = stringToFontRecord.values().iterator();
+        while (iterator.hasNext()) {
+            Object next = iterator.next();
+            (cast(FontRecord) next).dispose();
+        }
+
+        disposeFonts(staleFonts.iterator());
+        stringToFontRecord.clear();
+        staleFonts.clear();
+
+        displayDisposeHooked = false;
+    }
+
+    /**
+     * Dispose of all of the fonts in this iterator.
+     * @param iterator over Collection of Font
+     */
+    private void disposeFonts(Iterator iterator) {
+        while (iterator.hasNext()) {
+            Object next = iterator.next();
+            (cast(Font) next).dispose();
+        }
+    }
+
+    /**
+     * Hook a dispose listener on the SWT display.
+     */
+    private void hookDisplayDispose(Display display) {
+        displayDisposeHooked = true;
+        display.disposeExec(displayRunnable);
+    }
+
+    /**
+     * Checks whether the given font is in the list of fixed fonts.
+     */
+    private bool isFixedFont(FontData[] fixedFonts, FontData fd) {
+        // Can't use FontData.equals() since some values aren't
+        // set if a fontdata isn't used.
+        int height = fd.getHeight();
+        String name = fd.getName();
+        for (int i = 0; i < fixedFonts.length; i++) {
+            FontData fixed = fixedFonts[i];
+            if (fixed.getHeight() is height && fixed.getName().equals(name)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Converts a String into a FontData object.
+     */
+    private FontData makeFontData(String value) {
+        try {
+            return StringConverter.asFontData(value.trim());
+        } catch (DataFormatException e) {
+            throw new MissingResourceException(
+                    "Wrong font data format. Value is: \"" ~ value ~ "\"", this.classinfo.name, value); //$NON-NLS-2$//$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Adds (or replaces) a font to this font registry under the given
+     * symbolic name.
+     * <p>
+     * A property change event is reported whenever the mapping from
+     * a symbolic name to a font changes. The source of the event is
+     * this registry; the property name is the symbolic font name.
+     * </p>
+     *
+     * @param symbolicName the symbolic font name
+     * @param fontData an Array of FontData
+     */
+    public void put(String symbolicName, FontData[] fontData) {
+        put(symbolicName, fontData, true);
+    }
+
+    /**
+     * Adds (or replaces) a font to this font registry under the given
+     * symbolic name.
+     * <p>
+     * A property change event is reported whenever the mapping from
+     * a symbolic name to a font changes. The source of the event is
+     * this registry; the property name is the symbolic font name.
+     * </p>
+     *
+     * @param symbolicName the symbolic font name
+     * @param fontData an Array of FontData
+     * @param update - fire a font mapping changed if true. False
+     *  if this method is called from the get method as no setting
+     *  has changed.
+     */
+    private void put(String symbolicName, FontData[] fontData, bool update) {
+
+        Assert.isNotNull(symbolicName);
+        Assert.isTrue(fontData.length > 0 );
+
+        FontData[] existing = arrayFromObject!(FontData)( stringToFontData.get(stringcast(symbolicName)));
+        if (ArrayEquals(existing, fontData)) {
+            return;
+        }
+
+        FontRecord oldFont = cast(FontRecord) stringToFontRecord.remove(stringcast(symbolicName));
+        stringToFontData.put(symbolicName.dup, new ArrayWrapperObject(fontData));
+        if (update) {
+            fireMappingChanged(symbolicName, new ArrayWrapperT!(FontData)(existing), new ArrayWrapperObject(arraycast!(Object)(fontData)));
+        }
+
+        if (oldFont !is null) {
+            oldFont.addAllocatedFontsToStale(defaultFontRecord().getBaseFont());
+        }
+    }
+
+    /**
+     * Reads the resource bundle.  This puts FontData[] objects
+     * in the mapping table.  These will lazily be turned into
+     * real Font objects when requested.
+     */
+    private void readResourceBundle(ResourceBundle bundle, String bundleName) {
+        auto keys = bundle.getKeys();
+        foreach ( key; keys ) {
+            int pos = key.lastIndexOf('.');
+            if (pos is -1) {
+                stringToFontData.put(stringcast(key.dup), new ArrayWrapperObject(arraycast!(Object)([ makeFontData(bundle
+                        .getString(key)) ])));
+            } else {
+                String name = key.substring(0, pos);
+                int i = 0;
+                try {
+                    i = tango.text.convert.Integer.toInt(key.substring(pos + 1));
+                } catch (IllegalArgumentException e) {
+                    //Panic the file can not be parsed.
+                    throw new MissingResourceException(
+                            "Wrong key format ", bundleName, key); //$NON-NLS-1$
+                }
+                FontData[] elements = arrayFromObject!(FontData)( stringToFontData.get(stringcast(name)));
+                if (elements is null) {
+                    elements = new FontData[8];
+                    stringToFontData.put(name.dup, new ArrayWrapperObject(elements));
+                }
+                if (i > elements.length) {
+                    FontData[] na = new FontData[i + 8];
+                    System.arraycopy(elements, 0, na, 0, elements.length);
+                    elements = na;
+                    stringToFontData.put(name.dup, new ArrayWrapperObject(elements));
+                }
+                elements[i] = makeFontData(bundle.getString(key));
+            }
+        }
+    }
+
+    /**
+     * Returns the font descriptor for the JFace default font.
+     *
+     * @return the font descriptor for the JFace default font
+     * @since 3.3
+     */
+    public FontDescriptor defaultFontDescriptor() {
+        return FontDescriptor.createFrom(defaultFontData());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/ImageDataImageDescriptor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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 org.eclipse.jface.resource.ImageDataImageDescriptor;
+
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import java.lang.all;
+
+/**
+ * @since 3.1
+ */
+class ImageDataImageDescriptor : ImageDescriptor {
+
+    private ImageData data;
+
+    /**
+     * Original image being described, or null if this image is described
+     * completely using its ImageData
+     */
+    private Image originalImage = null;
+
+    /**
+     * Creates an image descriptor, given an image and the device it was created on.
+     *
+     * @param originalImage
+     */
+    this(Image originalImage) {
+        this(originalImage.getImageData());
+        this.originalImage = originalImage;
+    }
+
+    /**
+     * Creates an image descriptor, given some image data.
+     *
+     * @param data describing the image
+     */
+
+    this(ImageData data) {
+        this.data = data;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.DeviceResourceDescriptor#create(org.eclipse.swt.graphics.Device)
+     */
+    public override Object createResource(Device device) {
+
+        // If this descriptor is an existing font, then we can return the original font
+        // if this is the same device.
+        if (originalImage !is null) {
+            // If we're allocating on the same device as the original font, return the original.
+            if (originalImage.getDevice() is device) {
+                return originalImage;
+            }
+        }
+
+        return super.createResource(device);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.DeviceResourceDescriptor#destroy(java.lang.Object)
+     */
+    public override void destroyResource(Object previouslyCreatedObject) {
+        if (previouslyCreatedObject is originalImage) {
+            return;
+        }
+
+        super.destroyResource(previouslyCreatedObject);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.ImageDescriptor#getImageData()
+     */
+    public override ImageData getImageData() {
+        return data;
+    }
+
+    /* (non-Javadoc)
+     * @see Object#hashCode
+     */
+    public override hash_t toHash() {
+         if (originalImage !is null) {
+             return System.identityHashCode(originalImage);
+         }
+         return data.toHash();
+    }
+
+    /* (non-Javadoc)
+     * @see Object#equals
+     */
+    public override int opEquals(Object obj) {
+        if (!(cast(ImageDataImageDescriptor)obj )) {
+            return false;
+        }
+
+        ImageDataImageDescriptor imgWrap = cast(ImageDataImageDescriptor) obj;
+
+        //Intentionally using is instead of equals() as Image.hashCode() changes
+        //when the image is disposed and so leaks may occur with equals()
+
+        if (originalImage !is null) {
+            return imgWrap.originalImage is originalImage;
+        }
+
+        return (imgWrap.originalImage is null && data.opEquals(imgWrap.data));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/ImageDescriptor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,351 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.resource.ImageDescriptor;
+
+import org.eclipse.jface.resource.DeviceResourceDescriptor;
+import org.eclipse.jface.resource.FileImageDescriptor;
+import org.eclipse.jface.resource.URLImageDescriptor;
+import org.eclipse.jface.resource.ImageDataImageDescriptor;
+import org.eclipse.jface.resource.DerivedImageDescriptor;
+import org.eclipse.jface.resource.DeviceResourceException;
+import org.eclipse.jface.resource.MissingImageDescriptor;
+
+import tango.net.Uri;
+
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+
+import java.lang.all;
+
+/**
+ * An image descriptor is an object that knows how to create
+ * an SWT image.  It does not hold onto images or cache them,
+ * but rather just creates them on demand.  An image descriptor
+ * is intended to be a lightweight representation of an image
+ * that can be manipulated even when no SWT display exists.
+ * <p>
+ * This package defines a concrete image descriptor implementation
+ * which reads an image from a file (<code>FileImageDescriptor</code>).
+ * It also provides abstract framework classes (this one and
+ * <code>CompositeImageDescriptor</code>) which may be subclassed to define
+ * news kinds of image descriptors.
+ * </p>
+ * <p>
+ * Using this abstract class involves defining a concrete subclass
+ * and providing an implementation for the <code>getImageData</code>
+ * method.
+ * </p>
+ * <p>
+ * There are two ways to get an Image from an ImageDescriptor. The method
+ * createImage will always return a new Image which must be disposed by
+ * the caller. Alternatively, createResource() returns a shared
+ * Image. When the caller is done with an image obtained from createResource,
+ * they must call destroyResource() rather than disposing the Image directly.
+ * The result of createResource() can be safely cast to an Image.
+ * </p>
+ *
+ * @see org.eclipse.swt.graphics.Image
+ */
+public abstract class ImageDescriptor : DeviceResourceDescriptor {
+
+    /**
+     * A small red square used to warn that an image cannot be created.
+     * <p>
+     */
+    protected static ImageData DEFAULT_IMAGE_DATA_;
+    protected static ImageData DEFAULT_IMAGE_DATA(){
+        if( DEFAULT_IMAGE_DATA_ is null ){
+            synchronized( ImageDescriptor.classinfo ){
+                if( DEFAULT_IMAGE_DATA_ is null ){
+                    DEFAULT_IMAGE_DATA_ = new ImageData(6, 6, 1, new PaletteData( [ new RGB(255, 0, 0)  ] ));
+                }
+            }
+        }
+        return DEFAULT_IMAGE_DATA_;
+    }
+
+    /**
+     * Constructs an image descriptor.
+     */
+    protected this() {
+        // do nothing
+    }
+
+    /**
+     * Creates and returns a new image descriptor from a file.
+     * Convenience method for
+     * <code>new FileImageDescriptor(location,filename)</code>.
+     *
+     * @param location the class whose resource directory contain the file
+     * @param filename the file name
+     * @return a new image descriptor
+     */
+    public static ImageDescriptor createFromFile( ImportData importData) {
+        return new FileImageDescriptor(importData);
+    }
+
+    /**
+     * Creates and returns a new image descriptor given ImageData
+     * describing the image.
+     *
+     * @since 3.1
+     *
+     * @param data contents of the image
+     * @return newly created image descriptor
+     */
+    public static ImageDescriptor createFromImageData(ImageData data) {
+        return new ImageDataImageDescriptor(data);
+    }
+
+    /**
+     * Creates and returns a new image descriptor for the given image. Note
+     * that disposing the original Image will cause the descriptor to become invalid.
+     *
+     * @since 3.1
+     *
+     * @param img image to create
+     * @return a newly created image descriptor
+     */
+    public static ImageDescriptor createFromImage(Image img) {
+        return new ImageDataImageDescriptor(img);
+    }
+
+    /**
+     * Creates an ImageDescriptor based on the given original descriptor, but with additional
+     * SWT flags.
+     *
+     * <p>
+     * Note that this sort of ImageDescriptor is slower and consumes more resources than
+     * a regular image descriptor. It will also never generate results that look as nice as
+     * a hand-drawn image. Clients are encouraged to supply their own disabled/grayed/etc. images
+     * rather than using a default image and transforming it.
+     * </p>
+     *
+     * @param originalImage image to transform
+     * @param swtFlags any flag that can be passed to the flags argument of Image#Image(Device, Image, int)
+     * @return an ImageDescriptor that creates new images by transforming the given image descriptor
+     *
+     * @see Image#Image(Device, Image, int)
+     * @since 3.1
+     *
+     */
+    public static ImageDescriptor createWithFlags(ImageDescriptor originalImage, int swtFlags) {
+        return new DerivedImageDescriptor(originalImage, swtFlags);
+    }
+
+    /**
+     * Creates and returns a new image descriptor for the given image. This
+     * method takes the Device that created the Image as an argument, allowing
+     * the original Image to be reused if the descriptor is asked for another
+     * Image on the same device. Note that disposing the original Image will
+     * cause the descriptor to become invalid.
+     *
+     * @deprecated use {@link ImageDescriptor#createFromImage(Image)}
+     * @since 3.1
+     *
+     * @param img image to create
+     * @param theDevice the device that was used to create the Image
+     * @return a newly created image descriptor
+     */
+    public static ImageDescriptor createFromImage(Image img, Device theDevice) {
+        return new ImageDataImageDescriptor(img);
+    }
+
+    /**
+     * Creates and returns a new image descriptor from a URL.
+     *
+     * @param url The URL of the image file.
+     * @return a new image descriptor
+     */
+    public static ImageDescriptor createFromURL(Uri url) {
+        if (url is null) {
+            return getMissingImageDescriptor();
+        }
+        return new URLImageDescriptor(url);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.DeviceResourceDescriptor#createResource(org.eclipse.swt.graphics.Device)
+     */
+    public override Object createResource(Device device) {
+        Image result = createImage(false, device);
+        if (result is null) {
+            throw new DeviceResourceException(this);
+        }
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.DeviceResourceDescriptor#destroyResource(Object)
+     */
+    public override void destroyResource(Object previouslyCreatedObject) {
+        (cast(Image)previouslyCreatedObject).dispose();
+    }
+
+    /**
+     * Creates and returns a new SWT image for this image descriptor. Note that
+     * each call returns a new SWT image object. The returned image must be
+     * explicitly disposed using the image's dispose call. The image will not be
+     * automatically garbage collected. A default image is returned in the event
+     * of an error.
+     *
+     * <p>
+     * Note: this method differs from createResource(Device) in that the returned image
+     * must be disposed directly, whereas an image obtained from createResource(...)
+     * must be disposed by calling destroyResource(...). It is not possible to
+     * mix-and-match. If you obtained the Image from this method, you must not dispose
+     * it by calling destroyResource. Clients are encouraged to use
+     * create/destroyResource and downcast the result to Image rather than using
+     * createImage.
+     * </p>
+     *
+     * <p>
+     * Note: it is still possible for this method to return <code>null</code>
+     * in extreme cases, for example if SWT runs out of image handles.
+     * </p>
+     *
+     * @return a new image or <code>null</code> if the image could not be
+     *         created
+     */
+    public Image createImage() {
+        return createImage(true);
+    }
+
+    /**
+     * Creates and returns a new SWT image for this image descriptor. The
+     * returned image must be explicitly disposed using the image's dispose
+     * call. The image will not be automatically garbage collected. In the event
+     * of an error, a default image is returned if
+     * <code>returnMissingImageOnError</code> is true, otherwise
+     * <code>null</code> is returned.
+     * <p>
+     * Note: Even if <code>returnMissingImageOnError</code> is true, it is
+     * still possible for this method to return <code>null</code> in extreme
+     * cases, for example if SWT runs out of image handles.
+     * </p>
+     *
+     * @param returnMissingImageOnError
+     *            flag that determines if a default image is returned on error
+     * @return a new image or <code>null</code> if the image could not be
+     *         created
+     */
+    public Image createImage(bool returnMissingImageOnError) {
+        return createImage(returnMissingImageOnError, Display.getCurrent());
+    }
+
+    /**
+     * Creates and returns a new SWT image for this image descriptor. The
+     * returned image must be explicitly disposed using the image's dispose
+     * call. The image will not be automatically garbage collected. A default
+     * image is returned in the event of an error.
+     * <p>
+     * Note: it is still possible for this method to return <code>null</code>
+     * in extreme cases, for example if SWT runs out of image handles.
+     * </p>
+     *
+     * @param device
+     *            the device on which to create the image
+     * @return a new image or <code>null</code> if the image could not be
+     *         created
+     * @since 2.0
+     */
+    public Image createImage(Device device) {
+        return createImage(true, device);
+    }
+
+    /**
+     * Creates and returns a new SWT image for this image descriptor. The
+     * returned image must be explicitly disposed using the image's dispose
+     * call. The image will not be automatically garbage collected. In the even
+     * of an error, a default image is returned if
+     * <code>returnMissingImageOnError</code> is true, otherwise
+     * <code>null</code> is returned.
+     * <p>
+     * Note: Even if <code>returnMissingImageOnError</code> is true, it is
+     * still possible for this method to return <code>null</code> in extreme
+     * cases, for example if SWT runs out of image handles.
+     * </p>
+     *
+     * @param returnMissingImageOnError
+     *            flag that determines if a default image is returned on error
+     * @param device
+     *            the device on which to create the image
+     * @return a new image or <code>null</code> if the image could not be
+     *         created
+     * @since 2.0
+     */
+    public Image createImage(bool returnMissingImageOnError, Device device) {
+
+        ImageData data = getImageData();
+        if (data is null) {
+            if (!returnMissingImageOnError) {
+                return null;
+            }
+            data = DEFAULT_IMAGE_DATA;
+        }
+
+        /*
+         * Try to create the supplied image. If there is an SWT Exception try and create
+         * the default image if that was requested. Return null if this fails.
+         */
+
+        try {
+            if (data.transparentPixel >= 0) {
+                ImageData maskData = data.getTransparencyMask();
+                return new Image(device, data, maskData);
+            }
+            return new Image(device, data);
+        } catch (SWTException exception) {
+            //ExceptionPrintStackTrace(exception);
+            if (returnMissingImageOnError) {
+                try {
+                    return new Image(device, DEFAULT_IMAGE_DATA);
+                } catch (SWTException nextException) {
+                    return null;
+                }
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Creates and returns a new SWT <code>ImageData</code> object
+     * for this image descriptor.
+     * Note that each call returns a new SWT image data object.
+     * <p>
+     * This framework method is declared public so that it is
+     * possible to request an image descriptor's image data without
+     * creating an SWT image object.
+     * </p>
+     * <p>
+     * Returns <code>null</code> if the image data could not be created.
+     * </p>
+     *
+     * @return a new image data or <code>null</code>
+     */
+    public abstract ImageData getImageData();
+
+    /**
+     * Returns the shared image descriptor for a missing image.
+     *
+     * @return the missing image descriptor
+     */
+    public static ImageDescriptor getMissingImageDescriptor() {
+        return MissingImageDescriptor.getInstance();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/ImageRegistry.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,366 @@
+/*******************************************************************************
+ * 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
+ *     Steven Ketcham (sketcham@dsicdi.com) - Bug 42451
+ *     [Dialogs] ImageRegistry throws null pointer exception in
+ *     application with multiple Display's
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.resource.ImageRegistry;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ResourceManager;
+import org.eclipse.jface.resource.DeviceResourceException;
+import org.eclipse.jface.resource.JFaceResources;
+
+
+// import java.util.Iterator;
+// import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.dialogs.Dialog;
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * An image registry maintains a mapping between symbolic image names
+ * and SWT image objects or special image descriptor objects which
+ * defer the creation of SWT image objects until they are needed.
+ * <p>
+ * An image registry owns all of the image objects registered
+ * with it, and automatically disposes of them when the SWT Display
+ * that creates the images is disposed. Because of this, clients do not
+ * need to (indeed, must not attempt to) dispose of these images themselves.
+ * </p>
+ * <p>
+ * Clients may instantiate this class (it was not designed to be subclassed).
+ * </p>
+ * <p>
+ * Unlike the FontRegistry, it is an error to replace images. As a result
+ * there are no events that fire when values are changed in the registry
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ImageRegistry {
+    /**
+     * display used when getting images
+     */
+    private Display display;
+
+    private ResourceManager manager;
+
+    private Map table;
+
+    private Runnable disposeRunnable;
+    private void init_disposeRunnable(){
+        disposeRunnable = new class Runnable {
+            public void run() {
+                dispose();
+            }
+        };
+    }
+
+    /**
+     * Contains the data for an entry in the registry.
+     */
+    private static class Entry {
+        /** the image */
+        protected Image image;
+
+        /** the descriptor */
+        protected ImageDescriptor descriptor;
+    }
+
+    private static class OriginalImageDescriptor : ImageDescriptor {
+        private Image original;
+        private int refCount = 0;
+        private Device originalDisplay;
+
+        /**
+         * @param original the original image
+         * @param originalDisplay the device the image is part of
+         */
+        public this(Image original, Device originalDisplay) {
+            this.original = original;
+            this.originalDisplay = originalDisplay;
+        }
+
+        public override Object createResource(Device device) {
+            if (device is originalDisplay) {
+                refCount++;
+                return original;
+            }
+            return super.createResource(device);
+        }
+
+        public override void destroyResource(Object toDispose) {
+            if (original is toDispose) {
+                refCount--;
+                if (refCount is 0) {
+                    original.dispose();
+                    original = null;
+                }
+            } else {
+                super.destroyResource(toDispose);
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see org.eclipse.jface.resource.ImageDescriptor#getImageData()
+         */
+        public override ImageData getImageData() {
+            return original.getImageData();
+        }
+    }
+
+    /**
+     * Creates an empty image registry.
+     * <p>
+     * There must be an SWT Display created in the current
+     * thread before calling this method.
+     * </p>
+     */
+    public this() {
+        this(Display.getCurrent());
+    }
+
+    /**
+     * Creates an empty image registry using the given resource manager to allocate images.
+     *
+     * @param manager the resource manager used to allocate images
+     *
+     * @since 3.1
+     */
+    public this(ResourceManager manager) {
+        init_disposeRunnable();
+        Assert.isNotNull(manager);
+        Device dev = manager.getDevice();
+        if ( auto disp = cast(Display)dev ) {
+            this.display = disp;
+        }
+        this.manager = manager;
+        manager.disposeExec(disposeRunnable);
+    }
+
+    /**
+     * Creates an empty image registry.
+     *
+     * @param display this <code>Display</code> must not be
+     *        <code>null</code> and must not be disposed in order
+     *        to use this registry
+     */
+    public this(Display display) {
+        this(JFaceResources.getResources(display));
+    }
+
+    /**
+     * Returns the image associated with the given key in this registry,
+     * or <code>null</code> if none.
+     *
+     * @param key the key
+     * @return the image, or <code>null</code> if none
+     */
+    public Image get(String key) {
+
+        // can be null
+        if (key is null) {
+            return null;
+        }
+
+        if (display !is null) {
+            /**
+             * NOTE, for backwards compatibility the following images are supported
+             * here, they should never be disposed, hence we explicitly return them
+             * rather then registering images that SWT will dispose.
+             *
+             * Applications should go direclty to SWT for these icons.
+             *
+             * @see Display.getSystemIcon(int ID)
+             */
+            int swtKey = -1;
+            if (key.equals(Dialog.DLG_IMG_INFO)) {
+                swtKey = SWT.ICON_INFORMATION;
+            }
+            if (key.equals(Dialog.DLG_IMG_QUESTION)) {
+                swtKey = SWT.ICON_QUESTION;
+            }
+            if (key.equals(Dialog.DLG_IMG_WARNING)) {
+                swtKey = SWT.ICON_WARNING;
+            }
+            if (key.equals(Dialog.DLG_IMG_ERROR)) {
+                swtKey = SWT.ICON_ERROR;
+            }
+            // if we actually just want to return an SWT image do so without
+            // looking in the registry
+            if (swtKey !is -1) {
+                Image[1] image;
+                int id = swtKey;
+                display.syncExec(new class Runnable {
+                    public void run() {
+                        image[0] = display.getSystemImage(id);
+                    }
+                });
+                return image[0];
+            }
+        }
+
+        Entry entry = getEntry(key);
+        if (entry is null) {
+            return null;
+        }
+
+        if (entry.image is null) {
+            entry.image = manager.createImageWithDefault(entry.descriptor);
+        }
+
+        return entry.image;
+    }
+
+    /**
+     * Returns the descriptor associated with the given key in this registry,
+     * or <code>null</code> if none.
+     *
+     * @param key the key
+     * @return the descriptor, or <code>null</code> if none
+     * @since 2.1
+     */
+    public ImageDescriptor getDescriptor(String key) {
+        Entry entry = getEntry(key);
+        if (entry is null) {
+            return null;
+        }
+
+        return entry.descriptor;
+    }
+
+    /**
+     * Adds (or replaces) an image descriptor to this registry. The first time
+     * this new entry is retrieved, the image descriptor's image will be computed
+     * (via </code>ImageDescriptor.createImage</code>) and remembered.
+     * This method replaces an existing image descriptor associated with the
+     * given key, but fails if there is a real image associated with it.
+     *
+     * @param key the key
+     * @param descriptor the ImageDescriptor
+     * @exception IllegalArgumentException if the key already exists
+     */
+    public void put(String key, ImageDescriptor descriptor) {
+        Entry entry = getEntry(key);
+        if (entry is null) {
+            entry = new Entry();
+            getTable().put(stringcast(key), entry);
+        }
+
+        if (entry.image !is null) {
+            throw new IllegalArgumentException(
+                    "ImageRegistry key already in use: " ~ key); //$NON-NLS-1$
+        }
+
+        entry.descriptor = descriptor;
+    }
+
+    /**
+     * Adds an image to this registry.  This method fails if there
+     * is already an image or descriptor for the given key.
+     * <p>
+     * Note that an image registry owns all of the image objects registered
+     * with it, and automatically disposes of them when the SWT Display is disposed.
+     * Because of this, clients must not register an image object
+     * that is managed by another object.
+     * </p>
+     *
+     * @param key the key
+     * @param image the image, should not be <code>null</code>
+     * @exception IllegalArgumentException if the key already exists
+     */
+    public void put(String key, Image image) {
+        Entry entry = getEntry(key);
+
+        if (entry is null) {
+            entry = new Entry();
+            putEntry(key, entry);
+        }
+
+        if (entry.image !is null || entry.descriptor !is null) {
+            throw new IllegalArgumentException(
+                    "ImageRegistry key already in use: " ~ key); //$NON-NLS-1$
+        }
+
+        // Check for a null image here, otherwise the problem won't appear
+        // until dispose.
+        // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=130315
+        Assert.isNotNull(image, "Cannot register a null image."); //$NON-NLS-1$
+        entry.image = image;
+        entry.descriptor = new OriginalImageDescriptor(image, manager.getDevice());
+
+        try {
+            manager.create(entry.descriptor);
+        } catch (DeviceResourceException e) {
+        }
+    }
+
+    /**
+     * Removes an image from this registry.
+     * If an SWT image was allocated, it is disposed.
+     * This method has no effect if there is no image or descriptor for the given key.
+     * @param key the key
+     */
+    public void remove(String key) {
+        ImageDescriptor descriptor = getDescriptor(key);
+        if (descriptor !is null) {
+            manager.destroy(descriptor);
+            getTable().remove(stringcast(key));
+        }
+    }
+
+    private Entry getEntry(String key) {
+        return cast(Entry) getTable().get(stringcast(key));
+    }
+
+    private void putEntry(String key, Entry entry) {
+        getTable().put(stringcast(key), entry);
+    }
+
+    private Map getTable() {
+        if (table is null) {
+            table = new HashMap(10);
+        }
+        return table;
+    }
+
+    /**
+     * Disposes this image registry, disposing any images
+     * that were allocated for it, and clearing its entries.
+     *
+     * @since 3.1
+     */
+    public void dispose() {
+        manager.cancelDisposeExec(disposeRunnable);
+
+        if (table !is null) {
+            for (Iterator i = table.values().iterator(); i.hasNext();) {
+                Entry entry = cast(Entry) i.next();
+                if (entry.image !is null) {
+                    manager.destroyImage(entry.descriptor);
+                }
+            }
+            table = null;
+        }
+        display = null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/JFaceColors.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.resource.JFaceColors;
+
+import org.eclipse.jface.resource.JFaceResources;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.jface.preference.JFacePreferences;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * JFaceColors is the class that stores references
+ * to all of the colors used by JFace.
+ */
+public class JFaceColors {
+
+    /**
+     * @param display the display the color is from
+     * @return the Color used for banner backgrounds
+     * @see SWT#COLOR_LIST_BACKGROUND
+     * @see Display#getSystemColor(int)
+     */
+    public static Color getBannerBackground(Display display) {
+        return display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
+    }
+
+    /**
+     * @param display the display the color is from
+     * @return the Color used for banner foregrounds
+     * @see SWT#COLOR_LIST_FOREGROUND
+     * @see Display#getSystemColor(int)
+     */
+    public static Color getBannerForeground(Display display) {
+        return display.getSystemColor(SWT.COLOR_LIST_FOREGROUND);
+    }
+
+    /**
+     * @param display the display the color is from
+     * @return the background Color for widgets that display errors.
+     * @see SWT#COLOR_WIDGET_BACKGROUND
+     * @see Display#getSystemColor(int)
+     */
+    public static Color getErrorBackground(Display display) {
+        return display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
+    }
+
+    /**
+     * @param display the display the color is from
+     * @return the border Color for widgets that display errors.
+     * @see SWT#COLOR_WIDGET_DARK_SHADOW
+     * @see Display#getSystemColor(int)
+     */
+    public static Color getErrorBorder(Display display) {
+        return display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW);
+    }
+
+    /**
+     * @param display the display the color is from
+     * @return the default color to use for displaying errors.
+     * @see ColorRegistry#get(String)
+     * @see JFacePreferences#ERROR_COLOR
+     */
+    public static Color getErrorText(Display display) {
+        return JFaceResources.getColorRegistry().get(
+                JFacePreferences.ERROR_COLOR);
+    }
+
+    /**
+     * @param display the display the color is from
+     * @return the default color to use for displaying hyperlinks.
+     * @see ColorRegistry#get(String)
+     * @see JFacePreferences#HYPERLINK_COLOR
+     */
+    public static Color getHyperlinkText(Display display) {
+        return JFaceResources.getColorRegistry().get(
+                JFacePreferences.HYPERLINK_COLOR);
+    }
+
+    /**
+     * @param display the display the color is from
+     * @return the default color to use for displaying active hyperlinks.
+     * @see ColorRegistry#get(String)
+     * @see JFacePreferences#ACTIVE_HYPERLINK_COLOR
+     */
+    public static Color getActiveHyperlinkText(Display display) {
+        return JFaceResources.getColorRegistry().get(
+                JFacePreferences.ACTIVE_HYPERLINK_COLOR);
+    }
+
+    /**
+     * Clear out the cached color for name. This is generally
+     * done when the color preferences changed and any cached colors
+     * may be disposed. Users of the colors in this class should add a IPropertyChangeListener
+     * to detect when any of these colors change.
+     * @param colorName name of the color
+     *
+     * @deprecated JFaceColors no longer maintains a cache of colors.  This job
+     * is now handled by the ColorRegistry.
+     */
+    public static void clearColor(String colorName) {
+        //no-op
+    }
+
+    /**
+     * Dispose of all allocated colors. Called on workbench
+     * shutdown.
+     *
+     * @deprecated JFaceColors no longer maintains a cache of colors.  This job
+     * is now handled by the ColorRegistry.
+     */
+    public static void disposeColors() {
+        //no-op
+    }
+
+    /**
+     * Set the foreground and background colors of the
+     * control to the specified values. If the values are
+     * null than ignore them.
+     * @param control the control the foreground and/or background color should be set
+     *
+     * @param foreground Color the foreground color (maybe <code>null</code>)
+     * @param background Color the background color (maybe <code>null</code>)
+     */
+    public static void setColors(Control control, Color foreground,
+            Color background) {
+        if (foreground !is null) {
+            control.setForeground(foreground);
+        }
+        if (background !is null) {
+            control.setBackground(background);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/JFaceResources.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,614 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.resource.JFaceResources;
+
+import org.eclipse.jface.resource.FontRegistry;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.jface.resource.ResourceManager;
+import org.eclipse.jface.resource.ColorRegistry;
+import org.eclipse.jface.resource.FontDescriptor;
+import org.eclipse.jface.resource.DeviceResourceManager;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.PopupDialog;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.wizard.Wizard;
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+import java.util.MissingResourceException;
+import tango.text.convert.Format;
+/**
+ * Utility methods to access JFace-specific resources.
+ * <p>
+ * All methods declared on this class are static. This class cannot be
+ * instantiated.
+ * </p>
+ * <p>
+ * The following global state is also maintained by this class:
+ * <ul>
+ * <li>a font registry</li>
+ * <li>a color registry</li>
+ * <li>an image registry</li>
+ * <li>a resource bundle</li>
+ * </ul>
+ * </p>
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class JFaceResources {
+
+    /**
+     * The path to the icons in the resources.
+     */
+    private const static String ICONS_PATH = "$nl$/icons/full/";//$NON-NLS-1$
+
+    /**
+     * Map of Display onto DeviceResourceManager. Holds all the resources for
+     * the associated display.
+     */
+    private static Map registries;
+    private static void init_registries(){
+        if( registries is null ){
+            synchronized if( registries is null ){
+                registries = new HashMap();
+            }
+        }
+    }
+
+    /**
+     * The symbolic font name for the banner font (value
+     * <code>"org.eclipse.jface.bannerfont"</code>).
+     */
+    public static const String BANNER_FONT = "org.eclipse.jface.bannerfont"; //$NON-NLS-1$
+
+    /**
+     * The JFace resource bundle; eagerly initialized.
+     */
+    private static /+final+/ ResourceBundle bundle;
+    private static void init_bundle(){
+        if( bundle is null ){
+            synchronized if( bundle is null ){
+                bundle = ResourceBundle.getBundle( getImportData!("org.eclipse.jface.messages.properties")); //$NON-NLS-1$
+            }
+        }
+    }
+
+    /**
+     * The JFace color registry; <code>null</code> until lazily initialized or
+     * explicitly set.
+     */
+    private static ColorRegistry colorRegistry;
+
+    /**
+     * The symbolic font name for the standard font (value
+     * <code>"org.eclipse.jface.defaultfont"</code>).
+     */
+    public static const String DEFAULT_FONT = "org.eclipse.jface.defaultfont"; //$NON-NLS-1$
+
+    /**
+     * The symbolic font name for the dialog font (value
+     * <code>"org.eclipse.jface.dialogfont"</code>).
+     */
+    public static const String DIALOG_FONT = "org.eclipse.jface.dialogfont"; //$NON-NLS-1$
+
+    /**
+     * The JFace font registry; <code>null</code> until lazily initialized or
+     * explicitly set.
+     */
+    private static FontRegistry fontRegistry = null;
+
+    /**
+     * The symbolic font name for the header font (value
+     * <code>"org.eclipse.jface.headerfont"</code>).
+     */
+    public static const String HEADER_FONT = "org.eclipse.jface.headerfont"; //$NON-NLS-1$
+
+    /**
+     * The JFace image registry; <code>null</code> until lazily initialized.
+     */
+    private static ImageRegistry imageRegistry = null;
+
+    /**
+     * The symbolic font name for the text font (value
+     * <code>"org.eclipse.jface.textfont"</code>).
+     */
+    public static const String TEXT_FONT = "org.eclipse.jface.textfont"; //$NON-NLS-1$
+
+    /**
+     * The symbolic font name for the viewer font (value
+     * <code>"org.eclipse.jface.viewerfont"</code>).
+     *
+     * @deprecated This font is not in use
+     */
+    public static const String VIEWER_FONT = "org.eclipse.jface.viewerfont"; //$NON-NLS-1$
+
+    /**
+     * The symbolic font name for the window font (value
+     * <code>"org.eclipse.jface.windowfont"</code>).
+     *
+     * @deprecated This font is not in use
+     */
+    public static const String WINDOW_FONT = "org.eclipse.jface.windowfont"; //$NON-NLS-1$
+
+    /**
+     * Returns the formatted message for the given key in JFace's resource
+     * bundle.
+     *
+     * @param key
+     *            the resource name
+     * @param args
+     *            the message arguments
+     * @return the string
+     */
+    public static String format(String key, ... ) {
+        return Format.convert( _arguments, _argptr, getString(key) );
+    }
+
+    /**
+     * Returns the JFace's banner font. Convenience method equivalent to
+     *
+     * <pre>
+     * JFaceResources.getFontRegistry().get(JFaceResources.BANNER_FONT)
+     * </pre>
+     *
+     * @return the font
+     */
+    public static Font getBannerFont() {
+        return getFontRegistry().get(BANNER_FONT);
+    }
+
+    /**
+     * Returns the resource bundle for JFace itself. The resouble bundle is
+     * obtained from
+     * <code>ResourceBundle.getBundle("org.eclipse.jface.jface_nls")</code>.
+     * <p>
+     * Note that several static convenience methods are also provided on this
+     * class for directly accessing resources in this bundle.
+     * </p>
+     *
+     * @return the resource bundle
+     */
+    public static ResourceBundle getBundle() {
+        init_bundle();
+        return bundle;
+    }
+
+    /**
+     * Returns the color registry for JFace itself.
+     * <p>
+     *
+     * @return the <code>ColorRegistry</code>.
+     * @since 3.0
+     */
+    public static ColorRegistry getColorRegistry() {
+        if (colorRegistry is null) {
+            colorRegistry = new ColorRegistry();
+            initializeDefaultColors();
+        }
+        return colorRegistry;
+    }
+
+    /**
+     * Returns the global resource manager for the given display
+     *
+     * @since 3.1
+     *
+     * @param toQuery
+     *            display to query
+     * @return the global resource manager for the given display
+     */
+    public static ResourceManager getResources(Display toQuery) {
+        assert( toQuery );
+        init_registries();
+        ResourceManager reg = cast(ResourceManager) registries.get(toQuery);
+
+        if (reg is null) {
+            DeviceResourceManager mgr = new DeviceResourceManager(toQuery);
+            reg = mgr;
+            registries.put(toQuery, reg);
+            toQuery.disposeExec( dgRunnable( (DeviceResourceManager mgr_, Display toQuery_){
+                /*
+                 * (non-Javadoc)
+                 *
+                 * @see java.lang.Runnable#run()
+                 */
+                mgr_.dispose();
+                registries.remove(toQuery_);
+            }, mgr, toQuery ));
+        }
+
+        return reg;
+    }
+
+    /**
+     * Returns the ResourceManager for the current display. May only be called
+     * from a UI thread.
+     *
+     * @since 3.1
+     *
+     * @return the global ResourceManager for the current display
+     */
+    public static ResourceManager getResources() {
+        return getResources(Display.getCurrent());
+    }
+
+    /**
+     * Returns JFace's standard font. Convenience method equivalent to
+     *
+     * <pre>
+     * JFaceResources.getFontRegistry().get(JFaceResources.DEFAULT_FONT)
+     * </pre>
+     *
+     * @return the font
+     */
+    public static Font getDefaultFont() {
+        return getFontRegistry().defaultFont();
+    }
+
+    /**
+     * Returns the descriptor for JFace's standard font. Convenience method
+     * equivalent to
+     *
+     * <pre>
+     * JFaceResources.getFontRegistry().getDescriptor(JFaceResources.DEFAULT_FONT)
+     * </pre>
+     *
+     * @return the font
+     * @since 3.3
+     */
+    public static FontDescriptor getDefaultFontDescriptor() {
+        return getFontRegistry().defaultFontDescriptor();
+    }
+
+    /**
+     * Returns the JFace's dialog font. Convenience method equivalent to
+     *
+     * <pre>
+     * JFaceResources.getFontRegistry().get(JFaceResources.DIALOG_FONT)
+     * </pre>
+     *
+     * @return the font
+     */
+    public static Font getDialogFont() {
+        return getFontRegistry().get(DIALOG_FONT);
+    }
+
+    /**
+     * Returns the descriptor for JFace's dialog font. Convenience method
+     * equivalent to
+     *
+     * <pre>
+     * JFaceResources.getFontRegistry().getDescriptor(JFaceResources.DIALOG_FONT)
+     * </pre>
+     *
+     * @return the font
+     * @since 3.3
+     */
+    public static FontDescriptor getDialogFontDescriptor() {
+        return getFontRegistry().getDescriptor(DIALOG_FONT);
+    }
+
+    /**
+     * Returns the font in JFace's font registry with the given symbolic font
+     * name. Convenience method equivalent to
+     *
+     * <pre>
+     * JFaceResources.getFontRegistry().get(symbolicName)
+     * </pre>
+     *
+     * If an error occurs, return the default font.
+     *
+     * @param symbolicName
+     *            the symbolic font name
+     * @return the font
+     */
+    public static Font getFont(String symbolicName) {
+        return getFontRegistry().get(symbolicName);
+    }
+
+    /**
+     * Returns the font descriptor for in JFace's font registry with the given
+     * symbolic name. Convenience method equivalent to
+     *
+     * <pre>
+     * JFaceResources.getFontRegistry().getDescriptor(symbolicName)
+     * </pre>
+     *
+     * If an error occurs, return the default font.
+     *
+     * @param symbolicName
+     *            the symbolic font name
+     * @return the font descriptor (never null)
+     * @since 3.3
+     */
+    public static FontDescriptor getFontDescriptor(String symbolicName) {
+        return getFontRegistry().getDescriptor(symbolicName);
+    }
+
+    /**
+     * Returns the font registry for JFace itself. If the value has not been
+     * established by an earlier call to <code>setFontRegistry</code>, is it
+     * initialized to
+     * <code>new FontRegistry("org.eclipse.jface.resource.jfacefonts")</code>.
+     * <p>
+     * Note that several static convenience methods are also provided on this
+     * class for directly accessing JFace's standard fonts.
+     * </p>
+     *
+     * @return the JFace font registry
+     */
+    public static FontRegistry getFontRegistry() {
+        if (fontRegistry is null) {
+            fontRegistry = new FontRegistry(
+                    "org.eclipse.jface.resource.jfacefonts"); //$NON-NLS-1$
+        }
+        return fontRegistry;
+    }
+
+    /**
+     * Returns the JFace's header font. Convenience method equivalent to
+     *
+     * <pre>
+     * JFaceResources.getFontRegistry().get(JFaceResources.HEADER_FONT)
+     * </pre>
+     *
+     * @return the font
+     */
+    public static Font getHeaderFont() {
+        return getFontRegistry().get(HEADER_FONT);
+    }
+
+    /**
+     * Returns the descriptor for JFace's header font. Convenience method
+     * equivalent to
+     *
+     * <pre>
+     * JFaceResources.getFontRegistry().get(JFaceResources.HEADER_FONT)
+     * </pre>
+     *
+     * @return the font descriptor (never null)
+     * @since 3.3
+     */
+    public static FontDescriptor getHeaderFontDescriptor() {
+        return getFontRegistry().getDescriptor(HEADER_FONT);
+    }
+
+    /**
+     * Returns the image in JFace's image registry with the given key, or
+     * <code>null</code> if none. Convenience method equivalent to
+     *
+     * <pre>
+     * JFaceResources.getImageRegistry().get(key)
+     * </pre>
+     *
+     * @param key
+     *            the key
+     * @return the image, or <code>null</code> if none
+     */
+    public static Image getImage(String key) {
+        return getImageRegistry().get(key);
+    }
+
+    /**
+     * Returns the image registry for JFace itself.
+     * <p>
+     * Note that the static convenience method <code>getImage</code> is also
+     * provided on this class.
+     * </p>
+     *
+     * @return the JFace image registry
+     */
+    public static ImageRegistry getImageRegistry() {
+        if (imageRegistry is null) {
+            imageRegistry = new ImageRegistry(
+                    getResources(Display.getCurrent()));
+            initializeDefaultImages();
+        }
+        return imageRegistry;
+    }
+
+    /**
+     * Initialize default images in JFace's image registry.
+     *
+     */
+    private static void initializeDefaultImages() {
+
+        Object bundle = null;
+// SWT-Note: deactivated
+//         try {
+//          bundle = JFaceActivator.getBundle();
+//         } catch (NoClassDefFoundError exception) {
+//             // Test to see if OSGI is present
+//         }
+        declareImage(bundle, Wizard.DEFAULT_IMAGE, ICONS_PATH
+                ~ "page.gif", getImportData!( "org.eclipse.jface.wizard.images.page.gif" )); //$NON-NLS-1$
+        // register default images for dialogs
+        declareImage(bundle, Dialog.DLG_IMG_MESSAGE_INFO, ICONS_PATH
+                ~ "message_info.gif", getImportData!( "org.eclipse.jface.dialogs.images.message_info.gif")); //$NON-NLS-1$ //$NON-NLS-2$
+        declareImage(bundle, Dialog.DLG_IMG_MESSAGE_WARNING, ICONS_PATH
+                ~ "message_warning.gif", getImportData!( "org.eclipse.jface.dialogs.images.message_warning.gif")); //$NON-NLS-1$
+        declareImage(bundle, Dialog.DLG_IMG_MESSAGE_ERROR, ICONS_PATH
+                ~ "message_error.gif", getImportData!( "org.eclipse.jface.dialogs.images.message_error.gif"));//$NON-NLS-1$ //$NON-NLS-2$
+        declareImage(bundle, Dialog.DLG_IMG_HELP,
+                ICONS_PATH ~ "help.gif", getImportData!( "org.eclipse.jface.dialogs.images.help.gif"));//$NON-NLS-1$ //$NON-NLS-2$
+        declareImage(bundle, TitleAreaDialog.DLG_IMG_TITLE_BANNER, ICONS_PATH
+                ~ "title_banner.png", getImportData!( "org.eclipse.jface.dialogs.images.title_banner.gif"));//$NON-NLS-1$ //$NON-NLS-2$
+        declareImage(bundle, PreferenceDialog.PREF_DLG_TITLE_IMG, ICONS_PATH
+                ~ "pref_dialog_title.gif", getImportData!( "org.eclipse.jface.preference.images.pref_dialog_title.gif"));//$NON-NLS-1$ //$NON-NLS-2$
+        declareImage(bundle, PopupDialog.POPUP_IMG_MENU, ICONS_PATH
+                ~ "popup_menu.gif", getImportData!( "org.eclipse.jface.dialogs.images.popup_menu.gif"));//$NON-NLS-1$ //$NON-NLS-2$
+        declareImage(
+                bundle,
+                PopupDialog.POPUP_IMG_MENU_DISABLED,
+                ICONS_PATH ~ "popup_menu_disabled.gif", getImportData!( "org.eclipse.jface.dialogs.images.popup_menu_disabled.gif"));//$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    /**
+     * Declares a JFace image given the path of the image file (relative to the
+     * JFace plug-in). This is a helper method that creates the image descriptor
+     * and passes it to the main <code>declareImage</code> method.
+     *
+     * @param bundle
+     *            the {@link Bundle} or <code>null</code> of the Bundle cannot
+     *            be found
+     * @param key
+     *            the symbolic name of the image
+     * @param path
+     *            the path of the image file relative to the base of the
+     *            workbench plug-ins install directory
+     * @param fallback
+     *            the {@link Class} where the fallback implementation of the
+     *            image is relative to
+     * @param fallbackPath
+     *            the path relative to the fallback {@link Class}
+     *
+     */
+    private static final void declareImage(Object bundle, String key,
+            String path, ImportData importdata/+ClassInfo fallback, String fallbackPath+/) {
+
+        ImageDescriptor descriptor = null;
+
+        if (bundle !is null) {
+//             URL url = FileLocator.find((Bundle) bundle, new Path(path), null);
+//             if (url !is null)
+//                 descriptor = ImageDescriptor.createFromURL(url);
+        }
+
+        // If we failed then load from the backup file
+        if (descriptor is null)
+            descriptor = ImageDescriptor.createFromFile( importdata /+fallback, fallbackPath+/);
+
+        imageRegistry.put(key, descriptor);
+    }
+
+    /**
+     * Returns the resource object with the given key in JFace's resource
+     * bundle. If there isn't any value under the given key, the key is
+     * returned.
+     *
+     * @param key
+     *            the resource name
+     * @return the string
+     */
+    public static String getString(String key) {
+        init_bundle();
+        try {
+            return bundle.getString(key);
+        } catch (MissingResourceException e) {
+            return key;
+        }
+    }
+
+    /**
+     * Returns a list of string values corresponding to the given list of keys.
+     * The lookup is done with <code>getString</code>. The values are in the
+     * same order as the keys.
+     *
+     * @param keys
+     *            a list of keys
+     * @return a list of corresponding string values
+     */
+    public static String[] getStrings(String[] keys) {
+        Assert.isTrue(keys.length > 0 );
+        int length_ = keys.length;
+        String[] result = new String[](length_);
+        for (int i = 0; i < length_; i++) {
+            result[i] = getString(keys[i]);
+        }
+        return result;
+    }
+
+    /**
+     * Returns JFace's text font. Convenience method equivalent to
+     *
+     * <pre>
+     * JFaceResources.getFontRegistry().get(JFaceResources.TEXT_FONT)
+     * </pre>
+     *
+     * @return the font
+     */
+    public static Font getTextFont() {
+        return getFontRegistry().get(TEXT_FONT);
+    }
+
+    /**
+     * Returns the descriptor for JFace's text font. Convenience method
+     * equivalent to
+     *
+     * <pre>
+     * JFaceResources.getFontRegistry().getDescriptor(JFaceResources.TEXT_FONT)
+     * </pre>
+     *
+     * @return the font descriptor (never null)
+     * @since 3.3
+     */
+    public static FontDescriptor getTextFontDescriptor() {
+        return getFontRegistry().getDescriptor(TEXT_FONT);
+    }
+
+    /**
+     * Returns JFace's viewer font. Convenience method equivalent to
+     *
+     * <pre>
+     * JFaceResources.getFontRegistry().get(JFaceResources.VIEWER_FONT)
+     * </pre>
+     *
+     * @return the font
+     * @deprecated This font is not in use
+     */
+    public static Font getViewerFont() {
+        return getFontRegistry().get(VIEWER_FONT);
+    }
+
+    /**
+     * Sets JFace's font registry to the given value. This method may only be
+     * called once; the call must occur before
+     * <code>JFaceResources.getFontRegistry</code> is invoked (either directly
+     * or indirectly).
+     *
+     * @param registry
+     *            a font registry
+     */
+    public static void setFontRegistry(FontRegistry registry) {
+        Assert.isTrue(fontRegistry is null,
+                "Font registry can only be set once."); //$NON-NLS-1$
+        fontRegistry = registry;
+    }
+
+    /*
+     * (non-Javadoc) Declare a private constructor to block instantiation.
+     */
+    private this() {
+        // no-op
+    }
+
+    /*
+     * Initialize any JFace colors that may not be initialized via a client.
+     */
+    private static void initializeDefaultColors() {
+        // JFace Colors that may not be defined in a workbench theme should be
+        // defined here.
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/LocalResourceManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.resource.LocalResourceManager;
+
+import org.eclipse.jface.resource.AbstractResourceManager;
+import org.eclipse.jface.resource.ResourceManager;
+import org.eclipse.jface.resource.DeviceResourceDescriptor;
+
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Control;
+
+import java.lang.all;
+
+/**
+ * A local registry that shares its resources with some global registry.
+ * LocalResourceManager is typically used to safeguard against leaks. Clients
+ * can use a nested registry to allocate and deallocate resources in the
+ * global registry. Calling dispose() on the nested registry will deallocate
+ * everything allocated for the nested registry without affecting the rest
+ * of the global registry.
+ * <p>
+ * A nested registry can be used to manage the resources for, say, a dialog
+ * box.
+ * </p>
+ * @since 3.1
+ */
+public final class LocalResourceManager : AbstractResourceManager {
+
+    private ResourceManager parentRegistry;
+
+    /**
+     * Creates a local registry that delegates to the given global registry
+     * for all resource allocation and deallocation.
+     *
+     * @param parentRegistry global registry
+     */
+    public this(ResourceManager parentRegistry) {
+        this.parentRegistry = parentRegistry;
+    }
+
+    /**
+     * Creates a local registry that wraps the given global registry. Anything
+     * allocated by this registry will be automatically cleaned up with the given
+     * control is disposed. Note that registries created in this way should not
+     * be used to allocate any resource that must outlive the given control.
+     *
+     * @param parentRegistry global registry that handles resource allocation
+     * @param owner control whose disposal will trigger cleanup of everything
+     * in the registry.
+     */
+    public this(ResourceManager parentRegistry, Control owner) {
+        this(parentRegistry);
+
+        owner.addDisposeListener(new class DisposeListener {
+            /* (non-Javadoc)
+             * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
+             */
+            public void widgetDisposed(DisposeEvent e) {
+                this.outer.dispose();
+            }
+        });
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.ResourceManager#getDevice()
+     */
+    public override Device getDevice() {
+        return parentRegistry.getDevice();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.AbstractResourceManager#allocate(org.eclipse.jface.resource.DeviceResourceDescriptor)
+     */
+    protected override Object allocate(DeviceResourceDescriptor descriptor) {
+        return parentRegistry.create(descriptor);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.AbstractResourceManager#deallocate(java.lang.Object, org.eclipse.jface.resource.DeviceResourceDescriptor)
+     */
+    protected override void deallocate(Object resource,
+            DeviceResourceDescriptor descriptor) {
+
+        parentRegistry.destroy(descriptor);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.ResourceManager#getDefaultImage()
+     */
+    protected override Image getDefaultImage() {
+        return parentRegistry.getDefaultImage_();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/MissingImageDescriptor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.resource.MissingImageDescriptor;
+
+import java.lang.all;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import org.eclipse.swt.graphics.ImageData;
+
+/**
+ * The image descriptor for a missing image.
+ * <p>
+ * Use <code>MissingImageDescriptor.getInstance</code> to
+ * access the singleton instance maintained in an
+ * internal state variable.
+ * </p>
+ */
+class MissingImageDescriptor : ImageDescriptor {
+    private static MissingImageDescriptor instance;
+
+    /**
+     * Constructs a new missing image descriptor.
+     */
+    private this() {
+        super();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ImageDesciptor.
+     */
+    public override ImageData getImageData() {
+        return DEFAULT_IMAGE_DATA;
+    }
+
+    /**
+     * Returns the shared missing image descriptor instance.
+     *
+     * @return the image descriptor for a missing image
+     */
+    static MissingImageDescriptor getInstance() {
+        if (instance is null) {
+            instance = new MissingImageDescriptor();
+        }
+        return instance;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/RGBColorDescriptor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.resource.RGBColorDescriptor;
+
+import org.eclipse.jface.resource.ColorDescriptor;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.RGB;
+
+import java.lang.all;
+
+/**
+ * Describes a color by its RGB values.
+ *
+ * @since 3.1
+ */
+class RGBColorDescriptor : ColorDescriptor {
+
+    private RGB color;
+
+    /**
+     * Color being copied, or null if none
+     */
+    private Color originalColor = null;
+
+    /**
+     * Creates a new RGBColorDescriptor given some RGB values
+     *
+     * @param color RGB values (not null)
+     */
+    public this(RGB color) {
+        this.color = color;
+    }
+
+    /**
+     * Creates a new RGBColorDescriptor that describes an existing color.
+     *
+     * @since 3.1
+     *
+     * @param originalColor a color to describe
+     */
+    public this(Color originalColor) {
+        this(originalColor.getRGB());
+        this.originalColor = originalColor;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public override int opEquals(Object obj) {
+        if ( auto other = cast(RGBColorDescriptor)obj ) {
+            return other.color.opEquals(color) && other.originalColor is originalColor;
+        }
+
+        return false;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    public override hash_t toHash() {
+        return color.toHash();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resources.ColorDescriptor#createColor()
+     */
+    public override Color createColor(Device device) {
+        // If this descriptor is wrapping an existing color, then we can return the original color
+        // if this is the same device.
+        if (originalColor !is null) {
+            // If we're allocating on the same device as the original color, return the original.
+            if (originalColor.getDevice() is device) {
+                return originalColor;
+            }
+        }
+
+        return new Color(device, color);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.ColorDescriptor#destroyColor(org.eclipse.swt.graphics.Color)
+     */
+    public override void destroyColor(Color toDestroy) {
+        if (toDestroy is originalColor) {
+            return;
+        }
+
+        toDestroy.dispose();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/ResourceManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,408 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.resource.ResourceManager;
+
+import org.eclipse.jface.resource.DeviceResourceDescriptor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ColorDescriptor;
+import org.eclipse.jface.resource.FontDescriptor;
+import org.eclipse.jface.resource.DeviceResourceException;
+import org.eclipse.jface.resource.RGBColorDescriptor;
+
+// import java.util.ArrayList;
+
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.util.Policy;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * This class manages SWT resources. It manages reference-counted instances of resources
+ * such as Fonts, Images, and Colors, and allows them to be accessed using descriptors.
+ * Everything allocated through the registry should also be disposed through the registry.
+ * Since the resources are shared and reference counted, they should never be disposed
+ * directly.
+ * <p>
+ * ResourceManager handles correct allocation and disposal of resources. It differs from
+ * the various JFace *Registry classes, which also map symbolic IDs onto resources. In
+ * general, you should use a *Registry class to map IDs onto descriptors, and use a
+ * ResourceManager to convert the descriptors into real Images/Fonts/etc.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class ResourceManager {
+
+    /**
+     * List of Runnables scheduled to run when the ResourceManager is disposed.
+     * null if empty.
+     */
+    private List disposeExecs = null;
+
+    /**
+     * Returns the Device for which this ResourceManager will create resources
+     *
+     * @since 3.1
+     *
+     * @return the Device associated with this ResourceManager
+     */
+    public abstract Device getDevice();
+
+    /**
+     * Returns the resource described by the given descriptor. If the resource already
+     * exists, the reference count is incremented and the exiting resource is returned.
+     * Otherwise, a new resource is allocated. Every call to this method should have
+     * a corresponding call to {@link #destroy(DeviceResourceDescriptor)}.
+     *
+     * <p>If the resource is intended to live for entire lifetime of the resource manager,
+     * a subsequent call to {@link #destroy(DeviceResourceDescriptor)} may be omitted and the
+     * resource will be cleaned up when the resource manager is disposed. This pattern
+     * is useful for short-lived {@link LocalResourceManager}s, but should never be used
+     * with the global resource manager since doing so effectively leaks the resource.</p>
+     *
+     * <p>The resources returned from this method are reference counted and may be shared
+     * internally with other resource managers. They should never be disposed outside of the
+     * ResourceManager framework, or it will cause exceptions in other code that shares
+     * them. For example, never call {@link org.eclipse.swt.graphics.Resource#dispose()}
+     * on anything returned from this method.</p>
+     *
+     * <p>Callers may safely downcast the result to the resource type associated with
+     * the descriptor. For example, when given an ImageDescriptor, the return
+     * value of this method will always be an Image.</p>
+     *
+     * @since 3.1
+     *
+     * @param descriptor descriptor for the resource to allocate
+     * @return the newly allocated resource (not null)
+     * @throws DeviceResourceException if unable to allocate the resource
+     */
+    public abstract Object create(DeviceResourceDescriptor descriptor);
+
+    /**
+     * Deallocates a resource previously allocated by {@link #create(DeviceResourceDescriptor)}.
+     * Descriptors are compared by equality, not identity. If the same resource was
+     * created multiple times, this may decrement a reference count rather than
+     * disposing the actual resource.
+     *
+     * @since 3.1
+     *
+     * @param descriptor identifier for the resource
+     */
+    public abstract void destroy(DeviceResourceDescriptor descriptor);
+
+    /**
+     * <p>Returns a previously-allocated resource or allocates a new one if none
+     * exists yet. The resource will remain allocated for at least the lifetime
+     * of this resource manager. If necessary, the resource will be deallocated
+     * automatically when the resource manager is disposed.</p>
+     *
+     * <p>The resources returned from this method are reference counted and may be shared
+     * internally with other resource managers. They should never be disposed outside of the
+     * ResourceManager framework, or it will cause exceptions in other code that shares
+     * them. For example, never call {@link org.eclipse.swt.graphics.Resource#dispose()}
+     * on anything returned from this method.</p>
+     *
+     * <p>
+     * Callers may safely downcast the result to the resource type associated with
+     * the descriptor. For example, when given an ImageDescriptor, the return
+     * value of this method may be downcast to Image.
+     * </p>
+     *
+     * <p>
+     * This method should only be used for resources that should remain
+     * allocated for the lifetime of the resource manager. To allocate shorter-lived
+     * resources, manage them with <code>create</code>, and <code>destroy</code>
+     * rather than this method.
+     * </p>
+     *
+     * <p>
+     * This method should never be called on the global resource manager,
+     * since all resources will remain allocated for the lifetime of the app and
+     * will be effectively leaked.
+     * </p>
+     *
+     * @param descriptor identifier for the requested resource
+     * @return the requested resource. Never null.
+     * @throws DeviceResourceException if the resource does not exist yet and cannot
+     * be created for any reason.
+     *
+     * @since 3.3
+     */
+    public final Object get(DeviceResourceDescriptor descriptor) {
+        Object result = find(descriptor);
+
+        if (result is null) {
+            result = create(descriptor);
+        }
+
+        return result;
+    }
+
+    /**
+     * <p>Creates an image, given an image descriptor. Images allocated in this manner must
+     * be disposed by {@link #destroyImage(ImageDescriptor)}, and never by calling
+     * {@link Image#dispose()}.</p>
+     *
+     * <p>
+     * If the image is intended to remain allocated for the lifetime of the ResourceManager,
+     * the call to destroyImage may be omitted and the image will be cleaned up automatically
+     * when the ResourceManager is disposed. This should only be done with short-lived ResourceManagers,
+     * as doing so with the global manager effectively leaks the resource.
+     * </p>
+     *
+     * @since 3.1
+     *
+     * @param descriptor descriptor for the image to create
+     * @return the Image described by this descriptor (possibly shared by other equivalent
+     * ImageDescriptors)
+     * @throws DeviceResourceException if unable to allocate the Image
+     */
+    public final Image createImage(ImageDescriptor descriptor) {
+        // Assertion added to help diagnose client bugs.  See bug #83711 and bug #90454.
+        Assert.isNotNull(descriptor);
+
+        return cast(Image)create(descriptor);
+    }
+
+    /**
+     * Creates an image, given an image descriptor. Images allocated in this manner must
+     * be disposed by {@link #destroyImage(ImageDescriptor)}, and never by calling
+     * {@link Image#dispose()}.
+     *
+     * @since 3.1
+     *
+     * @param descriptor descriptor for the image to create
+     * @return the Image described by this descriptor (possibly shared by other equivalent
+     * ImageDescriptors)
+     */
+    public final Image createImageWithDefault(ImageDescriptor descriptor) {
+        if (descriptor is null) {
+            return getDefaultImage();
+        }
+
+        try {
+            return cast(Image) create(descriptor);
+        } catch (DeviceResourceException e) {
+            Policy.getLog().log(
+                    new Status(IStatus.WARNING, "org.eclipse.jface", 0, //$NON-NLS-1$
+                            "The image could not be loaded: " ~ descriptor.toString, //$NON-NLS-1$
+                            e));
+            return getDefaultImage();
+        } catch (SWTException e) {
+            Policy.getLog().log(
+                    new Status(IStatus.WARNING, "org.eclipse.jface", 0, //$NON-NLS-1$
+                            "The image could not be loaded: " ~ descriptor.toString, //$NON-NLS-1$
+                            e));
+            return getDefaultImage();
+        }
+    }
+
+    /**
+     * Returns the default image that will be returned in the event that the intended
+     * image is missing.
+     *
+     * @since 3.1
+     *
+     * @return a default image that will be returned in the event that the intended
+     * image is missing.
+     */
+    protected abstract Image getDefaultImage();
+    package Image getDefaultImage_(){
+        return getDefaultImage();
+    }
+
+    /**
+     * Undoes everything that was done by {@link #createImage(ImageDescriptor)}.
+     *
+     * @since 3.1
+     *
+     * @param descriptor identifier for the image to dispose
+     */
+    public final void destroyImage(ImageDescriptor descriptor) {
+        destroy(descriptor);
+    }
+
+    /**
+     * Allocates a color, given a color descriptor. Any color allocated in this
+     * manner must be disposed by calling {@link #destroyColor(ColorDescriptor)},
+     * or by an eventual call to {@link #dispose()}. {@link Color#dispose()} must
+     * never been called directly on the returned color.
+     *
+     * @since 3.1
+     *
+     * @param descriptor descriptor for the color to create
+     * @return the Color described by the given ColorDescriptor (not null)
+     * @throws DeviceResourceException if unable to create the color
+     */
+    public final Color createColor(ColorDescriptor descriptor) {
+        return cast(Color)create(descriptor);
+    }
+
+    /**
+     * Allocates a color, given its RGB value. Any color allocated in this
+     * manner must be disposed by calling {@link #destroyColor(RGB)},
+     * or by an eventual call to {@link #dispose()}. {@link Color#dispose()} must
+     * never been called directly on the returned color.
+     *
+     * @since 3.1
+     *
+     * @param descriptor descriptor for the color to create
+     * @return the Color described by the given ColorDescriptor (not null)
+     * @throws DeviceResourceException if unable to create the color
+     */
+    public final Color createColor(RGB descriptor) {
+        return createColor(new RGBColorDescriptor(descriptor));
+    }
+
+    /**
+     * Undoes everything that was done by a call to {@link #createColor(RGB)}.
+     *
+     * @since 3.1
+     *
+     * @param descriptor RGB value of the color to dispose
+     */
+    public final void destroyColor(RGB descriptor) {
+        destroyColor(new RGBColorDescriptor(descriptor));
+    }
+
+    /**
+     * Undoes everything that was done by a call to {@link #createColor(ColorDescriptor)}.
+     *
+     *
+     * @since 3.1
+     *
+     * @param descriptor identifier for the color to dispose
+     */
+    public final void destroyColor(ColorDescriptor descriptor) {
+        destroy(descriptor);
+    }
+
+    /**
+     * Returns the Font described by the given FontDescriptor. Any Font
+     * allocated in this manner must be deallocated by calling disposeFont(...),
+     * or by an eventual call to {@link #dispose()}.  The method {@link Font#dispose()}
+     * must never be called directly on the returned font.
+     *
+     * @since 3.1
+     *
+     * @param descriptor description of the font to create
+     * @return the Font described by the given descriptor
+     * @throws DeviceResourceException if unable to create the font
+     */
+    public final Font createFont(FontDescriptor descriptor) {
+        return cast(Font)create(descriptor);
+    }
+
+    /**
+     * Undoes everything that was done by a previous call to {@link #createFont(FontDescriptor)}.
+     *
+     * @since 3.1
+     *
+     * @param descriptor description of the font to destroy
+     */
+    public final void destroyFont(FontDescriptor descriptor) {
+        destroy(descriptor);
+    }
+
+    /**
+     * Disposes any remaining resources allocated by this manager.
+     */
+    public void dispose() {
+        if (disposeExecs is null) {
+            return;
+        }
+
+        // If one of the runnables throws an exception, we need to propagate it.
+        // However, this should not prevent the remaining runnables from being
+        // notified. If any runnables throw an exception, we remember one of them
+        // here and throw it at the end of the method.
+        RuntimeException foundException = null;
+
+        Runnable[] execs = arraycast!(Runnable)(disposeExecs.toArray());
+        for (int i = 0; i < execs.length; i++) {
+            Runnable exec = execs[i];
+
+            try {
+                exec.run();
+            } catch (RuntimeException e) {
+                // Ensure that we propagate an exception, but don't stop notifying
+                // the remaining runnables.
+                foundException = e;
+            }
+        }
+
+        if (foundException !is null) {
+            // If any runnables threw an exception, propagate one of them.
+            throw foundException;
+        }
+    }
+
+    /**
+     * Returns a previously allocated resource associated with the given descriptor, or
+     * null if none exists yet.
+     *
+     * @since 3.1
+     *
+     * @param descriptor descriptor to find
+     * @return a previously allocated resource for the given descriptor or null if none.
+     */
+    public abstract Object find(DeviceResourceDescriptor descriptor);
+
+    /**
+     * Causes the <code>run()</code> method of the runnable to
+     * be invoked just before the receiver is disposed. The runnable
+     * can be subsequently canceled by a call to <code>cancelDisposeExec</code>.
+     *
+     * @param r runnable to execute.
+     */
+    public void disposeExec(Runnable r) {
+        Assert.isNotNull(cast(Object)r);
+
+        if (disposeExecs is null) {
+            disposeExecs = new ArrayList();
+        }
+
+        disposeExecs.add(cast(Object)r);
+    }
+
+    /**
+     * Cancels a runnable that was previously scheduled with <code>disposeExec</code>.
+     * Has no effect if the given runnable was not previously registered with
+     * disposeExec.
+     *
+     * @param r runnable to cancel
+     */
+    public void cancelDisposeExec(Runnable r) {
+        Assert.isNotNull(cast(Object)r);
+
+        if (disposeExecs is null) {
+            return;
+        }
+
+        disposeExecs.remove(cast(Object)r);
+
+        if (disposeExecs.isEmpty()) {
+            disposeExecs = null;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/ResourceRegistry.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.resource.ResourceRegistry;
+
+
+import org.eclipse.core.commands.common.EventManager;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Abstract base class for various JFace registries.
+ *
+ * @since 3.0
+ */
+public abstract class ResourceRegistry : EventManager {
+
+    /**
+     * Adds a property change listener to this registry.
+     *
+     * @param listener a property change listener
+     */
+    public void addListener(IPropertyChangeListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Disposes all currently allocated resources.
+     */
+    protected abstract void clearCaches();
+
+    /**
+     * @return the set of keys this manager knows about.  This collection
+     * should be immutable.
+     */
+    public abstract Set getKeySet();
+
+    /**
+     * Return whether or not the receiver has a value for the supplied key.
+     *
+     * @param key the key
+     * @return <code>true</code> if there is a value for this key
+     */
+    public abstract bool hasValueFor(String key);
+
+    /**
+     * Fires a <code>PropertyChangeEvent</code>.
+     *
+     * @param name the name of the symbolic value that is changing.
+     * @param oldValue the old value.
+     * @param newValue the new value.
+     */
+    protected void fireMappingChanged(String name, Object oldValue,
+            Object newValue) {
+        Object[] myListeners = getListeners();
+        if (myListeners.length > 0) {
+            PropertyChangeEvent event = new PropertyChangeEvent(this, name,
+                    oldValue, newValue);
+            for (int i = 0; i < myListeners.length; ++i) {
+                try {
+                    (cast(IPropertyChangeListener) myListeners[i])
+                            .propertyChange(event);
+                } catch (Exception e) {
+                    // TODO: how to log?
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes the given listener from this registry. Has no affect if the
+     * listener is not registered.
+     *
+     * @param listener a property change listener
+     */
+    public void removeListener(IPropertyChangeListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/StringConverter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,883 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.resource.StringConverter;
+
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.DataFormatException;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+static import tango.text.Util;
+static import tango.text.convert.Float;
+static import tango.text.convert.Integer;
+
+/**
+ * Helper class for converting various data types to and from
+ * strings. Supported types include:
+ * <ul>
+ *   <li><code>bool</code></li>
+ *   <li><code>int</code></li>
+ *   <li><code>long</code></li>
+ *   <li><code>float</code></li>
+ *   <li><code>double</code></li>
+ *   <li><code>org.eclipse.swt.graphics.Point</code></li>
+ *   <li><code>org.eclipse.swt.graphics.Rectangle</code></li>
+ *   <li><code>org.eclipse.swt.graphics.RGB</code></li>
+ *   <li><code>org.eclipse.swt.graphics.FontData</code></li>
+ * </ul>
+ * <p>
+ * All methods declared on this class are static. This
+ * class cannot be instantiated.
+ * </p>
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class StringConverter {
+
+    /**
+     * Internal font style constant for regular fonts.
+     */
+    private static const String REGULAR = "regular"; //$NON-NLS-1$
+
+    /**
+     * Internal font style constant for bold fonts.
+     */
+    private static const String BOLD = "bold"; //$NON-NLS-1$
+
+    /**
+     * Internal font style constant for italic fonts.
+     */
+    private static const String ITALIC = "italic"; //$NON-NLS-1$
+
+    /**
+     * Internal font style constant for bold italic fonts.
+     */
+    private static const String BOLD_ITALIC = "bold italic"; //$NON-NLS-1$
+
+    /**
+     * Internal constant for the separator character used in
+     * font specifications.
+     */
+    private static const char SEPARATOR = '-';
+
+    /**
+     * Internal constant for the seperator character used in font list
+     * specifications.
+     */
+    private static const String FONT_SEPARATOR = ";"; //$NON-NLS-1$
+
+    /* (non-Javadoc)
+     * Declare a private constructor to block instantiation.
+     */
+    private this() {
+        //no-op
+    }
+
+    /**
+     * Breaks out space-separated words into an array of words.
+     * For example: <code>"no comment"</code> into an array
+     * <code>a[0]="no"</code> and <code>a[1]= "comment"</code>.
+     *
+     * @param value the string to be converted
+     * @return the list of words
+     * @throws DataFormatException thrown if request string could not seperated
+     */
+    public static String[] asArray(String value) {
+        return tango.text.Util.delimit( value, " \t\n\r\f" );
+    }
+
+    /**
+     /**
+     * Breaks out space-separated words into an array of words.
+     * For example: <code>"no comment"</code> into an array
+     * <code>a[0]="no"</code> and <code>a[1]= "comment"</code>.
+     * Returns the given default value if the value cannot be parsed.
+     *
+     * @param value the string to be converted
+     * @param dflt the default value
+     * @return the list of words, or the default value
+     */
+    public static String[] asArray(String value, String[] dflt) {
+        try {
+            return asArray(value);
+        } catch (DataFormatException e) {
+            return dflt;
+        }
+    }
+
+    /**
+     * Converts the given value into a bool.
+     * This method fails if the value does not represent a bool.
+     * <p>
+     * Valid representations of <code>true</code> include the strings
+     * "<code>t</code>", "<code>true</code>", or equivalent in mixed
+     * or upper case.
+     * Similarly, valid representations of <code>false</code> include the strings
+     * "<code>f</code>", "<code>false</code>", or equivalent in mixed
+     * or upper case.
+     * </p>
+     *
+     * @param value the value to be converted
+     * @return the value as a bool
+     * @exception DataFormatException if the given value does not represent
+     *  a bool
+     */
+    public static bool asBoolean(String value) {
+        String v = value.toLowerCase();
+        if (v.equals("t") || v.equals("true")) { //$NON-NLS-1$ //$NON-NLS-2$
+            return true;
+        }
+        if (value.equals("f") || v.equals("false")) { //$NON-NLS-1$ //$NON-NLS-2$
+            return false;
+        }
+        throw new DataFormatException(
+                "Value " ~ value ~ "doesn't represent a bool"); //$NON-NLS-2$//$NON-NLS-1$
+    }
+
+    /**
+     * Converts the given value into a bool.
+     * Returns the given default value if the
+     * value does not represent a bool.
+     *
+     * @param value the value to be converted
+     * @param dflt the default value
+     * @return the value as a bool, or the default value
+     */
+    public static bool asBoolean(String value, bool dflt) {
+        try {
+            return asBoolean(value);
+        } catch (DataFormatException e) {
+            return dflt;
+        }
+    }
+
+    /**
+     * Converts the given value into a double.
+     * This method fails if the value does not represent a double.
+     *
+     * @param value the value to be converted
+     * @return the value as a double
+     * @exception DataFormatException if the given value does not represent
+     *  a double
+     */
+    public static double asDouble(String value) {
+        try {
+            return tango.text.convert.Float.toFloat(value);
+        } catch (IllegalArgumentException e) {
+            throw new DataFormatException(e.msg);
+        }
+    }
+
+    /**
+     * Converts the given value into a double.
+     * Returns the given default value if the
+     * value does not represent a double.
+     *
+     * @param value the value to be converted
+     * @param dflt the default value
+     * @return the value as a double, or the default value
+     */
+    public static double asDouble(String value, double dflt) {
+        try {
+            return tango.text.convert.Float.toFloat(value);
+        } catch (IllegalArgumentException e) {
+            return dflt;
+        }
+    }
+
+    /**
+     * Converts the given value into a float.
+     * This method fails if the value does not represent a float.
+     *
+     * @param value the value to be converted
+     * @return the value as a float
+     * @exception DataFormatException if the given value does not represent
+     *  a float
+     */
+    public static float asFloat(String value) {
+        try {
+            return tango.text.convert.Float.toFloat(value);
+        } catch (IllegalArgumentException e) {
+            throw new DataFormatException(e.msg);
+        }
+    }
+
+    /**
+     * Converts the given value into a float.
+     * Returns the given default value if the
+     * value does not represent a float.
+     *
+     * @param value the value to be converted
+     * @param dflt the default value
+     * @return the value as a float, or the default value
+     */
+    public static float asFloat(String value, float dflt) {
+        try {
+            return tango.text.convert.Float.toFloat(value);
+        } catch (IllegalArgumentException e) {
+            return dflt;
+        }
+    }
+
+    /**
+     * Converts the given value into an SWT font data object.
+     * This method fails if the value does not represent font data.
+     * <p>
+     * A valid font data representation is a string of the form
+     * <code><it>fontname</it>-<it>style</it>-<it>height</it></code> where
+     * <code><it>fontname</it></code> is the name of a font,
+     * <code><it>style</it></code> is a font style (one of
+     * <code>"regular"</code>, <code>"bold"</code>,
+     * <code>"italic"</code>, or <code>"bold italic"</code>)
+     * and <code><it>height</it></code> is an integer representing the
+     * font height. Example: <code>Times New Roman-bold-36</code>.
+     * </p>
+     *
+     * @param value the value to be converted
+     * @return the value as font data
+     * @exception DataFormatException if the given value does not represent
+     *  font data
+     */
+    public static FontData asFontData(String value) {
+        if (value is null) {
+            throw new DataFormatException(
+                    "Null doesn't represent a valid font data"); //$NON-NLS-1$
+        }
+        String name = null;
+        int height = 0;
+        int style = 0;
+        try {
+            int length = value.length;
+            int heightIndex = value.lastIndexOf(SEPARATOR);
+            if (heightIndex is -1) {
+                throw new DataFormatException(
+                        "No correct font data format \"" ~ value ~ "\""); //$NON-NLS-2$//$NON-NLS-1$
+            }
+            height = StringConverter.asInt(value.substring(heightIndex + 1,
+                    length));
+            int faceIndex = value.lastIndexOf(SEPARATOR, heightIndex - 1);
+            if (faceIndex is -1) {
+                throw new DataFormatException(
+                        "No correct font data format \"" ~ value ~ "\""); //$NON-NLS-2$//$NON-NLS-1$
+            }
+            String s = value.substring(faceIndex + 1, heightIndex);
+            if (BOLD_ITALIC.equals(s)) {
+                style = SWT.BOLD | SWT.ITALIC;
+            } else if (BOLD.equals(s)) {
+                style = SWT.BOLD;
+            } else if (ITALIC.equals(s)) {
+                style = SWT.ITALIC;
+            } else if (REGULAR.equals(s)) {
+                style = SWT.NORMAL;
+            } else {
+                throw new DataFormatException("Unknown face name \"" ~ s ~ "\""); //$NON-NLS-2$//$NON-NLS-1$
+            }
+            name = value.substring(0, faceIndex);
+        } catch (/+NoSuchElement+/Exception e) {
+            throw new DataFormatException(e.msg);
+        }
+        return new FontData(name, height, style);
+    }
+
+    /**
+     * Returns the result of converting a list of comma-separated tokens into an array
+     *
+     * @return the array of string tokens
+     * @param prop the initial comma-separated string
+     */
+    private static String[] getArrayFromList(String prop, String separator) {
+        if (prop is null || prop.trim().equals("")) { //$NON-NLS-1$
+            return new String[0];
+        }
+        String[] list;
+        foreach ( s; tango.text.Util.delimit( prop, separator )) {
+            String token = s.trim();
+            if (!token.equals("")) { //$NON-NLS-1$
+                list ~= token;
+            }
+        }
+        return list;
+    }
+
+    /**
+     * Convert the given value into an array of SWT font data objects.
+     *
+     * @param value the font list string
+     * @return the value as a font list
+     * @since 3.0
+     */
+    public static FontData[] asFontDataArray(String value) {
+        String[] strings = getArrayFromList(value, FONT_SEPARATOR);
+        FontData[] data = new FontData[strings.length];
+        for (int i = 0; i < strings.length; i++) {
+            try {
+                data[i] = StringConverter.asFontData(strings[i]);
+            } catch (DataFormatException e) {
+                //do-nothing
+            }
+        }
+        return data;
+    }
+
+    /**
+     * Converts the given value into an SWT font data object.
+     * Returns the given default value if the
+     * value does not represent a font data object.
+     *
+     * @param value the value to be converted
+     * @param dflt the default value
+     * @return the value as a font data object, or the default value
+     */
+    public static FontData asFontData(String value, FontData dflt) {
+        try {
+            return asFontData(value);
+        } catch (DataFormatException e) {
+            return dflt;
+        }
+    }
+
+    /**
+     * Converts the given value into an int.
+     * This method fails if the value does not represent an int.
+     *
+     * @param value the value to be converted
+     * @return the value as an int
+     * @exception DataFormatException if the given value does not represent
+     *  an int
+     */
+    public static int asInt(String value) {
+        try {
+            return tango.text.convert.Integer.toInt(value);
+        } catch (IllegalArgumentException e) {
+            throw new DataFormatException(e.msg);
+        }
+    }
+
+    /**
+     * Converts the given value into an int.
+     * Returns the given default value if the
+     * value does not represent an int.
+     *
+     * @param value the value to be converted
+     * @param dflt the default value
+     * @return the value as an int, or the default value
+     */
+    public static int asInt(String value, int dflt) {
+        try {
+            return tango.text.convert.Integer.toInt(value);
+        } catch (IllegalArgumentException e) {
+            return dflt;
+        }
+    }
+
+    /**
+     * Converts the given value into a long.
+     * This method fails if the value does not represent a long.
+     *
+     * @param value the value to be converted
+     * @return the value as a long
+     * @exception DataFormatException if the given value does not represent
+     *  a long
+     */
+    public static long asLong(String value) {
+        try {
+            return tango.text.convert.Integer.toInt(value);
+        } catch (IllegalArgumentException e) {
+            throw new DataFormatException(e.msg);
+        }
+    }
+
+    /**
+     * Converts the given value into a long.
+     * Returns the given default value if the
+     * value does not represent a long.
+     *
+     * @param value the value to be converted
+     * @param dflt the default value
+     * @return the value as a long, or the default value
+     */
+    public static long asLong(String value, long dflt) {
+        try {
+            return tango.text.convert.Integer.toInt(value);
+        } catch (IllegalArgumentException e) {
+            return dflt;
+        }
+    }
+
+    /**
+     * Converts the given value into an SWT point.
+     * This method fails if the value does not represent a point.
+     * <p>
+     * A valid point representation is a string of the form
+     * <code><it>x</it>,<it>y</it></code> where
+     * <code><it>x</it></code> and <code><it>y</it></code>
+     * are valid ints.
+     * </p>
+     *
+     * @param value the value to be converted
+     * @return the value as a point
+     * @exception DataFormatException if the given value does not represent
+     *  a point
+     */
+    public static Point asPoint(String value) {
+        if (value is null) {
+            throw new DataFormatException(
+                    "Null doesn't represent a valid point"); //$NON-NLS-1$
+        }
+        auto stok = tango.text.Util.delimit( value, "," );
+        String x = stok[0];
+        String y = stok[1];
+        int xval = 0, yval = 0;
+        try {
+            xval = tango.text.convert.Integer.toInt(x);
+            yval = tango.text.convert.Integer.toInt(y);
+        } catch (IllegalArgumentException e) {
+            throw new DataFormatException(e.msg);
+        }
+        return new Point(xval, yval);
+    }
+
+    /**
+     * Converts the given value into an SWT point.
+     * Returns the given default value if the
+     * value does not represent a point.
+     *
+     * @param value the value to be converted
+     * @param dflt the default value
+     * @return the value as a point, or the default value
+     */
+    public static Point asPoint(String value, Point dflt) {
+        try {
+            return asPoint(value);
+        } catch (DataFormatException e) {
+            return dflt;
+        }
+    }
+
+    /**
+     * Converts the given value into an SWT rectangle.
+     * This method fails if the value does not represent a rectangle.
+     * <p>
+     * A valid rectangle representation is a string of the form
+     * <code><it>x</it>,<it>y</it>,<it>width</it>,<it>height</it></code>
+     * where <code><it>x</it></code>, <code><it>y</it></code>,
+     * <code><it>width</it></code>, and <code><it>height</it></code>
+     * are valid ints.
+     * </p>
+     *
+     * @param value the value to be converted
+     * @return the value as a rectangle
+     * @exception DataFormatException if the given value does not represent
+     *  a rectangle
+     */
+    public static Rectangle asRectangle(String value) {
+        if (value is null) {
+            throw new DataFormatException(
+                    "Null doesn't represent a valid rectangle"); //$NON-NLS-1$
+        }
+        auto stok = tango.text.Util.delimit( value, "," );
+        String x = stok[0];
+        String y = stok[1];
+        String width = stok[2];
+        String height = stok[3];
+        int xval = 0, yval = 0, wval = 0, hval = 0;
+        try {
+            xval = tango.text.convert.Integer.toInt(x);
+            yval = tango.text.convert.Integer.toInt(y);
+            wval = tango.text.convert.Integer.toInt(width);
+            hval = tango.text.convert.Integer.toInt(height);
+        } catch (IllegalArgumentException e) {
+            throw new DataFormatException(e.msg);
+        }
+        return new Rectangle(xval, yval, wval, hval);
+    }
+
+    /**
+     * Converts the given value into an SWT rectangle.
+     * Returns the given default value if the
+     * value does not represent a rectangle.
+     *
+     * @param value the value to be converted
+     * @param dflt the default value
+     * @return the value as a rectangle, or the default value
+     */
+    public static Rectangle asRectangle(String value, Rectangle dflt) {
+        try {
+            return asRectangle(value);
+        } catch (DataFormatException e) {
+            return dflt;
+        }
+    }
+
+    /**
+     * Converts the given value into an SWT RGB color value.
+     * This method fails if the value does not represent an RGB
+     * color value.
+     * <p>
+     * A valid RGB color value representation is a string of the form
+     * <code><it>red</it>,<it>green</it></code>,<it>blue</it></code> where
+     * <code><it>red</it></code>, <it>green</it></code>, and
+     * <code><it>blue</it></code> are valid ints.
+     * </p>
+     *
+     * @param value the value to be converted
+     * @return the value as an RGB color value
+     * @exception DataFormatException if the given value does not represent
+     *  an RGB color value
+     */
+    public static RGB asRGB(String value) {
+        if (value is null) {
+            throw new DataFormatException("Null doesn't represent a valid RGB"); //$NON-NLS-1$
+        }
+        auto stok = tango.text.Util.delimit( value, "," );
+        if( stok.length < 3 ){
+            throw new DataFormatException( "not enough values" );
+        }
+        String red = stok[0].trim();
+        String green = stok[1].trim();
+        String blue = stok[2].trim();
+        int rval = 0, gval = 0, bval = 0;
+        try {
+            rval = tango.text.convert.Integer.toInt(red);
+            gval = tango.text.convert.Integer.toInt(green);
+            bval = tango.text.convert.Integer.toInt(blue);
+        } catch (IllegalArgumentException e) {
+            throw new DataFormatException(e.msg);
+        }
+        return new RGB(rval, gval, bval);
+    }
+
+    /**
+     * Converts the given value into an SWT RGB color value.
+     * Returns the given default value if the
+     * value does not represent an RGB color value.
+     *
+     * @param value the value to be converted
+     * @param dflt the default value
+     * @return the value as a RGB color value, or the default value
+     */
+    public static RGB asRGB(String value, RGB dflt) {
+        try {
+            return asRGB(value);
+        } catch (DataFormatException e) {
+            return dflt;
+        }
+    }
+
+    /**
+     * Converts the given double value to a string.
+     * Equivalent to <code>String.valueOf(value)</code>.
+     *
+     * @param value the double value
+     * @return the string representing the given double
+     */
+    public static String asString(double value) {
+        return tango.text.convert.Float.toString(value);
+    }
+
+    /**
+     * Converts the given float value to a string.
+     * Equivalent to <code>String.valueOf(value)</code>.
+     *
+     * @param value the float value
+     * @return the string representing the given float
+     */
+    public static String asString(float value) {
+        return tango.text.convert.Float.toString(value);
+    }
+
+    /**
+     * Converts the given int value to a string.
+     * Equivalent to <code>String.valueOf(value)</code>.
+     *
+     * @param value the int value
+     * @return the string representing the given int
+     */
+    public static String asString(int value) {
+        return tango.text.convert.Integer.toString(value);
+    }
+
+    /**
+     * Converts the given long value to a string.
+     * Equivalent to <code>String.valueOf(value)</code>.
+     *
+     * @param value the long value
+     * @return the string representing the given long
+     */
+    public static String asString(long value) {
+        return tango.text.convert.Integer.toString(value);
+    }
+
+//     /**
+//      * Converts the given bool object to a string.
+//      * Equivalent to <code>String.valueOf(value.booleanValue())</code>.
+//      *
+//      * @param value the bool object
+//      * @return the string representing the given bool value
+//      */
+//     public static String asString(Boolean value) {
+//         Assert.isNotNull(value);
+//         return String.valueOf(value.booleanValue());
+//     }
+//
+//     /**
+//      * Converts the given double object to a string.
+//      * Equivalent to <code>String.valueOf(value.doubleValue())</code>.
+//      *
+//      * @param value the double object
+//      * @return the string representing the given double value
+//      */
+//     public static String asString(Double value) {
+//         Assert.isNotNull(value);
+//         return String.valueOf(value.doubleValue());
+//     }
+//
+//     /**
+//      * Converts the given float object to a string.
+//      * Equivalent to <code>String.valueOf(value.floatValue())</code>.
+//      *
+//      * @param value the float object
+//      * @return the string representing the given float value
+//      */
+//     public static String asString(Float value) {
+//         Assert.isNotNull(value);
+//         return String.valueOf(value.floatValue());
+//     }
+//
+//     /**
+//      * Converts the given integer object to a string.
+//      * Equivalent to <code>String.valueOf(value.intValue())</code>.
+//      *
+//      * @param value the integer object
+//      * @return the string representing the given integer value
+//      */
+//     public static String asString(Integer value) {
+//         Assert.isNotNull(value);
+//         return String.valueOf(value.intValue());
+//     }
+//
+//     /**
+//      * Converts the given long object to a string.
+//      * Equivalent to <code>String.valueOf(value.longValue())</code>.
+//      *
+//      * @param value the long object
+//      * @return the string representing the given long value
+//      */
+//     public static String asString(Long value) {
+//         Assert.isNotNull(value);
+//         return String.valueOf(value.longValue());
+//     }
+
+    /**
+     * Converts a font data array  to a string. The string representation is
+     * that of asString(FontData) seperated by ';'
+     *
+     * @param value The font data.
+     * @return The string representation of the font data arra.
+     * @since 3.0
+     */
+    public static String asString(FontData[] value) {
+        StringBuffer buffer = new StringBuffer();
+        for (int i = 0; i < value.length; i++) {
+            buffer.append(asString(value[i]));
+            if (i !is value.length - 1) {
+                buffer.append(FONT_SEPARATOR);
+            }
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Converts a font data object to a string. The string representation is
+     * "font name-style-height" (for example "Times New Roman-bold-36").
+     * @param value The font data.
+     * @return The string representation of the font data object.
+     */
+    public static String asString(FontData value) {
+        Assert.isNotNull(value);
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(value.getName());
+        buffer.append(SEPARATOR);
+        int style = value.getStyle();
+        bool bold = (style & SWT.BOLD) is SWT.BOLD;
+        bool italic = (style & SWT.ITALIC) is SWT.ITALIC;
+        if (bold && italic) {
+            buffer.append(BOLD_ITALIC);
+        } else if (bold) {
+            buffer.append(BOLD);
+        } else if (italic) {
+            buffer.append(ITALIC);
+        } else {
+            buffer.append(REGULAR);
+        }
+
+        buffer.append(SEPARATOR);
+        buffer.append(value.getHeight());
+        return buffer.toString();
+    }
+
+    /**
+     * Converts the given SWT point object to a string.
+     * <p>
+     * The string representation of a point has the form
+     * <code><it>x</it>,<it>y</it></code> where
+     * <code><it>x</it></code> and <code><it>y</it></code>
+     * are string representations of integers.
+     * </p>
+     *
+     * @param value the point object
+     * @return the string representing the given point
+     */
+    public static String asString(Point value) {
+        Assert.isNotNull(value);
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(value.x);
+        buffer.append(',');
+        buffer.append(value.y);
+        return buffer.toString();
+    }
+
+    /**
+     * Converts the given SWT rectangle object to a string.
+     * <p>
+     * The string representation of a rectangle has the form
+     * <code><it>x</it>,<it>y</it>,<it>width</it>,<it>height</it></code>
+     * where <code><it>x</it></code>, <code><it>y</it></code>,
+     * <code><it>width</it></code>, and <code><it>height</it></code>
+     * are string representations of integers.
+     * </p>
+     *
+     * @param value the rectangle object
+     * @return the string representing the given rectangle
+     */
+    public static String asString(Rectangle value) {
+        Assert.isNotNull(value);
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(value.x);
+        buffer.append(',');
+        buffer.append(value.y);
+        buffer.append(',');
+        buffer.append(value.width);
+        buffer.append(',');
+        buffer.append(value.height);
+        return buffer.toString();
+    }
+
+    /**
+     * Converts the given SWT RGB color value object to a string.
+     * <p>
+     * The string representation of an RGB color value has the form
+     * <code><it>red</it>,<it>green</it></code>,<it>blue</it></code> where
+     * <code><it>red</it></code>, <it>green</it></code>, and
+     * <code><it>blue</it></code> are string representations of integers.
+     * </p>
+     *
+     * @param value the RGB color value object
+     * @return the string representing the given RGB color value
+     */
+    public static String asString(RGB value) {
+        Assert.isNotNull(value);
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(value.red);
+        buffer.append(',');
+        buffer.append(value.green);
+        buffer.append(',');
+        buffer.append(value.blue);
+        return buffer.toString();
+    }
+
+    /**
+     * Converts the given bool value to a string.
+     * Equivalent to <code>String.valueOf(value)</code>.
+     *
+     * @param value the bool value
+     * @return the string representing the given bool
+     */
+    public static String asString(bool value) {
+        return value ? "true" : "false";
+    }
+
+    /**
+     * Returns the given string with all whitespace characters removed.
+     * <p>
+     * All characters that have codes less than or equal to <code>'&#92;u0020'</code>
+     * (the space character) are considered to be a white space.
+     * </p>
+     *
+     * @param s the source string
+     * @return the string with all whitespace characters removed
+     */
+    public static String removeWhiteSpaces(String s) {
+        //check for no whitespace (common case)
+        bool found = false;
+        int wsIndex = -1;
+        int size = s.length;
+        for (int i = 0; i < size; i++) {
+            found = CharacterIsWhitespace( s.charAt(i) );
+            if (found) {
+                wsIndex = i;
+                break;
+            }
+        }
+        if (!found) {
+            return s;
+        }
+
+        StringBuffer result = new StringBuffer(s.substring(0, wsIndex));
+        for (int i = wsIndex + 1; i < size; i++) {
+            char ch = s.charAt(i);
+            if (!CharacterIsWhitespace(ch)) {
+                result.append(ch);
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * Converts a font data object to a string representation for display.
+     *  The string representation is
+     * "font name-style-height" (for example "Times New Roman-bold-36").
+     * @param value The font data.
+     * @return The string representation of the font data object.
+     * @deprecated use asString(FontData)
+     */
+    public static String asDisplayableString(FontData value) {
+        Assert.isNotNull(value);
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(value.getName());
+        buffer.append(SEPARATOR);
+        int style = value.getStyle();
+        bool bold = (style & SWT.BOLD) is SWT.BOLD;
+        bool italic = (style & SWT.ITALIC) is SWT.ITALIC;
+        if (bold && italic) {
+            buffer.append(JFaceResources.getString("BoldItalicFont")); //$NON-NLS-1$
+        } else if (bold) {
+            buffer.append(JFaceResources.getString("BoldFont")); //$NON-NLS-1$
+        } else if (italic) {
+            buffer.append(JFaceResources.getString("ItalicFont")); //$NON-NLS-1$
+        } else {
+            buffer.append(JFaceResources.getString("RegularFont")); //$NON-NLS-1$
+        }
+        buffer.append(SEPARATOR);
+        buffer.append(value.getHeight());
+        return buffer.toString();
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/resource/URLImageDescriptor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.resource.URLImageDescriptor;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+
+// import java.io.BufferedInputStream;
+// import java.io.IOException;
+// import java.io.InputStream;
+import tango.net.Uri;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.util.Policy;
+
+import java.lang.all;
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+
+/**
+ * An ImageDescriptor that gets its information from a URL. This class is not
+ * public API. Use ImageDescriptor#createFromURL to create a descriptor that
+ * uses a URL.
+ */
+class URLImageDescriptor : ImageDescriptor {
+    /**
+     * Constant for the file protocol for optimized loading
+     */
+    private static final String FILE_PROTOCOL = "file";  //$NON-NLS-1$
+    private Uri url;
+
+    /**
+     * Creates a new URLImageDescriptor.
+     *
+     * @param url
+     *            The URL to load the image from. Must be non-null.
+     */
+    this(Uri url) {
+        this.url = url;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Object.
+     */
+    public override int opEquals(Object o) {
+        if (!(cast(URLImageDescriptor)o )) {
+            return false;
+        }
+        return (cast(URLImageDescriptor) o).url.opEquals(this.url) !is 0;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on ImageDesciptor. Returns null if the
+     * image data cannot be read.
+     */
+    public override ImageData getImageData() {
+        ImageData result = null;
+        InputStream in_ = getStream();
+        if (in_ !is null) {
+            scope(exit)
+                in_.close();
+            try {
+                result = new ImageData(in_);
+            } catch (SWTException e) {
+                if (e.code !is SWT.ERROR_INVALID_IMAGE) {
+                    throw e;
+                    // fall through otherwise
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns a stream on the image contents. Returns null if a stream could
+     * not be opened.
+     *
+     * @return the stream for loading the data
+     */
+    protected InputStream getStream() {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+        //FIXME
+        /+
+        try {
+            return new BufferedInputStream(url.openStream());
+        } catch (IOException e) {
+            return null;
+        }
+        +/
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Object.
+     */
+    public override hash_t toHash() {
+        return url.toHash();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Object.
+     */
+    /**
+     * The <code>URLImageDescriptor</code> implementation of this
+     * <code>Object</code> method returns a string representation of this
+     * object which is suitable only for debugging.
+     */
+    public override String toString() {
+        return "URLImageDescriptor(" ~ url.toString ~ ")"; //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    /**
+     * Returns the filename for the ImageData.
+     *
+     * @return {@link String} or <code>null</code> if the file cannot be found
+     */
+    private String getFilePath() {
+//         try {
+//             if (JFaceActivator.getBundleContext() is null) {
+//                 if (FILE_PROTOCOL.equalsIgnoreCase(url.getProtocol()))
+//                     return new Path(url.getFile()).toOSString();
+//                 return null;
+//             }
+//
+//             URL locatedURL = FileLocator.toFileURL(url);
+//             if (FILE_PROTOCOL.equalsIgnoreCase(locatedURL.getProtocol()))
+//                 return new Path(locatedURL.getPath()).toOSString();
+//             return null;
+//
+//         } catch (IOException e) {
+//             Policy.logException(e);
+//             return null;
+//         }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.resource.ImageDescriptor#createImage(bool,
+     *      org.eclipse.swt.graphics.Device)
+     */
+    public override Image createImage(bool returnMissingImageOnError, Device device) {
+
+        // Try to see if we can optimize using SWTs file based image support.
+        String path = getFilePath();
+        if (path is null)
+            return super.createImage(returnMissingImageOnError, device);
+
+        try {
+            return new Image(device, path);
+        } catch (SWTException exception) {
+            // If we fail fall back to the slower input stream method.
+        }
+        return super.createImage(returnMissingImageOnError, device);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/Assert.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.util.Assert;
+
+import java.lang.all;
+
+/**
+ * <code>Assert</code> is useful for for embedding runtime sanity checks
+ * in code. The static predicate methods all test a condition and throw some
+ * type of unchecked exception if the condition does not hold.
+ * <p>
+ * Assertion failure exceptions, like most runtime exceptions, are
+ * thrown when something is misbehaving. Assertion failures are invariably
+ * unspecified behavior; consequently, clients should never rely on
+ * these being thrown (or not thrown). <b>If you find yourself in the
+ * position where you need to catch an assertion failure, you have most
+ * certainly written your program incorrectly.</b>
+ * </p>
+ * <p>
+ * Note that an <code>assert</code> statement is slated to be added to the
+ * Java language in JDK 1.4, rending this class obsolete.
+ *
+ * @deprecated As of 3.3, replaced by {@link org.eclipse.core.runtime.Assert}
+ * </p>
+ */
+public final class Assert {
+
+    /**
+     * <code>AssertionFailedException</code> is a runtime exception thrown
+     * by some of the methods in <code>Assert</code>.
+     * <p>
+     * This class is not declared public to prevent some misuses; programs that catch
+     * or otherwise depend on assertion failures are susceptible to unexpected
+     * breakage when assertions in the code are added or removed.
+     * </p>
+     */
+    private static class AssertionFailedException : RuntimeException {
+
+        /**
+         * Generated serial version UID for this class.
+         * @since 3.1
+         */
+        private static final long serialVersionUID = 3257852073508024376L;
+
+        /**
+         * Constructs a new exception.
+         */
+        public this() {
+        }
+
+        /**
+         * Constructs a new exception with the given message.
+         * @param detail the detail message
+         */
+        public this(String detail) {
+            super(detail);
+        }
+    }
+
+    /* This class is not intended to be instantiated. */
+    private this() {
+    }
+
+    /**
+     * Asserts that an argument is legal. If the given bool is
+     * not <code>true</code>, an <code>IllegalArgumentException</code>
+     * is thrown.
+     *
+     * @param expression the outcome of the check
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     * @exception IllegalArgumentException if the legality test failed
+     */
+    public static bool isLegal(bool expression) {
+        // succeed as quickly as possible
+        if (expression) {
+            return true;
+        }
+        return isLegal(expression, "");//$NON-NLS-1$
+    }
+
+    /**
+     * Asserts that an argument is legal. If the given bool is
+     * not <code>true</code>, an <code>IllegalArgumentException</code>
+     * is thrown.
+     * The given message is included in that exception, to aid debugging.
+     *
+     * @param expression the outcome of the check
+     * @param message the message to include in the exception
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     * @exception IllegalArgumentException if the legality test failed
+     */
+    public static bool isLegal(bool expression, String message) {
+        if (!expression) {
+            throw new IllegalArgumentException("assertion failed; " ~ message); //$NON-NLS-1$
+        }
+        return expression;
+    }
+
+    /**
+     * Asserts that the given object is not <code>null</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     * <p>
+     * As a general rule, parameters passed to API methods must not be
+     * <code>null</code> unless <b>explicitly</b> allowed in the method's
+     * specification. Similarly, results returned from API methods are never
+     * <code>null</code> unless <b>explicitly</b> allowed in the method's
+     * specification. Implementations are encouraged to make regular use of
+     * <code>Assert.isNotNull</code> to ensure that <code>null</code>
+     * parameters are detected as early as possible.
+     * </p>
+     *
+     * @param object the value to test
+     * @exception AssertionFailedException an unspecified unchecked exception if the object
+     *   is <code>null</code>
+     */
+    public static void isNotNull(Object object) {
+        // succeed as quickly as possible
+        if (object !is null) {
+            return;
+        }
+        isNotNull(object, "");//$NON-NLS-1$
+    }
+
+    /**
+     * Asserts that the given object is not <code>null</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     * The given message is included in that exception, to aid debugging.
+     * <p>
+     * As a general rule, parameters passed to API methods must not be
+     * <code>null</code> unless <b>explicitly</b> allowed in the method's
+     * specification. Similarly, results returned from API methods are never
+     * <code>null</code> unless <b>explicitly</b> allowed in the method's
+     * specification. Implementations are encouraged to make regular use of
+     * <code>Assert.isNotNull</code> to ensure that <code>null</code>
+     * parameters are detected as early as possible.
+     * </p>
+     *
+     * @param object the value to test
+     * @param message the message to include in the exception
+     * @exception AssertionFailedException an unspecified unchecked exception if the object
+     *   is <code>null</code>
+     */
+    public static void isNotNull(Object object, String message) {
+        if (object is null) {
+            throw new AssertionFailedException("null argument;" ~ message);//$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Asserts that the given bool is <code>true</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     *
+     * @param expression the outcome of the check
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     */
+    public static bool isTrue(bool expression) {
+        // succeed as quickly as possible
+        if (expression) {
+            return true;
+        }
+        return isTrue(expression, "");//$NON-NLS-1$
+    }
+
+    /**
+     * Asserts that the given bool is <code>true</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     * The given message is included in that exception, to aid debugging.
+     *
+     * @param expression the outcome of the check
+     * @param message the message to include in the exception
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     */
+    public static bool isTrue(bool expression, String message) {
+        if (!expression) {
+            throw new AssertionFailedException("Assertion failed: " ~ message);//$NON-NLS-1$
+        }
+        return expression;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/DelegatingDragAdapter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,295 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.util.DelegatingDragAdapter;
+
+import org.eclipse.jface.util.TransferDragSourceListener;
+import org.eclipse.jface.util.SafeRunnable;
+
+
+import org.eclipse.swt.dnd.DragSource;
+import org.eclipse.swt.dnd.DragSourceEvent;
+import org.eclipse.swt.dnd.DragSourceListener;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.dnd.TransferData;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A <code>DelegatingDragAdapter</code> is a <code>DragSourceListener</code> that
+ * maintains and delegates to a set of {@link TransferDragSourceListener}s. Each
+ * TransferDragSourceListener can then be implemented as if it were the
+ * <code>DragSource's</code> only DragSourceListener.
+ * <p>
+ * When a drag is started, a subset of all <code>TransferDragSourceListeners</code>
+ * is generated and stored in a list of <i>active</i> listeners. This subset is
+ * calculated by forwarding {@link DragSourceListener#dragStart(DragSourceEvent)} to
+ * every listener, and checking if the {@link DragSourceEvent#doit doit} field is left
+ * set to <code>true</code>.
+ * </p>
+ * The <code>DragSource</code>'s set of supported Transfer types ({@link
+ * DragSource#setTransfer(Transfer[])}) is updated to reflect the Transfer types
+ * corresponding to the active listener subset.
+ * <p>
+ * If and when {@link #dragSetData(DragSourceEvent)} is called, a single
+ * <code>TransferDragSourceListener</code> is chosen, and only it is allowed to set the
+ * drag data. The chosen listener is the first listener in the subset of active listeners
+ * whose Transfer supports ({@link Transfer#isSupportedType(TransferData)}) the
+ * <code>dataType</code> in the <code>DragSourceEvent</code>.
+ * </p>
+ * <p>
+ * The following example snippet shows a <code>DelegatingDragAdapter</code> with two
+ * <code>TransferDragSourceListeners</code>. One implements drag of text strings,
+ * the other supports file transfer and demonstrates how a listener can be disabled using
+ * the dragStart method.
+ * </p>
+ * <code><pre>
+ *      final TreeViewer viewer = new TreeViewer(shell, SWT.NONE);
+ *
+ *      DelegatingDragAdapter dragAdapter = new DelegatingDragAdapter();
+ *      dragAdapter.addDragSourceListener(new TransferDragSourceListener() {
+ *          public Transfer getTransfer() {
+ *              return TextTransfer.getInstance();
+ *          }
+ *          public void dragStart(DragSourceEvent event) {
+ *              // always enabled, can control enablement based on selection etc.
+ *          }
+ *          public void dragSetData(DragSourceEvent event) {
+ *              event.data = "Transfer data";
+ *          }
+ *          public void dragFinished(DragSourceEvent event) {
+ *              // no clean-up required
+ *          }
+ *      });
+ *      dragAdapter.addDragSourceListener(new TransferDragSourceListener() {
+ *          public Transfer getTransfer() {
+ *              return FileTransfer.getInstance();
+ *          }
+ *          public void dragStart(DragSourceEvent event) {
+ *              // enable drag listener if there is a viewer selection
+ *              event.doit = !viewer.getSelection().isEmpty();
+ *          }
+ *          public void dragSetData(DragSourceEvent event) {
+ *              File file1 = new File("C:/temp/file1");
+ *              File file2 = new File("C:/temp/file2");
+ *              event.data = new String[] {file1.getAbsolutePath(), file2.getAbsolutePath()};
+ *          }
+ *          public void dragFinished(DragSourceEvent event) {
+ *              // no clean-up required
+ *          }
+ *      });
+ *      viewer.addDragSupport(DND.DROP_COPY | DND.DROP_MOVE, dragAdapter.getTransfers(), dragAdapter);
+ * </pre></code>
+ * @since 3.0
+ */
+public class DelegatingDragAdapter : DragSourceListener {
+    private List listeners;
+
+    private List activeListeners;
+
+    private TransferDragSourceListener currentListener;
+
+    this(){
+        listeners = new ArrayList();
+        activeListeners = new ArrayList();
+    }
+
+    /**
+     * Adds the given <code>TransferDragSourceListener</code>.
+     *
+     * @param listener the new listener
+     */
+    public void addDragSourceListener(TransferDragSourceListener listener) {
+        listeners.add(cast(Object)listener);
+    }
+
+    /**
+     * The drop has successfully completed. This event is forwarded to the current
+     * drag listener.
+     * Doesn't update the current listener, since the current listener  is already the one
+     * that completed the drag operation.
+     *
+     * @param event the drag source event
+     * @see DragSourceListener#dragFinished(DragSourceEvent)
+     */
+    public void dragFinished(DragSourceEvent event) {
+        //      if (Policy.DEBUG_DRAG_DROP)
+        //          System.out.println("Drag Finished: " + toString()); //$NON-NLS-1$
+        SafeRunnable.run(new class(event) SafeRunnable {
+            DragSourceEvent event_;
+            this(DragSourceEvent a){
+                event_=a;
+            }
+            public void run() {
+                if (currentListener !is null) {
+                    // there is a listener that can handle the drop, delegate the event
+                    currentListener.dragFinished(event_);
+                } else {
+                    // The drag was canceled and currentListener was never set, so send the
+                    // dragFinished event to all the active listeners.
+                    Iterator iterator = activeListeners.iterator();
+                    while (iterator.hasNext()) {
+                        (cast(TransferDragSourceListener) iterator.next())
+                                .dragFinished(event);
+                    }
+                }
+            }
+        });
+        currentListener = null;
+        activeListeners.clear();
+    }
+
+    /**
+     * The drop data is requested.
+     * Updates the current listener and then forwards the event to it.
+     *
+     * @param event the drag source event
+     * @see DragSourceListener#dragSetData(DragSourceEvent)
+     */
+    public void dragSetData(DragSourceEvent event) {
+        //      if (Policy.DEBUG_DRAG_DROP)
+        //          System.out.println("Drag Set Data: " + toString()); //$NON-NLS-1$
+
+        updateCurrentListener(event); // find a listener that can provide the given data type
+        if (currentListener !is null) {
+            SafeRunnable.run(new class(event) SafeRunnable {
+                DragSourceEvent event_;
+                this(DragSourceEvent a){
+                    event_=a;
+                }
+                public void run() {
+                    currentListener.dragSetData(event_);
+                }
+            });
+        }
+    }
+
+    /**
+     * A drag operation has started.
+     * Forwards this event to each listener. A listener must set <code>event.doit</code>
+     * to <code>false</code> if it cannot handle the drag operation. If a listener can
+     * handle the drag, it is added to the list of active listeners.
+     * The drag is aborted if there are no listeners that can handle it.
+     *
+     * @param event the drag source event
+     * @see DragSourceListener#dragStart(DragSourceEvent)
+     */
+    public void dragStart(DragSourceEvent event) {
+        //      if (Policy.DEBUG_DRAG_DROP)
+        //          System.out.println("Drag Start: " + toString()); //$NON-NLS-1$
+        bool doit = false; // true if any one of the listeners can handle the drag
+        List transfers = new ArrayList(listeners.size());
+
+        activeListeners.clear();
+        for (int i = 0; i < listeners.size(); i++) {
+            TransferDragSourceListener listener = cast(TransferDragSourceListener) listeners
+                    .get(i);
+            event.doit = true; // restore event.doit
+            SafeRunnable.run(new class(event,listener) SafeRunnable {
+                TransferDragSourceListener listener_;
+                DragSourceEvent event_;
+                this(DragSourceEvent a,TransferDragSourceListener b){
+                    event_=a;
+                    listener_=b;
+                }
+                public void run() {
+                    listener_.dragStart(event_);
+                }
+            });
+            if (event.doit) { // the listener can handle this drag
+                transfers.add(listener.getTransfer());
+                activeListeners.add(cast(Object)listener);
+            }
+            doit |= event.doit;
+        }
+
+        if (doit) {
+            (cast(DragSource) event.widget).setTransfer(arraycast!(Transfer)( transfers
+                    .toArray()));
+        }
+
+        event.doit = doit;
+    }
+
+    /**
+     * Returns the <code>Transfer<code>s from every <code>TransferDragSourceListener</code>.
+     *
+     * @return the combined <code>Transfer</code>s
+     */
+    public Transfer[] getTransfers() {
+        Transfer[] types = new Transfer[listeners.size()];
+        for (int i = 0; i < listeners.size(); i++) {
+            TransferDragSourceListener listener = cast(TransferDragSourceListener) listeners
+                    .get(i);
+            types[i] = listener.getTransfer();
+        }
+        return types;
+    }
+
+    /**
+     * Returns <code>true</code> if there are no listeners to delegate drag events to.
+     *
+     * @return <code>true</code> if there are no <code>TransferDragSourceListeners</code>
+     *  <code>false</code> otherwise.
+     */
+    public bool isEmpty() {
+        return listeners.isEmpty();
+    }
+
+    /**
+     * Removes the given <code>TransferDragSourceListener</code>.
+     * Listeners should not be removed while a drag and drop operation is in progress.
+     *
+     * @param listener the <code>TransferDragSourceListener</code> to remove
+     */
+    public void removeDragSourceListener(TransferDragSourceListener listener) {
+        listeners.remove(cast(Object)listener);
+        if (currentListener is listener) {
+            currentListener = null;
+        }
+        if (activeListeners.contains(cast(Object)listener)) {
+            activeListeners.remove(cast(Object)listener);
+        }
+    }
+
+    /**
+     * Updates the current listener to one that can handle the drag. There can
+     * be many listeners and each listener may be able to handle many <code>TransferData</code>
+     * types.  The first listener found that supports one of the <code>TransferData</ode>
+     * types specified in the <code>DragSourceEvent</code> will be selected.
+     *
+     * @param event the drag source event
+     */
+    private void updateCurrentListener(DragSourceEvent event) {
+        currentListener = null;
+        if (event.dataType is null) {
+            return;
+        }
+        Iterator iterator = activeListeners.iterator();
+        while (iterator.hasNext()) {
+            TransferDragSourceListener listener = cast(TransferDragSourceListener) iterator
+                    .next();
+
+            if (listener.getTransfer().isSupportedType(event.dataType)) {
+                //              if (Policy.DEBUG_DRAG_DROP)
+                //                  System.out.println("Current drag listener: " + listener); //$NON-NLS-1$
+                currentListener = listener;
+                return;
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/DelegatingDropAdapter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,416 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.util.DelegatingDropAdapter;
+
+import org.eclipse.jface.util.TransferDropTargetListener;
+import org.eclipse.jface.util.SafeRunnable;
+
+
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.DropTargetListener;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.dnd.TransferData;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A <code>DelegatingDropAdapter</code> is a <code>DropTargetListener</code> that
+ * maintains and delegates to a set of {@link TransferDropTargetListener}s. Each
+ * <code>TransferDropTargetListener</code> can then be implemented as if it were
+ * the DropTarget's only <code>DropTargetListener</code>.
+ * <p>
+ * On <code>dragEnter</code>, <code>dragOperationChanged</code>, <code>dragOver</code>
+ * and <code>drop</code>, a <i>current</i> listener is obtained from the set of all
+ * <code>TransferDropTargetListeners</code>. The current listener is the first listener
+ * to return <code>true</code> for
+ * {@link TransferDropTargetListener#isEnabled(DropTargetEvent)}.
+ * The current listener is forwarded all <code>DropTargetEvents</code> until some other
+ * listener becomes the current listener, or the drop terminates.
+ * </p>
+ * <p>
+ * After adding all <code>TransferDropTargetListeners</code> to the
+ * <code>DelegatingDropAdapter</code> the combined set of <code>Transfers</code> should
+ * be set in the SWT <code>DropTarget</code>. <code>#getTransfers()</code> provides the
+ * set of <code>Transfer</code> types of all <code>TransferDropTargetListeners</code>.
+ * </p>
+ * <p>
+ * The following example snippet shows a <code>DelegatingDropAdapter</code> with two
+ * <code>TransferDropTargetListeners</code>. One supports dropping resources and
+ * demonstrates how a listener can be disabled in the isEnabled method.
+ * The other listener supports text transfer.
+ * </p>
+ * <code><pre>
+ *      final TreeViewer viewer = new TreeViewer(shell, SWT.NONE);
+ *      DelegatingDropAdapter dropAdapter = new DelegatingDropAdapter();
+ *      dropAdapter.addDropTargetListener(new TransferDropTargetListener() {
+ *          public Transfer getTransfer() {
+ *              return ResourceTransfer.getInstance();
+ *          }
+ *          public bool isEnabled(DropTargetEvent event) {
+ *              // disable drop listener if there is no viewer selection
+ *              if (viewer.getSelection().isEmpty())
+ *                  return false;
+ *              return true;
+ *          }
+ *          public void dragEnter(DropTargetEvent event) {}
+ *          public void dragLeave(DropTargetEvent event) {}
+ *          public void dragOperationChanged(DropTargetEvent event) {}
+ *          public void dragOver(DropTargetEvent event) {}
+ *          public void drop(DropTargetEvent event) {
+ *              if (event.data is null)
+ *                  return;
+ *              IResource[] resources = (IResource[]) event.data;
+ *              if (event.detail is DND.DROP_COPY) {
+ *                  // copy resources
+ *              } else {
+ *                  // move resources
+ *              }
+ *
+ *          }
+ *          public void dropAccept(DropTargetEvent event) {}
+ *      });
+ *      dropAdapter.addDropTargetListener(new TransferDropTargetListener() {
+ *          public Transfer getTransfer() {
+ *              return TextTransfer.getInstance();
+ *          }
+ *          public bool isEnabled(DropTargetEvent event) {
+ *              return true;
+ *          }
+ *          public void dragEnter(DropTargetEvent event) {}
+ *          public void dragLeave(DropTargetEvent event) {}
+ *          public void dragOperationChanged(DropTargetEvent event) {}
+ *          public void dragOver(DropTargetEvent event) {}
+ *          public void drop(DropTargetEvent event) {
+ *              if (event.data is null)
+ *                  return;
+ *              System.out.println(event.data);
+ *          }
+ *          public void dropAccept(DropTargetEvent event) {}
+ *      });
+ *      viewer.addDropSupport(DND.DROP_COPY | DND.DROP_MOVE, dropAdapter.getTransfers(), dropAdapter);
+ * </pre></code>
+ * @since 3.0
+ */
+public class DelegatingDropAdapter : DropTargetListener {
+    private List listeners;
+
+    private TransferDropTargetListener currentListener;
+
+    private int originalDropType;
+
+    this(){
+        listeners = new ArrayList();
+    }
+
+    /**
+     * Adds the given <code>TransferDropTargetListener</code>.
+     *
+     * @param listener the new listener
+     */
+    public void addDropTargetListener(TransferDropTargetListener listener) {
+        listeners.add(cast(Object)listener);
+    }
+
+    /**
+     * The cursor has entered the drop target boundaries. The current listener is
+     * updated, and <code>#dragEnter()</code> is forwarded to the current listener.
+     *
+     * @param event the drop target event
+     * @see DropTargetListener#dragEnter(DropTargetEvent)
+     */
+    public void dragEnter(DropTargetEvent event) {
+        //      if (Policy.DEBUG_DRAG_DROP)
+        //          System.out.println("Drag Enter: " + toString()); //$NON-NLS-1$
+        originalDropType = event.detail;
+        updateCurrentListener(event);
+    }
+
+    /**
+     * The cursor has left the drop target boundaries. The event is forwarded to the
+     * current listener.
+     *
+     * @param event the drop target event
+     * @see DropTargetListener#dragLeave(DropTargetEvent)
+     */
+    public void dragLeave(DropTargetEvent event) {
+        //      if (Policy.DEBUG_DRAG_DROP)
+        //          System.out.println("Drag Leave: " + toString()); //$NON-NLS-1$
+        setCurrentListener(null, event);
+    }
+
+    /**
+     * The operation being performed has changed (usually due to the user changing
+     * a drag modifier key while dragging). Updates the current listener and forwards
+     * this event to that listener.
+     *
+     * @param event the drop target event
+     * @see DropTargetListener#dragOperationChanged(DropTargetEvent)
+     */
+    public void dragOperationChanged(DropTargetEvent event) {
+        //      if (Policy.DEBUG_DRAG_DROP)
+        //          System.out.println("Drag Operation Changed to: " + event.detail); //$NON-NLS-1$
+        originalDropType = event.detail;
+        TransferDropTargetListener oldListener = getCurrentListener();
+        updateCurrentListener(event);
+        TransferDropTargetListener newListener = getCurrentListener();
+        // only notify the current listener if it hasn't changed based on the
+        // operation change. otherwise the new listener would get a dragEnter
+        // followed by a dragOperationChanged with the exact same event.
+        if (newListener !is null && newListener is oldListener) {
+            SafeRunnable.run(new class(event,newListener) SafeRunnable {
+                DropTargetEvent event_;
+                TransferDropTargetListener newListener_;
+                this(DropTargetEvent a,TransferDropTargetListener b){
+                    event_=a;
+                    newListener_=b;
+                }
+                public void run() {
+                    newListener_.dragOperationChanged(event_);
+                }
+            });
+        }
+    }
+
+    /**
+     * The cursor is moving over the drop target. Updates the current listener and
+     * forwards this event to that listener. If no listener can handle the drag
+     * operation the <code>event.detail</code> field is set to <code>DND.DROP_NONE</code>
+     * to indicate an invalid drop.
+     *
+     * @param event the drop target event
+     * @see DropTargetListener#dragOver(DropTargetEvent)
+     */
+    public void dragOver(DropTargetEvent event) {
+        TransferDropTargetListener oldListener = getCurrentListener();
+        updateCurrentListener(event);
+        TransferDropTargetListener newListener = getCurrentListener();
+
+        // only notify the current listener if it hasn't changed based on the
+        // drag over. otherwise the new listener would get a dragEnter
+        // followed by a dragOver with the exact same event.
+        if (newListener !is null && newListener is oldListener) {
+            SafeRunnable.run(new class(event,newListener) SafeRunnable {
+                DropTargetEvent event_;
+                TransferDropTargetListener newListener_;
+                this(DropTargetEvent a,TransferDropTargetListener b){
+                    event_=a;
+                    newListener_=b;
+                }
+                public void run() {
+                    newListener_.dragOver(event_);
+                }
+            });
+        }
+    }
+
+    /**
+     * Forwards this event to the current listener, if there is one. Sets the
+     * current listener to <code>null</code> afterwards.
+     *
+     * @param event the drop target event
+     * @see DropTargetListener#drop(DropTargetEvent)
+     */
+    public void drop(DropTargetEvent event) {
+        //      if (Policy.DEBUG_DRAG_DROP)
+        //          System.out.println("Drop: " + toString()); //$NON-NLS-1$
+        updateCurrentListener(event);
+        if (getCurrentListener() !is null) {
+            SafeRunnable.run(new class(event) SafeRunnable {
+                DropTargetEvent event_;
+                this(DropTargetEvent a){ event_=a;}
+                public void run() {
+                    getCurrentListener().drop(event_);
+                }
+            });
+        }
+        setCurrentListener(null, event);
+    }
+
+    /**
+     * Forwards this event to the current listener if there is one.
+     *
+     * @param event the drop target event
+     * @see DropTargetListener#dropAccept(DropTargetEvent)
+     */
+    public void dropAccept(DropTargetEvent event) {
+        //      if (Policy.DEBUG_DRAG_DROP)
+        //          System.out.println("Drop Accept: " + toString()); //$NON-NLS-1$
+        if (getCurrentListener() !is null) {
+            SafeRunnable.run(new class(event) SafeRunnable {
+                DropTargetEvent event_;
+                this(DropTargetEvent a){ event_=a;}
+                public void run() {
+                    getCurrentListener().dropAccept(event_);
+                }
+            });
+        }
+    }
+
+    /**
+     * Returns the listener which currently handles drop events.
+     *
+     * @return the <code>TransferDropTargetListener</code> which currently
+     *  handles drop events.
+     */
+    private TransferDropTargetListener getCurrentListener() {
+        return currentListener;
+    }
+
+    /**
+     * Returns the transfer data type supported by the given listener.
+     * Returns <code>null</code> if the listener does not support any of the
+     * specified data types.
+     *
+     * @param dataTypes available data types
+     * @param listener <code>TransferDropTargetListener</code> to use for testing
+     *  supported data types.
+     * @return the transfer data type supported by the given listener or
+     *  <code>null</code>.
+     */
+    private TransferData getSupportedTransferType(TransferData[] dataTypes,
+            TransferDropTargetListener listener) {
+        for (int i = 0; i < dataTypes.length; i++) {
+            if (listener.getTransfer().isSupportedType(dataTypes[i])) {
+                return dataTypes[i];
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the combined set of <code>Transfer</code> types of all
+     * <code>TransferDropTargetListeners</code>.
+     *
+     * @return the combined set of <code>Transfer</code> types
+     */
+    public Transfer[] getTransfers() {
+        Transfer[] types = new Transfer[listeners.size()];
+        for (int i = 0; i < listeners.size(); i++) {
+            TransferDropTargetListener listener = cast(TransferDropTargetListener) listeners
+                    .get(i);
+            types[i] = listener.getTransfer();
+        }
+        return types;
+    }
+
+    /**
+     * Returns <code>true</code> if there are no listeners to delegate events to.
+     *
+     * @return <code>true</code> if there are no <code>TransferDropTargetListeners</code>
+     *  <code>false</code> otherwise
+     */
+    public bool isEmpty() {
+        return listeners.isEmpty();
+    }
+
+    /**
+     * Removes the given <code>TransferDropTargetListener</code>.
+     * Listeners should not be removed while a drag and drop operation is in progress.
+     *
+     * @param listener the listener to remove
+     */
+    public void removeDropTargetListener(TransferDropTargetListener listener) {
+        if (currentListener is listener) {
+            currentListener = null;
+        }
+        listeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Sets the current listener to <code>listener</code>. Sends the given
+     * <code>DropTargetEvent</code> if the current listener changes.
+     *
+     * @return <code>true</code> if the new listener is different than the previous
+     *  <code>false</code> otherwise
+     */
+    private bool setCurrentListener(TransferDropTargetListener listener,
+            DropTargetEvent event) {
+        if (currentListener is listener) {
+            return false;
+        }
+        if (currentListener !is null) {
+            SafeRunnable.run(new class(event) SafeRunnable {
+                DropTargetEvent event_;
+                this(DropTargetEvent a){ event_=a;}
+                public void run() {
+                    currentListener.dragLeave(event_);
+                }
+            });
+        }
+        currentListener = listener;
+        //      if (Policy.DEBUG_DRAG_DROP)
+        //          System.out.println("Current drop listener: " + listener); //$NON-NLS-1$
+        if (currentListener !is null) {
+            SafeRunnable.run(new class(event) SafeRunnable {
+                DropTargetEvent event_;
+                this(DropTargetEvent a){ event_=a;}
+                public void run() {
+                    currentListener.dragEnter(event_);
+                }
+            });
+        }
+        return true;
+    }
+
+    /**
+     * Updates the current listener to one that can handle the drop. There can be many
+     * listeners and each listener may be able to handle many <code>TransferData</code>
+     * types. The first listener found that can handle a drop of one of the given
+     * <code>TransferData</code> types will be selected.
+     * If no listener can handle the drag operation the <code>event.detail</code> field
+     * is set to <code>DND.DROP_NONE</code> to indicate an invalid drop.
+     *
+     * @param event the drop target event
+     */
+    private void updateCurrentListener(DropTargetEvent event) {
+        int originalDetail = event.detail;
+        // revert the detail to the "original" drop type that the User indicated.
+        // this is necessary because the previous listener may have changed the detail
+        // to something other than what the user indicated.
+        event.detail = originalDropType;
+
+        Iterator iter = listeners.iterator();
+        while (iter.hasNext()) {
+            TransferDropTargetListener listener = cast(TransferDropTargetListener) iter
+                    .next();
+            TransferData dataType = getSupportedTransferType(event.dataTypes,
+                    listener);
+            if (dataType !is null) {
+                TransferData originalDataType = event.currentDataType;
+                // set the data type supported by the drop listener
+                event.currentDataType = dataType;
+                if (listener.isEnabled(event)) {
+                    // if the listener stays the same, set its previously determined
+                    // event detail
+                    if (!setCurrentListener(listener, event)) {
+                        event.detail = originalDetail;
+                    }
+                    return;
+                }
+                event.currentDataType = originalDataType;
+            }
+        }
+        setCurrentListener(null, event);
+        event.detail = DND.DROP_NONE;
+
+        // -always- ensure that expand/scroll are on...otherwise
+        // if a valid drop target is a child of an invalid one
+        // you can't get there...
+        event.feedback = DND.FEEDBACK_EXPAND | DND.FEEDBACK_SCROLL;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/Geometry.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,816 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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 org.eclipse.jface.util.Geometry;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Contains static methods for performing simple geometric operations
+ * on the SWT geometry classes.
+ *
+ * @since 3.0
+ */
+public class Geometry {
+
+    /**
+     * Prevent this class from being instantiated.
+     *
+     * @since 3.0
+     */
+    private this() {
+        //This is not instantiated
+    }
+
+    /**
+     * Returns the square of the distance between two points.
+     * <p>This is preferred over the real distance when searching
+     * for the closest point, since it avoids square roots.</p>
+     *
+     * @param p1 first endpoint
+     * @param p2 second endpoint
+     * @return the square of the distance between the two points
+     *
+     * @since 3.0
+     */
+    public static int distanceSquared(Point p1, Point p2) {
+        int term1 = p1.x - p2.x;
+        int term2 = p1.y - p2.y;
+        return term1 * term1 + term2 * term2;
+    }
+
+    /**
+     * Returns the magnitude of the given 2d vector (represented as a Point)
+     *
+     * @param p point representing the 2d vector whose magnitude is being computed
+     * @return the magnitude of the given 2d vector
+     * @since 3.0
+     */
+    public static double magnitude(Point p) {
+        return Math.sqrt( cast(real) magnitudeSquared(p));
+    }
+
+    /**
+     * Returns the square of the magnitude of the given 2-space vector (represented
+     * using a point)
+     *
+     * @param p the point whose magnitude is being computed
+     * @return the square of the magnitude of the given vector
+     * @since 3.0
+     */
+    public static int magnitudeSquared(Point p) {
+        return p.x * p.x + p.y * p.y;
+    }
+
+    /**
+     * Returns the dot product of the given vectors (expressed as Points)
+     *
+     * @param p1 the first vector
+     * @param p2 the second vector
+     * @return the dot product of the two vectors
+     * @since 3.0
+     */
+    public static int dotProduct(Point p1, Point p2) {
+        return p1.x * p2.x + p1.y * p2.y;
+    }
+
+    /**
+     * Returns a new point whose coordinates are the minimum of the coordinates of the
+     * given points
+     *
+     * @param p1 a Point
+     * @param p2 a Point
+     * @return a new point whose coordinates are the minimum of the coordinates of the
+     * given points
+     * @since 3.0
+     */
+    public static Point min(Point p1, Point p2) {
+        return new Point(Math.min(p1.x, p2.x), Math.min(p1.y, p2.y));
+    }
+
+    /**
+     * Returns a new point whose coordinates are the maximum of the coordinates
+     * of the given points
+     * @param p1 a Point
+     * @param p2 a Point
+     * @return point a new point whose coordinates are the maximum of the coordinates
+     * @since 3.0
+     */
+    public static Point max(Point p1, Point p2) {
+        return new Point(Math.max(p1.x, p2.x), Math.max(p1.y, p2.y));
+    }
+
+    /**
+     * Returns a vector in the given direction with the given
+     * magnitude. Directions are given using SWT direction constants, and
+     * the resulting vector is in the screen's coordinate system. That is,
+     * the vector (0, 1) is down and the vector (1, 0) is right.
+     *
+     * @param distance magnitude of the vector
+     * @param direction one of SWT.TOP, SWT.BOTTOM, SWT.LEFT, or SWT.RIGHT
+     * @return a point representing a vector in the given direction with the given magnitude
+     * @since 3.0
+     */
+    public static Point getDirectionVector(int distance, int direction) {
+        switch (direction) {
+        case SWT.TOP:
+            return new Point(0, -distance);
+        case SWT.BOTTOM:
+            return new Point(0, distance);
+        case SWT.LEFT:
+            return new Point(-distance, 0);
+        case SWT.RIGHT:
+            return new Point(distance, 0);
+        default:
+        }
+
+        return new Point(0, 0);
+    }
+
+    /**
+     * Returns the point in the center of the given rectangle.
+     *
+     * @param rect rectangle being computed
+     * @return a Point at the center of the given rectangle.
+     * @since 3.0
+     */
+    public static Point centerPoint(Rectangle rect) {
+        return new Point(rect.x + rect.width / 2, rect.y + rect.height / 2);
+    }
+
+    /**
+     * Returns a copy of the given point
+     *
+     * @param toCopy point to copy
+     * @return a copy of the given point
+     */
+    public static Point copy(Point toCopy) {
+        return new Point(toCopy.x, toCopy.y);
+    }
+
+    /**
+     * Sets result equal to toCopy
+     *
+     * @param result object that will be modified
+     * @param toCopy object that will be copied
+     * @since 3.1
+     */
+    public static void set(Point result, Point toCopy) {
+        result.x = toCopy.x;
+        result.y = toCopy.y;
+    }
+
+    /**
+     * Sets result equal to toCopy
+     *
+     * @param result object that will be modified
+     * @param toCopy object that will be copied
+     * @since 3.1
+     */
+    public static void set(Rectangle result, Rectangle toCopy) {
+        result.x = toCopy.x;
+        result.y = toCopy.y;
+        result.width = toCopy.width;
+        result.height = toCopy.height;
+    }
+
+    /**
+     * <p>Returns a new difference Rectangle whose x, y, width, and height are equal to the difference of the corresponding
+     * attributes from the given rectangles</p>
+     *
+     * <p></p>
+     * <b>Example: Compute the margins for a given Composite, and apply those same margins to a new GridLayout</b>
+     *
+     * <code><pre>
+     *      // Compute the client area, in the coordinate system of the input composite's parent
+     *      Rectangle clientArea = Display.getCurrent().map(inputComposite,
+     *          inputComposite.getParent(), inputComposite.getClientArea());
+     *
+     *      // Compute the margins for a given Composite by subtracting the client area from the composite's bounds
+     *      Rectangle margins = Geometry.subtract(inputComposite.getBounds(), clientArea);
+     *
+     *      // Now apply these margins to a new GridLayout
+     *      GridLayout layout = GridLayoutFactory.fillDefaults().margins(margins).create();
+     * </pre></code>
+     *
+     * @param rect1 first rectangle
+     * @param rect2 rectangle to subtract
+     * @return the difference between the two rectangles (computed as rect1 - rect2)
+     * @since 3.3
+     */
+    public static Rectangle subtract(Rectangle rect1, Rectangle rect2) {
+        return new Rectangle(rect1.x - rect2.x, rect1.y - rect2.y, rect1.width - rect2.width, rect1.height - rect2.height);
+    }
+
+    /**
+     * <p>Returns a new Rectangle whose x, y, width, and height is the sum of the x, y, width, and height values of
+     * both rectangles respectively.</p>
+     *
+     * @param rect1 first rectangle to add
+     * @param rect2 second rectangle to add
+     * @return a new rectangle whose x, y, height, and width attributes are the sum of the corresponding attributes from
+     *         the arguments.
+     * @since 3.3
+     */
+    public static Rectangle add(Rectangle rect1, Rectangle rect2) {
+        return new Rectangle(rect1.x + rect2.x, rect1.y + rect2.y,
+                rect1.width + rect2.width, rect1.height + rect2.height);
+    }
+
+    /**
+     * Adds two points as 2d vectors. Returns a new point whose coordinates are
+     * the sum of the original two points.
+     *
+     * @param point1 the first point (not null)
+     * @param point2 the second point (not null)
+     * @return a new point whose coordinates are the sum of the given points
+     * @since 3.0
+     */
+    public static Point add(Point point1, Point point2) {
+        return new Point(point1.x + point2.x, point1.y + point2.y);
+    }
+
+    /**
+     * Divides both coordinates of the given point by the given scalar.
+     *
+     * @since 3.1
+     *
+     * @param toDivide point to divide
+     * @param scalar denominator
+     * @return a new Point whose coordinates are equal to the original point divided by the scalar
+     */
+    public static Point divide(Point toDivide, int scalar) {
+        return new Point(toDivide.x / scalar, toDivide.y / scalar);
+    }
+
+
+    /**
+     * Performs vector subtraction on two points. Returns a new point equal to
+     * (point1 - point2).
+     *
+     * @param point1 initial point
+     * @param point2 vector to subtract
+     * @return the difference (point1 - point2)
+     * @since 3.0
+     */
+    public static Point subtract(Point point1, Point point2) {
+        return new Point(point1.x - point2.x, point1.y - point2.y);
+    }
+
+    /**
+     * Swaps the X and Y coordinates of the given point.
+     *
+     * @param toFlip modifies this point
+     * @since 3.1
+     */
+    public static void flipXY(Point toFlip) {
+        int temp = toFlip.x;
+        toFlip.x = toFlip.y;
+        toFlip.y = temp;
+    }
+
+    /**
+     * Swaps the X and Y coordinates of the given rectangle, along with the height and width.
+     *
+     * @param toFlip modifies this rectangle
+     * @since 3.1
+     */
+    public static void flipXY(Rectangle toFlip) {
+        int temp = toFlip.x;
+        toFlip.x = toFlip.y;
+        toFlip.y = temp;
+
+        temp = toFlip.width;
+        toFlip.width = toFlip.height;
+        toFlip.height = temp;
+    }
+
+    /**
+     * Returns the height or width of the given rectangle.
+     *
+     * @param toMeasure rectangle to measure
+     * @param width returns the width if true, and the height if false
+     * @return the width or height of the given rectangle
+     * @since 3.0
+     */
+    public static int getDimension(Rectangle toMeasure, bool width) {
+        if (width) {
+            return toMeasure.width;
+        }
+        return toMeasure.height;
+    }
+
+    /**
+     * Returns the x or y coordinates of the given point.
+     *
+     * @param toMeasure point being measured
+     * @param width if true, returns x. Otherwise, returns y.
+     * @return the x or y coordinate
+     * @since 3.1
+     */
+    public static int getCoordinate(Point toMeasure, bool width) {
+        return width ? toMeasure.x : toMeasure.y;
+    }
+
+    /**
+     * Returns the x or y coordinates of the given rectangle.
+     *
+     * @param toMeasure rectangle being measured
+     * @param width if true, returns x. Otherwise, returns y.
+     * @return the x or y coordinate
+     * @since 3.1
+     */
+    public static int getCoordinate(Rectangle toMeasure, bool width) {
+        return width ? toMeasure.x : toMeasure.y;
+    }
+
+    /**
+     * Sets one dimension of the given rectangle. Modifies the given rectangle.
+     *
+     * @param toSet rectangle to modify
+     * @param width if true, the width is modified. If false, the height is modified.
+     * @param newCoordinate new value of the width or height
+     * @since 3.1
+     */
+    public static void setDimension(Rectangle toSet, bool width, int newCoordinate) {
+        if (width) {
+            toSet.width = newCoordinate;
+        } else {
+            toSet.height = newCoordinate;
+        }
+    }
+
+    /**
+     * Sets one coordinate of the given rectangle. Modifies the given rectangle.
+     *
+     * @param toSet rectangle to modify
+     * @param width if true, the x coordinate is modified. If false, the y coordinate is modified.
+     * @param newCoordinate new value of the x or y coordinates
+     * @since 3.1
+     */
+    public static void setCoordinate(Rectangle toSet, bool width, int newCoordinate) {
+        if (width) {
+            toSet.x = newCoordinate;
+        } else {
+            toSet.y = newCoordinate;
+        }
+    }
+
+    /**
+     * Sets one coordinate of the given point. Modifies the given point.
+     *
+     * @param toSet point to modify
+     * @param width if true, the x coordinate is modified. If false, the y coordinate is modified.
+     * @param newCoordinate new value of the x or y coordinates
+     * @since 3.1
+     */
+    public static void setCoordinate(Point toSet, bool width, int newCoordinate) {
+        if (width) {
+            toSet.x = newCoordinate;
+        } else {
+            toSet.y = newCoordinate;
+        }
+    }
+
+    /**
+     * Returns the distance of the given point from a particular side of the given rectangle.
+     * Returns negative values for points outside the rectangle.
+     *
+     * @param rectangle a bounding rectangle
+     * @param testPoint a point to test
+     * @param edgeOfInterest side of the rectangle to test against
+     * @return the distance of the given point from the given edge of the rectangle
+     * @since 3.0
+     */
+    public static int getDistanceFromEdge(Rectangle rectangle, Point testPoint,
+            int edgeOfInterest) {
+        switch (edgeOfInterest) {
+        case SWT.TOP:
+            return testPoint.y - rectangle.y;
+        case SWT.BOTTOM:
+            return rectangle.y + rectangle.height - testPoint.y;
+        case SWT.LEFT:
+            return testPoint.x - rectangle.x;
+        case SWT.RIGHT:
+            return rectangle.x + rectangle.width - testPoint.x;
+        default:
+        }
+
+        return 0;
+    }
+
+    /**
+     * Extrudes the given edge inward by the given distance. That is, if one side of the rectangle
+     * was sliced off with a given thickness, this returns the rectangle that forms the slice. Note
+     * that the returned rectangle will be inside the given rectangle if size > 0.
+     *
+     * @param toExtrude the rectangle to extrude. The resulting rectangle will share three sides
+     * with this rectangle.
+     * @param size distance to extrude. A negative size will extrude outwards (that is, the resulting
+     * rectangle will overlap the original iff this is positive).
+     * @param orientation the side to extrude.  One of SWT.LEFT, SWT.RIGHT, SWT.TOP, or SWT.BOTTOM. The
+     * resulting rectangle will always share this side with the original rectangle.
+     * @return a rectangle formed by extruding the given side of the rectangle by the given distance.
+     * @since 3.0
+     */
+    public static Rectangle getExtrudedEdge(Rectangle toExtrude, int size,
+            int orientation) {
+        Rectangle bounds = new Rectangle(toExtrude.x, toExtrude.y,
+                toExtrude.width, toExtrude.height);
+
+        if (!isHorizontal(orientation)) {
+            bounds.width = size;
+        } else {
+            bounds.height = size;
+        }
+
+        switch (orientation) {
+        case SWT.RIGHT:
+            bounds.x = toExtrude.x + toExtrude.width - bounds.width;
+            break;
+        case SWT.BOTTOM:
+            bounds.y = toExtrude.y + toExtrude.height - bounds.height;
+            break;
+        default:
+        }
+
+        normalize(bounds);
+
+        return bounds;
+    }
+
+    /**
+     * Returns the opposite of the given direction. That is, returns SWT.LEFT if
+     * given SWT.RIGHT and visa-versa.
+     *
+     * @param swtDirectionConstant one of SWT.LEFT, SWT.RIGHT, SWT.TOP, or SWT.BOTTOM
+     * @return one of SWT.LEFT, SWT.RIGHT, SWT.TOP, or SWT.BOTTOM
+     * @since 3.0
+     */
+    public static int getOppositeSide(int swtDirectionConstant) {
+        switch (swtDirectionConstant) {
+        case SWT.TOP:
+            return SWT.BOTTOM;
+        case SWT.BOTTOM:
+            return SWT.TOP;
+        case SWT.LEFT:
+            return SWT.RIGHT;
+        case SWT.RIGHT:
+            return SWT.LEFT;
+        default:
+        }
+
+        return swtDirectionConstant;
+    }
+
+    /**
+     * Converts the given bool into an SWT orientation constant.
+     *
+     * @param horizontal if true, returns SWT.HORIZONTAL. If false, returns SWT.VERTICAL
+     * @return SWT.HORIZONTAL or SWT.VERTICAL.
+     * @since 3.0
+     */
+    public static int getSwtHorizontalOrVerticalConstant(bool horizontal) {
+        if (horizontal) {
+            return SWT.HORIZONTAL;
+        }
+        return SWT.VERTICAL;
+    }
+
+    /**
+     * Returns true iff the given SWT side constant corresponds to a horizontal side
+     * of a rectangle. That is, returns true for the top and bottom but false for the
+     * left and right.
+     *
+     * @param swtSideConstant one of SWT.TOP, SWT.BOTTOM, SWT.LEFT, or SWT.RIGHT
+     * @return true iff the given side is horizontal.
+     * @since 3.0
+     */
+    public static bool isHorizontal(int swtSideConstant) {
+        return !(swtSideConstant is SWT.LEFT || swtSideConstant is SWT.RIGHT);
+    }
+
+    /**
+     * Moves the given rectangle by the given delta.
+     *
+     * @param rect rectangle to move (will be modified)
+     * @param delta direction vector to move the rectangle by
+     * @since 3.0
+     */
+    public static void moveRectangle(Rectangle rect, Point delta) {
+        rect.x += delta.x;
+        rect.y += delta.y;
+    }
+
+    /**
+     * Moves each edge of the given rectangle outward by the given amount. Negative values
+     * cause the rectangle to contract. Does not allow the rectangle's width or height to be
+     * reduced below zero.
+     *
+     * @param rect normalized rectangle to modify
+     * @param differenceRect difference rectangle to be added to rect
+     * @since 3.3
+     */
+    public static void expand(Rectangle rect, Rectangle differenceRect) {
+        rect.x += differenceRect.x;
+        rect.y += differenceRect.y;
+        rect.height = Math.max(0, rect.height + differenceRect.height);
+        rect.width = Math.max(0, rect.width + differenceRect.width);
+    }
+
+    /**
+     * <p>Returns a rectangle which, when added to another rectangle, will expand each side
+     * by the given number of units.</p>
+     *
+     * <p>This is commonly used to store margin sizes. For example:</p>
+     *
+     * <code><pre>
+     *     // Expands the left, right, top, and bottom
+     *     // of the given control by 10, 5, 1, and 15 units respectively
+     *
+     *     Rectangle margins = Geometry.createDifferenceRect(10,5,1,15);
+     *     Rectangle bounds = someControl.getBounds();
+     *     someControl.setBounds(Geometry.add(bounds, margins));
+     * </pre></code>
+     *
+     * @param left distance to expand the left side (negative values move the edge inward)
+     * @param right distance to expand the right side (negative values move the edge inward)
+     * @param top distance to expand the top (negative values move the edge inward)
+     * @param bottom distance to expand the bottom (negative values move the edge inward)
+     *
+     * @return a difference rectangle that, when added to another rectangle, will cause each
+     * side to expand by the given number of units
+     * @since 3.3
+     */
+    public static Rectangle createDiffRectangle(int left, int right, int top, int bottom) {
+        return new Rectangle(-left, -top, left + right, top + bottom);
+    }
+
+    /**
+     * Moves each edge of the given rectangle outward by the given amount. Negative values
+     * cause the rectangle to contract. Does not allow the rectangle's width or height to be
+     * reduced below zero.
+     *
+     * @param rect normalized rectangle to modify
+     * @param left distance to move the left edge outward (negative values move the edge inward)
+     * @param right distance to move the right edge outward (negative values move the edge inward)
+     * @param top distance to move the top edge outward (negative values move the edge inward)
+     * @param bottom distance to move the bottom edge outward (negative values move the edge inward)
+     * @since 3.1
+     */
+    public static void expand(Rectangle rect, int left, int right, int top, int bottom) {
+        rect.x -= left;
+        rect.width = Math.max(0, rect.width + left + right);
+        rect.y -= top;
+        rect.height = Math.max(0, rect.height + top + bottom);
+    }
+
+    /**
+     * Normalizes the given rectangle. That is, any rectangle with
+     * negative width or height becomes a rectangle with positive
+     * width or height that extends to the upper-left of the original
+     * rectangle.
+     *
+     * @param rect rectangle to modify
+     * @since 3.0
+     */
+    public static void normalize(Rectangle rect) {
+        if (rect.width < 0) {
+            rect.width = -rect.width;
+            rect.x -= rect.width;
+        }
+
+        if (rect.height < 0) {
+            rect.height = -rect.height;
+            rect.y -= rect.height;
+        }
+    }
+
+    /**
+     * Converts the given rectangle from display coordinates to the local coordinate system
+     * of the given object into display coordinates.
+     *
+     * @param coordinateSystem local coordinate system being converted to
+     * @param toConvert rectangle to convert
+     * @return a rectangle in control coordinates
+     * @since 3.0
+     */
+    public static Rectangle toControl(Control coordinateSystem,
+            Rectangle toConvert) {
+        return(coordinateSystem.getDisplay().map
+                (null,coordinateSystem,toConvert));
+    }
+
+    /**
+     * Converts the given rectangle from the local coordinate system of the given object
+     * into display coordinates.
+     *
+     * @param coordinateSystem local coordinate system being converted from
+     * @param toConvert rectangle to convert
+     * @return a rectangle in display coordinates
+     * @since 3.0
+     */
+    public static Rectangle toDisplay(Control coordinateSystem,
+            Rectangle toConvert) {
+        return(coordinateSystem.getDisplay().map
+                (coordinateSystem,null,toConvert));
+
+    }
+
+    /**
+     * Determines where the given point lies with respect to the given rectangle.
+     * Returns a combination of SWT.LEFT, SWT.RIGHT, SWT.TOP, and SWT.BOTTOM, combined
+     * with bitwise or (for example, returns SWT.TOP | SWT.LEFT if the point is to the
+     * upper-left of the rectangle). Returns 0 if the point lies within the rectangle.
+     * Positions are in screen coordinates (ie: a point is to the upper-left of the
+     * rectangle if its x and y coordinates are smaller than any point in the rectangle)
+     *
+     * @param boundary normalized boundary rectangle
+     * @param toTest point whose relative position to the rectangle is being computed
+     * @return one of SWT.LEFT | SWT.TOP, SWT.TOP, SWT.RIGHT | SWT.TOP, SWT.LEFT, 0,
+     * SWT.RIGHT, SWT.LEFT | SWT.BOTTOM, SWT.BOTTOM, SWT.RIGHT | SWT.BOTTOM
+     * @since 3.0
+     */
+    public static int getRelativePosition(Rectangle boundary, Point toTest) {
+        int result = 0;
+
+        if (toTest.x < boundary.x) {
+            result |= SWT.LEFT;
+        } else if (toTest.x >= boundary.x + boundary.width) {
+            result |= SWT.RIGHT;
+        }
+
+        if (toTest.y < boundary.y) {
+            result |= SWT.TOP;
+        } else if (toTest.y >= boundary.y + boundary.height) {
+            result |= SWT.BOTTOM;
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns the distance from the point to the nearest edge of the given
+     * rectangle. Returns negative values if the point lies outside the rectangle.
+     *
+     * @param boundary rectangle to test
+     * @param toTest point to test
+     * @return the distance between the given point and the nearest edge of the rectangle.
+     * Returns positive values for points inside the rectangle and negative values for points
+     * outside the rectangle.
+     * @since 3.1
+     */
+    public static int getDistanceFrom(Rectangle boundary, Point toTest) {
+        int side = getClosestSide(boundary, toTest);
+        return getDistanceFromEdge(boundary, toTest, side);
+    }
+
+    /**
+     * Returns the edge of the given rectangle is closest to the given
+     * point.
+     *
+     * @param boundary rectangle to test
+     * @param toTest point to compare
+     * @return one of SWT.LEFT, SWT.RIGHT, SWT.TOP, or SWT.BOTTOM
+     *
+     * @since 3.0
+     */
+    public static int getClosestSide(Rectangle boundary, Point toTest) {
+        int[] sides = [ SWT.LEFT, SWT.RIGHT, SWT.TOP, SWT.BOTTOM ];
+
+        int closestSide = SWT.LEFT;
+        int closestDistance = Integer.MAX_VALUE;
+
+        for (int idx = 0; idx < sides.length; idx++) {
+            int side = sides[idx];
+
+            int distance = getDistanceFromEdge(boundary, toTest, side);
+
+            if (distance < closestDistance) {
+                closestDistance = distance;
+                closestSide = side;
+            }
+        }
+
+        return closestSide;
+    }
+
+    /**
+     * Returns a copy of the given rectangle
+     *
+     * @param toCopy rectangle to copy
+     * @return a copy of the given rectangle
+     * @since 3.0
+     */
+    public static Rectangle copy(Rectangle toCopy) {
+        return new Rectangle(toCopy.x, toCopy.y, toCopy.width, toCopy.height);
+    }
+
+    /**
+     * Returns the size of the rectangle, as a Point
+     *
+     * @param rectangle rectangle whose size is being computed
+     * @return the size of the given rectangle
+     * @since 3.0
+     */
+    public static Point getSize(Rectangle rectangle) {
+        return new Point(rectangle.width, rectangle.height);
+    }
+
+    /**
+     * Sets the size of the given rectangle to the given size
+     *
+     * @param rectangle rectangle to modify
+     * @param newSize new size of the rectangle
+     * @since 3.0
+     */
+    public static void setSize(Rectangle rectangle, Point newSize) {
+        rectangle.width = newSize.x;
+        rectangle.height = newSize.y;
+    }
+
+    /**
+     * Sets the x,y position of the given rectangle. For a normalized
+     * rectangle (a rectangle with positive width and height), this will
+     * be the upper-left corner of the rectangle.
+     *
+     * @param rectangle rectangle to modify
+     * @param newLocation new location of the rectangle
+     *
+     * @since 3.0
+     */
+    public static void setLocation(Rectangle rectangle, Point newLocation) {
+        rectangle.x = newLocation.x;
+        rectangle.y = newLocation.y;
+    }
+
+    /**
+     * Returns the x,y position of the given rectangle. For normalized rectangles
+     * (rectangles with positive width and height), this is the upper-left
+     * corner of the rectangle.
+     *
+     * @param toQuery rectangle to query
+     * @return a Point containing the x,y position of the rectangle
+     *
+     * @since 3.0
+     */
+    public static Point getLocation(Rectangle toQuery) {
+        return new Point(toQuery.x, toQuery.y);
+    }
+
+    /**
+     * Returns a new rectangle with the given position and dimensions, expressed
+     * as points.
+     *
+     * @param position the (x,y) position of the rectangle
+     * @param size the size of the new rectangle, where (x,y) -> (width, height)
+     * @return a new Rectangle with the given position and size
+     *
+     * @since 3.0
+     */
+    public static Rectangle createRectangle(Point position, Point size) {
+        return new Rectangle(position.x, position.y, size.x, size.y);
+    }
+
+    /**
+     * Repositions the 'inner' rectangle to lie completely within the bounds of the 'outer'
+     * rectangle if possible. One use for this is to ensure that, when setting a control's bounds,
+     * that they will always lie within its parent's client area (to avoid clipping).
+     *
+     * @param inner The 'inner' rectangle to be repositioned (should be smaller than the 'outer' rectangle)
+     * @param outer The 'outer' rectangle
+     */
+    public static void moveInside(Rectangle inner, Rectangle outer) {
+        // adjust X
+        if (inner.x < outer.x) {
+            inner.x = outer.x;
+        }
+        if ((inner.x + inner.width) > (outer.x + outer.width)) {
+            inner.x -= (inner.x + inner.width) - (outer.x + outer.width);
+        }
+
+        // Adjust Y
+        if (inner.y < outer.y) {
+            inner.y = outer.y;
+        }
+        if ((inner.y + inner.height) > (outer.y + outer.height)) {
+            inner.y -= (inner.y + inner.height) - (outer.y + outer.height);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/ILogger.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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:
+ *     Chris Gross (schtoo@schtoo.com) - initial API and implementation
+ *       (bug 49497 [RCP] JFace dependency on org.eclipse.core.runtime enlarges standalone JFace applications)
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.util.ILogger;
+
+import java.lang.all;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * A mechanism to log errors throughout JFace.
+ * <p>
+ * Clients may provide their own implementation to change
+ * how errors are logged from within JFace.
+ * </p>
+ *
+ * @see org.eclipse.jface.util.Policy#getLog()
+ * @see org.eclipse.jface.util.Policy#setLog(ILogger)
+ * @since 3.1
+ */
+public interface ILogger {
+
+    /**
+     * Logs the given status.
+     *
+     * @param status the status to log
+     */
+    public void log(IStatus status);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/IOpenEventListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.util.IOpenEventListener;
+
+import java.lang.all;
+import java.util.EventListener;
+
+import org.eclipse.swt.events.SelectionEvent;
+
+/**
+ * Listener for open events which are generated on selection
+ * of default selection depending on the user preferences.
+ *
+ * <p>
+ * Usage:
+ * <pre>
+ *  OpenStrategy handler = new OpenStrategy(control);
+ *  handler.addOpenListener(new IOpenEventListener() {
+ *      public void handleOpen(SelectionEvent e) {
+ *          ... // code to handle the open event.
+ *      }
+ *  });
+ * </pre>
+ * </p>
+ *
+ * @see OpenStrategy
+ */
+public interface IOpenEventListener {
+    /**
+     * Called when a selection or default selection occurs
+     * depending on the user preference.
+     * @param e the selection event
+     */
+    public void handleOpen(SelectionEvent e);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/IPropertyChangeListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.util.IPropertyChangeListener;
+
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import java.lang.all;
+import java.util.EventListener;
+
+/**
+ * Listener for property changes.
+ * <p>
+ * Usage:
+ * <pre>
+ * IPropertyChangeListener listener =
+ *   new IPropertyChangeListener() {
+ *      public void propertyChange(PropertyChangeEvent event) {
+ *         ... // code to deal with occurrence of property change
+ *      }
+ *   };
+ * emitter.addPropertyChangeListener(listener);
+ * ...
+ * emitter.removePropertyChangeListener(listener);
+ * </pre>
+ * </p>
+ */
+public interface IPropertyChangeListener : EventListener {
+    /**
+     * Notification that a property has changed.
+     * <p>
+     * This method gets called when the observed object fires a property
+     * change event.
+     * </p>
+     *
+     * @param event the property change event object describing which property
+     * changed and how
+     */
+    public void propertyChange(PropertyChangeEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/ISafeRunnableRunner.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * 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:
+ *     Chris Gross (schtoo@schtoo.com) - initial API and implementation
+ *       (bug 49497 [RCP] JFace dependency on org.eclipse.core.runtime enlarges standalone JFace applications)
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.util.ISafeRunnableRunner;
+
+import java.lang.all;
+
+import org.eclipse.core.runtime.ISafeRunnable;
+
+/**
+ * Runs a safe runnables.
+ * <p>
+ * Clients may provide their own implementation to change
+ * how safe runnables are run from within JFace.
+ * </p>
+ *
+ * @see SafeRunnable#getRunner()
+ * @see SafeRunnable#setRunner(ISafeRunnableRunner)
+ * @see SafeRunnable#run(ISafeRunnable)
+ * @since 3.1
+ */
+public interface ISafeRunnableRunner {
+
+    /**
+     * Runs the runnable.  All <code>ISafeRunnableRunners</code> must catch any exception
+     * thrown by the <code>ISafeRunnable</code> and pass the exception to
+     * <code>ISafeRunnable.handleException()</code>.
+     * @param code the code executed as a save runnable
+     *
+     * @see SafeRunnable#run(ISafeRunnable)
+     */
+    public abstract void run(ISafeRunnable code);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/ListenerList.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.util.ListenerList;
+
+static import org.eclipse.core.runtime.ListenerList;
+
+import java.lang.all;
+
+/**
+ * This class is used to maintain a list of listeners, and is used in the
+ * implementations of several classes within JFace which allow you to register
+ * listeners of various kinds. It is a fairly lightweight object, occupying
+ * minimal space when no listeners are registered.
+ * <p>
+ * Note that the <code>add</code> method checks for and eliminates duplicates
+ * based on identity (not equality). Likewise, the <code>remove</code> method
+ * compares based on identity.
+ * </p>
+ * <p>
+ * Use the <code>getListeners</code> method when notifying listeners. Note
+ * that no garbage is created if no listeners are registered. The recommended
+ * code sequence for notifying all registered listeners of say,
+ * <code>FooListener.eventHappened</code>, is:
+ *
+ * <pre>
+ * Object[] listeners = myListenerList.getListeners();
+ * for (int i = 0; i &lt; listeners.length; ++i) {
+ *  ((FooListener) listeners[i]).eventHappened(event);
+ * }
+ * </pre>
+ *
+ * </p>
+ *
+ * @deprecated Please use {@link org.eclipse.core.runtime.ListenerList} instead.
+ *             Please note that the {@link #ListenerList(int)} and
+ *             {@link org.eclipse.core.runtime.ListenerList#ListenerList(int)}
+ *             constructors have different semantics. Please read the javadoc
+ *             carefully. Also note that the equivalent of
+ *             {@link #ListenerList()} is actually
+ *             {@link org.eclipse.core.runtime.ListenerList#ListenerList(int)}
+ *             with {@link org.eclipse.core.runtime.ListenerList#IDENTITY} as
+ *             the argument.
+ */
+public class ListenerList : org.eclipse.core.runtime.ListenerList.ListenerList {
+
+    /**
+     * Creates a listener list with an initial capacity of 1.
+     */
+    public this() {
+        super(IDENTITY);
+    }
+
+    /**
+     * Creates a listener list with the given initial capacity.
+     *
+     * @param capacity
+     *            the number of listeners which this list can initially accept
+     *            without growing its internal representation; must be at least
+     *            1
+     */
+    public this(int capacity) {
+        // the runtime ListenerList does not support capacity
+        this();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/LocalSelectionTransfer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.util.LocalSelectionTransfer;
+
+import org.eclipse.jface.util.Policy;
+
+import org.eclipse.swt.dnd.ByteArrayTransfer;
+import org.eclipse.swt.dnd.TransferData;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.ISelection;
+
+import java.lang.all;
+import java.util.Set;
+import tango.text.convert.Format;
+
+/**
+ * A LocalSelectionTransfer may be used for drag and drop operations
+ * within the same instance of Eclipse.
+ * The selection is made available directly for use in the DropTargetListener.
+ * dropAccept method. The DropTargetEvent passed to dropAccept does not contain
+ * the drop data. The selection may be used for validation purposes so that the
+ * drop can be aborted if appropriate.
+ *
+ * This class is not intended to be subclassed.
+ *
+ * @since 3.2
+ */
+public class LocalSelectionTransfer : ByteArrayTransfer {
+
+    // First attempt to create a UUID for the type name to make sure that
+    // different Eclipse applications use different "types" of
+    // <code>LocalSelectionTransfer</code>
+    private static const String TYPE_NAME;
+
+    private static const int TYPEID;
+
+    private static const LocalSelectionTransfer INSTANCE;
+
+    private ISelection selection;
+
+    private long selectionSetTime;
+
+    static this(){
+        TYPE_NAME = Format("local-selection-transfer-format{}", System.currentTimeMillis()); //$NON-NLS-1$;
+        TYPEID = registerType(TYPE_NAME);
+        INSTANCE = new LocalSelectionTransfer();
+    }
+
+    /**
+     * Only the singleton instance of this class may be used.
+     */
+    protected this() {
+        // do nothing
+    }
+
+    /**
+     * Returns the singleton.
+     *
+     * @return the singleton
+     */
+    public static LocalSelectionTransfer getTransfer() {
+        return INSTANCE;
+    }
+
+    /**
+     * Returns the local transfer data.
+     *
+     * @return the local transfer data
+     */
+    public ISelection getSelection() {
+        return selection;
+    }
+
+    /**
+     * Tests whether native drop data matches this transfer type.
+     *
+     * @param result result of converting the native drop data to Java
+     * @return true if the native drop data does not match this transfer type.
+     *  false otherwise.
+     */
+    private bool isInvalidNativeType(Object result) {
+        return !(cast(ArrayWrapperByte)result )
+                || !TYPE_NAME.equals(cast(char[])(cast(ArrayWrapperByte) result).array);
+    }
+
+    /**
+     * Returns the type id used to identify this transfer.
+     *
+     * @return the type id used to identify this transfer.
+     */
+    protected override int[] getTypeIds() {
+        return [ TYPEID ];
+    }
+
+    /**
+     * Returns the type name used to identify this transfer.
+     *
+     * @return the type name used to identify this transfer.
+     */
+    protected override String[] getTypeNames() {
+        return [ TYPE_NAME ];
+    }
+
+    /**
+     * Overrides org.eclipse.swt.dnd.ByteArrayTransfer#javaToNative(Object,
+     * TransferData).
+     * Only encode the transfer type name since the selection is read and
+     * written in the same process.
+     *
+     * @see org.eclipse.swt.dnd.ByteArrayTransfer#javaToNative(java.lang.Object, org.eclipse.swt.dnd.TransferData)
+     */
+    public override void javaToNative(Object object, TransferData transferData) {
+        auto check = new ArrayWrapperByte( cast(byte[])TYPE_NAME );
+        super.javaToNative(check, transferData);
+    }
+
+    /**
+     * Overrides org.eclipse.swt.dnd.ByteArrayTransfer#nativeToJava(TransferData).
+     * Test if the native drop data matches this transfer type.
+     *
+     * @see org.eclipse.swt.dnd.ByteArrayTransfer#nativeToJava(TransferData)
+     */
+    public override Object nativeToJava(TransferData transferData) {
+        Object result = super.nativeToJava(transferData);
+        if (isInvalidNativeType(result)) {
+            Policy.getLog().log(new Status(
+                            IStatus.ERROR,
+                            Policy.JFACE,
+                            IStatus.ERROR,
+                            JFaceResources.getString("LocalSelectionTransfer.errorMessage"), null)); //$NON-NLS-1$
+        }
+        return cast(Object) selection;
+    }
+
+    /**
+     * Sets the transfer data for local use.
+     *
+     * @param s the transfer data
+     */
+    public void setSelection(ISelection s) {
+        selection = s;
+    }
+
+    /**
+     * Returns the time when the selection operation
+     * this transfer is associated with was started.
+     *
+     * @return the time when the selection operation has started
+     *
+     * @see org.eclipse.swt.events.TypedEvent#time
+     */
+    public long getSelectionSetTime() {
+        return selectionSetTime;
+    }
+
+    /**
+     * Sets the time when the selection operation this
+     * transfer is associated with was started.
+     * If assigning this from an SWT event, be sure to use
+     * <code>setSelectionTime(event.time & 0xFFFF)</code>
+     *
+     * @param time the time when the selection operation was started
+     *
+     * @see org.eclipse.swt.events.TypedEvent#time
+     */
+    public void setSelectionSetTime(long time) {
+        selectionSetTime = time;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/OpenStrategy.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,506 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.util.OpenStrategy;
+
+import org.eclipse.jface.util.IOpenEventListener;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.TableTree;
+import org.eclipse.swt.custom.TableTreeItem;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.core.runtime.ListenerList;
+
+import java.lang.all;
+import java.util.Set;
+import java.util.EventListener;
+
+/**
+ * Implementation of single-click and double-click strategies.
+ * <p>
+ * Usage:
+ * <pre>
+ *  OpenStrategy handler = new OpenStrategy(control);
+ *  handler.addOpenListener(new IOpenEventListener() {
+ *      public void handleOpen(SelectionEvent e) {
+ *          ... // code to handle the open event.
+ *      }
+ *  });
+ * </pre>
+ * </p>
+ */
+public class OpenStrategy {
+    /**
+     * Default behavior. Double click to open the item.
+     */
+    public static const int DOUBLE_CLICK = 0;
+
+    /**
+     * Single click will open the item.
+     */
+    public static const int SINGLE_CLICK = 1;
+
+    /**
+     * Hover will select the item.
+     */
+    public static const int SELECT_ON_HOVER = 1 << 1;
+
+    /**
+     * Open item when using arrow keys
+     */
+    public static const int ARROW_KEYS_OPEN = 1 << 2;
+
+    /** A single click will generate
+     * an open event but key arrows will not do anything.
+     *
+     * @deprecated
+     */
+    public static const int NO_TIMER = SINGLE_CLICK;
+
+    /** A single click will generate an open
+     * event and key arrows will generate an open event after a
+     * small time.
+     *
+     * @deprecated
+     */
+    public static const int FILE_EXPLORER = SINGLE_CLICK | ARROW_KEYS_OPEN;
+
+    /** Pointing to an item will change the selection
+     * and a single click will gererate an open event
+     *
+     * @deprecated
+     */
+    public static const int ACTIVE_DESKTOP = SINGLE_CLICK | SELECT_ON_HOVER;
+
+    // Time used in FILE_EXPLORER and ACTIVE_DESKTOP
+    private static const int TIME = 500;
+
+    /* SINGLE_CLICK or DOUBLE_CLICK;
+     * In case of SINGLE_CLICK, the bits SELECT_ON_HOVER and ARROW_KEYS_OPEN
+     * my be set as well. */
+    private static int CURRENT_METHOD = DOUBLE_CLICK;
+
+    private Listener eventHandler;
+
+    private ListenerList openEventListeners;
+
+    private ListenerList selectionEventListeners;
+
+    private ListenerList postSelectionEventListeners;
+
+    /**
+     * @param control the control the strategy is applied to
+     */
+    public this(Control control) {
+        openEventListeners = new ListenerList();
+        selectionEventListeners = new ListenerList();
+        postSelectionEventListeners = new ListenerList();
+        initializeHandler(control.getDisplay());
+        addListener(control);
+    }
+
+    /**
+     * Adds an IOpenEventListener to the collection of openEventListeners
+     * @param listener the listener to add
+     */
+    public void addOpenListener(IOpenEventListener listener) {
+        openEventListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes an IOpenEventListener to the collection of openEventListeners
+     * @param listener the listener to remove
+     */
+    public void removeOpenListener(IOpenEventListener listener) {
+        openEventListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Adds an SelectionListener to the collection of selectionEventListeners
+     * @param listener the listener to add
+     */
+    public void addSelectionListener(SelectionListener listener) {
+        selectionEventListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes an SelectionListener to the collection of selectionEventListeners
+     * @param listener the listener to remove
+     */
+    public void removeSelectionListener(SelectionListener listener) {
+        selectionEventListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Adds an SelectionListener to the collection of selectionEventListeners
+     * @param listener the listener to add
+     */
+    public void addPostSelectionListener(SelectionListener listener) {
+        postSelectionEventListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes an SelectionListener to the collection of selectionEventListeners
+     * @param listener the listener to remove
+     */
+    public void removePostSelectionListener(SelectionListener listener) {
+        postSelectionEventListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * This method is internal to the framework; it should not be implemented outside
+     * the framework.
+     * @return the current used single/double-click method
+     *
+     */
+    public static int getOpenMethod() {
+        return CURRENT_METHOD;
+    }
+
+    /**
+     * Set the current used single/double-click method.
+     *
+     * This method is internal to the framework; it should not be implemented outside
+     * the framework.
+     * @param method the method to be used
+     * @see OpenStrategy#DOUBLE_CLICK
+     * @see OpenStrategy#SINGLE_CLICK
+     * @see OpenStrategy#SELECT_ON_HOVER
+     * @see OpenStrategy#ARROW_KEYS_OPEN
+     */
+    public static void setOpenMethod(int method) {
+        if (method is DOUBLE_CLICK) {
+            CURRENT_METHOD = method;
+            return;
+        }
+        if ((method & SINGLE_CLICK) is 0) {
+            throw new IllegalArgumentException("Invalid open mode"); //$NON-NLS-1$
+        }
+        if ((method & (SINGLE_CLICK | SELECT_ON_HOVER | ARROW_KEYS_OPEN)) is 0) {
+            throw new IllegalArgumentException("Invalid open mode"); //$NON-NLS-1$
+        }
+        CURRENT_METHOD = method;
+    }
+
+    /**
+     * @return true if editors should be activated when opened.
+     */
+    public static bool activateOnOpen() {
+        return getOpenMethod() is DOUBLE_CLICK;
+    }
+
+    /*
+     * Adds all needed listener to the control in order to implement
+     * single-click/double-click strategies.
+     */
+    private void addListener(Control c) {
+        c.addListener(SWT.MouseEnter, eventHandler);
+        c.addListener(SWT.MouseExit, eventHandler);
+        c.addListener(SWT.MouseMove, eventHandler);
+        c.addListener(SWT.MouseDown, eventHandler);
+        c.addListener(SWT.MouseUp, eventHandler);
+        c.addListener(SWT.KeyDown, eventHandler);
+        c.addListener(SWT.Selection, eventHandler);
+        c.addListener(SWT.DefaultSelection, eventHandler);
+        c.addListener(SWT.Collapse, eventHandler);
+        c.addListener(SWT.Expand, eventHandler);
+    }
+
+    /*
+     * Fire the selection event to all selectionEventListeners
+     */
+    private void fireSelectionEvent(SelectionEvent e) {
+        if (e.item !is null && e.item.isDisposed()) {
+            return;
+        }
+        Object l[] = selectionEventListeners.getListeners();
+        for (int i = 0; i < l.length; i++) {
+            (cast(SelectionListener) l[i]).widgetSelected(e);
+        }
+    }
+
+    /*
+     * Fire the default selection event to all selectionEventListeners
+     */
+    private void fireDefaultSelectionEvent(SelectionEvent e) {
+        Object l[] = selectionEventListeners.getListeners();
+        for (int i = 0; i < l.length; i++) {
+            (cast(SelectionListener) l[i]).widgetDefaultSelected(e);
+        }
+    }
+
+    /*
+     * Fire the post selection event to all postSelectionEventListeners
+     */
+    private void firePostSelectionEvent(SelectionEvent e) {
+        if (e.item !is null && e.item.isDisposed()) {
+            return;
+        }
+        Object l[] = postSelectionEventListeners.getListeners();
+        for (int i = 0; i < l.length; i++) {
+            (cast(SelectionListener) l[i]).widgetSelected(e);
+        }
+    }
+
+    /*
+     * Fire the open event to all openEventListeners
+     */
+    private void fireOpenEvent(SelectionEvent e) {
+        if (e.item !is null && e.item.isDisposed()) {
+            return;
+        }
+        Object l[] = openEventListeners.getListeners();
+        for (int i = 0; i < l.length; i++) {
+            (cast(IOpenEventListener) l[i]).handleOpen(e);
+        }
+    }
+
+    //Initialize event handler.
+    private void initializeHandler( Display display_) {
+        eventHandler = new class(display_) Listener {
+            Display display;
+            bool timerStarted = false;
+
+            Event mouseUpEvent = null;
+
+            Event mouseMoveEvent = null;
+
+            SelectionEvent selectionPendent = null;
+
+            bool enterKeyDown = false;
+
+            SelectionEvent defaultSelectionPendent = null;
+
+            bool arrowKeyDown = false;
+
+            int[1] count;
+
+            long startTime;
+
+            bool collapseOccurred = false;
+
+            bool expandOccurred = false;
+
+            this(Display a){
+                display = a;
+                startTime = System.currentTimeMillis();
+            }
+
+            public void handleEvent( Event e) {
+                if (e.type is SWT.DefaultSelection) {
+                    SelectionEvent event = new SelectionEvent(e);
+                    fireDefaultSelectionEvent(event);
+                    if (CURRENT_METHOD is DOUBLE_CLICK) {
+                        fireOpenEvent(event);
+                    } else {
+                        if (enterKeyDown) {
+                            fireOpenEvent(event);
+                            enterKeyDown = false;
+                            defaultSelectionPendent = null;
+                        } else {
+                            defaultSelectionPendent = event;
+                        }
+                    }
+                    return;
+                }
+
+                switch (e.type) {
+                case SWT.MouseEnter:
+                case SWT.MouseExit:
+                    mouseUpEvent = null;
+                    mouseMoveEvent = null;
+                    selectionPendent = null;
+                    break;
+                case SWT.MouseMove:
+                    if ((CURRENT_METHOD & SELECT_ON_HOVER) is 0) {
+                        return;
+                    }
+                    if (e.stateMask !is 0) {
+                        return;
+                    }
+                    if (e.widget.getDisplay().getFocusControl() !is e.widget) {
+                        return;
+                    }
+                    mouseMoveEvent = e;
+                    Runnable runnable = new class() Runnable {
+                        public void run() {
+                            long time = System.currentTimeMillis();
+                            int diff = cast(int) (time - startTime);
+                            if (diff <= TIME) {
+                                display.timerExec(diff * 2 / 3, this );
+                            } else {
+                                timerStarted = false;
+                                setSelection(mouseMoveEvent);
+                            }
+                        }
+                    };
+                    startTime = System.currentTimeMillis();
+                    if (!timerStarted) {
+                        timerStarted = true;
+                        display.timerExec(TIME * 2 / 3, runnable );
+                    }
+                    break;
+                case SWT.MouseDown:
+                    mouseUpEvent = null;
+                    arrowKeyDown = false;
+                    break;
+                case SWT.Expand:
+                    expandOccurred = true;
+                    break;
+                case SWT.Collapse:
+                    collapseOccurred = true;
+                    break;
+                case SWT.MouseUp:
+                    mouseMoveEvent = null;
+                    if ((e.button !is 1) || ((e.stateMask & ~SWT.BUTTON1) !is 0)) {
+                        return;
+                    }
+                    if (selectionPendent !is null
+                            && !(collapseOccurred || expandOccurred)) {
+                        mouseSelectItem(selectionPendent);
+                    } else {
+                        mouseUpEvent = e;
+                        collapseOccurred = false;
+                        expandOccurred = false;
+                    }
+                    break;
+                case SWT.KeyDown:
+                    mouseMoveEvent = null;
+                    mouseUpEvent = null;
+                    arrowKeyDown = ((e.keyCode is SWT.ARROW_UP) || (e.keyCode is SWT.ARROW_DOWN))
+                            && e.stateMask is 0;
+                    if (e.character is SWT.CR) {
+                        if (defaultSelectionPendent !is null) {
+                            fireOpenEvent(new SelectionEvent(e));
+                            enterKeyDown = false;
+                            defaultSelectionPendent = null;
+                        } else {
+                            enterKeyDown = true;
+                        }
+                    }
+                    break;
+                case SWT.Selection:
+                    SelectionEvent event = new SelectionEvent(e);
+                    fireSelectionEvent(event);
+                    mouseMoveEvent = null;
+                    if (mouseUpEvent !is null) {
+                        mouseSelectItem(event);
+                    } else {
+                        selectionPendent = event;
+                    }
+                    count[0]++;
+                    // In the case of arrowUp/arrowDown when in the arrowKeysOpen mode, we
+                    // want to delay any selection until the last arrowDown/Up occurs.  This
+                    // handles the case where the user presses arrowDown/Up successively.
+                    // We only want to open an editor for the last selected item.
+                    display.asyncExec(new class( count, e) Runnable {
+                        int id_;
+                        int[] count_;
+                        Event e_;
+                        this( int[] a, Event b){
+                            count_ = a;
+                            e_ = b;
+                            id_ = count_[0];
+                        }
+                        public void run() {
+                            if (arrowKeyDown) {
+                                display.timerExec(TIME, new class(id_,count_,e_) Runnable {
+                                    int id__;
+                                    Event e__;
+                                    int[] count__;
+                                    this(int a, int[] b, Event c){
+                                        id__ = a;
+                                        count__ = b;
+                                        e__ = c;
+                                    }
+                                    public void run() {
+                                        if (id__ is count__[0]) {
+                                            firePostSelectionEvent(new SelectionEvent(e__));
+                                            if ((CURRENT_METHOD & ARROW_KEYS_OPEN) !is 0) {
+                                                fireOpenEvent(new SelectionEvent(e__));
+                                            }
+                                        }
+                                    }
+                                });
+                            } else {
+                                firePostSelectionEvent(new SelectionEvent(e_));
+                            }
+                        }
+                    });
+                    break;
+                default:
+                }
+            }
+
+            void mouseSelectItem(SelectionEvent e) {
+                if ((CURRENT_METHOD & SINGLE_CLICK) !is 0) {
+                    fireOpenEvent(e);
+                }
+                mouseUpEvent = null;
+                selectionPendent = null;
+            }
+
+            void setSelection(Event e) {
+                if (e is null) {
+                    return;
+                }
+                Widget w = e.widget;
+                if (w.isDisposed()) {
+                    return;
+                }
+
+                SelectionEvent selEvent = new SelectionEvent(e);
+
+                /*ISSUE: May have to create a interface with method:
+                 setSelection(Point p) so that user's custom widgets
+                 can use this class. If we keep this option. */
+                if ( auto tree = cast(Tree)w) {
+                    TreeItem item = tree.getItem(new Point(e.x, e.y));
+                    if (item !is null) {
+                        tree.setSelection([ item ]);
+                    }
+                    selEvent.item = item;
+                } else if ( auto table = cast(Table)w) {
+                    TableItem item = table.getItem(new Point(e.x, e.y));
+                    if (item !is null) {
+                        table.setSelection([ item ]);
+                    }
+                    selEvent.item = item;
+                } else if ( auto table = cast(TableTree)w) {
+                    TableTreeItem item = table.getItem(new Point(e.x, e.y));
+                    if (item !is null) {
+                        table.setSelection([ item ]);
+                    }
+                    selEvent.item = item;
+                } else {
+                    return;
+                }
+                if (selEvent.item is null) {
+                    return;
+                }
+                fireSelectionEvent(selEvent);
+                firePostSelectionEvent(selEvent);
+            }
+        };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/Policy.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,298 @@
+/*******************************************************************************
+ * 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
+ *     Chris Gross (schtoo@schtoo.com) - support for ILogger added
+ *       (bug 49497 [RCP] JFace dependency on org.eclipse.core.runtime enlarges standalone JFace applications)
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.util.Policy;
+
+static import org.eclipse.core.runtime.Assert;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.AnimatorFactory;
+import org.eclipse.jface.dialogs.ErrorSupportProvider;
+
+import org.eclipse.jface.util.StatusHandler;
+import org.eclipse.jface.util.SafeRunnableDialog;
+import org.eclipse.jface.util.ILogger;
+
+import java.lang.all;
+import java.util.Set;
+import tango.io.Stdout;
+
+/**
+ * The Policy class handles settings for behaviour, debug flags and logging
+ * within JFace.
+ *
+ * @since 3.0
+ */
+public class Policy {
+
+    /**
+     * Constant for the the default setting for debug options.
+     */
+    public static const bool DEFAULT = false;
+
+    /**
+     * The unique identifier of the JFace plug-in.
+     */
+    public static const String JFACE = "org.eclipse.jface"; //$NON-NLS-1$
+
+    private static ILogger log;
+
+    private static Comparator viewerComparator;
+
+    private static AnimatorFactory animatorFactory;
+
+    /**
+     * A flag to indicate whether unparented dialogs should be checked.
+     */
+    public static bool DEBUG_DIALOG_NO_PARENT = DEFAULT;
+
+    /**
+     * A flag to indicate whether actions are being traced.
+     */
+    public static bool TRACE_ACTIONS = DEFAULT;
+
+    /**
+     * A flag to indicate whether toolbars are being traced.
+     */
+
+    public static bool TRACE_TOOLBAR = DEFAULT;
+
+    private static ErrorSupportProvider errorSupportProvider;
+
+    private static StatusHandler statusHandler;
+
+    /**
+     * Returns the dummy log to use if none has been set
+     */
+    private static ILogger getDummyLog() {
+        return new class ILogger {
+            public void log(IStatus status) {
+                Stderr.formatln(status.getMessage());
+                if (status.getException() !is null) {
+                    auto e = status.getException();
+                    Stderr.formatln( "Exception of type {} in {}({}): {}", e.classinfo.name, e.file, e.line, e.msg );
+                    if( e.info !is null ){
+                        foreach( msg; e.info ){
+                            Stderr.formatln( "    trc: {}", msg );
+                        }
+                    }
+//                     status.getException().printStackTrace();
+                }
+            }
+        };
+    }
+
+    /**
+     * Sets the logger used by JFace to log errors.
+     *
+     * @param logger
+     *            the logger to use, or <code>null</code> to use the default
+     *            logger
+     * @since 3.1
+     */
+    public static void setLog(ILogger logger) {
+        log = logger;
+    }
+
+    /**
+     * Returns the logger used by JFace to log errors.
+     * <p>
+     * The default logger prints the status to <code>System.err</code>.
+     * </p>
+     *
+     * @return the logger
+     * @since 3.1
+     */
+    public static ILogger getLog() {
+        if (log is null) {
+            log = getDummyLog();
+        }
+        return log;
+    }
+
+    /**
+     * Sets the status handler used by JFace to handle statuses.
+     *
+     * @param status
+     *            the handler to use, or <code>null</code> to use the default
+     *            one
+     * @since 3.4
+     */
+    public static void setStatusHandler(StatusHandler status) {
+        statusHandler = status;
+    }
+
+    /**
+     * Returns the status handler used by JFace to handle statuses.
+     *
+     * @return the status handler
+     * @since 3.4
+     */
+    public static StatusHandler getStatusHandler() {
+        if (statusHandler is null) {
+            statusHandler = getDummyStatusHandler();
+        }
+        return statusHandler;
+    }
+
+    private static StatusHandler getDummyStatusHandler() {
+        return new class StatusHandler {
+            private SafeRunnableDialog dialog;
+
+            public void show(IStatus status, String title) {
+                Runnable runnable = dgRunnable( (IStatus status_) {
+                    if (dialog is null || dialog.getShell().isDisposed()) {
+                        dialog = new SafeRunnableDialog(status_);
+                        dialog.create();
+                        dialog.getShell().addDisposeListener(
+                                new class DisposeListener {
+                                    public void widgetDisposed(
+                                            DisposeEvent e) {
+                                        dialog = null;
+                                    }
+                                });
+                        dialog.open();
+                    } else {
+                        dialog.addStatus(status_);
+                        dialog.refresh();
+                    }
+                }, status );
+                if (Display.getCurrent() !is null) {
+                    runnable.run();
+                } else {
+                    Display.getDefault().asyncExec(runnable);
+                }
+            }
+        };
+    }
+
+    /**
+     * Return the default comparator used by JFace to sort strings.
+     *
+     * @return a default comparator used by JFace to sort strings
+     */
+    private static Comparator getDefaultComparator() {
+        return new class() Comparator {
+            /**
+             * Compares string s1 to string s2.
+             *
+             * @param s1
+             *            string 1
+             * @param s2
+             *            string 2
+             * @return Returns an integer value. Value is less than zero if
+             *         source is less than target, value is zero if source and
+             *         target are equal, value is greater than zero if source is
+             *         greater than target.
+             * @exception ClassCastException
+             *                the arguments cannot be cast to Strings.
+             */
+            public int compare(Object s1, Object s2) {
+                auto a = (cast(ArrayWrapperString) s1).array;
+                auto b = (cast(ArrayWrapperString) s2).array;
+                return a < b;
+            }
+        };
+    }
+
+    /**
+     * Return the comparator used by JFace to sort strings.
+     *
+     * @return the comparator used by JFace to sort strings
+     * @since 3.2
+     */
+    public static Comparator getComparator() {
+        if (viewerComparator is null) {
+            viewerComparator = getDefaultComparator();
+        }
+        return viewerComparator;
+    }
+
+    /**
+     * Sets the comparator used by JFace to sort strings.
+     *
+     * @param comparator
+     *            comparator used by JFace to sort strings
+     * @since 3.2
+     */
+    public static void setComparator(Comparator comparator) {
+        org.eclipse.core.runtime.Assert.Assert.isTrue(viewerComparator is null);
+        viewerComparator = comparator;
+    }
+
+    /**
+     * Sets the animator factory used by JFace to create control animator
+     * instances.
+     *
+     * @param factory
+     *            the AnimatorFactory to use.
+     * @since 3.2
+     * @deprecated this is no longer in use as of 3.3
+     */
+    public static void setAnimatorFactory(AnimatorFactory factory) {
+        animatorFactory = factory;
+    }
+
+    /**
+     * Returns the animator factory used by JFace to create control animator
+     * instances.
+     *
+     * @return the animator factory used to create control animator instances.
+     * @since 3.2
+     * @deprecated this is no longer in use as of 3.3
+     */
+    public static AnimatorFactory getAnimatorFactory() {
+        if (animatorFactory is null)
+            animatorFactory = new AnimatorFactory();
+        return animatorFactory;
+    }
+
+    /**
+     * Set the error support provider for error dialogs.
+     *
+     * @param provider
+     * @since 3.3
+     */
+    public static void setErrorSupportProvider(ErrorSupportProvider provider) {
+        errorSupportProvider = provider;
+    }
+
+    /**
+     * Return the ErrorSupportProvider for the receiver.
+     *
+     * @return ErrorSupportProvider or <code>null</code> if this has not been
+     *         set
+     * @since 3.3
+     */
+    public static ErrorSupportProvider getErrorSupportProvider() {
+        return errorSupportProvider;
+    }
+
+    /**
+     * Log the Exception to the logger.
+     *
+     * @param exception
+     * @since 3.4
+     */
+    public static void logException(Exception exception) {
+        getLog().log(
+                new Status(IStatus.ERROR, JFACE, ExceptionGetLocalizedMessage( exception )
+                        , exception));
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/PropertyChangeEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.util.PropertyChangeEvent;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.EventObject;
+
+/**
+ * An event object describing a change to a named property.
+ * <p>
+ * This concrete class was designed to be instantiated, but may
+ * also be subclassed if required.
+ * </p>
+ * <p>
+ * The JFace frameworks contain classes that report property
+ * change events for internal state changes that may be of interest
+ * to external parties. A special listener interface
+ * (<code>IPropertyChangeListener</code>) is defined for this purpose,
+ * and a typical class allow listeners to be registered via
+ * an <code>addPropertyChangeListener</code> method.
+ * </p>
+ *
+ * @see IPropertyChangeListener
+ */
+public class PropertyChangeEvent : EventObject {
+
+    /**
+     * Generated serial version UID for this class.
+     * @since 3.1
+     */
+    private static final long serialVersionUID = 3256726173533811256L;
+
+    /**
+     * The name of the changed property.
+     */
+    private String propertyName;
+
+    /**
+     * The old value of the changed property, or <code>null</code> if
+     * not known or not relevant.
+     */
+    private Object oldValue;
+
+    /**
+     * The new value of the changed property, or <code>null</code> if
+     * not known or not relevant.
+     */
+    private Object newValue;
+
+    /**
+     * Creates a new property change event.
+     *
+     * @param source the object whose property has changed
+     * @param property the property that has changed (must not be <code>null</code>)
+     * @param oldValue the old value of the property, or <code>null</code> if none
+     * @param newValue the new value of the property, or <code>null</code> if none
+     */
+    public this(Object source, String property, Object oldValue,
+            Object newValue) {
+        super(source);
+        Assert.isTrue( property.length > 0 );
+        this.propertyName = property;
+        this.oldValue = oldValue;
+        this.newValue = newValue;
+    }
+
+    /**
+     * Returns the new value of the property.
+     *
+     * @return the new value, or <code>null</code> if not known
+     *  or not relevant (for instance if the property was removed).
+     */
+    public Object getNewValue() {
+        return newValue;
+    }
+
+    /**
+     * Returns the old value of the property.
+     *
+     * @return the old value, or <code>null</code> if not known
+     *  or not relevant (for instance if the property was just
+     *  added and there was no old value).
+     */
+    public Object getOldValue() {
+        return oldValue;
+    }
+
+    /**
+     * Returns the name of the property that changed.
+     * <p>
+     * Warning: there is no guarantee that the property name returned
+     * is a constant string.  Callers must compare property names using
+     * equals, not is.
+     * </p>
+     *
+     * @return the name of the property that changed
+     */
+    public String getProperty() {
+        return propertyName;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/SafeRunnable.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,214 @@
+/*******************************************************************************
+ * 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
+ *     Chris Gross (schtoo@schtoo.com) - support for ISafeRunnableRunner added
+ *       (bug 49497 [RCP] JFace dependency on org.eclipse.core.runtime enlarges standalone JFace applications)
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.util.SafeRunnable;
+
+import org.eclipse.jface.util.ISafeRunnableRunner;
+import org.eclipse.jface.util.SafeRunnableDialog;
+import org.eclipse.jface.util.Policy;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Implements a default implementation of ISafeRunnable. The default
+ * implementation of <code>handleException</code> opens a dialog to show any
+ * errors as they accumulate.
+ * <p>
+ * This may be executed on any thread.
+ */
+public abstract class SafeRunnable : ISafeRunnable {
+
+    private static bool ignoreErrors = false;
+
+    private static ISafeRunnableRunner runner;
+
+    private String message;
+
+    /**
+     * Creates a new instance of SafeRunnable with a default error message.
+     */
+    public this() {
+        // do nothing
+    }
+
+    /**
+     * Creates a new instance of SafeRunnable with the given error message.
+     *
+     * @param message
+     *            the error message to use
+     */
+    public this(String message) {
+        this.message = message;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.runtime.ISafeRunnable#handleException(java.lang.Throwable)
+     */
+    public void handleException(Exception e) {
+        // Workaround to avoid interactive error dialogs during
+        // automated testing
+        if (ignoreErrors)
+            return;
+
+        if (message is null)
+            message = JFaceResources.getString("SafeRunnable.errorMessage"); //$NON-NLS-1$
+
+        Policy.getStatusHandler().show(
+                new Status(IStatus.ERROR, Policy.JFACE, message, e),
+                JFaceResources.getString("SafeRunnable.errorMessage")); //$NON-NLS-1$
+    }
+
+    /**
+     * Flag to avoid interactive error dialogs during automated testing.
+     *
+     * @param flag
+     * @return true if errors should be ignored
+     * @deprecated use getIgnoreErrors()
+     */
+    public static bool getIgnoreErrors(bool flag) {
+        return ignoreErrors;
+    }
+
+    /**
+     * Flag to avoid interactive error dialogs during automated testing.
+     *
+     * @return true if errors should be ignored
+     *
+     * @since 3.0
+     */
+    public static bool getIgnoreErrors() {
+        return ignoreErrors;
+    }
+
+    /**
+     * Flag to avoid interactive error dialogs during automated testing.
+     *
+     * @param flag
+     *            set to true if errors should be ignored
+     */
+    public static void setIgnoreErrors(bool flag) {
+        ignoreErrors = flag;
+    }
+
+    /**
+     * Returns the safe runnable runner.
+     *
+     * @return the safe runnable runner
+     *
+     * @since 3.1
+     */
+    public static ISafeRunnableRunner getRunner() {
+        if (runner is null) {
+            runner = createDefaultRunner();
+        }
+        return runner;
+    }
+
+    /**
+     * Creates the default safe runnable runner.
+     *
+     * @return the default safe runnable runner
+     * @since 3.1
+     */
+    private static ISafeRunnableRunner createDefaultRunner() {
+        return new class ISafeRunnableRunner {
+            public void run(ISafeRunnable code) {
+                try {
+                    code.run();
+                } catch (Exception e) {
+                    handleException(code, e);
+//                 } catch (LinkageError e) {
+//                     handleException(code, e);
+                }
+            }
+
+            private void handleException(ISafeRunnable code, Exception e) {
+                if (!(cast(OperationCanceledException)e )) {
+                    try {
+                        Policy.getLog()
+                                .log(
+                                        new Status(IStatus.ERROR, Policy.JFACE,
+                                                IStatus.ERROR,
+                                                "Exception occurred", e)); //$NON-NLS-1$
+                    } catch (Exception ex) {
+                        ExceptionPrintStackTrace(e);
+                    }
+                }
+                code.handleException(e);
+            }
+        };
+    }
+
+    /**
+     * Sets the safe runnable runner.
+     *
+     * @param runner
+     *            the runner to set, or <code>null</code> to reset to the
+     *            default runner
+     * @since 3.1
+     */
+    public static void setRunner(ISafeRunnableRunner runner) {
+        SafeRunnable.runner = runner;
+    }
+
+    /**
+     * Runs the given safe runnable using the safe runnable runner. This is a
+     * convenience method, equivalent to:
+     * <code>SafeRunnable.getRunner().run(runnable)</code>.
+     *
+     * @param runnable
+     *            the runnable to run
+     * @since 3.1
+     */
+    public static void run(ISafeRunnable runnable) {
+        getRunner().run(runnable);
+    }
+
+}
+
+import tango.core.Tuple;
+import tango.core.Traits;
+
+class _DgSafeRunnableT(Dg,T...) : SafeRunnable {
+
+    alias ParameterTupleOf!(Dg) DgArgs;
+    static assert( is(DgArgs == Tuple!(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;
+        }
+    }
+
+    public void run( ){
+        dg(t);
+    }
+}
+
+public _DgSafeRunnableT!(Dg,T) dgSafeRunnable(Dg,T...)( Dg dg, T args ){
+    return new _DgSafeRunnableT!(Dg,T)(dg,args);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/SafeRunnableDialog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,338 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.util.SafeRunnableDialog;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.CellLabelProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * SafeRunnableDialog is a dialog that can show the results of multiple safe
+ * runnable errors.
+ *
+ */
+class SafeRunnableDialog : ErrorDialog {
+
+    private TableViewer statusListViewer;
+
+    private Collection statuses;
+
+    /**
+     * Create a new instance of the receiver on a status.
+     *
+     * @param status
+     *            The status to display.
+     */
+    this(IStatus status) {
+
+        super(null, JFaceResources.getString("error"), status.getMessage(), //$NON-NLS-1$
+                status, IStatus.ERROR);
+        statuses = new ArrayList();
+        setShellStyle(SWT.DIALOG_TRIM | SWT.MODELESS | SWT.RESIZE | SWT.MIN | SWT.MAX
+                | getDefaultOrientation());
+
+        setStatus(status);
+        statuses.add(cast(Object)status);
+
+        setBlockOnOpen(false);
+
+        String reason = JFaceResources
+                .getString("SafeRunnableDialog_checkDetailsMessage"); //$NON-NLS-1$
+        if (status.getException() !is null) {
+            reason = status.getException().msg is null ? status
+                    .getException().toString() : status.getException()
+                    .msg;
+        }
+        this.message = JFaceResources.format(JFaceResources
+                .getString("SafeRunnableDialog_reason"), [ //$NON-NLS-1$
+                status.getMessage(), reason ]);
+    }
+
+    /**
+     * Method which should be invoked when new errors become available for
+     * display
+     */
+    void refresh() {
+
+        if (AUTOMATED_MODE)
+            return;
+
+        createStatusList(cast(Composite) dialogArea);
+        updateEnablements();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.ErrorDialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+     */
+    protected override Control createDialogArea(Composite parent) {
+        Control area = super.createDialogArea(parent);
+        createStatusList(cast(Composite) area);
+        return area;
+    }
+
+    /**
+     * Create the status list if required.
+     *
+     * @param parent
+     *            the Control to create it in.
+     */
+    private void createStatusList(Composite parent) {
+        if (isMultipleStatusDialog()) {
+            if (statusListViewer is null) {
+                // The job list doesn't exist so create it.
+                setMessage(JFaceResources
+                        .getString("SafeRunnableDialog_MultipleErrorsMessage")); //$NON-NLS-1$
+                getShell()
+                        .setText(
+                                JFaceResources
+                                        .getString("SafeRunnableDialog_MultipleErrorsTitle")); //$NON-NLS-1$
+                createStatusListArea(parent);
+                showDetailsArea();
+            }
+            refreshStatusList();
+        }
+    }
+
+    /*
+     * Update the button enablements
+     */
+    private void updateEnablements() {
+        Button details = getButton(IDialogConstants.DETAILS_ID);
+        if (details !is null) {
+            details.setEnabled(true);
+        }
+    }
+
+    /**
+     * This method sets the message in the message label.
+     *
+     * @param messageString -
+     *            the String for the message area
+     */
+    private void setMessage(String messageString) {
+        // must not set null text in a label
+        message = messageString is null ? "" : messageString; //$NON-NLS-1$
+        if (messageLabel is null || messageLabel.isDisposed()) {
+            return;
+        }
+        messageLabel.setText(message);
+    }
+
+    /**
+     * Create an area that allow the user to select one of multiple jobs that
+     * have reported errors
+     *
+     * @param parent -
+     *            the parent of the area
+     */
+    private void createStatusListArea(Composite parent) {
+        // Display a list of jobs that have reported errors
+        statusListViewer = new TableViewer(parent, SWT.SINGLE | SWT.H_SCROLL
+                | SWT.V_SCROLL | SWT.BORDER);
+        statusListViewer.setComparator(getViewerComparator());
+        Control control = statusListViewer.getControl();
+        GridData data = new GridData(GridData.FILL_BOTH
+                | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
+        data.heightHint = convertHeightInCharsToPixels(10);
+        control.setLayoutData(data);
+        statusListViewer.setContentProvider(getStatusContentProvider());
+        statusListViewer.setLabelProvider(getStatusListLabelProvider());
+        statusListViewer
+                .addSelectionChangedListener(new class ISelectionChangedListener {
+                    public void selectionChanged(SelectionChangedEvent event) {
+                        handleSelectionChange();
+                    }
+                });
+        applyDialogFont(parent);
+        statusListViewer.setInput(this);
+    }
+
+    /**
+     * Return the label provider for the status list.
+     *
+     * @return CellLabelProvider
+     */
+    private CellLabelProvider getStatusListLabelProvider() {
+        return new class CellLabelProvider {
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.jface.viewers.CellLabelProvider#update(org.eclipse.jface.viewers.ViewerCell)
+             */
+            public void update(ViewerCell cell) {
+                cell.setText((cast(IStatus) cell.getElement()).getMessage());
+
+            }
+        };
+    }
+
+    /**
+     * Return the content provider for the statuses.
+     *
+     * @return IStructuredContentProvider
+     */
+    private IStructuredContentProvider getStatusContentProvider() {
+        return new class IStructuredContentProvider {
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
+             */
+            public Object[] getElements(Object inputElement) {
+                return statuses.toArray();
+            }
+
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.jface.viewers.IContentProvider#dispose()
+             */
+            public void dispose() {
+
+            }
+
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
+             *      java.lang.Object, java.lang.Object)
+             */
+            public void inputChanged(Viewer viewer, Object oldInput,
+                    Object newInput) {
+
+            }
+        };
+    }
+
+    /*
+     * Return whether there are multiple errors to be displayed
+     */
+    private bool isMultipleStatusDialog() {
+        return statuses.size() > 1;
+    }
+
+    /**
+     * Return a viewer sorter for looking at the jobs.
+     *
+     * @return ViewerSorter
+     */
+    private ViewerComparator getViewerComparator() {
+        return new class ViewerComparator {
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer,
+             *      java.lang.Object, java.lang.Object)
+             */
+            public int compare(Viewer testViewer, Object e1, Object e2) {
+                String message1 = (cast(IStatus) e1).getMessage();
+                String message2 = (cast(IStatus) e2).getMessage();
+                if (message1 is null)
+                    return 1;
+                if (message2 is null)
+                    return -1;
+
+                //TODO: was compareTo, is this sufficient?
+                return message1 < message2;
+            }
+        };
+    }
+
+    /**
+     * Refresh the contents of the viewer.
+     */
+    void refreshStatusList() {
+        if (statusListViewer !is null
+                && !statusListViewer.getControl().isDisposed()) {
+            statusListViewer.refresh();
+            Point newSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
+            getShell().setSize(newSize);
+        }
+    }
+
+    /**
+     * Get the single selection. Return null if the selection is not just one
+     * element.
+     *
+     * @return IStatus or <code>null</code>.
+     */
+    private IStatus getSingleSelection() {
+        ISelection rawSelection = statusListViewer.getSelection();
+        if (rawSelection !is null
+                && cast(IStructuredSelection)rawSelection ) {
+            IStructuredSelection selection = cast(IStructuredSelection) rawSelection;
+            if (selection.size() is 1) {
+                return cast(IStatus) selection.getFirstElement();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * The selection in the multiple job list has changed. Update widget
+     * enablements and repopulate the list.
+     */
+    void handleSelectionChange() {
+        IStatus newSelection = getSingleSelection();
+        setStatus(newSelection);
+        updateEnablements();
+        showDetailsArea();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.ErrorDialog#shouldShowDetailsButton()
+     */
+    protected override bool shouldShowDetailsButton() {
+        return true;
+    }
+
+    /**
+     * Add the status to the receiver.
+     * @param status
+     */
+    public void addStatus(IStatus status) {
+        statuses.add(cast(Object)status);
+        refresh();
+
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/StatusHandler.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.util.StatusHandler;
+
+import org.eclipse.core.runtime.IStatus;
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A mechanism to handle statuses throughout JFace.
+ * <p>
+ * Clients may provide their own implementation to change how statuses are
+ * handled from within JFace.
+ * </p>
+ *
+ * @see org.eclipse.jface.util.Policy#getStatusHandler()
+ * @see org.eclipse.jface.util.Policy#setStatusHandler(StatusHandler)
+ *
+ * @since 3.4
+ */
+abstract public class StatusHandler {
+
+    /**
+     * Show the given status.
+     *
+     * @param status
+     *            status to handle
+     * @param title
+     *            title for the status
+     */
+    abstract public void show(IStatus status, String title);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/TransferDragSourceListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.util.TransferDragSourceListener;
+
+import org.eclipse.swt.dnd.DragSourceListener;
+import org.eclipse.swt.dnd.Transfer;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A <code>TransferDragSourceListener</code> is a <code>DragSourceListener</code>
+ * that can handle one type of SWT {@link Transfer}.
+ * The purpose of a <code>TransferDragSourceListener</code> is to:
+ * <ul>
+ *   <li>Determine enablement for a drag operation. A <code>TransferDragSourceListener</code>
+ *  will not be used in a drag operation if the <code>DragSourceEvent#doit</code> field
+ *  is set to false in <code>DragSourceListener#dragStart(DragSourceEvent)</code>.
+ *   <li>Set data for a single type of drag and <code>Transfer</code> type.
+ * </ul>
+ * <p>
+ * A <code>DelegatingDragAdapter</code> allows these functions to be implemented
+ * separately for unrelated types of drags. <code>DelegatingDragAdapter</code> then
+ * combines the function of each <code>TransferDragSourceListener</code>, while
+ * allowing them to be implemented as if they were the only <code>DragSourceListener</code>.
+ * </p>
+ * @since 3.0
+ */
+public interface TransferDragSourceListener : DragSourceListener {
+    /**
+     * Returns the <code>Transfer</code> type that this listener can provide data for.
+     *
+     * @return the <code>Transfer</code> associated with this listener
+     */
+    Transfer getTransfer();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/TransferDropTargetListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.util.TransferDropTargetListener;
+
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.DropTargetListener;
+import org.eclipse.swt.dnd.Transfer;
+
+import java.lang.all;
+
+/**
+ * A <code>TransferDropTargetListener</code> is a <code>DropTragetListener</code>
+ * that handles one type of SWT {@link Transfer}.
+ * The purpose of a <code>TransferDropTargetListener</code> is to:
+ * <ul>
+ *   <li>Determine enablement for a drop operation. A <code>TransferDropTargetListener</code>
+ *      will not be used if <code>isEnabled</code> returns false.
+ *   <li>When enabled, optionally show feedback on the <code>DropTarget</code>.
+ *   <li>Perform the actual drop
+ * </ul>
+ * A <code>DelegatingDropAdapter</code> allows these functions to be implemented
+ * separately for unrelated types of drags. <code>DelegatingDropAdapter</code> then
+ * combines the function of each <code>TransferDropTargetListener</code>, while
+ * allowing them to be implemented as if they were the only <code>DragSourceListener</code>.
+ * @since 3.0
+ */
+public interface TransferDropTargetListener : DropTargetListener {
+    /**
+     * Returns the <code>Transfer</code> type that this listener can
+     * accept a drop operation for.
+     *
+     * @return the <code>Transfer</code> for this listener
+     */
+    Transfer getTransfer();
+
+    /**
+     * Returns <code>true</code> if this listener can handle the drop
+     * based on the given <code>DropTargetEvent</code>.
+     * <p>
+     * This method is called by the <code>DelegatingDropAdapter</code> only
+     * if the <code>DropTargetEvent</code> contains a transfer data type
+     * supported by this listener. The <code>Transfer</code> returned by the
+     * <code>#getTransfer()</code> method is used for this purpose.
+     * </p>
+     *
+     * @param event the drop target event
+     * @return <code>true</code> if the listener is enabled for the given
+     *  drop target event.
+     */
+    bool isEnabled(DropTargetEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/util/Util.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,543 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.util.Util;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import java.lang.all;
+import java.util.Set;
+import tango.core.Exception;
+private extern(C) int _d_isbaseof(ClassInfo *b, ClassInfo *c);
+
+/**
+ * <p>
+ * A static class providing utility methods to all of JFace.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class Util {
+
+//     /**
+//      * An unmodifiable, empty, sorted set. This value is guaranteed to never
+//      * change and never be <code>null</code>.
+//      */
+//     public static final SortedSet EMPTY_SORTED_SET = Collections
+//             .unmodifiableSortedSet(new TreeSet());
+
+    /**
+     * A common zero-length string. It avoids needing write <code>NON-NLS</code>
+     * next to code fragments. It's also a bit clearer to read.
+     */
+    public static final String ZERO_LENGTH_STRING = ""; //$NON-NLS-1$
+
+    /**
+     * Verifies that the given object is an instance of the given class.
+     *
+     * @param object
+     *            The object to check; may be <code>null</code>.
+     * @param c
+     *            The class which the object should be; must not be
+     *            <code>null</code>.
+     */
+    public static final void assertInstance(Object object, ClassInfo c) {
+        assertInstance(object, c, false);
+    }
+
+    /**
+     * Verifies the given object is an instance of the given class. It is
+     * possible to specify whether the object is permitted to be
+     * <code>null</code>.
+     *
+     * @param object
+     *            The object to check; may be <code>null</code>.
+     * @param c
+     *            The class which the object should be; must not be
+     *            <code>null</code>.
+     * @param allowNull
+     *            Whether the object is allowed to be <code>null</code>.
+     */
+    private static final void assertInstance(Object object,
+            ClassInfo c,  bool allowNull) {
+        if (object is null && allowNull) {
+            return;
+        }
+
+        if (object is null || c is null) {
+            throw new NullPointerException();
+        } else if (!_d_isbaseof( &object.classinfo, &c ) ) {
+            throw new IllegalArgumentException(null);
+        }
+    }
+
+    /**
+     * Compares two bool values. <code>false</code> is considered to be
+     * "less than" <code>true</code>.
+     *
+     * @param left
+     *            The left value to compare
+     * @param right
+     *            The right value to compare
+     * @return <code>-1</code> if the left is <code>false</code> and the
+     *         right is <code>true</code>. <code>1</code> if the opposite
+     *         is true. If they are equal, then it returns <code>0</code>.
+     */
+    public static final int compare(bool left, bool right) {
+        return left is false ? (right is true ? -1 : 0) : 1;
+    }
+
+    /**
+     * Compares two integer values.
+     *
+     * @param left
+     *            The left value to compare
+     * @param right
+     *            The right value to compare
+     * @return <code>left - right</code>
+     */
+    public static final int compare(int left, int right) {
+        return left - right;
+    }
+
+    /**
+     * Compares to comparable objects -- defending against <code>null</code>.
+     *
+     * @param left
+     *            The left object to compare; may be <code>null</code>.
+     * @param right
+     *            The right object to compare; may be <code>null</code>.
+     * @return The result of the comparison. <code>null</code> is considered
+     *         to be the least possible value.
+     */
+    public static final int compare(Comparable left,
+            Comparable right) {
+        if (left is null && right is null) {
+            return 0;
+        } else if (left is null) {
+            return -1;
+        } else if (right is null) {
+            return 1;
+        } else {
+            return left.compareTo(cast(Object)right);
+        }
+    }
+
+    /**
+     * Compares two arrays of comparable objects -- accounting for
+     * <code>null</code>.
+     *
+     * @param left
+     *            The left array to be compared; may be <code>null</code>.
+     * @param right
+     *            The right array to be compared; may be <code>null</code>.
+     * @return The result of the comparison. <code>null</code> is considered
+     *         to be the least possible value. A shorter array is considered
+     *         less than a longer array.
+     */
+    public static final int compare(Comparable[] left,
+            Comparable[] right) {
+        if (left is null && right is null) {
+            return 0;
+        } else if (left is null) {
+            return -1;
+        } else if (right is null) {
+            return 1;
+        } else {
+            int l = left.length;
+            int r = right.length;
+
+            if (l !is r) {
+                return l - r;
+            }
+
+            for (int i = 0; i < l; i++) {
+                int compareTo = compare(left[i], right[i]);
+
+                if (compareTo !is 0) {
+                    return compareTo;
+                }
+            }
+
+            return 0;
+        }
+    }
+    public static final int compare(String left, String right) {
+        return left < right;
+    }
+
+//     /**
+//      * Compares two lists -- account for <code>null</code>. The lists must
+//      * contain comparable objects.
+//      *
+//      * @param left
+//      *            The left list to compare; may be <code>null</code>. This
+//      *            list must only contain instances of <code>Comparable</code>.
+//      * @param right
+//      *            The right list to compare; may be <code>null</code>. This
+//      *            list must only contain instances of <code>Comparable</code>.
+//      * @return The result of the comparison. <code>null</code> is considered
+//      *         to be the least possible value. A shorter list is considered less
+//      *         than a longer list.
+//      */
+//     public static final int compare(SeqView!(Object) left, SeqView!(Object) right) {
+//         if (left is null && right is null) {
+//             return 0;
+//         } else if (left is null) {
+//             return -1;
+//         } else if (right is null) {
+//             return 1;
+//         } else {
+//             int l = left.size();
+//             int r = right.size();
+//
+//             if (l !is r) {
+//                 return l - r;
+//             }
+//
+//             for (int i = 0; i < l; i++) {
+//                 int compareTo = compare((Comparable) left.get(i),
+//                         (Comparable) right.get(i));
+//
+//                 if (compareTo !is 0) {
+//                     return compareTo;
+//                 }
+//             }
+//
+//             return 0;
+//         }
+//     }
+
+    /**
+     * Tests whether the first array ends with the second array.
+     *
+     * @param left
+     *            The array to check (larger); may be <code>null</code>.
+     * @param right
+     *            The array that should be a subsequence (smaller); may be
+     *            <code>null</code>.
+     * @param equals
+     *            Whether the two array are allowed to be equal.
+     * @return <code>true</code> if the second array is a subsequence of the
+     *         array list, and they share end elements.
+     */
+    public static final bool endsWith(Object[] left,
+            Object[] right, bool equals) {
+        if (left is null || right is null) {
+            return false;
+        }
+
+        int l = left.length;
+        int r = right.length;
+
+        if (r > l || !equals && r is l) {
+            return false;
+        }
+
+        for (int i = 0; i < r; i++) {
+            if (!Util.opEquals(left[l - i - 1], right[r - i - 1])) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks whether the two objects are <code>null</code> -- allowing for
+     * <code>null</code>.
+     *
+     * @param left
+     *            The left object to compare; may be <code>null</code>.
+     * @param right
+     *            The right object to compare; may be <code>null</code>.
+     * @return <code>true</code> if the two objects are equivalent;
+     *         <code>false</code> otherwise.
+     */
+    public static final bool opEquals(Object left, Object right) {
+        return left is null ? right is null : ((right !is null) && left
+                .opEquals(right));
+    }
+    public static final bool opEquals(String left, String right) {
+        return left == right;
+    }
+
+    /**
+     * Tests whether two arrays of objects are equal to each other. The arrays
+     * must not be <code>null</code>, but their elements may be
+     * <code>null</code>.
+     *
+     * @param leftArray
+     *            The left array to compare; may be <code>null</code>, and
+     *            may be empty and may contain <code>null</code> elements.
+     * @param rightArray
+     *            The right array to compare; may be <code>null</code>, and
+     *            may be empty and may contain <code>null</code> elements.
+     * @return <code>true</code> if the arrays are equal length and the
+     *         elements at the same position are equal; <code>false</code>
+     *         otherwise.
+     */
+    public static final bool opEquals(Object[] leftArray,
+            Object[] rightArray) {
+        if (leftArray is rightArray) {
+            return true;
+        }
+
+        if (leftArray is null) {
+            return (rightArray is null);
+        } else if (rightArray is null) {
+            return false;
+        }
+
+        if (leftArray.length !is rightArray.length) {
+            return false;
+        }
+
+        for (int i = 0; i < leftArray.length; i++) {
+            Object left = leftArray[i];
+            Object right = rightArray[i];
+            bool equal = ((left is null) ? (right is null) : (left.opEquals(right))) !is 0;
+            if (!equal) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public static final bool opEquals(String[] leftArray, String[] rightArray) {
+        if (leftArray.length !is rightArray.length) {
+            return false;
+        }
+
+        for (int i = 0; i < leftArray.length; i++) {
+            String left = leftArray[i];
+            String right = rightArray[i];
+            if (left != right) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Provides a hash code based on the given integer value.
+     *
+     * @param i
+     *            The integer value
+     * @return <code>i</code>
+     */
+    public static final hash_t toHash(int i) {
+        return i;
+    }
+
+    /**
+     * Provides a hash code for the object -- defending against
+     * <code>null</code>.
+     *
+     * @param object
+     *            The object for which a hash code is required.
+     * @return <code>object.hashCode</code> or <code>0</code> if
+     *         <code>object</code> if <code>null</code>.
+     */
+    public static final hash_t toHash( Object object) {
+        return object !is null ? object.toHash() : 0;
+    }
+
+    /**
+     * Computes the hash code for an array of objects, but with defense against
+     * <code>null</code>.
+     *
+     * @param objects
+     *            The array of objects for which a hash code is needed; may be
+     *            <code>null</code>.
+     * @return The hash code for <code>objects</code>; or <code>0</code> if
+     *         <code>objects</code> is <code>null</code>.
+     */
+    public static final hash_t toHash(Object[] objects) {
+        if (objects is null) {
+            return 0;
+        }
+
+        int hashCode = 89;
+        for (int i = 0; i < objects.length; i++) {
+            final Object object = objects[i];
+            if (object !is null) {
+                hashCode = hashCode * 31 + object.toHash();
+            }
+        }
+
+        return hashCode;
+    }
+    public static final hash_t toHash(String str) {
+        return java.lang.all.toHash(str);
+    }
+    public static final hash_t toHash(String[] objects) {
+        int hashCode = 89;
+        for (int i = 0; i < objects.length; i++) {
+            auto object = objects[i];
+            hashCode = hashCode * 31 + toHash(object);
+        }
+
+        return hashCode;
+    }
+
+    /**
+     * Checks whether the second array is a subsequence of the first array, and
+     * that they share common starting elements.
+     *
+     * @param left
+     *            The first array to compare (large); may be <code>null</code>.
+     * @param right
+     *            The second array to compare (small); may be <code>null</code>.
+     * @param equals
+     *            Whether it is allowed for the two arrays to be equivalent.
+     * @return <code>true</code> if the first arrays starts with the second
+     *         list; <code>false</code> otherwise.
+     */
+    public static final bool startsWith(Object[] left,
+            Object[] right, bool equals) {
+        if (left is null || right is null) {
+            return false;
+        }
+
+        int l = left.length;
+        int r = right.length;
+
+        if (r > l || !equals && r is l) {
+            return false;
+        }
+
+        for (int i = 0; i < r; i++) {
+            if (!opEquals(left[i], right[i])) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Converts an array into a string representation that is suitable for
+     * debugging.
+     *
+     * @param array
+     *            The array to convert; may be <code>null</code>.
+     * @return The string representation of the array; never <code>null</code>.
+     */
+    public static final String toString(Object[] array) {
+        if (array is null) {
+            return "null"; //$NON-NLS-1$
+        }
+
+        final StringBuffer buffer = new StringBuffer();
+        buffer.append('[');
+
+        final int length = array.length;
+        for (int i = 0; i < length; i++) {
+            if (i !is 0) {
+                buffer.append(',');
+            }
+            Object object = array[i];
+            String element = (object is null ) ? "null" : object.toString;
+            buffer.append(element);
+        }
+        buffer.append(']');
+
+        return buffer.toString();
+    }
+
+    /**
+     * Provides a translation of a particular key from the resource bundle.
+     *
+     * @param resourceBundle
+     *            The key to look up in the resource bundle; should not be
+     *            <code>null</code>.
+     * @param key
+     *            The key to look up in the resource bundle; should not be
+     *            <code>null</code>.
+     * @param defaultString
+     *            The value to return if the resource cannot be found; may be
+     *            <code>null</code>.
+     * @return The value of the translated resource at <code>key</code>. If
+     *         the key cannot be found, then it is simply the
+     *         <code>defaultString</code>.
+     */
+    public static final String translateString(
+            ResourceBundle resourceBundle, String key,
+            String defaultString) {
+        if (resourceBundle !is null && key !is null) {
+            try {
+                String translatedString = resourceBundle.getString(key);
+
+                if (translatedString !is null) {
+                    return translatedString;
+                }
+            } catch (MissingResourceException eMissingResource) {
+                // Such is life. We'll return the key
+            }
+        }
+
+        return defaultString;
+    }
+
+    /**
+     * Foundation replacement for String.replaceAll(*).
+     *
+     * @param src the starting string.
+     * @param find the string to find.
+     * @param replacement the string to replace.
+     * @return The new string.
+     * @since 3.4
+     */
+    public static final String replaceAll(String src, String find, String replacement) {
+        final int len = src.length;
+        final int findLen = find.length;
+
+        int idx = src.indexOf(find);
+        if (idx < 0) {
+            return src;
+        }
+
+        StringBuffer buf = new StringBuffer();
+        int beginIndex = 0;
+        while (idx !is -1 && idx < len) {
+            buf.append(src.substring(beginIndex, idx));
+            buf.append(replacement);
+
+            beginIndex = idx + findLen;
+            if (beginIndex < len) {
+                idx = src.indexOf(find, beginIndex);
+            } else {
+                idx = -1;
+            }
+        }
+        if (beginIndex<len) {
+            buf.append(src.substring(beginIndex, (idx is -1 ? len : idx)));
+        }
+        return buf.toString();
+    }
+
+    /**
+     * This class should never be constructed.
+     */
+    private this() {
+        // Not allowed.
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/AbstractComboBoxCellEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Tom Schindl and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation (bug 174739)
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module org.eclipse.jface.viewers.AbstractComboBoxCellEditor;
+
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.widgets.Composite;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Abstract base class for Cell-Editors presented as combo boxes
+ *
+ * @since 3.4
+ *
+ */
+abstract class AbstractComboBoxCellEditor : CellEditor {
+    /**
+     * The list is dropped down when the activation is done through the mouse
+     */
+    public static const int DROP_DOWN_ON_MOUSE_ACTIVATION = 1;
+
+    /**
+     * The list is dropped down when the activation is done through the keyboard
+     */
+    public static const int DROP_DOWN_ON_KEY_ACTIVATION = 1 << 1;
+
+    /**
+     * The list is dropped down when the activation is done without
+     * ui-interaction
+     */
+    public static const int DROP_DOWN_ON_PROGRAMMATIC_ACTIVATION = 1 << 2;
+
+    /**
+     * The list is dropped down when the activation is done by traversing from
+     * cell to cell
+     */
+    public static const int DROP_DOWN_ON_TRAVERSE_ACTIVATION = 1 << 3;
+
+    private int activationStyle = SWT.NONE;
+
+    /**
+     * Create a new cell-editor
+     *
+     * @param parent
+     *            the parent of the combo
+     * @param style
+     *            the style used to create the combo
+     */
+    this(Composite parent, int style) {
+        super(parent, style);
+    }
+
+    /**
+     * Creates a new cell editor with no control and no st of choices.
+     * Initially, the cell editor has no cell validator.
+     *
+     */
+    this() {
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.CellEditor#activate(org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent)
+     */
+    public void activate(ColumnViewerEditorActivationEvent activationEvent) {
+        super.activate(activationEvent);
+        if (activationStyle !is SWT.NONE) {
+            bool dropDown = false;
+            if ((activationEvent.eventType is ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION || activationEvent.eventType is ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION)
+                    && (activationStyle & DROP_DOWN_ON_MOUSE_ACTIVATION) !is 0 ) {
+                dropDown = true;
+            } else if (activationEvent.eventType is ColumnViewerEditorActivationEvent.KEY_PRESSED
+                    && (activationStyle & DROP_DOWN_ON_KEY_ACTIVATION) !is 0 ) {
+                dropDown = true;
+            } else if (activationEvent.eventType is ColumnViewerEditorActivationEvent.PROGRAMMATIC
+                    && (activationStyle & DROP_DOWN_ON_PROGRAMMATIC_ACTIVATION) !is 0) {
+                dropDown = true;
+            } else if (activationEvent.eventType is ColumnViewerEditorActivationEvent.TRAVERSAL
+                    && (activationStyle & DROP_DOWN_ON_TRAVERSE_ACTIVATION) !is 0) {
+                dropDown = true;
+            }
+
+            if (dropDown) {
+                getControl().getDisplay().asyncExec(new class Runnable {
+
+                    public void run() {
+                        (cast(CCombo) getControl()).setListVisible(true);
+                    }
+
+                });
+
+            }
+        }
+    }
+
+    /**
+     * This method allows to control how the combo reacts when activated
+     *
+     * @param activationStyle
+     *            the style used
+     */
+    public void setActivationStyle(int activationStyle) {
+        this.activationStyle = activationStyle;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/AbstractListViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,566 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ *     Chris Longfield <clongfield@internap.com> - Fix for Bug 70856
+ *     Tom Schindl - fix for bug 157309
+ *     Brad Reynolds - bug 141435
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.AbstractListViewer;
+
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.viewers.IElementComparer;
+
+
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Collection;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * Abstract base class for viewers that contain lists of items (such as a combo or list).
+ * Most of the viewer implementation is in this base class, except for the minimal code that
+ * actually communicates with the underlying widget.
+ *
+ * @see org.eclipse.jface.viewers.ListViewer
+ * @see org.eclipse.jface.viewers.ComboViewer
+ *
+ * @since 3.0
+ */
+public abstract class AbstractListViewer : StructuredViewer {
+    alias StructuredViewer.internalRefresh internalRefresh;
+    alias StructuredViewer.setSelectionToWidget setSelectionToWidget;
+
+    /**
+     * A list of viewer elements (element type: <code>Object</code>).
+     */
+    private List listMap;
+
+    /**
+     * Adds the given string to the underlying widget at the given index
+     *
+     * @param string the string to add
+     * @param index position to insert the string into
+     */
+    protected abstract void listAdd(String string, int index);
+
+    /**
+     * Sets the text of the item at the given index in the underlying widget.
+     *
+     * @param index index to modify
+     * @param string new text
+     */
+    protected abstract void listSetItem(int index, String string);
+
+    /**
+     * Returns the zero-relative indices of the items which are currently
+     * selected in the underlying widget.  The array is empty if no items are selected.
+     * <p>
+     * Note: This is not the actual structure used by the receiver
+     * to maintain its selection, so modifying the array will
+     * not affect the receiver.
+     * </p>
+     * @return the array of indices of the selected items
+     */
+    protected abstract int[] listGetSelectionIndices();
+
+    /**
+     * Returns the number of items contained in the underlying widget.
+     *
+     * @return the number of items
+     */
+    protected abstract int listGetItemCount();
+
+    /**
+     * Sets the underlying widget's items to be the given array of items.
+     *
+     * @param labels the array of label text
+     */
+    protected abstract void listSetItems(String[] labels);
+
+    /**
+     * Removes all of the items from the underlying widget.
+     */
+    protected abstract void listRemoveAll();
+
+    /**
+     * Removes the item from the underlying widget at the given
+     * zero-relative index.
+     *
+     * @param index the index for the item
+     */
+    protected abstract void listRemove(int index);
+
+    /**
+     * Selects the items at the given zero-relative indices in the underlying widget.
+     * The current selection is cleared before the new items are selected.
+     * <p>
+     * Indices that are out of range and duplicate indices are ignored.
+     * If the receiver is single-select and multiple indices are specified,
+     * then all indices are ignored.
+     *
+     * @param ixs the indices of the items to select
+     */
+    protected abstract void listSetSelection(int[] ixs);
+
+    /**
+     * Shows the selection.  If the selection is already showing in the receiver,
+     * this method simply returns.  Otherwise, the items are scrolled until
+     * the selection is visible.
+     */
+    protected abstract void listShowSelection();
+
+    /**
+     * Deselects all selected items in the underlying widget.
+     */
+    protected abstract void listDeselectAll();
+
+    public this(){
+        listMap = new ArrayList();
+    }
+
+    /**
+     * Adds the given elements to this list viewer.
+     * If this viewer does not have a sorter, the elements are added at the end
+     * in the order given; otherwise the elements are inserted at appropriate positions.
+     * <p>
+     * This method should be called (by the content provider) when elements
+     * have been added to the model, in order to cause the viewer to accurately
+     * reflect the model. This method only affects the viewer, not the model.
+     * </p>
+     *
+     * @param elements the elements to add
+     */
+    public void add(Object[] elements) {
+        assertElementsNotNull(elements);
+        Object[] filtered = filter(elements);
+        ILabelProvider labelProvider = cast(ILabelProvider) getLabelProvider();
+        for (int i = 0; i < filtered.length; i++) {
+            Object element = filtered[i];
+            int ix = indexForElement(element);
+            insertItem(labelProvider, element, ix);
+        }
+    }
+
+    private void insertItem(ILabelProvider labelProvider, Object element, int index) {
+        listAdd(getLabelProviderText(labelProvider, element), index);
+        listMap.add(index, element);
+        mapElement(element, getControl()); // must map it, since findItem only looks in map, if enabled
+    }
+
+    /**
+     * Inserts the given element into this list viewer at the given position.
+     * If this viewer has a sorter, the position is ignored and the element is
+     * inserted at the correct position in the sort order.
+     * <p>
+     * This method should be called (by the content provider) when elements have
+     * been added to the model, in order to cause the viewer to accurately
+     * reflect the model. This method only affects the viewer, not the model.
+     * </p>
+     *
+     * @param element
+     *            the element
+     * @param position
+     *            a 0-based position relative to the model, or -1 to indicate
+     *            the last position
+     * @since 3.3
+     */
+    public void insert(Object element, int position) {
+        if (getComparator() !is null || hasFilters()) {
+            add(element);
+            return;
+        }
+
+        insertItem(cast(ILabelProvider) getLabelProvider(), element, position);
+    }
+
+
+    /**
+     * Return the text for the element from the labelProvider.
+     * If it is null then return the empty String.
+     * @param labelProvider ILabelProvider
+     * @param element
+     * @return String. Return the emptyString if the labelProvider
+     * returns null for the text.
+     *
+     * @since 3.1
+     */
+    private String getLabelProviderText(ILabelProvider labelProvider, Object element){
+        String text = labelProvider.getText(element);
+        if(text is null) {
+            return "";//$NON-NLS-1$
+        }
+        return text;
+    }
+
+    /**
+     * Adds the given element to this list viewer.
+     * If this viewer does not have a sorter, the element is added at the end;
+     * otherwise the element is inserted at the appropriate position.
+     * <p>
+     * This method should be called (by the content provider) when a single element
+     * has been added to the model, in order to cause the viewer to accurately
+     * reflect the model. This method only affects the viewer, not the model.
+     * Note that there is another method for efficiently processing the simultaneous
+     * addition of multiple elements.
+     * </p>
+     *
+     * @param element the element
+     */
+    public void add(Object element) {
+        add([ element ]);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     * Since SWT.List doesn't use items we always return the List itself.
+     */
+    protected override Widget doFindInputItem(Object element) {
+        if (element !is null && opEquals(element, getRoot())) {
+            return getControl();
+        }
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     * Since SWT.List doesn't use items we always return the List itself.
+     */
+    protected override Widget doFindItem(Object element) {
+        if (element !is null) {
+            if (listMapContains(element)) {
+                return getControl();
+            }
+        }
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     */
+    protected override void doUpdateItem(Widget data, Object element, bool fullMap) {
+        if (element !is null) {
+            int ix = getElementIndex(element);
+            if (ix >= 0) {
+                ILabelProvider labelProvider = cast(ILabelProvider) getLabelProvider();
+                listSetItem(ix, getLabelProviderText(labelProvider,element));
+            }
+        }
+    }
+
+    /**
+     * Returns the element with the given index from this list viewer.
+     * Returns <code>null</code> if the index is out of range.
+     *
+     * @param index the zero-based index
+     * @return the element at the given index, or <code>null</code> if the
+     *   index is out of range
+     */
+    public Object getElementAt(int index) {
+        if (index >= 0 && index < listMap.size()) {
+            return listMap.get(index);
+        }
+        return null;
+    }
+
+    /**
+     * The list viewer implementation of this <code>Viewer</code> framework
+     * method returns the label provider, which in the case of list
+     * viewers will be an instance of <code>ILabelProvider</code>.
+     */
+    public override IBaseLabelProvider getLabelProvider() {
+        return super.getLabelProvider();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on Viewer.
+     */
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     */
+    protected override List getSelectionFromWidget() {
+        int[] ixs = listGetSelectionIndices();
+        ArrayList list = new ArrayList(ixs.length);
+        for (int i = 0; i < ixs.length; i++) {
+            Object e = getElementAt(ixs[i]);
+            if (e !is null) {
+                list.add(e);
+            }
+        }
+        return list;
+    }
+
+    /**
+     * @param element the element to insert
+     * @return the index where the item should be inserted.
+     */
+    protected int indexForElement(Object element) {
+        ViewerComparator comparator = getComparator();
+        if (comparator is null) {
+            return listGetItemCount();
+        }
+        int count = listGetItemCount();
+        int min = 0, max = count - 1;
+        while (min <= max) {
+            int mid = (min + max) / 2;
+            Object data = listMap.get(mid);
+            int compare = comparator.compare(this, data, element);
+            if (compare is 0) {
+                // find first item > element
+                while (compare is 0) {
+                    ++mid;
+                    if (mid >= count) {
+                        break;
+                    }
+                    data = listMap.get(mid);
+                    compare = comparator.compare(this, data, element);
+                }
+                return mid;
+            }
+            if (compare < 0) {
+                min = mid + 1;
+            } else {
+                max = mid - 1;
+            }
+        }
+        return min;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on Viewer.
+     */
+    protected override void inputChanged(Object input, Object oldInput) {
+        listMap.clear();
+        Object[] children = getSortedChildren(getRoot());
+        int size = children.length;
+
+        listRemoveAll();
+        String[] labels = new String[size];
+        for (int i = 0; i < size; i++) {
+            Object el = children[i];
+            labels[i] = getLabelProviderText(cast(ILabelProvider) getLabelProvider(),el);
+            listMap.add(el);
+            mapElement(el, getControl()); // must map it, since findItem only looks in map, if enabled
+        }
+        listSetItems(labels);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     */
+    protected override void internalRefresh(Object element) {
+        Control list = getControl();
+        if (element is null || opEquals(element, getRoot())) {
+            // the parent
+            if (listMap !is null) {
+                listMap.clear();
+            }
+            unmapAllElements();
+            List selection = getSelectionFromWidget();
+
+            int topIndex = -1;
+            if (selection is null || selection.isEmpty()) {
+                topIndex = listGetTopIndex();
+            }
+
+            Object[] children = null;
+            list.setRedraw(false);
+            try {
+                listRemoveAll();
+
+                children = getSortedChildren(getRoot());
+                String[] items = new String[children.length];
+
+                ILabelProvider labelProvider = cast(ILabelProvider) getLabelProvider();
+
+                for (int i = 0; i < items.length; i++) {
+                    Object el = children[i];
+                    items[i] = getLabelProviderText(labelProvider, el);
+                    listMap.add(el);
+                    mapElement(el, list); // must map it, since findItem only looks in map, if enabled
+                }
+
+                listSetItems(items);
+            } finally {
+                list.setRedraw(true);
+            }
+
+            if (topIndex is -1) {
+                setSelectionToWidget(selection, false);
+            } else {
+                listSetTopIndex(Math.min(topIndex, children.length));
+            }
+        } else {
+            doUpdateItem(list, element, true);
+        }
+    }
+
+    /**
+     * Returns the index of the item currently at the top of the viewable area.
+     * <p>
+     * Default implementation returns -1.
+     * </p>
+     * @return index, -1 for none
+     * @since 3.3
+     */
+    protected int listGetTopIndex(){
+        return -1;
+    }
+
+    /**
+     * Sets the index of the item to be at the top of the viewable area.
+     * <p>
+     * Default implementation does nothing.
+     * </p>
+     * @param index the given index. -1 for none.  index will always refer to a valid index.
+     * @since 3.3
+     */
+    protected void listSetTopIndex(int index) {
+    }
+
+    /**
+     * Removes the given elements from this list viewer.
+     *
+     * @param elements the elements to remove
+     */
+    private void internalRemove(Object[] elements) {
+        Object input = getInput();
+        for (int i = 0; i < elements.length; ++i) {
+            if ( opEquals(elements[i], input)) {
+                setInput(null);
+                return;
+            }
+            int ix = getElementIndex(elements[i]);
+            if (ix >= 0) {
+                listRemove(ix);
+                listMap.remove(ix);
+                unmapElement(elements[i], getControl());
+            }
+        }
+    }
+
+    /**
+     * Removes the given elements from this list viewer.
+     * The selection is updated if required.
+     * <p>
+     * This method should be called (by the content provider) when elements
+     * have been removed from the model, in order to cause the viewer to accurately
+     * reflect the model. This method only affects the viewer, not the model.
+     * </p>
+     *
+     * @param elements the elements to remove
+     */
+    public void remove(Object[] elements) {
+        assertElementsNotNull(elements);
+        if (elements.length is 0) {
+            return;
+        }
+        preservingSelection( dgRunnable( (Object[] elements_){
+            internalRemove(elements_);
+        }, elements));
+    }
+
+    /**
+     * Removes the given element from this list viewer.
+     * The selection is updated if necessary.
+     * <p>
+     * This method should be called (by the content provider) when a single element
+     * has been removed from the model, in order to cause the viewer to accurately
+     * reflect the model. This method only affects the viewer, not the model.
+     * Note that there is another method for efficiently processing the simultaneous
+     * removal of multiple elements.
+     * </p>
+     *
+     * @param element the element
+     */
+    public void remove(Object element) {
+        remove([ element ]);
+    }
+
+    /**
+     * The list viewer implementation of this <code>Viewer</code> framework
+     * method ensures that the given label provider is an instance of
+     * <code>ILabelProvider</code>.
+     *
+     * <b>The optional interfaces {@link IColorProvider} and
+     * {@link IFontProvider} have no effect for this type of viewer</b>
+     */
+    public override void setLabelProvider(IBaseLabelProvider labelProvider) {
+        Assert.isTrue( null !is cast(ILabelProvider)labelProvider );
+        super.setLabelProvider(labelProvider);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     */
+    protected override void setSelectionToWidget(List in_, bool reveal) {
+        if (in_ is null || in_.size() is 0) { // clear selection
+            listDeselectAll();
+        } else {
+            int n = in_.size();
+            int[] ixs = new int[n];
+            int count = 0;
+            for (int i = 0; i < n; ++i) {
+                Object el = in_.get(i);
+                int ix = getElementIndex(el);
+                if (ix >= 0) {
+                    ixs[count++] = ix;
+                }
+            }
+            if (count < n) {
+                System.arraycopy(ixs, 0, ixs = new int[count], 0, count);
+            }
+            listSetSelection(ixs);
+            if (reveal) {
+                listShowSelection();
+            }
+        }
+    }
+
+    /**
+     * Returns the index of the given element in listMap, or -1 if the element cannot be found.
+     * As of 3.3, uses the element comparer if available.
+     *
+     * @param element
+     * @return the index
+     */
+    int getElementIndex(Object element) {
+        IElementComparer comparer = getComparer();
+        if (comparer is null) {
+            return listMap.indexOf(element);
+        }
+        int size = listMap.size();
+        for (int i = 0; i < size; i++) {
+            if (comparer.opEquals(element, listMap.get(i)))
+                return i;
+        }
+        return -1;
+    }
+
+    /**
+     * @param element
+     * @return true if listMap contains the given element
+     *
+     * @since 3.3
+     */
+    private bool listMapContains(Object element) {
+        return getElementIndex(element) !is -1;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/AbstractTableViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1358 @@
+/*******************************************************************************
+ * 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation bug 154329
+ *                                               - fixes in bug 170381, 198665, 200731
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.AbstractTableViewer;
+
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ViewerRow;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ILazyContentProvider;
+import org.eclipse.jface.viewers.ViewerColumn;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.HashSet;
+import tango.core.Array;
+
+/**
+ * This is a widget independent class implementors of
+ * {@link org.eclipse.swt.widgets.Table} like widgets can use to provide a
+ * viewer on top of their widget implementations.
+ *
+ * @since 3.3
+ */
+public abstract class AbstractTableViewer : ColumnViewer {
+    alias ColumnViewer.getLabelProvider getLabelProvider;
+    alias ColumnViewer.setSelectionToWidget setSelectionToWidget;
+
+    private class VirtualManager {
+
+        /**
+         * The currently invisible elements as provided by the content provider
+         * or by addition. This will not be populated by an
+         * ILazyStructuredContentProvider as an ILazyStructuredContentProvider
+         * is only queried on the virtual callback.
+         */
+        private Object[] cachedElements = null;
+
+        /**
+         * Create a new instance of the receiver.
+         *
+         */
+        public this() {
+            addTableListener();
+        }
+
+        /**
+         * Add the listener for SetData on the table
+         */
+        private void addTableListener() {
+            getControl().addListener(SWT.SetData, dgListener( (Event event){
+                /*
+                 * (non-Javadoc)
+                 *
+                 * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+                 */
+                Item item = cast(Item) event.item;
+                final int index = doIndexOf(item);
+                Object element = resolveElement(index);
+                if (element is null) {
+                    // Didn't find it so make a request
+                    // Keep looking if it is not in the cache.
+                    IContentProvider contentProvider = getContentProvider();
+                    // If we are building lazily then request lookup now
+                    if (auto lcp = cast(ILazyContentProvider)contentProvider ) {
+                        lcp.updateElement(index);
+                        return;
+                    }
+                }
+
+                associate(element, item);
+                updateItem(item, element);
+            }));
+        }
+
+        /**
+         * Get the element at index.Resolve it lazily if this is available.
+         *
+         * @param index
+         * @return Object or <code>null</code> if it could not be found
+         */
+        protected Object resolveElement(int index) {
+
+            Object element = null;
+            if (index < cachedElements.length) {
+                element = cachedElements[index];
+            }
+
+            return element;
+        }
+
+        /**
+         * A non visible item has been added.
+         *
+         * @param element
+         * @param index
+         */
+        public void notVisibleAdded(Object element, int index) {
+
+            int requiredCount = doGetItemCount() + 1;
+
+            Object[] newCache = new Object[requiredCount];
+            System.arraycopy(cachedElements, 0, newCache, 0, index);
+            if (index < cachedElements.length) {
+                System.arraycopy(cachedElements, index, newCache, index + 1,
+                        cachedElements.length - index);
+            }
+            newCache[index] = element;
+            cachedElements = newCache;
+
+            doSetItemCount(requiredCount);
+        }
+
+        /**
+         * The elements with the given indices need to be removed from the
+         * cache.
+         *
+         * @param indices
+         */
+        public void removeIndices(int[] indices) {
+            if (indices.length is 1) {
+                removeIndicesFromTo(indices[0], indices[0]);
+            }
+            int requiredCount = doGetItemCount() - indices.length;
+
+            tango.core.Array.sort( indices );
+            Object[] newCache = new Object[requiredCount];
+            int indexInNewCache = 0;
+            int nextToSkip = 0;
+            for (int i = 0; i < cachedElements.length; i++) {
+                if (nextToSkip < indices.length && i is indices[nextToSkip]) {
+                    nextToSkip++;
+                } else {
+                    newCache[indexInNewCache++] = cachedElements[i];
+                }
+            }
+            cachedElements = newCache;
+        }
+
+        /**
+         * The elements between the given indices (inclusive) need to be removed
+         * from the cache.
+         *
+         * @param from
+         * @param to
+         */
+        public void removeIndicesFromTo(int from, int to) {
+            int indexAfterTo = to + 1;
+            Object[] newCache = new Object[cachedElements.length
+                    - (indexAfterTo - from)];
+            System.arraycopy(cachedElements, 0, newCache, 0, from);
+            if (indexAfterTo < cachedElements.length) {
+                System.arraycopy(cachedElements, indexAfterTo, newCache, from,
+                        cachedElements.length - indexAfterTo);
+            }
+        }
+
+        /**
+         * @param element
+         * @return the index of the element in the cache, or null
+         */
+        public int find(Object element) {
+            int res = tango.core.Array.find( cachedElements, element );
+            if( res is cachedElements.length ) res = -1;
+            return res;
+        }
+
+        /**
+         * @param count
+         */
+        public void adjustCacheSize(int count) {
+            if (count is cachedElements.length) {
+                return;
+            } else if (count < cachedElements.length) {
+                Object[] newCache = new Object[count];
+                System.arraycopy(cachedElements, 0, newCache, 0, count);
+                cachedElements = newCache;
+            } else {
+                Object[] newCache = new Object[count];
+                System.arraycopy(cachedElements, 0, newCache, 0,
+                        cachedElements.length);
+                cachedElements = newCache;
+            }
+        }
+
+    }
+
+    private VirtualManager virtualManager;
+
+    /**
+     * Create the new viewer for table like widgets
+     */
+    public this() {
+        super();
+    }
+
+    protected override void hookControl(Control control) {
+        super.hookControl(control);
+        initializeVirtualManager(getControl().getStyle());
+    }
+
+    /**
+     * Initialize the virtual manager to manage the virtual state if the table
+     * is VIRTUAL. If not use the default no-op version.
+     *
+     * @param style
+     */
+    private void initializeVirtualManager(int style) {
+        if ((style & SWT.VIRTUAL) is 0) {
+            return;
+        }
+
+        virtualManager = new VirtualManager();
+    }
+
+    /**
+     * Adds the given elements to this table viewer. If this viewer does not
+     * have a sorter, the elements are added at the end in the order given;
+     * otherwise the elements are inserted at appropriate positions.
+     * <p>
+     * This method should be called (by the content provider) when elements have
+     * been added to the model, in order to cause the viewer to accurately
+     * reflect the model. This method only affects the viewer, not the model.
+     * </p>
+     *
+     * @param elements
+     *            the elements to add
+     */
+    public void add(Object[] elements) {
+        assertElementsNotNull(elements);
+        if (checkBusy())
+            return;
+        Object[] filtered = filter(elements);
+
+        for (int i = 0; i < filtered.length; i++) {
+            Object element = filtered[i];
+            int index = indexForElement(element);
+            createItem(element, index);
+        }
+    }
+
+    /**
+     * Create a new TableItem at index if required.
+     *
+     * @param element
+     * @param index
+     *
+     * @since 3.1
+     */
+    private void createItem(Object element, int index) {
+        if (virtualManager is null) {
+            updateItem(internalCreateNewRowPart(SWT.NONE, index).getItem(),
+                    element);
+        } else {
+            virtualManager.notVisibleAdded(element, index);
+
+        }
+    }
+
+    /**
+     * Create a new row.  Callers can only use the returned object locally and before
+     * making the next call on the viewer since it may be re-used for subsequent method
+     * calls.
+     *
+     * @param style
+     *            the style for the new row
+     * @param rowIndex
+     *            the index of the row or -1 if the row is appended at the end
+     * @return the newly created row
+     */
+    protected abstract ViewerRow internalCreateNewRowPart(int style,
+            int rowIndex);
+
+    /**
+     * Adds the given element to this table viewer. If this viewer does not have
+     * a sorter, the element is added at the end; otherwise the element is
+     * inserted at the appropriate position.
+     * <p>
+     * This method should be called (by the content provider) when a single
+     * element has been added to the model, in order to cause the viewer to
+     * accurately reflect the model. This method only affects the viewer, not
+     * the model. Note that there is another method for efficiently processing
+     * the simultaneous addition of multiple elements.
+     * </p>
+     *
+     * @param element
+     *            the element to add
+     */
+    public void add(Object element) {
+        add([ element ]);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#doFindInputItem(java.lang.Object)
+     */
+    protected override Widget doFindInputItem(Object element) {
+        if (opEquals(element, getRoot())) {
+            return getControl();
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#doFindItem(java.lang.Object)
+     */
+    protected override Widget doFindItem(Object element) {
+
+        Item[] children = doGetItems();
+        for (int i = 0; i < children.length; i++) {
+            Item item = children[i];
+            Object data = item.getData();
+            if (data !is null && opEquals(data, element)) {
+                return item;
+            }
+        }
+
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#doUpdateItem(org.eclipse.swt.widgets.Widget,
+     *      java.lang.Object, bool)
+     */
+    protected override void doUpdateItem(Widget widget, Object element, bool fullMap) {
+        bool oldBusy = isBusy();
+        setBusy(true);
+        try {
+            if ( auto item = cast(Item)widget ) {
+
+                // remember element we are showing
+                if (fullMap) {
+                    associate(element, item);
+                } else {
+                    Object data = item.getData();
+                    if (data !is null) {
+                        unmapElement(data, item);
+                    }
+                    item.setData(element);
+                    mapElement(element, item);
+                }
+
+                int columnCount = doGetColumnCount();
+                if (columnCount is 0)
+                    columnCount = 1;// If there are no columns do the first one
+
+                ViewerRow viewerRowFromItem = getViewerRowFromItem(item);
+
+                bool isVirtual = (getControl().getStyle() & SWT.VIRTUAL) !is 0;
+
+                // If the control is virtual, we cannot use the cached viewer row object. See bug 188663.
+                if (isVirtual) {
+                    viewerRowFromItem = cast(ViewerRow) viewerRowFromItem.clone();
+                }
+
+                // Also enter loop if no columns added. See 1G9WWGZ: JFUIF:WINNT -
+                // TableViewer with 0 columns does not work
+                for (int column = 0; column < columnCount || column is 0; column++) {
+                    ViewerColumn columnViewer = getViewerColumn(column);
+                    ViewerCell cellToUpdate = updateCell(viewerRowFromItem,
+                            column, element);
+
+                    // If the control is virtual, we cannot use the cached cell object. See bug 188663.
+                    if (isVirtual) {
+                        cellToUpdate = new ViewerCell(cellToUpdate.getViewerRow(), cellToUpdate.getColumnIndex(), element);
+                    }
+
+                    columnViewer.refresh(cellToUpdate);
+
+                    // clear cell (see bug 201280)
+                    updateCell(null, 0, null);
+
+                    // As it is possible for user code to run the event
+                    // loop check here.
+                    if (item.isDisposed()) {
+                        unmapElement(element, item);
+                        return;
+                    }
+
+                }
+
+            }
+        } finally {
+            setBusy(oldBusy);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ColumnViewer#getColumnViewerOwner(int)
+     */
+    protected override Widget getColumnViewerOwner(int columnIndex) {
+        int columnCount = doGetColumnCount();
+
+        if (columnIndex < 0
+                || (columnIndex > 0 && columnIndex >= columnCount)) {
+            return null;
+        }
+
+        if (columnCount is 0)// Hang it off the table if it
+            return getControl();
+
+        return doGetColumn(columnIndex);
+    }
+
+    /**
+     * Returns the element with the given index from this table viewer. Returns
+     * <code>null</code> if the index is out of range.
+     * <p>
+     * This method is internal to the framework.
+     * </p>
+     *
+     * @param index
+     *            the zero-based index
+     * @return the element at the given index, or <code>null</code> if the
+     *         index is out of range
+     */
+    public Object getElementAt(int index) {
+        if (index >= 0 && index < doGetItemCount()) {
+            Item i = doGetItem(index);
+            if (i !is null) {
+                return i.getData();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * The table viewer implementation of this <code>Viewer</code> framework
+     * method returns the label provider, which in the case of table viewers
+     * will be an instance of either <code>ITableLabelProvider</code> or
+     * <code>ILabelProvider</code>. If it is an
+     * <code>ITableLabelProvider</code>, then it provides a separate label
+     * text and image for each column. If it is an <code>ILabelProvider</code>,
+     * then it provides only the label text and image for the first column, and
+     * any remaining columns are blank.
+     */
+    public override IBaseLabelProvider getLabelProvider() {
+        return super.getLabelProvider();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#getSelectionFromWidget()
+     */
+    protected override List getSelectionFromWidget() {
+        if (virtualManager !is null) {
+            return getVirtualSelection();
+        }
+        Widget[] items = doGetSelection();
+        ArrayList list = new ArrayList(items.length);
+        for (int i = 0; i < items.length; i++) {
+            Widget item = items[i];
+            Object e = item.getData();
+            if (e !is null) {
+                list.add(e);
+            }
+        }
+        return list;
+    }
+
+    /**
+     * Get the virtual selection. Avoid calling SWT whenever possible to prevent
+     * extra widget creation.
+     *
+     * @return List of Object
+     */
+
+    private List getVirtualSelection() {
+
+        List result = new ArrayList();
+        int[] selectionIndices = doGetSelectionIndices();
+        if (auto lazy_ = cast(ILazyContentProvider) getContentProvider() ) {
+            for (int i = 0; i < selectionIndices.length; i++) {
+                int selectionIndex = selectionIndices[i];
+                lazy_.updateElement(selectionIndex);// Start the update
+                Object element = doGetItem(selectionIndex).getData();
+                // Only add the element if it got updated.
+                // If this is done deferred the selection will
+                // be incomplete until selection is finished.
+                if (element !is null) {
+                    result.add(element);
+                }
+            }
+        } else {
+            for (int i = 0; i < selectionIndices.length; i++) {
+                Object element = null;
+                // See if it is cached
+                int selectionIndex = selectionIndices[i];
+                if (selectionIndex < virtualManager.cachedElements.length) {
+                    element = virtualManager.cachedElements[selectionIndex];
+                }
+                if (element is null) {
+                    // Not cached so try the item's data
+                    Item item = doGetItem(selectionIndex);
+                    element = item.getData();
+                }
+                if (element !is null) {
+                    result.add(element);
+                }
+            }
+
+        }
+        return result;
+    }
+
+    /**
+     * @param element
+     *            the element to insert
+     * @return the index where the item should be inserted.
+     */
+    protected int indexForElement(Object element) {
+        ViewerComparator comparator = getComparator();
+        if (comparator is null) {
+            return doGetItemCount();
+        }
+        int count = doGetItemCount();
+        int min = 0, max = count - 1;
+        while (min <= max) {
+            int mid = (min + max) / 2;
+            Object data = doGetItem(mid).getData();
+            int compare = comparator.compare(this, data, element);
+            if (compare is 0) {
+                // find first item > element
+                while (compare is 0) {
+                    ++mid;
+                    if (mid >= count) {
+                        break;
+                    }
+                    data = doGetItem(mid).getData();
+                    compare = comparator.compare(this, data, element);
+                }
+                return mid;
+            }
+            if (compare < 0) {
+                min = mid + 1;
+            } else {
+                max = mid - 1;
+            }
+        }
+        return min;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object,
+     *      java.lang.Object)
+     */
+    protected override void inputChanged(Object input, Object oldInput) {
+        getControl().setRedraw(false);
+        try {
+            preservingSelection(dgRunnable( {
+                internalRefresh(getRoot());
+            }));
+        } finally {
+            getControl().setRedraw(true);
+        }
+    }
+
+    /**
+     * Inserts the given element into this table viewer at the given position.
+     * If this viewer has a sorter, the position is ignored and the element is
+     * inserted at the correct position in the sort order.
+     * <p>
+     * This method should be called (by the content provider) when elements have
+     * been added to the model, in order to cause the viewer to accurately
+     * reflect the model. This method only affects the viewer, not the model.
+     * </p>
+     *
+     * @param element
+     *            the element
+     * @param position
+     *            a 0-based position relative to the model, or -1 to indicate
+     *            the last position
+     */
+    public void insert(Object element, int position) {
+        applyEditorValue();
+        if (getComparator() !is null || hasFilters()) {
+            add(element);
+            return;
+        }
+        if (position is -1) {
+            position = doGetItemCount();
+        }
+        if (checkBusy())
+            return;
+        createItem(element, position);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object)
+     */
+    protected override void internalRefresh(Object element) {
+        internalRefresh(element, true);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object,
+     *      bool)
+     */
+    protected override void internalRefresh(Object element, bool updateLabels) {
+        applyEditorValue();
+        if (element is null || opEquals(element, getRoot())) {
+            if (virtualManager is null) {
+                internalRefreshAll(updateLabels);
+            } else {
+                internalVirtualRefreshAll();
+            }
+        } else {
+            Widget w = findItem(element);
+            if (w !is null) {
+                updateItem(w, element);
+            }
+        }
+    }
+
+    /**
+     * Refresh all with virtual elements.
+     *
+     * @since 3.1
+     */
+    private void internalVirtualRefreshAll() {
+
+        Object root = getRoot();
+        IContentProvider contentProvider = getContentProvider();
+
+        // Invalidate for lazy
+        if (!(cast(ILazyContentProvider)contentProvider )
+                && cast(IStructuredContentProvider)contentProvider ) {
+            // Don't cache if the root is null but cache if it is not lazy.
+            if (root !is null) {
+                virtualManager.cachedElements = getSortedChildren(root);
+                doSetItemCount(virtualManager.cachedElements.length);
+            }
+        }
+        doClearAll();
+    }
+
+    /**
+     * Refresh all of the elements of the table. update the labels if
+     * updatLabels is true;
+     *
+     * @param updateLabels
+     *
+     * @since 3.1
+     */
+    private void internalRefreshAll(bool updateLabels) {
+        // the parent
+
+        // in the code below, it is important to do all disassociates
+        // before any associates, since a later disassociate can undo an
+        // earlier associate
+        // e.g. if (a, b) is replaced by (b, a), the disassociate of b to
+        // item 1 could undo
+        // the associate of b to item 0.
+
+        Object[] children = getSortedChildren(getRoot());
+        Item[] items = doGetItems();
+        int min = Math.min(children.length, items.length);
+        for (int i = 0; i < min; ++i) {
+
+            Item item = items[i];
+
+            // if the element is unchanged, update its label if appropriate
+            if (opEquals(children[i], item.getData())) {
+                if (updateLabels) {
+                    updateItem(item, children[i]);
+                } else {
+                    // associate the new element, even if equal to the old
+                    // one,
+                    // to remove stale references (see bug 31314)
+                    associate(children[i], item);
+                }
+            } else {
+                // updateItem does an associate(...), which can mess up
+                // the associations if the order of elements has changed.
+                // E.g. (a, b) -> (b, a) first replaces a->0 with b->0, then
+                // replaces b->1 with a->1, but this actually removes b->0.
+                // So, if the object associated with this item has changed,
+                // just disassociate it for now, and update it below.
+                // we also need to reset the item (set its text,images etc. to
+                // default values) because the label decorators rely on this
+                disassociate(item);
+                doClear(i);
+            }
+        }
+        // dispose of all items beyond the end of the current elements
+        if (min < items.length) {
+            for (int i = items.length; --i >= min;) {
+
+                disassociate(items[i]);
+            }
+            if (virtualManager !is null) {
+                virtualManager.removeIndicesFromTo(min, items.length - 1);
+            }
+            doRemove(min, items.length - 1);
+        }
+        // Workaround for 1GDGN4Q: ITPUI:WIN2000 - TableViewer icons get
+        // scrunched
+        if (doGetItemCount() is 0) {
+            doRemoveAll();
+        }
+        // Update items which were disassociated above
+        for (int i = 0; i < min; ++i) {
+
+            Item item = items[i];
+            if (item.getData() is null) {
+                updateItem(item, children[i]);
+            }
+        }
+        // add any remaining elements
+        for (int i = min; i < children.length; ++i) {
+            createItem(children[i], i);
+        }
+    }
+
+    /**
+     * Removes the given elements from this table viewer.
+     *
+     * @param elements
+     *            the elements to remove
+     */
+    private void internalRemove(Object[] elements) {
+        Object input = getInput();
+        for (int i = 0; i < elements.length; ++i) {
+            if (opEquals(elements[i], input)) {
+                bool oldBusy = isBusy();
+                setBusy(false);
+                try {
+                    setInput(null);
+                } finally {
+                    setBusy(oldBusy);
+                }
+                return;
+            }
+        }
+        // use remove(int[]) rather than repeated TableItem.dispose() calls
+        // to allow SWT to optimize multiple removals
+        int[] indices = new int[elements.length];
+        int count = 0;
+        for (int i = 0; i < elements.length; ++i) {
+            Widget w = findItem(elements[i]);
+            if (w is null && virtualManager !is null) {
+                int index = virtualManager.find(elements[i]);
+                if (index !is -1) {
+                    indices[count++] = index;
+                }
+            } else if (auto item = cast(Item) w ) {
+                disassociate(item);
+                indices[count++] = doIndexOf(item);
+            }
+        }
+        if (count < indices.length) {
+            System.arraycopy(indices, 0, indices = new int[count], 0, count);
+        }
+        if (virtualManager !is null) {
+            virtualManager.removeIndices(indices);
+        }
+        doRemove(indices);
+
+        // Workaround for 1GDGN4Q: ITPUI:WIN2000 - TableViewer icons get
+        // scrunched
+        if (doGetItemCount() is 0) {
+            doRemoveAll();
+        }
+    }
+
+    /**
+     * Removes the given elements from this table viewer. The selection is
+     * updated if required.
+     * <p>
+     * This method should be called (by the content provider) when elements have
+     * been removed from the model, in order to cause the viewer to accurately
+     * reflect the model. This method only affects the viewer, not the model.
+     * </p>
+     *
+     * @param elements
+     *            the elements to remove
+     */
+    public void remove( Object[] elements) {
+        assertElementsNotNull(elements);
+        if (checkBusy())
+            return;
+        if (elements.length is 0) {
+            return;
+        }
+        preservingSelection(dgRunnable( (Object[] elements_){
+            internalRemove(elements_);
+        }, elements));
+    }
+
+    /**
+     * Removes the given element from this table viewer. The selection is
+     * updated if necessary.
+     * <p>
+     * This method should be called (by the content provider) when a single
+     * element has been removed from the model, in order to cause the viewer to
+     * accurately reflect the model. This method only affects the viewer, not
+     * the model. Note that there is another method for efficiently processing
+     * the simultaneous removal of multiple elements.
+     * </p>
+     * <strong>NOTE:</strong> removing an object from a virtual table will
+     * decrement the itemCount.
+     *
+     * @param element
+     *            the element
+     */
+    public void remove(Object element) {
+        remove([ element ]);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#reveal(java.lang.Object)
+     */
+    public override void reveal(Object element) {
+        Assert.isNotNull(element);
+        Widget w = findItem(element);
+        if (auto i = cast(Item)w ) {
+            doShowItem(i);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#setSelectionToWidget(java.util.List,
+     *      bool)
+     */
+    protected override void setSelectionToWidget(List list, bool reveal) {
+        if (list is null) {
+            doDeselectAll();
+            return;
+        }
+
+        if (virtualManager !is null) {
+            virtualSetSelectionToWidget(list, reveal);
+            return;
+        }
+
+        // This is vital to use doSetSelection because on SWT-Table on Win32 this will also
+        // move the focus to this row (See bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=198665)
+        if (reveal) {
+            int size = list.size();
+            Item[] items = new Item[size];
+            int count = 0;
+            for (int i = 0; i < size; ++i) {
+                Object o = list.get(i);
+                Widget w = findItem(o);
+                if (auto item = cast(Item)w ) {
+                    items[count++] = item;
+                }
+            }
+            if (count < size) {
+                System.arraycopy(items, 0, items = new Item[count], 0, count);
+            }
+            doSetSelection(items);
+        } else {
+            doDeselectAll(); // Clear the selection
+            if( ! list.isEmpty() ) {
+                int[] indices = new int[list.size()];
+
+                Iterator it = list.iterator();
+                Item[] items = doGetItems();
+                Object modelElement;
+
+                int count = 0;
+                while( it.hasNext() ) {
+                    modelElement = it.next();
+                    bool found = false;
+                    for (int i = 0; i < items.length && !found; i++) {
+                        if (opEquals(modelElement, items[i].getData())) {
+                            indices[count++] = i;
+                            found = true;
+                        }
+                    }
+                }
+
+                if (count < indices.length) {
+                    System.arraycopy(indices, 0, indices = new int[count], 0, count);
+                }
+
+                doSelect(indices);
+            }
+        }
+    }
+
+    /**
+     * Set the selection on a virtual table
+     *
+     * @param list
+     *            The elements to set
+     * @param reveal
+     *            Whether or not reveal the first item.
+     */
+    private void virtualSetSelectionToWidget(List list, bool reveal) {
+        int size = list.size();
+        int[] indices = new int[list.size()];
+
+        Item firstItem = null;
+        int count = 0;
+        HashSet virtualElements = new HashSet();
+        for (int i = 0; i < size; ++i) {
+            Object o = list.get(i);
+            Widget w = findItem(o);
+            if (auto item = cast(Item)w ) {
+                indices[count++] = doIndexOf(item);
+                if (firstItem is null) {
+                    firstItem = item;
+                }
+            } else {
+                virtualElements.add(o);
+            }
+        }
+
+        if ( auto provider = cast(ILazyContentProvider) getContentProvider() ) {
+
+            // Now go through it again until all is done or we are no longer
+            // virtual
+            // This may create all items so it is not a good
+            // idea in general.
+            // Use #setSelection (int [] indices,bool reveal) instead
+            for (int i = 0; virtualElements.size() > 0 && i < doGetItemCount(); i++) {
+                provider.updateElement(i);
+                Item item = doGetItem(i);
+                if (virtualElements.contains(item.getData())) {
+                    indices[count++] = i;
+                    virtualElements.remove(item.getData());
+                    if (firstItem is null) {
+                        firstItem = item;
+                    }
+                }
+            }
+        } else {
+
+            if (count !is list.size()) {// As this is expensive skip it if all
+                // have been found
+                // If it is not lazy we can use the cache
+                for (int i = 0; i < virtualManager.cachedElements.length; i++) {
+                    Object element = virtualManager.cachedElements[i];
+                    if (virtualElements.contains(element)) {
+                        Item item = doGetItem(i);
+                        item.getText();// Be sure to fire the update
+                        indices[count++] = i;
+                        virtualElements.remove(element);
+                        if (firstItem is null) {
+                            firstItem = item;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (count < size) {
+            System.arraycopy(indices, 0, indices = new int[count], 0, count);
+        }
+        doDeselectAll();
+        doSelect(indices);
+
+        if (reveal && firstItem !is null) {
+            doShowItem(firstItem);
+        }
+    }
+
+    /**
+     * Set the item count of the receiver.
+     *
+     * @param count
+     *            the new table size.
+     *
+     * @since 3.1
+     */
+    public void setItemCount(int count) {
+        if (checkBusy())
+            return;
+        int oldCount = doGetItemCount();
+        if (count < oldCount) {
+            // need to disassociate elements that are being disposed
+            for (int i = count; i < oldCount; i++) {
+                Item item = doGetItem(i);
+                if (item.getData() !is null) {
+                    disassociate(item);
+                }
+            }
+        }
+        doSetItemCount(count);
+        if (virtualManager !is null) {
+            virtualManager.adjustCacheSize(count);
+        }
+        getControl().redraw();
+    }
+
+    /**
+     * Replace the element at the given index with the given element. This
+     * method will not call the content provider to verify. <strong>Note that
+     * this method will materialize a TableItem the given index.</strong>.
+     *
+     * @param element
+     * @param index
+     * @see ILazyContentProvider
+     *
+     * @since 3.1
+     */
+    public void replace(Object element, int index) {
+        if (checkBusy())
+            return;
+        Item item = doGetItem(index);
+        refreshItem(item, element);
+    }
+
+    /**
+     * Clear the table item at the specified index
+     *
+     * @param index
+     *            the index of the table item to be cleared
+     *
+     * @since 3.1
+     */
+    public void clear(int index) {
+        Item item = doGetItem(index);
+        if (item.getData() !is null) {
+            disassociate(item);
+        }
+        doClear(index);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#getRawChildren(java.lang.Object)
+     */
+    protected override Object[] getRawChildren(Object parent) {
+
+        Assert.isTrue(!( null !is cast(ILazyContentProvider) getContentProvider() ),
+                "Cannot get raw children with an ILazyContentProvider");//$NON-NLS-1$
+        return super.getRawChildren(parent);
+
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#assertContentProviderType(org.eclipse.jface.viewers.IContentProvider)
+     */
+    protected override void assertContentProviderType(IContentProvider provider) {
+        Assert.isTrue(null !is cast(IStructuredContentProvider)provider
+                || null !is cast(ILazyContentProvider)provider );
+    }
+
+    /**
+     * Searches the receiver's list starting at the first item (index 0) until
+     * an item is found that is equal to the argument, and returns the index of
+     * that item. If no item is found, returns -1.
+     *
+     * @param item
+     *            the search item
+     * @return the index of the item
+     *
+     * @since 3.3
+     */
+    protected abstract int doIndexOf(Item item);
+
+    /**
+     * Returns the number of items contained in the receiver.
+     *
+     * @return the number of items
+     *
+     * @since 3.3
+     */
+    protected abstract int doGetItemCount();
+
+    /**
+     * Sets the number of items contained in the receiver.
+     *
+     * @param count
+     *            the number of items
+     *
+     * @since 3.3
+     */
+    protected abstract void doSetItemCount(int count);
+
+    /**
+     * Returns a (possibly empty) array of TableItems which are the items in the
+     * receiver.
+     *
+     * @return the items in the receiver
+     *
+     * @since 3.3
+     */
+    protected abstract Item[] doGetItems();
+
+    /**
+     * Returns the column at the given, zero-relative index in the receiver.
+     * Throws an exception if the index is out of range. Columns are returned in
+     * the order that they were created. If no TableColumns were created by the
+     * programmer, this method will throw ERROR_INVALID_RANGE despite the fact
+     * that a single column of data may be visible in the table. This occurs
+     * when the programmer uses the table like a list, adding items but never
+     * creating a column.
+     *
+     * @param index
+     *            the index of the column to return
+     * @return the column at the given index
+     * @exception IllegalArgumentException -
+     *                if the index is not between 0 and the number of elements
+     *                in the list minus 1 (inclusive)
+     *
+     * @since 3.3
+     */
+    protected abstract Widget doGetColumn(int index);
+
+    /**
+     * Returns the item at the given, zero-relative index in the receiver.
+     * Throws an exception if the index is out of range.
+     *
+     * @param index
+     *            the index of the item to return
+     * @return the item at the given index
+     * @exception IllegalArgumentException -
+     *                if the index is not between 0 and the number of elements
+     *                in the list minus 1 (inclusive)
+     *
+     * @since 3.3
+     */
+    protected abstract Item doGetItem(int index);
+
+    /**
+     * Returns an array of {@link Item} that are currently selected in the
+     * receiver. The order of the items is unspecified. An empty array indicates
+     * that no items are selected.
+     *
+     * @return an array representing the selection
+     *
+     * @since 3.3
+     */
+    protected abstract Item[] doGetSelection();
+
+    /**
+     * Returns the zero-relative indices of the items which are currently
+     * selected in the receiver. The order of the indices is unspecified. The
+     * array is empty if no items are selected.
+     *
+     * @return an array representing the selection
+     *
+     * @since 3.3
+     */
+    protected abstract int[] doGetSelectionIndices();
+
+    /**
+     * Clears all the items in the receiver. The text, icon and other attributes
+     * of the items are set to their default values. If the table was created
+     * with the <code>SWT.VIRTUAL</code> style, these attributes are requested
+     * again as needed.
+     *
+     * @since 3.3
+     */
+    protected abstract void doClearAll();
+
+    /**
+     * Resets the given item in the receiver. The text, icon and other attributes
+     * of the item are set to their default values.
+     *
+     * @param item the item to reset
+     *
+     * @since 3.3
+     */
+    protected abstract void doResetItem(Item item);
+
+    /**
+     * Removes the items from the receiver which are between the given
+     * zero-relative start and end indices (inclusive).
+     *
+     * @param start
+     *            the start of the range
+     * @param end
+     *            the end of the range
+     *
+     * @exception IllegalArgumentException -
+     *                if either the start or end are not between 0 and the
+     *                number of elements in the list minus 1 (inclusive)
+     *
+     * @since 3.3
+     */
+    protected abstract void doRemove(int start, int end);
+
+    /**
+     * Removes all of the items from the receiver.
+     *
+     * @since 3.3
+     */
+    protected abstract void doRemoveAll();
+
+    /**
+     * Removes the items from the receiver's list at the given zero-relative
+     * indices.
+     *
+     * @param indices
+     *            the array of indices of the items
+     *
+     * @exception IllegalArgumentException -
+     *                if the array is null, or if any of the indices is not
+     *                between 0 and the number of elements in the list minus 1
+     *                (inclusive)
+     *
+     * @since 3.3
+     */
+    protected abstract void doRemove(int[] indices);
+
+    /**
+     * Shows the item. If the item is already showing in the receiver, this
+     * method simply returns. Otherwise, the items are scrolled until the item
+     * is visible.
+     *
+     * @param item
+     *            the item to be shown
+     *
+     * @exception IllegalArgumentException -
+     *                if the item is null
+     *
+     * @since 3.3
+     */
+    protected abstract void doShowItem(Item item);
+
+    /**
+     * Deselects all selected items in the receiver.
+     *
+     * @since 3.3
+     */
+    protected abstract void doDeselectAll();
+
+    /**
+     * Sets the receiver's selection to be the given array of items. The current
+     * selection is cleared before the new items are selected.
+     * <p>
+     * Items that are not in the receiver are ignored. If the receiver is
+     * single-select and multiple items are specified, then all items are
+     * ignored.
+     * </p>
+     *
+     * @param items
+     *            the array of items
+     *
+     * @exception IllegalArgumentException -
+     *                if the array of items is null
+     *
+     * @since 3.3
+     */
+    protected abstract void doSetSelection(Item[] items);
+
+    /**
+     * Shows the selection. If the selection is already showing in the receiver,
+     * this method simply returns. Otherwise, the items are scrolled until the
+     * selection is visible.
+     *
+     * @since 3.3
+     */
+    protected abstract void doShowSelection();
+
+    /**
+     * Selects the items at the given zero-relative indices in the receiver. The
+     * current selection is cleared before the new items are selected.
+     * <p>
+     * Indices that are out of range and duplicate indices are ignored. If the
+     * receiver is single-select and multiple indices are specified, then all
+     * indices are ignored.
+     * </p>
+     *
+     * @param indices
+     *            the indices of the items to select
+     *
+     * @exception IllegalArgumentException -
+     *                if the array of indices is null
+     *
+     * @since 3.3
+     */
+    protected abstract void doSetSelection(int[] indices);
+
+    /**
+     * Clears the item at the given zero-relative index in the receiver. The
+     * text, icon and other attributes of the item are set to the default value.
+     * If the table was created with the <code>SWT.VIRTUAL</code> style, these
+     * attributes are requested again as needed.
+     *
+     * @param index
+     *            the index of the item to clear
+     *
+     * @exception IllegalArgumentException -
+     *                if the index is not between 0 and the number of elements
+     *                in the list minus 1 (inclusive)
+     *
+     * @see SWT#VIRTUAL
+     * @see SWT#SetData
+     *
+     * @since 3.3
+     */
+    protected abstract void doClear(int index);
+
+
+
+    /**
+     * Selects the items at the given zero-relative indices in the receiver.
+     * The current selection is not cleared before the new items are selected.
+     * <p>
+     * If the item at a given index is not selected, it is selected.
+     * If the item at a given index was already selected, it remains selected.
+     * Indices that are out of range and duplicate indices are ignored.
+     * If the receiver is single-select and multiple indices are specified,
+     * then all indices are ignored.
+     * </p>
+     *
+     * @param indices the array of indices for the items to select
+     *
+     * @exception IllegalArgumentException - if the array of indices is null
+     *
+     */
+    protected abstract void doSelect(int[] indices);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/AbstractTreeViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,3084 @@
+/*******************************************************************************
+ * 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - bug 153993, bug 167323, bug 175192
+ *     Lasse Knudsen, bug 205700
+ *     Micah Hainline, bug 210448
+ *     Michael Schneider, bug 210747
+ *     Bruce Sutton, bug 221768
+ *     Matthew Hall, bug 221988
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.AbstractTreeViewer;
+
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.viewers.ITreeViewerListener;
+import org.eclipse.jface.viewers.TreeExpansionEvent;
+import org.eclipse.jface.viewers.CustomHashtable;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ColumnViewerEditor;
+import org.eclipse.jface.viewers.ViewerLabel;
+import org.eclipse.jface.viewers.ViewerRow;
+import org.eclipse.jface.viewers.TreePathViewerSorter;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.jface.viewers.ViewerColumn;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ITreePathContentProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IElementComparer;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ITreePathLabelProvider;
+
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.TreeEvent;
+import org.eclipse.swt.events.TreeListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.util.SafeRunnable;
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Abstract base implementation for tree-structure-oriented viewers (trees and
+ * table trees).
+ * <p>
+ * Nodes in the tree can be in either an expanded or a collapsed state,
+ * depending on whether the children on a node are visible. This class
+ * introduces public methods for controlling the expanding and collapsing of
+ * nodes.
+ * </p>
+ * <p>
+ * As of 3.2, AbstractTreeViewer supports multiple equal elements (each with a
+ * different parent chain) in the tree. This support requires that clients
+ * enable the element map by calling <code>setUseHashLookup(true)</code>.
+ * </p>
+ * <p>
+ * Content providers for abstract tree viewers must implement one of the
+ * interfaces <code>ITreeContentProvider</code> or (as of 3.2, to support
+ * multiple equal elements) <code>ITreePathContentProvider</code>.
+ * </p>
+ *
+ * @see TreeViewer
+ */
+public abstract class AbstractTreeViewer : ColumnViewer {
+    alias ColumnViewer.buildLabel buildLabel;
+    alias ColumnViewer.filter filter;
+    alias ColumnViewer.setSelection setSelection;
+
+    /**
+     * Constant indicating that all levels of the tree should be expanded or
+     * collapsed.
+     *
+     * @see #expandToLevel(int)
+     * @see #collapseToLevel(Object, int)
+     */
+    public static const int ALL_LEVELS = -1;
+
+    /**
+     * List of registered tree listeners (element type:
+     * <code>TreeListener</code>).
+     */
+    private ListenerList treeListeners;
+
+    /**
+     * The level to which the tree is automatically expanded each time the
+     * viewer's input is changed (that is, by <code>setInput</code>). A value
+     * of 0 means that auto-expand is off.
+     *
+     * @see #setAutoExpandLevel
+     */
+    private int expandToLevel_ = 0;
+
+    /**
+     * Safe runnable used to update an item.
+     */
+    class UpdateItemSafeRunnable : SafeRunnable {
+        private Object element;
+
+        private Item item;
+
+        this(Item item, Object element) {
+            this.item = item;
+            this.element = element;
+        }
+
+        public void run() {
+            doUpdateItem(item, element);
+        }
+
+    }
+
+    /**
+     * Creates an abstract tree viewer. The viewer has no input, no content
+     * provider, a default label provider, no sorter, no filters, and has
+     * auto-expand turned off.
+     */
+    protected this() {
+        treeListeners = new ListenerList();
+        // do nothing
+    }
+
+    /**
+     * Adds the given child elements to this viewer as children of the given
+     * parent element. If this viewer does not have a sorter, the elements are
+     * added at the end of the parent's list of children in the order given;
+     * otherwise, the elements are inserted at the appropriate positions.
+     * <p>
+     * This method should be called (by the content provider) when elements have
+     * been added to the model, in order to cause the viewer to accurately
+     * reflect the model. This method only affects the viewer, not the model.
+     * </p>
+     *
+     * @param parentElementOrTreePath
+     *            the parent element
+     * @param childElements
+     *            the child elements to add
+     */
+    public void add(Object parentElementOrTreePath, Object[] childElements) {
+        Assert.isNotNull(parentElementOrTreePath);
+        assertElementsNotNull(childElements);
+        if (checkBusy())
+            return;
+        Widget[] widgets = internalFindItems(parentElementOrTreePath);
+        // If parent hasn't been realized yet, just ignore the add.
+        if (widgets.length is 0) {
+            return;
+        }
+
+        for (int i = 0; i < widgets.length; i++) {
+            internalAdd(widgets[i], parentElementOrTreePath, childElements);
+        }
+    }
+
+    /**
+     * Find the items for the given element of tree path
+     *
+     * @param parentElementOrTreePath
+     *            the element or tree path
+     * @return the items for that element
+     *
+     * @since 3.3
+     */
+    final protected Widget[] internalFindItems(Object parentElementOrTreePath) {
+        Widget[] widgets;
+        if ( auto path = cast(TreePath) parentElementOrTreePath ) {
+            Widget w = internalFindItem(path);
+            if (w is null) {
+                widgets = null;
+            } else {
+                widgets = [ w ];
+            }
+        } else {
+            widgets = findItems(parentElementOrTreePath);
+        }
+        return widgets;
+    }
+
+    /**
+     * Return the item at the given path or <code>null</code>
+     *
+     * @param path
+     *            the path
+     * @return {@link Widget} the item at that path
+     */
+    private Widget internalFindItem(TreePath path) {
+        Widget[] widgets = findItems(path.getLastSegment());
+        for (int i = 0; i < widgets.length; i++) {
+            Widget widget = widgets[i];
+            if ( auto item = cast(Item)widget ) {
+                TreePath p = getTreePathFromItem(item);
+                if (p.opEquals(path)) {
+                    return widget;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Adds the given child elements to this viewer as children of the given
+     * parent element.
+     * <p>
+     * EXPERIMENTAL. Not to be used except by JDT. This method was added to
+     * support JDT's explorations into grouping by working sets, which requires
+     * viewers to support multiple equal elements. See bug 76482 for more
+     * details. This support will likely be removed in Eclipse 3.2 in favor of
+     * proper support for multiple equal elements.
+     * </p>
+     *
+     * @param widget
+     *            the widget for the parent element
+     * @param parentElementOrTreePath
+     *            the parent element
+     * @param childElements
+     *            the child elements to add
+     * @since 3.1
+     */
+    protected void internalAdd(Widget widget, Object parentElementOrTreePath,
+            Object[] childElements) {
+        Object parent;
+        TreePath path;
+        if ( auto path = cast(TreePath) parentElementOrTreePath ) {
+            parent = path.getLastSegment();
+        } else {
+            parent = parentElementOrTreePath;
+            path = null;
+        }
+
+        // optimization!
+        // if the widget is not expanded we just invalidate the subtree
+        if ( auto ti = cast(Item)widget ) {
+            if (!getExpanded(ti)) {
+                bool needDummy = isExpandable(ti, path, parent);
+                bool haveDummy = false;
+                // remove all children
+                Item[] items = getItems(ti);
+                for (int i = 0; i < items.length; i++) {
+                    if (items[i].getData() !is null) {
+                        disassociate(items[i]);
+                        items[i].dispose();
+                    } else {
+                        if (needDummy && !haveDummy) {
+                            haveDummy = true;
+                        } else {
+                            items[i].dispose();
+                        }
+                    }
+                }
+                // append a dummy if necessary
+                if (needDummy && !haveDummy) {
+                    newItem(ti, SWT.NULL, -1);
+                }
+                return;
+            }
+        }
+
+        if (childElements.length > 0) {
+            // TODO: Add filtering back?
+            Object[] filtered = filter(parentElementOrTreePath, childElements);
+            ViewerComparator comparator = getComparator();
+            if (comparator !is null) {
+                if ( auto tpvs = cast(TreePathViewerSorter) comparator ) {
+                    if (path is null) {
+                        path = internalGetSorterParentPath(widget, comparator);
+                    }
+                    tpvs.sort(this, path, filtered);
+                } else {
+                    comparator.sort(this, filtered);
+                }
+            }
+            createAddedElements(widget, filtered);
+        }
+    }
+
+    /**
+     * Filter the children elements.
+     *
+     * @param parentElementOrTreePath
+     *            the parent element or path
+     * @param elements
+     *            the child elements
+     * @return the filter list of children
+     */
+    private Object[] filter(Object parentElementOrTreePath, Object[] elements) {
+        ViewerFilter[] filters = getFilters();
+        if (filters !is null) {
+            ArrayList filtered = new ArrayList(elements.length);
+            for (int i = 0; i < elements.length; i++) {
+                bool add = true;
+                for (int j = 0; j < filters.length; j++) {
+                    add = filters[j].select(this, parentElementOrTreePath,
+                            elements[i]);
+                    if (!add) {
+                        break;
+                    }
+                }
+                if (add) {
+                    filtered.add(elements[i]);
+                }
+            }
+            return filtered.toArray();
+        }
+        return elements;
+    }
+
+    /**
+     * Create the new elements in the parent widget. If the child already exists
+     * do nothing.
+     *
+     * @param widget
+     * @param elements
+     *            Sorted list of elements to add.
+     */
+    private void createAddedElements(Widget widget, Object[] elements) {
+
+        if (elements.length is 1) {
+            if (opEquals(elements[0], widget.getData())) {
+                return;
+            }
+        }
+
+        ViewerComparator comparator = getComparator();
+        TreePath parentPath = internalGetSorterParentPath(widget, comparator);
+        Item[] items = getChildren(widget);
+
+        // Optimize for the empty case
+        if (items.length is 0) {
+            for (int i = 0; i < elements.length; i++) {
+                createTreeItem(widget, elements[i], -1);
+            }
+            return;
+        }
+
+        // Optimize for no comparator
+        if (comparator is null) {
+            for (int i = 0; i < elements.length; i++) {
+                Object element = elements[i];
+                if (itemExists(items, element)) {
+                    internalRefresh(element);
+                } else {
+                    createTreeItem(widget, element, -1);
+                }
+            }
+            return;
+        }
+        // As the items are sorted already we optimize for a
+        // start position. This is the insertion position relative to the
+        // original item array.
+        int indexInItems = 0;
+
+        // Count of elements we have added. See bug 205700 for why this is needed.
+        int newItems = 0;
+
+        elementloop: for (int i = 0; i < elements.length; i++) {
+            Object element = elements[i];
+            // update the index relative to the original item array
+            indexInItems = insertionPosition(items, comparator,
+                    indexInItems, element, parentPath);
+            if (indexInItems is items.length) {
+                createTreeItem(widget, element, -1);
+                newItems++;
+            } else {
+                // Search for an item for the element. The comparator might
+                // regard elements as equal when they are not.
+
+                // Use a separate index variable to search within the existing
+                // elements that compare equally, see
+                // TreeViewerTestBug205700.testAddEquallySortedElements.
+                int insertionIndexInItems = indexInItems;
+                while( insertionIndexInItems < items.length
+                        && internalCompare(comparator, parentPath, element,
+                                items[insertionIndexInItems].getData()) is 0) {
+                    // As we cannot assume the sorter is consistent with
+                    // equals() - therefore we can
+                    // just check against the item prior to this index (if
+                    // any)
+                    if (items[insertionIndexInItems].getData().opEquals(element)) {
+                        // Found the item for the element.
+                        // Refresh the element in case it has new children.
+                        internalRefresh(element);
+                        // Do not create a new item - continue with the next element.
+                        continue elementloop;
+                    }
+                    insertionIndexInItems++;
+                }
+                // Did we get to the end?
+                if (insertionIndexInItems is items.length) {
+                    createTreeItem(widget, element, -1);
+                    newItems++;
+                } else {
+                    // InsertionIndexInItems is the index in the original array. We
+                    // need to correct by the number of new items we have
+                    // created. See bug 205700.
+                    createTreeItem(widget, element, insertionIndexInItems + newItems);
+                    newItems++;
+                }
+            }
+        }
+    }
+
+    /**
+     * See if element is the data of one of the elements in items.
+     *
+     * @param items
+     * @param element
+     * @return <code>true</code> if the element matches.
+     */
+    private bool itemExists(Item[] items, Object element) {
+        if (usingElementMap()) {
+            Widget[] existingItems = findItems(element);
+            // optimization for two common cases
+            if (existingItems.length is 0) {
+                return false;
+            } else if (existingItems.length is 1) {
+                if (items.length > 0 && null !is cast(Item)existingItems[0] ) {
+                    Item existingItem = cast(Item) existingItems[0];
+                    return getParentItem(existingItem) is getParentItem(items[0]);
+                }
+            }
+        }
+        for (int i = 0; i < items.length; i++) {
+            if (items[i].getData().opEquals(element)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the index where the item should be inserted. It uses sorter to
+     * determine the correct position, if sorter is not assigned, returns the
+     * index of the element after the last.
+     *
+     * @param items
+     *            the items to search
+     * @param comparator
+     *            The comparator to use.
+     * @param lastInsertion
+     *            the start index to start search for position from this allows
+     *            optimizing search for multiple elements that are sorted
+     *            themselves.
+     * @param element
+     *            element to find position for.
+     * @param parentPath
+     *            the tree path for the element's parent or <code>null</code>
+     *            if the element is a root element or the sorter is not a
+     *            {@link TreePathViewerSorter}
+     * @return the index to use when inserting the element.
+     *
+     */
+
+    private int insertionPosition(Item[] items, ViewerComparator comparator,
+            int lastInsertion, Object element, TreePath parentPath) {
+
+        int size = items.length;
+        if (comparator is null) {
+            return size;
+        }
+        int min = lastInsertion, max = size - 1;
+
+        while (min <= max) {
+            int mid = (min + max) / 2;
+            Object data = items[mid].getData();
+            int compare = internalCompare(comparator, parentPath, data, element);
+            if (compare is 0) {
+                return mid;// Return if we already match
+            }
+            if (compare < 0) {
+                min = mid + 1;
+            } else {
+                max = mid - 1;
+            }
+        }
+        return min;
+
+    }
+
+    /**
+     * Returns the index where the item should be inserted. It uses sorter to
+     * determine the correct position, if sorter is not assigned, returns the
+     * index of the element after the last.
+     *
+     * @param parent
+     *            The parent widget
+     * @param sorter
+     *            The sorter to use.
+     * @param startIndex
+     *            the start index to start search for position from this allows
+     *            optimizing search for multiple elements that are sorted
+     *            themselves.
+     * @param element
+     *            element to find position for.
+     * @param currentSize
+     *            the current size of the collection
+     * @return the index to use when inserting the element.
+     *
+     */
+
+    /**
+     * Returns the index where the item should be inserted.
+     *
+     * @param parent
+     *            The parent widget the element will be inserted into.
+     * @param element
+     *            The element to insert.
+     * @return the index of the element
+     */
+    protected int indexForElement(Widget parent, Object element) {
+        ViewerComparator comparator = getComparator();
+        TreePath parentPath = internalGetSorterParentPath(parent, comparator);
+
+        Item[] items = getChildren(parent);
+        int count = items.length;
+
+        if (comparator is null) {
+            return count;
+        }
+        int min = 0, max = count - 1;
+
+        while (min <= max) {
+            int mid = (min + max) / 2;
+            Object data = items[mid].getData();
+            int compare = internalCompare(comparator, parentPath, data, element);
+            if (compare is 0) {
+                // find first item > element
+                while (compare is 0) {
+                    ++mid;
+                    if (mid >= count) {
+                        break;
+                    }
+                    data = items[mid].getData();
+                    compare = internalCompare(comparator, parentPath, data,
+                            element);
+                }
+                return mid;
+            }
+            if (compare < 0) {
+                min = mid + 1;
+            } else {
+                max = mid - 1;
+            }
+        }
+        return min;
+    }
+
+    /**
+     * Return the tree path that should be used as the parent path for the given
+     * widget and sorter. A <code>null</code> is returned if either the sorter
+     * is not a {@link TreePathViewerSorter} or if the parent widget is not an
+     * {@link Item} (i.e. is the root of the tree).
+     *
+     * @param parent
+     *            the parent widget
+     * @param comparator
+     *            the sorter
+     * @return the tree path that should be used as the parent path for the
+     *         given widget and sorter
+     */
+    private TreePath internalGetSorterParentPath(Widget parent,
+            ViewerComparator comparator) {
+        TreePath path;
+        if ( null !is cast(TreePathViewerSorter)comparator
+                && null !is cast(Item)parent ) {
+            Item item = cast(Item) parent;
+            path = getTreePathFromItem(item);
+        } else {
+            path = null;
+        }
+        return path;
+    }
+
+    /**
+     * Compare the two elements using the given sorter. If the sorter is a
+     * {@link TreePathViewerSorter}, the provided tree path will be used. If
+     * the tree path is null and the sorter is a tree path sorter, then the
+     * elements are root elements
+     *
+     * @param comparator
+     *            the sorter
+     * @param parentPath
+     *            the path of the elements' parent
+     * @param e1
+     *            the first element
+     * @param e2
+     *            the second element
+     * @return the result of comparing the two elements
+     */
+    private int internalCompare(ViewerComparator comparator,
+            TreePath parentPath, Object e1, Object e2) {
+        if ( auto tpvs = cast(TreePathViewerSorter) comparator ) {
+            return tpvs.compare(this, parentPath, e1, e2);
+        }
+        return comparator.compare(this, e1, e2);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#getSortedChildren(java.lang.Object)
+     */
+    protected override Object[] getSortedChildren(Object parentElementOrTreePath) {
+        Object[] result = getFilteredChildren(parentElementOrTreePath);
+        ViewerComparator comparator = getComparator();
+        if (parentElementOrTreePath !is null
+                && null !is cast(TreePathViewerSorter) comparator ) {
+            TreePathViewerSorter tpvs = cast(TreePathViewerSorter) comparator;
+
+            // be sure we're not modifying the original array from the model
+            result = result.dup;
+
+            TreePath path = null;
+            if ( auto p = cast(TreePath) parentElementOrTreePath ) {
+                path = p;
+            } else {
+                Object parent = parentElementOrTreePath;
+                Widget w = internalGetWidgetToSelect(parent);
+                if (w !is null) {
+                    path = internalGetSorterParentPath(w, comparator);
+                }
+            }
+            tpvs.sort(this, path, result);
+        } else if (comparator !is null) {
+            // be sure we're not modifying the original array from the model
+            result = result.dup;
+            comparator.sort(this, result);
+        }
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#getFilteredChildren(java.lang.Object)
+     */
+    protected override Object[] getFilteredChildren(Object parentElementOrTreePath) {
+        Object[] result = getRawChildren(parentElementOrTreePath);
+        ViewerFilter[] filters = getFilters();
+        for (int i = 0; i < filters.length; i++) {
+            ViewerFilter filter = filters[i];
+            result = filter.filter(this, parentElementOrTreePath, result);
+        }
+        return result;
+    }
+
+    /**
+     * Adds the given child element to this viewer as a child of the given
+     * parent element. If this viewer does not have a sorter, the element is
+     * added at the end of the parent's list of children; otherwise, the element
+     * is inserted at the appropriate position.
+     * <p>
+     * This method should be called (by the content provider) when a single
+     * element has been added to the model, in order to cause the viewer to
+     * accurately reflect the model. This method only affects the viewer, not
+     * the model. Note that there is another method for efficiently processing
+     * the simultaneous addition of multiple elements.
+     * </p>
+     *
+     * @param parentElementOrTreePath
+     *            the parent element or path
+     * @param childElement
+     *            the child element
+     */
+    public void add(Object parentElementOrTreePath, Object childElement) {
+        add(parentElementOrTreePath, [ childElement ]);
+    }
+
+    /**
+     * Adds the given SWT selection listener to the given SWT control.
+     *
+     * @param control
+     *            the SWT control
+     * @param listener
+     *            the SWT selection listener
+     * @deprecated
+     */
+    protected void addSelectionListener(Control control,
+            SelectionListener listener) {
+        // do nothing
+    }
+
+    /**
+     * Adds a listener for expand and collapse events in this viewer. Has no
+     * effect if an identical listener is already registered.
+     *
+     * @param listener
+     *            a tree viewer listener
+     */
+    public void addTreeListener(ITreeViewerListener listener) {
+        treeListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Adds the given SWT tree listener to the given SWT control.
+     *
+     * @param control
+     *            the SWT control
+     * @param listener
+     *            the SWT tree listener
+     */
+    protected abstract void addTreeListener(Control control,
+            TreeListener listener);
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see StructuredViewer#associate(Object, Item)
+     */
+    protected override void associate(Object element, Item item) {
+        Object data = item.getData();
+        if (data !is null && data !is element && opEquals(data, element)) {
+            // workaround for PR 1FV62BT
+            // assumption: elements are equal but not identical
+            // -> remove from map but don't touch children
+            unmapElement(data, item);
+            item.setData(element);
+            mapElement(element, item);
+        } else {
+            // recursively disassociate all
+            super.associate(element, item);
+        }
+    }
+
+    /**
+     * Collapses all nodes of the viewer's tree, starting with the root. This
+     * method is equivalent to <code>collapseToLevel(ALL_LEVELS)</code>.
+     */
+    public void collapseAll() {
+        Object root = getRoot();
+        if (root !is null) {
+            collapseToLevel(root, ALL_LEVELS);
+        }
+    }
+
+    /**
+     * Collapses the subtree rooted at the given element or tree path to the
+     * given level.
+     *
+     * @param elementOrTreePath
+     *            the element or tree path
+     * @param level
+     *            non-negative level, or <code>ALL_LEVELS</code> to collapse
+     *            all levels of the tree
+     */
+    public void collapseToLevel(Object elementOrTreePath, int level) {
+        Assert.isNotNull(elementOrTreePath);
+        Widget w = internalGetWidgetToSelect(elementOrTreePath);
+        if (w !is null) {
+            internalCollapseToLevel(w, level);
+        }
+    }
+
+    /**
+     * Creates all children for the given widget.
+     * <p>
+     * The default implementation of this framework method assumes that
+     * <code>widget.getData()</code> returns the element corresponding to the
+     * node. Note: the node is not visually expanded! You may have to call
+     * <code>parent.setExpanded(true)</code>.
+     * </p>
+     *
+     * @param widget
+     *            the widget
+     */
+    protected void createChildren(Widget widget) {
+        bool oldBusy = isBusy();
+        setBusy(true);
+        try {
+            Item[] tis = getChildren(widget);
+            if (tis !is null && tis.length > 0) {
+                Object data = tis[0].getData();
+                if (data !is null) {
+                    return; // children already there!
+                }
+            }
+
+            BusyIndicator.showWhile(widget.getDisplay(), dgRunnable( (Widget widget_, Item[] tis_){
+                // fix for PR 1FW89L7:
+                // don't complain and remove all "dummies" ...
+                if (tis_ !is null) {
+                    for (int i = 0; i < tis_.length; i++) {
+                        if (tis_[i].getData() !is null) {
+                            disassociate(tis_[i]);
+                            Assert.isTrue(tis_[i].getData() is null,
+                                    "Second or later child is non -null");//$NON-NLS-1$
+
+                        }
+                        tis_[i].dispose();
+                    }
+                }
+                Object d = widget_.getData();
+                if (d !is null) {
+                    Object parentElement = d;
+                    Object[] children;
+                    if (isTreePathContentProvider() && (null !is cast(Item)widget_) ) {
+                        TreePath path = getTreePathFromItem(cast(Item) widget_);
+                        children = getSortedChildren(path);
+                    } else {
+                        children = getSortedChildren(parentElement);
+                    }
+                    for (int i = 0; i < children.length; i++) {
+                        createTreeItem(widget_, children[i], -1);
+                    }
+                }
+            }, widget,tis ));
+        } finally {
+            setBusy(oldBusy);
+        }
+    }
+
+    /**
+     * Creates a single item for the given parent and synchronizes it with the
+     * given element.
+     *
+     * @param parent
+     *            the parent widget
+     * @param element
+     *            the element
+     * @param index
+     *            if non-negative, indicates the position to insert the item
+     *            into its parent
+     */
+    protected void createTreeItem(Widget parent, Object element, int index) {
+        Item item = newItem(parent, SWT.NULL, index);
+        updateItem(item, element);
+        updatePlus(item, element);
+    }
+
+    /**
+     * The <code>AbstractTreeViewer</code> implementation of this method also
+     * recurses over children of the corresponding element.
+     */
+    protected override void disassociate(Item item) {
+        super.disassociate(item);
+        // recursively unmapping the items is only required when
+        // the hash map is used. In the other case disposing
+        // an item will recursively dispose its children.
+        if (usingElementMap()) {
+            disassociateChildren(item);
+        }
+    }
+
+    /**
+     * Disassociates the children of the given SWT item from their corresponding
+     * elements.
+     *
+     * @param item
+     *            the widget
+     */
+    private void disassociateChildren(Item item) {
+        Item[] items = getChildren(item);
+        for (int i = 0; i < items.length; i++) {
+            if (items[i].getData() !is null) {
+                disassociate(items[i]);
+            }
+        }
+    }
+
+    /* (non-Javadoc) Method declared on StructuredViewer. */
+    protected override Widget doFindInputItem(Object element) {
+        // compare with root
+        Object root = getRoot();
+        if (root is null) {
+            return null;
+        }
+
+        if (opEquals(root, element)) {
+            return getControl();
+        }
+        return null;
+    }
+
+    /* (non-Javadoc) Method declared on StructuredViewer. */
+    protected override Widget doFindItem(Object element) {
+        // compare with root
+        Object root = getRoot();
+        if (root is null) {
+            return null;
+        }
+
+        Item[] items = getChildren(getControl());
+        if (items !is null) {
+            for (int i = 0; i < items.length; i++) {
+                Widget o = internalFindItem(items[i], element);
+                if (o !is null) {
+                    return o;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Copies the attributes of the given element into the given SWT item.
+     *
+     * @param item
+     *            the SWT item
+     * @param element
+     *            the element
+     */
+    protected void doUpdateItem(Item item, Object element) {
+        if (item.isDisposed()) {
+            unmapElement(element, item);
+            return;
+        }
+
+        int columnCount = doGetColumnCount();
+        if (columnCount is 0)// If no columns are created then fake one
+            columnCount = 1;
+
+        ViewerRow viewerRowFromItem = getViewerRowFromItem(item);
+
+        bool isVirtual = (getControl().getStyle() & SWT.VIRTUAL) !is 0;
+
+        // If the control is virtual, we cannot use the cached viewer row object. See bug 188663.
+        if (isVirtual) {
+            viewerRowFromItem = cast(ViewerRow) viewerRowFromItem.clone();
+        }
+
+        for (int column = 0; column < columnCount; column++) {
+            ViewerColumn columnViewer = getViewerColumn(column);
+            ViewerCell cellToUpdate = updateCell(viewerRowFromItem, column,
+                    element);
+
+            // If the control is virtual, we cannot use the cached cell object. See bug 188663.
+            if (isVirtual) {
+                cellToUpdate = new ViewerCell(cellToUpdate.getViewerRow(), cellToUpdate.getColumnIndex(), element);
+            }
+
+            columnViewer.refresh(cellToUpdate);
+
+            // clear cell (see bug 201280)
+            updateCell(null, 0, null);
+
+            // As it is possible for user code to run the event
+            // loop check here.
+            if (item.isDisposed()) {
+                unmapElement(element, item);
+                return;
+            }
+
+        }
+    }
+
+    /**
+     * Returns <code>true</code> if the given list and array of items refer to
+     * the same model elements. Order is unimportant.
+     * <p>
+     * This method is not intended to be overridden by subclasses.
+     * </p>
+     *
+     * @param items
+     *            the list of items
+     * @param current
+     *            the array of items
+     * @return <code>true</code> if the refer to the same elements,
+     *         <code>false</code> otherwise
+     *
+     * @since 3.1 in TreeViewer, moved to AbstractTreeViewer in 3.3
+     */
+    protected bool isSameSelection(List items, Item[] current) {
+        // If they are not the same size then they are not equivalent
+        int n = items.size();
+        if (n !is current.length) {
+            return false;
+        }
+
+        CustomHashtable itemSet = newHashtable(n * 2 + 1);
+        for (Iterator i = items.iterator(); i.hasNext();) {
+            Item item = cast(Item) i.next();
+            Object element = item.getData();
+            itemSet.put(element, element);
+        }
+
+        // Go through the items of the current collection
+        // If there is a mismatch return false
+        for (int i = 0; i < current.length; i++) {
+            if (current[i].getData() is null
+                    || !itemSet.containsKey(current[i].getData())) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+
+    /* (non-Javadoc) Method declared on StructuredViewer. */
+    protected override void doUpdateItem(Widget widget, Object element, bool fullMap) {
+        bool oldBusy = isBusy();
+        setBusy(true);
+        try {
+            if ( auto item = cast(Item)widget ) {
+
+                // ensure that back pointer is correct
+                if (fullMap) {
+                    associate(element, item);
+                } else {
+                    Object data = item.getData();
+                    if (data !is null) {
+                        unmapElement(data, item);
+                    }
+                    item.setData(element);
+                    mapElement(element, item);
+                }
+
+                // update icon and label
+                SafeRunnable.run(new UpdateItemSafeRunnable(item, element));
+            }
+        } finally {
+            setBusy(oldBusy);
+        }
+    }
+
+    /**
+     * Expands all nodes of the viewer's tree, starting with the root. This
+     * method is equivalent to <code>expandToLevel(ALL_LEVELS)</code>.
+     */
+    public void expandAll() {
+        expandToLevel(ALL_LEVELS);
+    }
+
+    /**
+     * Expands the root of the viewer's tree to the given level.
+     *
+     * @param level
+     *            non-negative level, or <code>ALL_LEVELS</code> to expand all
+     *            levels of the tree
+     */
+    public void expandToLevel(int level) {
+        expandToLevel(getRoot(), level);
+    }
+
+    /**
+     * Expands all ancestors of the given element or tree path so that the given
+     * element becomes visible in this viewer's tree control, and then expands
+     * the subtree rooted at the given element to the given level.
+     *
+     * @param elementOrTreePath
+     *            the element
+     * @param level
+     *            non-negative level, or <code>ALL_LEVELS</code> to expand all
+     *            levels of the tree
+     */
+    public void expandToLevel(Object elementOrTreePath, int level) {
+        if (checkBusy())
+            return;
+        Widget w = internalExpand(elementOrTreePath, true);
+        if (w !is null) {
+            internalExpandToLevel(w, level);
+        }
+    }
+
+    /**
+     * Fires a tree collapsed event. Only listeners registered at the time this
+     * method is called are notified.
+     *
+     * @param event
+     *            the tree expansion event
+     * @see ITreeViewerListener#treeCollapsed
+     */
+    protected void fireTreeCollapsed(TreeExpansionEvent event) {
+        Object[] listeners = treeListeners.getListeners();
+        bool oldBusy = isBusy();
+        setBusy(true);
+        try {
+            for (int i = 0; i < listeners.length; ++i) {
+                SafeRunnable.run(new class(event,cast(ITreeViewerListener) listeners[i]) SafeRunnable {
+                    TreeExpansionEvent event_;
+                    ITreeViewerListener l;
+                    this(TreeExpansionEvent a,ITreeViewerListener b){
+                        event_=a;
+                        l = b;
+                    }
+                    public void run() {
+                        l.treeCollapsed(event_);
+                    }
+                });
+            }
+        } finally {
+            setBusy(oldBusy);
+        }
+    }
+
+    /**
+     * Fires a tree expanded event. Only listeners registered at the time this
+     * method is called are notified.
+     *
+     * @param event
+     *            the tree expansion event
+     * @see ITreeViewerListener#treeExpanded
+     */
+    protected void fireTreeExpanded(TreeExpansionEvent event) {
+        Object[] listeners = treeListeners.getListeners();
+        bool oldBusy = isBusy();
+        setBusy(true);
+        try {
+            for (int i = 0; i < listeners.length; ++i) {
+                SafeRunnable.run(new class( event, cast(ITreeViewerListener) listeners[i]) SafeRunnable {
+                    TreeExpansionEvent event_;
+                    ITreeViewerListener l;
+                    this(TreeExpansionEvent a,ITreeViewerListener b){
+                        event_=a;
+                        l = b;
+                    }
+                    public void run() {
+                        l.treeExpanded(event_);
+                    }
+                });
+            }
+        } finally {
+            setBusy(oldBusy);
+        }
+    }
+
+    /**
+     * Returns the auto-expand level.
+     *
+     * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of
+     *         the tree are expanded automatically
+     * @see #setAutoExpandLevel
+     */
+    public int getAutoExpandLevel() {
+        return expandToLevel_;
+    }
+
+    /**
+     * Returns the SWT child items for the given SWT widget.
+     *
+     * @param widget
+     *            the widget
+     * @return the child items
+     */
+    protected abstract Item[] getChildren(Widget widget);
+
+    /**
+     * Get the child for the widget at index. Note that the default
+     * implementation is not very efficient and should be overridden if this
+     * class is implemented.
+     *
+     * @param widget
+     *            the widget to check
+     * @param index
+     *            the index of the widget
+     * @return Item or <code>null</code> if widget is not a type that can
+     *         contain items.
+     *
+     * @throws ArrayIndexOutOfBoundsException
+     *             if the index is not valid.
+     * @since 3.1
+     */
+    protected Item getChild(Widget widget, int index) {
+        return getChildren(widget)[index];
+    }
+
+    /**
+     * Returns whether the given SWT item is expanded or collapsed.
+     *
+     * @param item
+     *            the item
+     * @return <code>true</code> if the item is considered expanded and
+     *         <code>false</code> if collapsed
+     */
+    protected abstract bool getExpanded(Item item);
+
+    /**
+     * Returns a list of elements corresponding to expanded nodes in this
+     * viewer's tree, including currently hidden ones that are marked as
+     * expanded but are under a collapsed ancestor.
+     * <p>
+     * This method is typically used when preserving the interesting state of a
+     * viewer; <code>setExpandedElements</code> is used during the restore.
+     * </p>
+     *
+     * @return the array of expanded elements
+     * @see #setExpandedElements
+     */
+    public Object[] getExpandedElements() {
+        ArrayList items = new ArrayList();
+        internalCollectExpandedItems(items, getControl());
+        ArrayList result = new ArrayList(items.size());
+        for (Iterator it = items.iterator(); it.hasNext();) {
+            Item item = cast(Item) it.next();
+            Object data = item.getData();
+            if (data !is null) {
+                result.add(data);
+            }
+        }
+        return result.toArray();
+    }
+
+    /**
+     * Returns whether the node corresponding to the given element or tree path
+     * is expanded or collapsed.
+     *
+     * @param elementOrTreePath
+     *            the element
+     * @return <code>true</code> if the node is expanded, and
+     *         <code>false</code> if collapsed
+     */
+    public bool getExpandedState(Object elementOrTreePath) {
+        Assert.isNotNull(elementOrTreePath);
+        Widget item = internalGetWidgetToSelect(elementOrTreePath);
+        if ( auto i = cast(Item)item ) {
+            return getExpanded(i);
+        }
+        return false;
+    }
+
+    /**
+     * Returns the number of child items of the given SWT control.
+     *
+     * @param control
+     *            the control
+     * @return the number of children
+     */
+    protected abstract int getItemCount(Control control);
+
+    /**
+     * Returns the number of child items of the given SWT item.
+     *
+     * @param item
+     *            the item
+     * @return the number of children
+     */
+    protected abstract int getItemCount(Item item);
+
+    /**
+     * Returns the child items of the given SWT item.
+     *
+     * @param item
+     *            the item
+     * @return the child items
+     */
+    protected abstract Item[] getItems(Item item);
+
+    /**
+     * Returns the item after the given item in the tree, or <code>null</code>
+     * if there is no next item.
+     *
+     * @param item
+     *            the item
+     * @param includeChildren
+     *            <code>true</code> if the children are considered in
+     *            determining which item is next, and <code>false</code> if
+     *            subtrees are ignored
+     * @return the next item, or <code>null</code> if none
+     */
+    protected Item getNextItem(Item item, bool includeChildren) {
+        if (item is null) {
+            return null;
+        }
+        if (includeChildren && getExpanded(item)) {
+            Item[] children = getItems(item);
+            if (children !is null && children.length > 0) {
+                return children[0];
+            }
+        }
+
+        // next item is either next sibling or next sibling of first
+        // parent that has a next sibling.
+        Item parent = getParentItem(item);
+        if (parent is null) {
+            return null;
+        }
+        Item[] siblings = getItems(parent);
+        if (siblings !is null) {
+            if (siblings.length <= 1) {
+                return getNextItem(parent, false);
+            }
+
+            for (int i = 0; i < siblings.length; i++) {
+                if (siblings[i] is item && i < (siblings.length - 1)) {
+                    return siblings[i + 1];
+                }
+            }
+        }
+        return getNextItem(parent, false);
+    }
+
+    /**
+     * Returns the parent item of the given item in the tree, or
+     * <code>null</code> if there is no parent item.
+     *
+     * @param item
+     *            the item
+     * @return the parent item, or <code>null</code> if none
+     */
+    protected abstract Item getParentItem(Item item);
+
+    /**
+     * Returns the item before the given item in the tree, or <code>null</code>
+     * if there is no previous item.
+     *
+     * @param item
+     *            the item
+     * @return the previous item, or <code>null</code> if none
+     */
+    protected Item getPreviousItem(Item item) {
+        // previous item is either right-most visible descendent of previous
+        // sibling or parent
+        Item parent = getParentItem(item);
+        if (parent is null) {
+            return null;
+        }
+        Item[] siblings = getItems(parent);
+        if (siblings.length is 0 || siblings[0] is item) {
+            return parent;
+        }
+        Item previous = siblings[0];
+        for (int i = 1; i < siblings.length; i++) {
+            if (siblings[i] is item) {
+                return rightMostVisibleDescendent(previous);
+            }
+            previous = siblings[i];
+        }
+        return null;
+    }
+
+    /* (non-Javadoc) Method declared on StructuredViewer. */
+    protected override Object[] getRawChildren(Object parentElementOrTreePath) {
+        bool oldBusy = isBusy();
+        setBusy(true);
+        try {
+            Object parent;
+            TreePath path;
+            if ( auto p = cast(TreePath)parentElementOrTreePath ) {
+                path = p;
+                parent = path.getLastSegment();
+            } else {
+                parent = parentElementOrTreePath;
+                path = null;
+            }
+            if (parent !is null) {
+                if (opEquals(parent, getRoot())) {
+                    return super.getRawChildren(parent);
+                }
+                IContentProvider cp = getContentProvider();
+                if ( auto tpcp = cast(ITreePathContentProvider)cp ) {
+                    if (path is null) {
+                        // A path was not provided so try and find one
+                        Widget w = findItem(parent);
+                        if ( auto item = cast(Item)w ) {
+                            path = getTreePathFromItem(item);
+                        }
+                        if (path is null) {
+                            path = new TreePath([parent ]);
+                        }
+                    }
+                    Object[] result = tpcp.getChildren(path);
+                    if (result !is null) {
+                        return result;
+                    }
+                } else if ( auto tcp = cast(ITreeContentProvider)cp ) {
+                    Object[] result = tcp.getChildren(parent);
+                    if (result !is null) {
+                        return result;
+                    }
+                }
+            }
+            return null;
+        } finally {
+            setBusy(oldBusy);
+        }
+    }
+
+    /**
+     * Returns all selected items for the given SWT control.
+     *
+     * @param control
+     *            the control
+     * @return the list of selected items
+     */
+    protected abstract Item[] getSelection(Control control);
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#getSelectionFromWidget()
+     */
+    protected override List getSelectionFromWidget() {
+        Widget[] items = getSelection(getControl());
+        ArrayList list = new ArrayList(items.length);
+        for (int i = 0; i < items.length; i++) {
+            Widget item = items[i];
+            Object e = item.getData();
+            if (e !is null) {
+                list.add(e);
+            }
+        }
+        return list;
+    }
+
+    /*
+     * Overridden in AbstractTreeViewer to fix bug 108102 (code copied from
+     * StructuredViewer to avoid introducing new API) (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#handleDoubleSelect(org.eclipse.swt.events.SelectionEvent)
+     */
+    protected override void handleDoubleSelect(SelectionEvent event) {
+        // handle case where an earlier selection listener disposed the control.
+        Control control = getControl();
+        if (control !is null && !control.isDisposed()) {
+            // If the double-clicked element can be obtained from the event, use
+            // it
+            // otherwise get it from the control. Some controls like List do
+            // not have the notion of item.
+            // For details, see bug 90161 [Navigator] DefaultSelecting folders
+            // shouldn't always expand first one
+            ISelection selection;
+            if (event.item !is null && event.item.getData() !is null) {
+
+                // changes to fix bug 108102 follow
+                TreePath treePath = getTreePathFromItem(cast(Item) event.item);
+                selection = new TreeSelection(treePath);
+                // end of changes
+
+            } else {
+                selection = getSelection();
+                updateSelection(selection);
+            }
+            fireDoubleClick(new DoubleClickEvent(this, selection));
+        }
+    }
+
+    /**
+     * Handles a tree collapse event from the SWT widget.
+     *
+     * @param event
+     *            the SWT tree event
+     */
+    protected void handleTreeCollapse(TreeEvent event) {
+        if (event.item.getData() !is null) {
+            fireTreeCollapsed(new TreeExpansionEvent(this, event.item.getData()));
+        }
+    }
+
+    /**
+     * Handles a tree expand event from the SWT widget.
+     *
+     * @param event
+     *            the SWT tree event
+     */
+    protected void handleTreeExpand(TreeEvent event) {
+        createChildren(event.item);
+        if (event.item.getData() !is null) {
+            fireTreeExpanded(new TreeExpansionEvent(this, event.item.getData()));
+        }
+    }
+
+    /* (non-Javadoc) Method declared on Viewer. */
+    protected override void hookControl(Control control) {
+        super.hookControl(control);
+        addTreeListener(control, new class TreeListener {
+            public void treeExpanded(TreeEvent event) {
+                handleTreeExpand(event);
+            }
+
+            public void treeCollapsed(TreeEvent event) {
+                handleTreeCollapse(event);
+            }
+        });
+    }
+
+    /*
+     * (non-Javadoc) Method declared on StructuredViewer. Builds the initial
+     * tree and handles the automatic expand feature.
+     */
+    protected override void inputChanged(Object input, Object oldInput) {
+        preservingSelection(new class Runnable {
+            public void run() {
+                Control tree = getControl();
+                tree.setRedraw(false);
+                try {
+                    removeAll(tree);
+                    tree.setData(getRoot());
+                    internalInitializeTree(tree);
+                } finally {
+                    tree.setRedraw(true);
+                }
+            }
+        });
+    }
+
+    /**
+     * Initializes the tree with root items, expanding to the appropriate
+     * level if necessary.
+     *
+     * @param tree the tree control
+     * @since 3.3
+     */
+    protected void internalInitializeTree(Control tree) {
+        createChildren(tree);
+        internalExpandToLevel(tree, expandToLevel_);
+    }
+
+    /**
+     * Recursively collapses the subtree rooted at the given widget to the given
+     * level.
+     * <p>
+     * </p>
+     * Note that the default implementation of this method does not call
+     * <code>setRedraw</code>.
+     *
+     * @param widget
+     *            the widget
+     * @param level
+     *            non-negative level, or <code>ALL_LEVELS</code> to collapse
+     *            all levels of the tree
+     */
+    protected void internalCollapseToLevel(Widget widget, int level) {
+        if (level is ALL_LEVELS || level > 0) {
+
+            if ( auto i = cast(Item)widget ) {
+                setExpanded(i, false);
+            }
+
+            if (level is ALL_LEVELS || level > 1) {
+                Item[] children = getChildren(widget);
+                if (children !is null) {
+                    int nextLevel = (level is ALL_LEVELS ? ALL_LEVELS
+                            : level - 1);
+                    for (int i = 0; i < children.length; i++) {
+                        internalCollapseToLevel(children[i], nextLevel);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Recursively collects all expanded items from the given widget.
+     *
+     * @param result
+     *            a list (element type: <code>Item</code>) into which to
+     *            collect the elements
+     * @param widget
+     *            the widget
+     */
+    private void internalCollectExpandedItems(List result, Widget widget) {
+        Item[] items = getChildren(widget);
+        for (int i = 0; i < items.length; i++) {
+            Item item = items[i];
+            if (getExpanded(item)) {
+                result.add(item);
+            }
+            internalCollectExpandedItems(result, item);
+        }
+    }
+
+    /**
+     * Tries to create a path of tree items for the given element or tree path.
+     * This method recursively walks up towards the root of the tree and in the
+     * case of an element (rather than a tree path) assumes that
+     * <code>getParent</code> returns the correct parent of an element.
+     *
+     * @param elementOrPath
+     *            the element
+     * @param expand
+     *            <code>true</code> if all nodes on the path should be
+     *            expanded, and <code>false</code> otherwise
+     * @return Widget
+     */
+    protected Widget internalExpand(Object elementOrPath, bool expand) {
+
+        if (elementOrPath is null) {
+            return null;
+        }
+
+        Widget w = internalGetWidgetToSelect(elementOrPath);
+        if (w is null) {
+            if (opEquals(elementOrPath, getRoot())) { // stop at root
+                return null;
+            }
+            // my parent has to create me
+            Object parent = getParentElement(elementOrPath);
+            if (parent !is null) {
+                Widget pw = internalExpand(parent, false);
+                if (pw !is null) {
+                    // let my parent create me
+                    createChildren(pw);
+                    Object element = internalToElement(elementOrPath);
+                    w = internalFindChild(pw, element);
+                    if (expand && null !is cast(Item)pw ) {
+                        // expand parent items top-down
+                        Item item = cast(Item) pw;
+                        LinkedList toExpandList = new LinkedList();
+                        while (item !is null && !getExpanded(item)) {
+                            toExpandList.addFirst(item);
+                            item = getParentItem(item);
+                        }
+                        for (Iterator it = toExpandList.iterator(); it
+                                .hasNext();) {
+                            Item toExpand = cast(Item) it.next();
+                            setExpanded(toExpand, true);
+                        }
+                    }
+                }
+            }
+        }
+        return w;
+    }
+
+    /**
+     * If the argument is a tree path, returns its last segment, otherwise
+     * return the argument
+     *
+     * @param elementOrPath
+     *            an element or a tree path
+     * @return the element, or the last segment of the tree path
+     */
+    private Object internalToElement(Object elementOrPath) {
+        if (auto tp = cast(TreePath)elementOrPath ) {
+            return tp.getLastSegment();
+        }
+        return elementOrPath;
+    }
+
+    /**
+     * This method takes a tree path or an element. If the argument is not a
+     * tree path, returns the parent of the given element or <code>null</code>
+     * if the parent is not known. If the argument is a tree path with more than
+     * one segment, returns its parent tree path, otherwise returns
+     * <code>null</code>.
+     *
+     * @param elementOrTreePath
+     * @return the parent element, or parent path, or <code>null</code>
+     *
+     * @since 3.2
+     */
+    protected Object getParentElement(Object elementOrTreePath) {
+        if (auto tp = cast(TreePath)elementOrTreePath) {
+            return tp.getParentPath();
+        }
+        IContentProvider cp = getContentProvider();
+        if ( auto tpcp = cast(ITreePathContentProvider)cp ) {
+            TreePath[] paths = tpcp.getParents(elementOrTreePath);
+            if (paths.length > 0) {
+                if (paths[0].getSegmentCount() is 0) {
+                    return getRoot();
+                }
+                return paths[0].getLastSegment();
+            }
+        }
+        if ( auto tcp = cast(ITreeContentProvider) cp ) {
+            return tcp.getParent(elementOrTreePath);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the widget to be selected for the given element or tree path.
+     *
+     * @param elementOrTreePath
+     *            the element or tree path to select
+     * @return the widget to be selected, or <code>null</code> if not found
+     *
+     * @since 3.1
+     */
+    protected Widget internalGetWidgetToSelect(Object elementOrTreePath) {
+        if ( auto treePath = cast(TreePath) elementOrTreePath ) {
+            if (treePath.getSegmentCount() is 0) {
+                return getControl();
+            }
+            Widget[] candidates = findItems(treePath.getLastSegment());
+            for (int i = 0; i < candidates.length; i++) {
+                Widget candidate = candidates[i];
+                if (!(cast(Item)candidate )) {
+                    continue;
+                }
+                if (treePath.opEquals(getTreePathFromItem(cast(Item) candidate),
+                        getComparer())) {
+                    return candidate;
+                }
+            }
+            return null;
+        }
+        return findItem(elementOrTreePath);
+    }
+
+    /**
+     * Recursively expands the subtree rooted at the given widget to the given
+     * level.
+     * <p>
+     * </p>
+     * Note that the default implementation of this method does not call
+     * <code>setRedraw</code>.
+     *
+     * @param widget
+     *            the widget
+     * @param level
+     *            non-negative level, or <code>ALL_LEVELS</code> to collapse
+     *            all levels of the tree
+     */
+    protected void internalExpandToLevel(Widget widget, int level) {
+        if (level is ALL_LEVELS || level > 0) {
+            if ( cast(Item)widget && widget.getData() !is null
+                    && !isExpandable(cast(Item) widget, null, widget.getData())) {
+                return;
+            }
+            createChildren(widget);
+            if ( auto i = cast(Item)widget ) {
+                setExpanded(i, true);
+            }
+            if (level is ALL_LEVELS || level > 1) {
+                Item[] children = getChildren(widget);
+                if (children !is null) {
+                    int newLevel = (level is ALL_LEVELS ? ALL_LEVELS
+                            : level - 1);
+                    for (int i = 0; i < children.length; i++) {
+                        internalExpandToLevel(children[i], newLevel);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Non-recursively tries to find the given element as a child of the given
+     * parent (item or tree).
+     *
+     * @param parent
+     *            the parent item
+     * @param element
+     *            the element
+     * @return Widget
+     */
+    private Widget internalFindChild(Widget parent, Object element) {
+        Item[] items = getChildren(parent);
+        for (int i = 0; i < items.length; i++) {
+            Item item = items[i];
+            Object data = item.getData();
+            if (data !is null && opEquals(data, element)) {
+                return item;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Recursively tries to find the given element.
+     *
+     * @param parent
+     *            the parent item
+     * @param element
+     *            the element
+     * @return Widget
+     */
+    private Widget internalFindItem(Item parent, Object element) {
+
+        // compare with node
+        Object data = parent.getData();
+        if (data !is null) {
+            if (opEquals(data, element)) {
+                return parent;
+            }
+        }
+        // recurse over children
+        Item[] items = getChildren(parent);
+        for (int i = 0; i < items.length; i++) {
+            Item item = items[i];
+            Widget o = internalFindItem(item, element);
+            if (o !is null) {
+                return o;
+            }
+        }
+        return null;
+    }
+
+    /* (non-Javadoc) Method declared on StructuredViewer. */
+    protected override void internalRefresh(Object element) {
+        internalRefresh(element, true);
+    }
+
+    /* (non-Javadoc) Method declared on StructuredViewer. */
+    protected override void internalRefresh(Object element, bool updateLabels) {
+        // If element is null, do a full refresh.
+        if (element is null) {
+            internalRefresh(getControl(), getRoot(), true, updateLabels);
+            return;
+        }
+        Widget[] items = findItems(element);
+        if (items.length !is 0) {
+            for (int i = 0; i < items.length; i++) {
+                // pick up structure changes too
+                internalRefresh(items[i], element, true, updateLabels);
+            }
+        }
+    }
+
+    /**
+     * Refreshes the tree starting at the given widget.
+     * <p>
+     * EXPERIMENTAL. Not to be used except by JDT. This method was added to
+     * support JDT's explorations into grouping by working sets, which requires
+     * viewers to support multiple equal elements. See bug 76482 for more
+     * details. This support will likely be removed in Eclipse 3.2 in favor of
+     * proper support for multiple equal elements.
+     * </p>
+     *
+     * @param widget
+     *            the widget
+     * @param element
+     *            the element
+     * @param doStruct
+     *            <code>true</code> if structural changes are to be picked up,
+     *            and <code>false</code> if only label provider changes are of
+     *            interest
+     * @param updateLabels
+     *            <code>true</code> to update labels for existing elements,
+     *            <code>false</code> to only update labels as needed, assuming
+     *            that labels for existing elements are unchanged.
+     * @since 3.1
+     */
+    protected void internalRefresh(Widget widget, Object element,
+            bool doStruct, bool updateLabels) {
+
+        if ( auto i = cast(Item)widget ) {
+            if (doStruct) {
+                updatePlus(i, element);
+            }
+            if (updateLabels || !opEquals(element, widget.getData())) {
+                doUpdateItem(widget, element, true);
+            } else {
+                associate(element, cast(Item) widget);
+            }
+        }
+
+        if (doStruct) {
+            internalRefreshStruct(widget, element, updateLabels);
+        } else {
+            Item[] children = getChildren(widget);
+            if (children !is null) {
+                for (int i = 0; i < children.length; i++) {
+                    Widget item = children[i];
+                    Object data = item.getData();
+                    if (data !is null) {
+                        internalRefresh(item, data, doStruct, updateLabels);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Update the structure and recurse. Items are updated in updateChildren, as
+     * needed.
+     *
+     * @param widget
+     * @param element
+     * @param updateLabels
+     */
+    /* package */void internalRefreshStruct(Widget widget, Object element,
+            bool updateLabels) {
+        updateChildren(widget, element, null, updateLabels);
+        Item[] children = getChildren(widget);
+        if (children !is null) {
+            for (int i = 0; i < children.length; i++) {
+                Widget item = children[i];
+                Object data = item.getData();
+                if (data !is null) {
+                    internalRefreshStruct(item, data, updateLabels);
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes the given elements from this viewer.
+     * <p>
+     * EXPERIMENTAL. Not to be used except by JDT. This method was added to
+     * support JDT's explorations into grouping by working sets, which requires
+     * viewers to support multiple equal elements. See bug 76482 for more
+     * details. This support will likely be removed in Eclipse 3.2 in favor of
+     * proper support for multiple equal elements.
+     * </p>
+     *
+     * @param elementsOrPaths
+     *            the elements or element paths to remove
+     * @since 3.1
+     */
+    protected void internalRemove(Object[] elementsOrPaths) {
+        Object input = getInput();
+        for (int i = 0; i < elementsOrPaths.length; ++i) {
+            Object element = elementsOrPaths[i];
+            if (opEquals(element, input)) {
+                setInput(null);
+                return;
+            }
+            Widget[] childItems = internalFindItems(element);
+            if (childItems.length > 0) {
+                for (int j = 0; j < childItems.length; j++) {
+                    Widget childItem = childItems[j];
+                    if ( auto it = cast(Item)childItem ) {
+                        disassociate(it);
+                        childItem.dispose();
+                    }
+                }
+            } else {
+                // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=210747
+                Object parent = getParentElement(element);
+                if (parent !is null
+                        && !opEquals(parent, getRoot())
+                        && !((null !is cast(TreePath)parent) && (cast(TreePath) parent)
+                                .getSegmentCount() is 0)) {
+                    Widget[] parentItems = internalFindItems(parent);
+                    for (int j = 0; j < parentItems.length; j++) {
+                        Widget parentItem = parentItems[j];
+                        if (null !is cast(Item)parentItem ) {
+                            updatePlus(cast(Item) parentItem, parent);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes the given elements from this viewer, whenever those elements
+     * appear as children of the given parent.
+     *
+     * @param parent the parent element
+     * @param elements
+     *            the elements to remove
+     * @since 3.1
+     */
+    protected void internalRemove(Object parent, Object[] elements) {
+
+        CustomHashtable toRemove = new CustomHashtable(getComparer());
+        for (int i = 0; i < elements.length; i++) {
+            toRemove.put(elements[i], elements[i]);
+        }
+
+        // Find each place the parent appears in the tree
+        Widget[] parentItemArray = findItems(parent);
+        for (int i = 0; i < parentItemArray.length; i++) {
+            Widget parentItem = parentItemArray[i];
+
+            // May happen if parent element is a descendent of of a previously
+            // removed element
+            if (parentItem.isDisposed())
+                continue;
+
+            // Iterate over the child items and remove each one
+            Item[] children = getChildren(parentItem);
+
+            if (children.length is 1 && children[0].getData() is null &&
+                    (null !is cast(Item)parentItem )) { // dummy node
+                // Remove plus if parent element has no children
+                updatePlus(cast(Item) parentItem, parent);
+            } else {
+                for (int j = 0; j < children.length; j++) {
+                    Item child = children[j];
+
+                    Object data = child.getData();
+                    if (data !is null && toRemove.containsKey(data)) {
+                        disassociate(child);
+                        child.dispose();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets the expanded state of all items to correspond to the given set of
+     * expanded elements.
+     *
+     * @param expandedElements
+     *            the set (element type: <code>Object</code>) of elements
+     *            which are expanded
+     * @param widget
+     *            the widget
+     */
+    private void internalSetExpanded(CustomHashtable expandedElements,
+            Widget widget) {
+        Item[] items = getChildren(widget);
+        for (int i = 0; i < items.length; i++) {
+            Item item = items[i];
+            Object data = item.getData();
+            if (data !is null) {
+                // remove the element to avoid an infinite loop
+                // if the same element appears on a child item
+                bool expanded = expandedElements.remove(data) !is null;
+                if (expanded !is getExpanded(item)) {
+                    if (expanded) {
+                        createChildren(item);
+                    }
+                    setExpanded(item, expanded);
+                }
+            }
+            if (expandedElements.size() > 0) {
+                internalSetExpanded(expandedElements, item);
+            }
+        }
+    }
+
+    /**
+     * Sets the expanded state of all items to correspond to the given set of
+     * expanded tree paths.
+     *
+     * @param expandedTreePaths
+     *            the set (element type: <code>TreePath</code>) of elements
+     *            which are expanded
+     * @param widget
+     *            the widget
+     */
+    private void internalSetExpandedTreePaths(
+            CustomHashtable expandedTreePaths, Widget widget,
+            TreePath currentPath) {
+        Item[] items = getChildren(widget);
+        for (int i = 0; i < items.length; i++) {
+            Item item = items[i];
+            Object data = item.getData();
+            TreePath childPath = data is null ? null : currentPath
+                    .createChildPath(data);
+            if (data !is null && childPath !is null) {
+                // remove the element to avoid an infinite loop
+                // if the same element appears on a child item
+                bool expanded = expandedTreePaths.remove(childPath) !is null;
+                if (expanded !is getExpanded(item)) {
+                    if (expanded) {
+                        createChildren(item);
+                    }
+                    setExpanded(item, expanded);
+                }
+            }
+            internalSetExpandedTreePaths(expandedTreePaths, item, childPath);
+        }
+    }
+
+    /**
+     * Return whether the tree node representing the given element or path can
+     * be expanded. Clients should query expandability by path if the viewer's
+     * content provider is an {@link ITreePathContentProvider}.
+     * <p>
+     * The default implementation of this framework method calls
+     * <code>hasChildren</code> on this viewer's content provider. It may be
+     * overridden if necessary.
+     * </p>
+     *
+     * @param elementOrTreePath
+     *            the element or path
+     * @return <code>true</code> if the tree node representing the given
+     *         element can be expanded, or <code>false</code> if not
+     */
+    public bool isExpandable(Object elementOrTreePath) {
+        Object element;
+        TreePath path;
+        if (auto p = cast(TreePath)elementOrTreePath) {
+            path = p;
+            element = path.getLastSegment();
+        } else {
+            element = elementOrTreePath;
+            path = null;
+        }
+        IContentProvider cp = getContentProvider();
+        if ( auto tpcp = cast(ITreePathContentProvider) cp ) {
+            if (path is null) {
+                // A path was not provided so try and find one
+                Widget w = findItem(element);
+                if ( auto item = cast(Item)w ) {
+                    path = getTreePathFromItem(item);
+                }
+                if (path is null) {
+                    path = new TreePath([ element ]);
+                }
+            }
+            return tpcp.hasChildren(path);
+        }
+        if (auto tcp = cast(ITreeContentProvider)cp ) {
+            return tcp.hasChildren(element);
+        }
+        return false;
+    }
+
+    /**
+     * Return whether the given element is expandable.
+     *
+     * @param item
+     *            the tree item for the element
+     * @param parentPath
+     *            the parent path if it is known or <code>null</code> if it
+     *            needs to be determines
+     * @param element
+     *            the element
+     * @return whether the given element is expandable
+     */
+    private bool isExpandable(Item item, TreePath parentPath, Object element) {
+        Object elementOrTreePath = element;
+        if (isTreePathContentProvider()) {
+            if (parentPath !is null) {
+                elementOrTreePath = parentPath.createChildPath(element);
+            } else {
+                elementOrTreePath = getTreePathFromItem(item);
+            }
+        }
+        return isExpandable(elementOrTreePath);
+    }
+
+    /* (non-Javadoc) Method declared on Viewer. */
+    protected override void labelProviderChanged() {
+        // we have to walk the (visible) tree and update every item
+        Control tree = getControl();
+        tree.setRedraw(false);
+        // don't pick up structure changes, but do force label updates
+        internalRefresh(tree, getRoot(), false, true);
+        tree.setRedraw(true);
+    }
+
+    /**
+     * Creates a new item.
+     *
+     * @param parent
+     *            the parent widget
+     * @param style
+     *            SWT style bits
+     * @param index
+     *            if non-negative, indicates the position to insert the item
+     *            into its parent
+     * @return the newly-created item
+     */
+    protected abstract Item newItem(Widget parent, int style, int index);
+
+    /**
+     * Removes the given elements from this viewer. The selection is updated if
+     * required.
+     * <p>
+     * This method should be called (by the content provider) when elements have
+     * been removed from the model, in order to cause the viewer to accurately
+     * reflect the model. This method only affects the viewer, not the model.
+     * </p>
+     *
+     * @param elementsOrTreePaths
+     *            the elements to remove
+     */
+    public void remove(Object[] elementsOrTreePaths) {
+        assertElementsNotNull(elementsOrTreePaths);
+        if (elementsOrTreePaths.length is 0) {
+            return;
+        }
+        if (checkBusy())
+            return;
+        preservingSelection( dgRunnable( (Object[] elementsOrTreePaths_){
+            internalRemove(elementsOrTreePaths_);
+        }, elementsOrTreePaths));
+    }
+
+    /**
+     * Removes the given elements from this viewer whenever they appear as
+     * children of the given parent element. If the given elements also appear
+     * as children of some other parent, the other parent will remain unchanged.
+     * The selection is updated if required.
+     * <p>
+     * This method should be called (by the content provider) when elements have
+     * been removed from the model, in order to cause the viewer to accurately
+     * reflect the model. This method only affects the viewer, not the model.
+     * </p>
+     *
+     * @param parent
+     *            the parent of the elements to remove
+     * @param elements
+     *            the elements to remove
+     *
+     * @since 3.2
+     */
+    public void remove(Object parent, Object[] elements) {
+        assertElementsNotNull(elements);
+        if (elements.length is 0) {
+            return;
+        }
+        if (checkBusy())
+            return;
+        preservingSelection( dgRunnable( (Object parent_,Object[] elements_){
+            internalRemove(parent_, elements_);
+        }, parent, elements ));
+    }
+
+    /**
+     * Removes the given element from the viewer. The selection is updated if
+     * necessary.
+     * <p>
+     * This method should be called (by the content provider) when a single
+     * element has been removed from the model, in order to cause the viewer to
+     * accurately reflect the model. This method only affects the viewer, not
+     * the model. Note that there is another method for efficiently processing
+     * the simultaneous removal of multiple elements.
+     * </p>
+     *
+     * @param elementsOrTreePaths
+     *            the element
+     */
+    public void remove(Object elementsOrTreePaths) {
+        remove([ elementsOrTreePaths ]);
+    }
+
+    /**
+     * Removes all items from the given control.
+     *
+     * @param control
+     *            the control
+     */
+    protected abstract void removeAll(Control control);
+
+    /**
+     * Removes a listener for expand and collapse events in this viewer. Has no
+     * affect if an identical listener is not registered.
+     *
+     * @param listener
+     *            a tree viewer listener
+     */
+    public void removeTreeListener(ITreeViewerListener listener) {
+        treeListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * This implementation of reveal() reveals the given element or tree path.
+     */
+    public override void reveal(Object elementOrTreePath) {
+        Assert.isNotNull(elementOrTreePath);
+        Widget w = internalExpand(elementOrTreePath, true);
+        if ( auto item = cast(Item)w ) {
+            showItem(item);
+        }
+    }
+
+    /**
+     * Returns the rightmost visible descendent of the given item. Returns the
+     * item itself if it has no children.
+     *
+     * @param item
+     *            the item to compute the descendent of
+     * @return the rightmost visible descendent or the item itself if it has no
+     *         children
+     */
+    private Item rightMostVisibleDescendent(Item item) {
+        Item[] children = getItems(item);
+        if (getExpanded(item) && children !is null && children.length > 0) {
+            return rightMostVisibleDescendent(children[children.length - 1]);
+        }
+        return item;
+    }
+
+    /* (non-Javadoc) Method declared on Viewer. */
+    public override Item scrollDown(int x, int y) {
+        Item current = getItem(x, y);
+        if (current !is null) {
+            Item next = getNextItem(current, true);
+            showItem(next is null ? current : next);
+            return next;
+        }
+        return null;
+    }
+
+    /* (non-Javadoc) Method declared on Viewer. */
+    public override Item scrollUp(int x, int y) {
+        Item current = getItem(x, y);
+        if (current !is null) {
+            Item previous = getPreviousItem(current);
+            showItem(previous is null ? current : previous);
+            return previous;
+        }
+        return null;
+    }
+
+    /**
+     * Sets the auto-expand level to be used when the input of the viewer is set
+     * using {@link #setInput(Object)}. The value 0 means that there is no
+     * auto-expand; 1 means that the invisible root element is expanded (since
+     * most concrete subclasses do not show the root element, there is usually
+     * no practical difference between using the values 0 and 1); 2 means that
+     * top-level elements are expanded, but not their children; 3 means that
+     * top-level elements are expanded, and their children, but not
+     * grandchildren; and so on.
+     * <p>
+     * The value <code>ALL_LEVELS</code> means that all subtrees should be
+     * expanded.
+     * </p>
+     * <p>
+     * Note that in previous releases, the Javadoc for this method had an off-by
+     * one error. See bug 177669 for details.
+     * </p>
+     *
+     * @param level
+     *            non-negative level, or <code>ALL_LEVELS</code> to expand all
+     *            levels of the tree
+     */
+    public void setAutoExpandLevel(int level) {
+        expandToLevel_ = level;
+    }
+
+    /**
+     * The <code>AbstractTreeViewer</code> implementation of this method
+     * checks to ensure that the content provider is an
+     * <code>ITreeContentProvider</code>.
+     */
+    public override void setContentProvider(IContentProvider provider) {
+        // the actual check is in assertContentProviderType
+        super.setContentProvider(provider);
+    }
+
+    protected override void assertContentProviderType(IContentProvider provider) {
+        Assert.isTrue(cast(ITreeContentProvider)provider
+                || cast(ITreePathContentProvider)provider );
+    }
+
+    /**
+     * Sets the expand state of the given item.
+     *
+     * @param item
+     *            the item
+     * @param expand
+     *            the expand state of the item
+     */
+    protected abstract void setExpanded(Item item, bool expand);
+
+    /**
+     * Sets which nodes are expanded in this viewer's tree. The given list
+     * contains the elements that are to be expanded; all other nodes are to be
+     * collapsed.
+     * <p>
+     * This method is typically used when restoring the interesting state of a
+     * viewer captured by an earlier call to <code>getExpandedElements</code>.
+     * </p>
+     *
+     * @param elements
+     *            the array of expanded elements
+     * @see #getExpandedElements
+     */
+    public void setExpandedElements(Object[] elements) {
+        assertElementsNotNull(elements);
+        if (checkBusy()) {
+            return;
+        }
+        CustomHashtable expandedElements = newHashtable(elements.length * 2 + 1);
+        for (int i = 0; i < elements.length; ++i) {
+            Object element = elements[i];
+            // Ensure item exists for element. This will materialize items for
+            // each element and their parents, if possible. This is important
+            // to support expanding of inner tree nodes without necessarily
+            // expanding their parents.
+            internalExpand(element, false);
+            expandedElements.put(element, element);
+        }
+        // this will traverse all existing items, and create children for
+        // elements that need to be expanded. If the tree contains multiple
+        // equal elements, and those are in the set of elements to be expanded,
+        // only the first item found for each element will be expanded.
+        internalSetExpanded(expandedElements, getControl());
+    }
+
+    /**
+     * Sets which nodes are expanded in this viewer's tree. The given list
+     * contains the tree paths that are to be expanded; all other nodes are to
+     * be collapsed.
+     * <p>
+     * This method is typically used when restoring the interesting state of a
+     * viewer captured by an earlier call to <code>getExpandedTreePaths</code>.
+     * </p>
+     *
+     * @param treePaths
+     *            the array of expanded tree paths
+     * @see #getExpandedTreePaths()
+     *
+     * @since 3.2
+     */
+    public void setExpandedTreePaths(TreePath[] treePaths) {
+        assertElementsNotNull(treePaths);
+        if (checkBusy())
+            return;
+        IElementComparer treePathComparer = new class(getComparer()) IElementComparer {
+            IElementComparer comparer;
+            this(IElementComparer c){
+                comparer = c;
+            }
+            public int opEquals(Object a, Object b) {
+                return (cast(TreePath) a).opEquals((cast(TreePath) b), comparer);
+            }
+
+            public hash_t toHash(Object element) {
+                return (cast(TreePath) element).toHash(comparer);
+            }
+        };
+        CustomHashtable expandedTreePaths = new CustomHashtable(
+                treePaths.length * 2 + 1, treePathComparer);
+        for (int i = 0; i < treePaths.length; ++i) {
+            TreePath treePath = treePaths[i];
+            // Ensure item exists for element. This will materialize items for
+            // each element and their parents, if possible. This is important
+            // to support expanding of inner tree nodes without necessarily
+            // expanding their parents.
+            internalExpand(treePath, false);
+            expandedTreePaths.put(treePath, treePath);
+        }
+        // this will traverse all existing items, and create children for
+        // elements that need to be expanded. If the tree contains multiple
+        // equal elements, and those are in the set of elements to be expanded,
+        // only the first item found for each element will be expanded.
+        internalSetExpandedTreePaths(expandedTreePaths, getControl(),
+                new TreePath(new Object[0]));
+    }
+
+    /**
+     * Sets whether the node corresponding to the given element or tree path is
+     * expanded or collapsed.
+     *
+     * @param elementOrTreePath
+     *            the element
+     * @param expanded
+     *            <code>true</code> if the node is expanded, and
+     *            <code>false</code> if collapsed
+     */
+    public void setExpandedState(Object elementOrTreePath, bool expanded) {
+        Assert.isNotNull(elementOrTreePath);
+        if (checkBusy())
+            return;
+        Widget item = internalExpand(elementOrTreePath, false);
+        if ( cast(Item)item ) {
+            if (expanded) {
+                createChildren(item);
+            }
+            setExpanded(cast(Item) item, expanded);
+        }
+    }
+
+    /**
+     * Sets the selection to the given list of items.
+     *
+     * @param items
+     *            list of items (element type:
+     *            <code>org.eclipse.swt.widgets.Item</code>)
+     */
+    protected abstract void setSelection(List items);
+
+    /**
+     * This implementation of setSelectionToWidget accepts a list of elements or
+     * a list of tree paths.
+     */
+    protected override void setSelectionToWidget(List v, bool reveal) {
+        if (v is null) {
+            setSelection(new ArrayList(0));
+            return;
+        }
+        int size = v.size();
+        List newSelection = new ArrayList(size);
+        for (int i = 0; i < size; ++i) {
+            Object elementOrTreePath = v.get(i);
+            // Use internalExpand since item may not yet be created. See
+            // 1G6B1AR.
+            Widget w = internalExpand(elementOrTreePath, false);
+            if ( auto it = cast(Item)w ) {
+                newSelection.add(it);
+            } else if (w is null && null !is cast(TreePath)elementOrTreePath ) {
+                TreePath treePath = cast(TreePath) elementOrTreePath;
+                Object element = treePath.getLastSegment();
+                if (element !is null) {
+                    w = internalExpand(element, false);
+                    if ( auto it = cast(Item)w ) {
+                        newSelection.add(it);
+                    }
+                }
+            }
+        }
+        setSelection(newSelection);
+
+        // Although setting the selection in the control should reveal it,
+        // setSelection may be a no-op if the selection is unchanged,
+        // so explicitly reveal items in the selection here.
+        // See bug 100565 for more details.
+        if (reveal && newSelection.size() > 0) {
+            // Iterate backwards so the first item in the list
+            // is the one guaranteed to be visible
+            for (int i = (newSelection.size()-1); i >= 0; i--) {
+                showItem(cast(Item) newSelection.get(i));
+            }
+        }
+    }
+
+    /**
+     * Shows the given item.
+     *
+     * @param item
+     *            the item
+     */
+    protected abstract void showItem(Item item);
+
+    /**
+     * Updates the tree items to correspond to the child elements of the given
+     * parent element. If null is passed for the children, this method obtains
+     * them (only if needed).
+     *
+     * @param widget
+     *            the widget
+     * @param parent
+     *            the parent element
+     * @param elementChildren
+     *            the child elements, or null
+     * @deprecated this is no longer called by the framework
+     */
+    protected void updateChildren(Widget widget, Object parent,
+            Object[] elementChildren) {
+        updateChildren(widget, parent, elementChildren, true);
+    }
+
+    /**
+     * Updates the tree items to correspond to the child elements of the given
+     * parent element. If null is passed for the children, this method obtains
+     * them (only if needed).
+     *
+     * @param widget
+     *            the widget
+     * @param parent
+     *            the parent element
+     * @param elementChildren
+     *            the child elements, or null
+     * @param updateLabels
+     *            <code>true</code> to update labels for existing elements,
+     *            <code>false</code> to only update labels as needed, assuming
+     *            that labels for existing elements are unchanged.
+     * @since 2.1
+     */
+    private void updateChildren(Widget widget, Object parent,
+            Object[] elementChildren, bool updateLabels) {
+        // optimization! prune collapsed subtrees
+        if (auto ti = cast(Item)widget ) {
+            if (!getExpanded(ti)) {
+                // need a dummy node if element is expandable;
+                // but try to avoid recreating the dummy node
+                bool needDummy = isExpandable(ti, null, parent);
+                bool haveDummy = false;
+                // remove all children
+                Item[] items = getItems(ti);
+                for (int i = 0; i < items.length; i++) {
+                    if (items[i].getData() !is null) {
+                        disassociate(items[i]);
+                        items[i].dispose();
+                    } else {
+                        if (needDummy && !haveDummy) {
+                            haveDummy = true;
+                        } else {
+                            items[i].dispose();
+                        }
+                    }
+                }
+                if (needDummy && !haveDummy) {
+                    newItem(ti, SWT.NULL, -1);
+                }
+
+                return;
+            }
+        }
+
+        // If the children weren't passed in, get them now since they're needed
+        // below.
+        if (elementChildren is null) {
+            if (isTreePathContentProvider() && null !is cast(Item) widget ) {
+                TreePath path = getTreePathFromItem(cast(Item) widget);
+                elementChildren = getSortedChildren(path);
+            } else {
+                elementChildren = getSortedChildren(parent);
+            }
+        }
+
+        Control tree = getControl();
+
+        // WORKAROUND
+        int oldCnt = -1;
+        if (widget is tree) {
+            oldCnt = getItemCount(tree);
+        }
+
+        Item[] items = getChildren(widget,elementChildren);
+
+        // save the expanded elements
+        CustomHashtable expanded = newHashtable(CustomHashtable.DEFAULT_CAPACITY); // assume
+                                                                                    // num
+                                                                                    // expanded
+                                                                                    // is
+                                                                                    // small
+        for (int i = 0; i < items.length; ++i) {
+            if (getExpanded(items[i])) {
+                Object element = items[i].getData();
+                if (element !is null) {
+                    expanded.put(element, element);
+                }
+            }
+        }
+
+        int min = Math.min(elementChildren.length, items.length);
+
+        // dispose of surplus items, optimizing for the case where elements have
+        // been deleted but not reordered, or all elements have been removed.
+        int numItemsToDispose = items.length - min;
+        if (numItemsToDispose > 0) {
+            CustomHashtable children = newHashtable(elementChildren.length * 2);
+            for (int i = 0; i < elementChildren.length; i++) {
+                Object elementChild = elementChildren[i];
+                children.put(elementChild, elementChild);
+            }
+            int i = 0;
+            while (numItemsToDispose > 0 && i < items.length) {
+                Object data = items[i].getData();
+                if (data is null || items.length - i <= numItemsToDispose || !children.containsKey(data)) {
+                    if (data !is null) {
+                        disassociate(items[i]);
+                    }
+                    items[i].dispose();
+                    if (i + 1 < items.length) {
+                        // The components at positions i+1 through
+                        // items.length-1 in the source array are copied into
+                        // positions i through items.length-2
+                        System.arraycopy(items, i + 1, items, i, items.length - (i+1));
+                    }
+                    numItemsToDispose--;
+                } else {
+                    i++;
+                }
+            }
+        }
+
+        // compare first min items, and update item if necessary
+        // need to do it in two passes:
+        // 1: disassociate old items
+        // 2: associate new items
+        // because otherwise a later disassociate can remove a mapping made for
+        // a previous associate,
+        // making the map inconsistent
+        for (int i = 0; i < min; ++i) {
+            Item item = items[i];
+            Object oldElement = item.getData();
+            if (oldElement !is null) {
+                Object newElement = elementChildren[i];
+                if (newElement !is oldElement) {
+                    if (opEquals(newElement, oldElement)) {
+                        // update the data to be the new element, since
+                        // although the elements
+                        // may be equal, they may still have different labels
+                        // or children
+                        Object data = item.getData();
+                        if (data !is null) {
+                            unmapElement(data, item);
+                        }
+                        item.setData(newElement);
+                        mapElement(newElement, item);
+                    } else {
+                        disassociate(item);
+                        // Clear the text and image to force a label update
+                        item.setImage(null);
+                        item.setText("");//$NON-NLS-1$
+
+                    }
+                }
+            }
+        }
+
+        for (int i = 0; i < min; ++i) {
+            Item item = items[i];
+            Object newElement = elementChildren[i];
+            if (item.getData() is null) {
+                // old and new elements are not equal
+                associate(newElement, item);
+                updatePlus(item, newElement);
+                updateItem(item, newElement);
+            } else {
+                // old and new elements are equal
+                updatePlus(item, newElement);
+                if (updateLabels) {
+                    updateItem(item, newElement);
+                }
+            }
+        }
+
+        // Restore expanded state for items that changed position.
+        // Make sure setExpanded is called after updatePlus, since
+        // setExpanded(false) fails if item has no children.
+        // Need to call setExpanded for both expanded and unexpanded
+        // cases since the expanded state can change either way.
+        // This needs to be done in a second loop, see bug 148025.
+        for (int i = 0; i < min; ++i) {
+            Item item = items[i];
+            Object newElement = elementChildren[i];
+            setExpanded(item, expanded.containsKey(newElement));
+        }
+
+        // add any remaining elements
+        if (min < elementChildren.length) {
+            for (int i = min; i < elementChildren.length; ++i) {
+                createTreeItem(widget, elementChildren[i], i);
+            }
+
+            // Need to restore expanded state in a separate pass
+            // because createTreeItem does not return the new item.
+            // Avoid doing this unless needed.
+            if (expanded.size() > 0) {
+                // get the items again, to include the new items
+                items = getChildren(widget);
+                for (int i = min; i < elementChildren.length; ++i) {
+                    // Restore expanded state for items that changed position.
+                    // Make sure setExpanded is called after updatePlus (called
+                    // in createTreeItem), since
+                    // setExpanded(false) fails if item has no children.
+                    // Only need to call setExpanded if element was expanded
+                    // since new items are initially unexpanded.
+                    if (expanded.containsKey(elementChildren[i])) {
+                        setExpanded(items[i], true);
+                    }
+                }
+            }
+        }
+
+        // WORKAROUND
+        if (widget is tree && oldCnt is 0 && getItemCount(tree) !is 0) {
+            // System.out.println("WORKAROUND setRedraw");
+            tree.setRedraw(false);
+            tree.setRedraw(true);
+        }
+    }
+
+    /**
+     * Return the items to be refreshed as part of an update. elementChildren are the
+     * new elements.
+     * @param widget
+     * @param elementChildren
+     * @since 3.4
+     * @return Item[]
+     * <strong>NOTE:</strong> This API is experimental and may be deleted
+     * before 3.4 is released.
+     */
+    public Item[] getChildren(Widget widget,  Object[] elementChildren) {
+        return getChildren(widget);
+    }
+
+    /**
+     * Updates the "+"/"-" icon of the tree node from the given element. It
+     * calls <code>isExpandable</code> to determine whether an element is
+     * expandable.
+     *
+     * @param item
+     *            the item
+     * @param element
+     *            the element
+     */
+    protected void updatePlus(Item item, Object element) {
+        bool hasPlus = getItemCount(item) > 0;
+        bool needsPlus = isExpandable(item, null, element);
+        bool removeAll = false;
+        bool addDummy = false;
+        Object data = item.getData();
+        if (data !is null && opEquals(element, data)) {
+            // item shows same element
+            if (hasPlus !is needsPlus) {
+                if (needsPlus) {
+                    addDummy = true;
+                } else {
+                    removeAll = true;
+                }
+            }
+        } else {
+            // item shows different element
+            removeAll = true;
+            addDummy = needsPlus;
+
+            // we cannot maintain expand state so collapse it
+            setExpanded(item, false);
+        }
+        if (removeAll) {
+            // remove all children
+            Item[] items = getItems(item);
+            for (int i = 0; i < items.length; i++) {
+                if (items[i].getData() !is null) {
+                    disassociate(items[i]);
+                }
+                items[i].dispose();
+            }
+        }
+        if (addDummy) {
+            newItem(item, SWT.NULL, -1); // append a dummy
+        }
+    }
+
+    /**
+     * Gets the expanded elements that are visible to the user. An expanded
+     * element is only visible if the parent is expanded.
+     *
+     * @return the visible expanded elements
+     * @since 2.0
+     */
+    public Object[] getVisibleExpandedElements() {
+        ArrayList v = new ArrayList();
+        internalCollectVisibleExpanded(v, getControl());
+        return v.toArray();
+    }
+
+    private void internalCollectVisibleExpanded(ArrayList result, Widget widget) {
+        Item[] items = getChildren(widget);
+        for (int i = 0; i < items.length; i++) {
+            Item item = items[i];
+            if (getExpanded(item)) {
+                Object data = item.getData();
+                if (data !is null) {
+                    result.add(data);
+                }
+                // Only recurse if it is expanded - if
+                // not then the children aren't visible
+                internalCollectVisibleExpanded(result, item);
+            }
+        }
+    }
+
+    /**
+     * Returns the tree path for the given item.
+     * @param item
+     * @return {@link TreePath}
+     *
+     * @since 3.2
+     */
+    protected TreePath getTreePathFromItem(Item item) {
+        LinkedList segments = new LinkedList();
+        while (item !is null) {
+            Object segment = item.getData();
+            Assert.isNotNull(segment);
+            segments.addFirst(segment);
+            item = getParentItem(item);
+        }
+        return new TreePath(segments.toArray());
+    }
+    package TreePath getTreePathFromItem_package(Item item) {
+        return getTreePathFromItem_package(item);
+    }
+
+    /**
+     * This implementation of getSelection() returns an instance of
+     * ITreeSelection.
+     *
+     * @since 3.2
+     */
+    public override ISelection getSelection() {
+        Control control = getControl();
+        if (control is null || control.isDisposed()) {
+            return TreeSelection.EMPTY;
+        }
+        Widget[] items = getSelection(getControl());
+        ArrayList list = new ArrayList(items.length);
+        for (int i = 0; i < items.length; i++) {
+            Widget item = items[i];
+            if (item.getData() !is null) {
+                list.add(getTreePathFromItem(cast(Item) item));
+            }
+        }
+        return new TreeSelection( arraycast!(TreePath)(list.toArray()), getComparer());
+    }
+
+    protected override void setSelectionToWidget(ISelection selection, bool reveal) {
+        if ( auto treeSelection = cast(ITreeSelection)selection ) {
+            setSelectionToWidget(Arrays.asList(treeSelection.getPaths()),
+                    reveal);
+        } else {
+            super.setSelectionToWidget(selection, reveal);
+        }
+    }
+
+    /**
+     * Returns a list of tree paths corresponding to expanded nodes in this
+     * viewer's tree, including currently hidden ones that are marked as
+     * expanded but are under a collapsed ancestor.
+     * <p>
+     * This method is typically used when preserving the interesting state of a
+     * viewer; <code>setExpandedElements</code> is used during the restore.
+     * </p>
+     *
+     * @return the array of expanded tree paths
+     * @see #setExpandedElements
+     *
+     * @since 3.2
+     */
+    public TreePath[] getExpandedTreePaths() {
+        ArrayList items = new ArrayList();
+        internalCollectExpandedItems(items, getControl());
+        ArrayList result = new ArrayList(items.size());
+        for (Iterator it = items.iterator(); it.hasNext();) {
+            Item item = cast(Item) it.next();
+            TreePath treePath = getTreePathFromItem(item);
+            if (treePath !is null) {
+                result.add(treePath);
+            }
+        }
+        return arraycast!(TreePath)(result.toArray());
+    }
+
+    private bool isTreePathContentProvider() {
+        return null !is cast(ITreePathContentProvider)getContentProvider() ;
+    }
+
+    /**
+     * Inserts the given element as a new child element of the given parent
+     * element at the given position. If this viewer has a sorter, the position
+     * is ignored and the element is inserted at the correct position in the
+     * sort order.
+     * <p>
+     * This method should be called (by the content provider) when elements have
+     * been added to the model, in order to cause the viewer to accurately
+     * reflect the model. This method only affects the viewer, not the model.
+     * </p>
+     *
+     * @param parentElementOrTreePath
+     *            the parent element, or the tree path to the parent
+     * @param element
+     *            the element
+     * @param position
+     *            a 0-based position relative to the model, or -1 to indicate
+     *            the last position
+     *
+     * @since 3.2
+     */
+    public void insert(Object parentElementOrTreePath, Object element,
+            int position) {
+        Assert.isNotNull(parentElementOrTreePath);
+        Assert.isNotNull(element);
+        if (checkBusy())
+            return;
+        if (getComparator() !is null || hasFilters()) {
+            add(parentElementOrTreePath, [ element ]);
+            return;
+        }
+        Widget[] items;
+        if (internalIsInputOrEmptyPath(parentElementOrTreePath)) {
+            items = [ getControl() ];
+        } else {
+            items = internalFindItems(parentElementOrTreePath);
+        }
+
+        for (int i = 0; i < items.length; i++) {
+            Widget widget = items[i];
+            if (auto item = cast(Item)widget ) {
+
+                Item[] childItems = getChildren(item);
+                if (getExpanded(item)
+                        || (childItems.length > 0 && childItems[0].getData() !is null)) {
+                    // item has real children, go ahead and add
+                    int insertionPosition = position;
+                    if (insertionPosition is -1) {
+                        insertionPosition = getItemCount(item);
+                    }
+
+                    createTreeItem(item, element, insertionPosition);
+                } else {
+                    Object parentElement = parentElementOrTreePath;
+                    if (null !is cast(TreePath)element )
+                        parentElement = (cast(TreePath) parentElement).getLastSegment();
+                    updatePlus(item, parentElement);
+                }
+            } else {
+                int insertionPosition = position;
+                if (insertionPosition is -1) {
+                    insertionPosition = getItemCount(cast(Control) widget);
+                }
+
+                createTreeItem(widget, element, insertionPosition);
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ColumnViewer#getColumnViewerOwner(int)
+     */
+    protected override Widget getColumnViewerOwner(int columnIndex) {
+        // Return null by default
+        return null;
+    }
+
+    /**
+     * This implementation of {@link #getItemAt(Point)} returns null to ensure
+     * API backwards compatibility. Subclasses should override.
+     *
+     * @since 3.3
+     */
+    protected override Item getItemAt(Point point) {
+        return null;
+    }
+
+    /**
+     * This implementation of {@link #createViewerEditor()} returns null to ensure
+     * API backwards compatibility. Subclasses should override.
+     *
+     * @since 3.3
+     */
+    protected override ColumnViewerEditor createViewerEditor() {
+        return null;
+    }
+
+    /**
+     * Returns the number of columns of this viewer.
+     * <p><b>Subclasses should overwrite this method, which has a default
+     * implementation (returning 0) for API backwards compatility reasons</b></p>
+     *
+     * @return the number of columns
+     *
+     * @since 3.3
+     */
+    protected override int doGetColumnCount() {
+        return 0;
+    }
+
+
+    /**
+     * This implementation of buildLabel handles tree paths as well as elements.
+     *
+     * @param updateLabel
+     *            the ViewerLabel to collect the result in
+     * @param elementOrPath
+     *            the element or tree path for which a label should be built
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#buildLabel(org.eclipse.jface.viewers.ViewerLabel,
+     *      java.lang.Object)
+     */
+    protected override void buildLabel(ViewerLabel updateLabel, Object elementOrPath) {
+        Object element;
+        if (auto path = cast(TreePath)elementOrPath ) {
+            IBaseLabelProvider provider = getLabelProvider();
+            if ( auto pprov = cast(ITreePathLabelProvider) provider ) {
+                buildLabel(updateLabel, path, pprov);
+                return;
+            }
+            element = path.getLastSegment();
+        } else {
+            element = elementOrPath;
+        }
+        super.buildLabel(updateLabel, element);
+    }
+
+    /**
+     * Returns true if the given object is either the input or an empty tree path.
+     *
+     * @param elementOrTreePath an element which could either be the viewer's input, or a tree path
+     *
+     * @return <code>true</code> if the given object is either the input or an empty tree path,
+     * <code>false</code> otherwise.
+     * @since 3.3
+     */
+    final protected bool internalIsInputOrEmptyPath(Object elementOrTreePath) {
+        if (elementOrTreePath.opEquals(getRoot()))
+            return true;
+        if (!(cast(TreePath)elementOrTreePath ))
+            return false;
+        return (cast(TreePath) elementOrTreePath).getSegmentCount() is 0;
+    }
+
+    /*
+     * Subclasses should implement
+     */
+    protected override ViewerRow getViewerRowFromItem(Widget item) {
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/AcceptAllFilter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.AcceptAllFilter;
+
+import org.eclipse.jface.viewers.IFilter;
+
+import java.lang.all;
+
+
+/**
+ * Filter that accepts everything. Available as a singleton since having
+ * more than one instance would be wasteful.
+ *
+ * @since 3.1
+ */
+public final class AcceptAllFilter : IFilter {
+
+    /**
+     * Returns the singleton instance of AcceptAllFilter
+     *
+     * @return the singleton instance of AcceptAllFilter
+     */
+    public static IFilter getInstance() {
+        if( singleton is null ){
+            synchronized{
+                if( singleton is null ){
+                    singleton = new AcceptAllFilter();
+                }
+            }
+        }
+        return singleton;
+    }
+
+    /**
+     * The singleton instance
+     */
+    private static IFilter singleton = null;
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.deferred.IFilter#select(java.lang.Object)
+     */
+    public bool select(Object toTest) {
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public override int opEquals(Object other) {
+        return other is this || null !is cast(AcceptAllFilter)other ;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ArrayContentProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.ArrayContentProvider;
+
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+
+import java.lang.all;
+import java.util.Collection;
+
+/**
+ * This implementation of <code>IStructuredContentProvider</code> handles
+ * the case where the viewer input is an unchanging array or collection of elements.
+ * <p>
+ * This class is not intended to be subclassed outside the viewer framework.
+ * </p>
+ *
+ * @since 2.1
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ArrayContentProvider(T) : IStructuredContentProvider {
+
+    /**
+     * Returns the elements in the input, which must be either an array or a
+     * <code>Collection</code>.
+     */
+    public Object[] getElements(Object inputElement) {
+        if ( auto aw = cast(ArrayWrapperT!(T)) inputElement ) {
+            return aw.array;
+        }
+        if ( auto col = cast(Collection) inputElement ) {
+            return col.toArray();
+        }
+        return null;
+    }
+
+    /**
+     * This implementation does nothing.
+     */
+    public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+        // do nothing.
+    }
+
+    /**
+     * This implementation does nothing.
+     */
+    public void dispose() {
+        // do nothing.
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/BaseLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.viewers.BaseLabelProvider;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.LabelProviderChangedEvent;
+
+import org.eclipse.core.commands.common.EventManager;
+import org.eclipse.jface.util.SafeRunnable;
+
+import java.lang.all;
+
+/**
+ * BaseLabelProvider is a default concrete implementation of
+ * {@link IBaseLabelProvider}
+ *
+ * @since 3.3
+ *
+ */
+public class BaseLabelProvider : EventManager, IBaseLabelProvider {
+
+    /* (non-Javadoc)
+     * Method declared on IBaseLabelProvider.
+     */
+    public void addListener(ILabelProviderListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * The <code>BaseLabelProvider</code> implementation of this
+     * <code>IBaseLabelProvider</code> method clears its internal listener list.
+     * Subclasses may extend but should call the super implementation.
+     */
+    public void dispose() {
+        clearListeners();
+    }
+
+    /**
+     * The <code>BaseLabelProvider</code> implementation of this
+     * <code>IBaseLabelProvider</code> method returns <code>true</code>. Subclasses may
+     * override.
+     */
+    public bool isLabelProperty(Object element, String property) {
+        return true;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
+     */
+    public void removeListener(ILabelProviderListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Fires a label provider changed event to all registered listeners Only
+     * listeners registered at the time this method is called are notified.
+     *
+     * @param event
+     *            a label provider changed event
+     *
+     * @see ILabelProviderListener#labelProviderChanged
+     */
+    protected void fireLabelProviderChanged(LabelProviderChangedEvent event) {
+        Object[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; ++i) {
+            SafeRunnable.run(new class(event,cast(ILabelProviderListener) listeners[i]) SafeRunnable {
+                LabelProviderChangedEvent event_;
+                ILabelProviderListener l;
+                this(LabelProviderChangedEvent a,ILabelProviderListener b){
+                    event_=a;
+                    l = b;
+                }
+                public void run() {
+                    l.labelProviderChanged(event_);
+                }
+            });
+
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/CellEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,981 @@
+/*******************************************************************************
+ * 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - bugfix in: 187963, 218336
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.CellEditor;
+
+import org.eclipse.jface.viewers.ICellEditorValidator;
+import org.eclipse.jface.viewers.ICellEditorListener;
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
+import org.eclipse.jface.viewers.ColumnViewerEditorDeactivationEvent;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.util.SafeRunnable;
+
+import java.lang.all;
+import java.util.List;
+import java.util.Set;
+
+    /**
+     * Struct-like layout data for cell editors, with reasonable defaults for
+     * all fields.
+     *
+     * @noextend This class is not intended to be subclassed by clients.
+     */
+    public static class LayoutData {
+        /**
+         * Horizontal alignment; <code>SWT.LEFT</code> by default.
+         */
+        public int horizontalAlignment = SWT.LEFT;
+
+        /**
+         * Indicates control grabs additional space; <code>true</code> by
+         * default.
+         */
+        public bool grabHorizontal = true;
+
+        /**
+         * Minimum width in pixels; <code>50</code> pixels by default.
+         */
+        public int minimumWidth = 50;
+
+        /**
+         * Minimum height in pixels; by default the height is aligned to the
+         * row-height
+         * @since 3.4
+         */
+        public int minimumHeight = SWT.DEFAULT;
+
+        /**
+         * The vertical alignment; <code>SWT.CENTER</code> by default.
+         * @since 3.4
+         */
+        public int verticalAlignment = SWT.CENTER;
+    }
+/**
+ * Abstract base class for cell editors. Implements property change listener
+ * handling, and SWT window management.
+ * <p>
+ * Subclasses implement particular kinds of cell editors. This package contains
+ * various specialized cell editors:
+ * <ul>
+ * <li><code>TextCellEditor</code> - for simple text strings</li>
+ * <li><code>ColorCellEditor</code> - for colors</li>
+ * <li><code>ComboBoxCellEditor</code> - value selected from drop-down combo
+ * box</li>
+ * <li><code>CheckboxCellEditor</code> - bool valued checkbox</li>
+ * <li><code>DialogCellEditor</code> - value from arbitrary dialog</li>
+ * </ul>
+ * </p>
+ */
+public abstract class CellEditor {
+
+    /**
+     * List of cell editor listeners (element type:
+     * <code>ICellEditorListener</code>).
+     */
+    private ListenerList listeners;
+
+    /**
+     * List of cell editor property change listeners (element type:
+     * <code>IPropertyChangeListener</code>).
+     */
+    private ListenerList propertyChangeListeners;
+
+    /**
+     * Indicates whether this cell editor's current value is valid.
+     */
+    private bool valid = false;
+
+    /**
+     * Optional cell editor validator; <code>null</code> if none.
+     */
+    private ICellEditorValidator validator = null;
+
+    /**
+     * The error message string to display for invalid values; <code>null</code>
+     * if none (that is, the value is valid).
+     */
+    private String errorMessage = null;
+
+    /**
+     * Indicates whether this cell editor has been changed recently.
+     */
+    private bool dirty = false;
+
+    /**
+     * This cell editor's control, or <code>null</code> if not created yet.
+     */
+    private Control control = null;
+
+    /**
+     * Default cell editor style
+     */
+    private static const int defaultStyle = SWT.NONE;
+
+    /**
+     * This cell editor's style
+     */
+    private int style = defaultStyle;
+
+    //public alias org.eclipse.jface.viewers.CellEditor.LayoutData LayoutData;
+    /**
+     * Property name for the copy action
+     */
+    public static const String COPY = "copy"; //$NON-NLS-1$
+
+    /**
+     * Property name for the cut action
+     */
+    public static const String CUT = "cut"; //$NON-NLS-1$
+
+    /**
+     * Property name for the delete action
+     */
+    public static const String DELETE = "delete"; //$NON-NLS-1$
+
+    /**
+     * Property name for the find action
+     */
+    public static const String FIND = "find"; //$NON-NLS-1$
+
+    /**
+     * Property name for the paste action
+     */
+    public static const String PASTE = "paste"; //$NON-NLS-1$
+
+    /**
+     * Property name for the redo action
+     */
+    public static const String REDO = "redo"; //$NON-NLS-1$
+
+    /**
+     * Property name for the select all action
+     */
+    public static const String SELECT_ALL = "selectall"; //$NON-NLS-1$
+
+    /**
+     * Property name for the undo action
+     */
+    public static const String UNDO = "undo"; //$NON-NLS-1$
+
+    /**
+     * Creates a new cell editor with no control The cell editor has no cell
+     * validator.
+     *
+     * @since 2.1
+     */
+    protected this() {
+        propertyChangeListeners = new ListenerList();
+        listeners = new ListenerList();
+    }
+
+    /**
+     * Creates a new cell editor under the given parent control. The cell editor
+     * has no cell validator.
+     *
+     * @param parent
+     *            the parent control
+     */
+    protected this(Composite parent) {
+        this(parent, defaultStyle);
+    }
+
+    /**
+     * Creates a new cell editor under the given parent control. The cell editor
+     * has no cell validator.
+     *
+     * @param parent
+     *            the parent control
+     * @param style
+     *            the style bits
+     * @since 2.1
+     */
+    protected this(Composite parent, int style) {
+        propertyChangeListeners = new ListenerList();
+        listeners = new ListenerList();
+        this.style = style;
+        create(parent);
+    }
+
+    /**
+     * Activates this cell editor.
+     * <p>
+     * The default implementation of this framework method does nothing.
+     * Subclasses may reimplement.
+     * </p>
+     */
+    public void activate() {
+    }
+
+    /**
+     * Adds a listener to this cell editor. Has no effect if an identical
+     * listener is already registered.
+     *
+     * @param listener
+     *            a cell editor listener
+     */
+    public void addListener(ICellEditorListener listener) {
+        listeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Adds a property change listener to this cell editor. Has no effect if an
+     * identical property change listener is already registered.
+     *
+     * @param listener
+     *            a property change listener
+     */
+    public void addPropertyChangeListener(IPropertyChangeListener listener) {
+        propertyChangeListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Creates the control for this cell editor under the given parent control.
+     * <p>
+     * This framework method must be implemented by concrete subclasses.
+     * </p>
+     *
+     * @param parent
+     *            the parent control
+     * @return the new control, or <code>null</code> if this cell editor has
+     *         no control
+     */
+    protected abstract Control createControl(Composite parent);
+
+    /**
+     * Creates the control for this cell editor under the given parent control.
+     *
+     * @param parent
+     *            the parent control
+     * @since 2.1
+     */
+    public void create(Composite parent) {
+        Assert.isTrue(control is null);
+        control = createControl(parent);
+        // See 1GD5CA6: ITPUI:ALL - TaskView.setSelection does not work
+        // Control is created with getVisible()is true by default.
+        // This causes composite.setFocus() to work incorrectly.
+        // The cell editor's control grabs focus instead, even if it is not
+        // active.
+        // Make the control invisible here by default.
+        deactivate();
+    }
+
+    /**
+     * Hides this cell editor's control. Does nothing if this cell editor is not
+     * visible.
+     */
+    public void deactivate() {
+        if (control !is null && !control.isDisposed()) {
+            control.setVisible(false);
+        }
+    }
+
+    /**
+     * Disposes of this cell editor and frees any associated SWT resources.
+     */
+    public void dispose() {
+        if (control !is null && !control.isDisposed()) {
+            control.dispose();
+        }
+        control = null;
+    }
+
+    /**
+     * Returns this cell editor's value.
+     * <p>
+     * This framework method must be implemented by concrete subclasses.
+     * </p>
+     *
+     * @return the value of this cell editor
+     * @see #getValue
+     */
+    protected abstract Object doGetValue();
+
+    /**
+     * Sets the focus to the cell editor's control.
+     * <p>
+     * This framework method must be implemented by concrete subclasses.
+     * </p>
+     *
+     * @see #setFocus
+     */
+    protected abstract void doSetFocus();
+
+    /**
+     * Sets this cell editor's value.
+     * <p>
+     * This framework method must be implemented by concrete subclasses.
+     * </p>
+     *
+     * @param value
+     *            the value of this cell editor
+     * @see #setValue
+     */
+    protected abstract void doSetValue(Object value);
+
+    /**
+     * Notifies all registered cell editor listeners of an apply event. Only
+     * listeners registered at the time this method is called are notified.
+     *
+     * @see ICellEditorListener#applyEditorValue
+     */
+    protected void fireApplyEditorValue() {
+        Object[] array = listeners.getListeners();
+        for (int i = 0; i < array.length; i++) {
+            SafeRunnable.run(new class(cast(ICellEditorListener) array[i]) SafeRunnable {
+                ICellEditorListener l;
+                this(ICellEditorListener a){
+                    l = a;
+                }
+                public void run() {
+                    l.applyEditorValue();
+                }
+            });
+        }
+    }
+
+    /**
+     * Notifies all registered cell editor listeners that editing has been
+     * canceled.
+     *
+     * @see ICellEditorListener#cancelEditor
+     */
+    protected void fireCancelEditor() {
+        Object[] array = listeners.getListeners();
+        for (int i = 0; i < array.length; i++) {
+            SafeRunnable.run(new class(cast(ICellEditorListener) array[i]) SafeRunnable {
+                ICellEditorListener l;
+                this(ICellEditorListener a){
+                    l = a;
+                }
+                public void run() {
+                    l.cancelEditor();
+                }
+            });
+        }
+    }
+
+    /**
+     * Notifies all registered cell editor listeners of a value change.
+     *
+     * @param oldValidState
+     *            the valid state before the end user changed the value
+     * @param newValidState
+     *            the current valid state
+     * @see ICellEditorListener#editorValueChanged
+     */
+    protected void fireEditorValueChanged(bool oldValidState,
+            bool newValidState) {
+        Object[] array = listeners.getListeners();
+        for (int i = 0; i < array.length; i++) {
+            SafeRunnable.run(new class(newValidState,oldValidState,cast(ICellEditorListener) array[i]) SafeRunnable {
+                bool newValidState_;
+                bool oldValidState_;
+                ICellEditorListener l;
+                this(bool a, bool b, ICellEditorListener c){
+                    newValidState_=a;
+                    oldValidState_=b;
+                    l = c;
+                }
+                public void run() {
+                    l.editorValueChanged(oldValidState_, newValidState_);
+                }
+            });
+        }
+    }
+
+    /**
+     * Notifies all registered property listeners of an enablement change.
+     *
+     * @param actionId
+     *            the id indicating what action's enablement has changed.
+     */
+    protected void fireEnablementChanged(String actionId) {
+        Object[] array = propertyChangeListeners.getListeners();
+        for (int i = 0; i < array.length; i++) {
+            SafeRunnable.run(new class(actionId,cast(IPropertyChangeListener) array[i]) SafeRunnable {
+                String actionId_;
+                IPropertyChangeListener l;
+                this(String a, IPropertyChangeListener b){
+                    actionId_=a;
+                    l = b;
+                }
+                public void run() {
+                    l.propertyChange(new PropertyChangeEvent(this, actionId_,
+                            null, null));
+                }
+            });
+        }
+    }
+
+    /**
+     * Sets the style bits for this cell editor.
+     *
+     * @param style
+     *            the SWT style bits for this cell editor
+     * @since 2.1
+     */
+    public void setStyle(int style) {
+        this.style = style;
+    }
+
+    /**
+     * Returns the style bits for this cell editor.
+     *
+     * @return the style for this cell editor
+     * @since 2.1
+     */
+    public int getStyle() {
+        return style;
+    }
+
+    /**
+     * Returns the control used to implement this cell editor.
+     *
+     * @return the control, or <code>null</code> if this cell editor has no
+     *         control
+     */
+    public Control getControl() {
+        return control;
+    }
+
+    /**
+     * Returns the current error message for this cell editor.
+     *
+     * @return the error message if the cell editor is in an invalid state, and
+     *         <code>null</code> if the cell editor is valid
+     */
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+
+    /**
+     * Returns a layout data object for this cell editor. This is called each
+     * time the cell editor is activated and controls the layout of the SWT
+     * table editor.
+     * <p>
+     * The default implementation of this method sets the minimum width to the
+     * control's preferred width. Subclasses may extend or reimplement.
+     * </p>
+     *
+     * @return the layout data object
+     */
+    public LayoutData getLayoutData() {
+        LayoutData result = new LayoutData();
+        Control control = getControl();
+        if (control !is null) {
+            result.minimumWidth = control.computeSize(SWT.DEFAULT, SWT.DEFAULT,
+                    true).x;
+        }
+        return result;
+    }
+
+    /**
+     * Returns the input validator for this cell editor.
+     *
+     * @return the input validator, or <code>null</code> if none
+     */
+    public ICellEditorValidator getValidator() {
+        return validator;
+    }
+
+    /**
+     * Returns this cell editor's value provided that it has a valid one.
+     *
+     * @return the value of this cell editor, or <code>null</code> if the cell
+     *         editor does not contain a valid value
+     */
+    public final Object getValue() {
+        if (!valid) {
+            return null;
+        }
+
+        return doGetValue();
+    }
+
+    /**
+     * Returns whether this cell editor is activated.
+     *
+     * @return <code>true</code> if this cell editor's control is currently
+     *         activated, and <code>false</code> if not activated
+     */
+    public bool isActivated() {
+        // Use the state of the visible style bit (getVisible()) rather than the
+        // window's actual visibility (isVisible()) to get correct handling when
+        // an ancestor control goes invisible, see bug 85331.
+        return control !is null && control.getVisible();
+    }
+
+    /**
+     * Returns <code>true</code> if this cell editor is able to perform the
+     * copy action.
+     * <p>
+     * This default implementation always returns <code>false</code>.
+     * </p>
+     * <p>
+     * Subclasses may override
+     * </p>
+     *
+     * @return <code>true</code> if copy is possible, <code>false</code>
+     *         otherwise
+     */
+    public bool isCopyEnabled() {
+        return false;
+    }
+
+    /**
+     * Returns whether the given value is valid for this cell editor. This cell
+     * editor's validator (if any) makes the actual determination.
+     *
+     * @param value
+     *            the value to check for
+     *
+     * @return <code>true</code> if the value is valid, and <code>false</code>
+     *         if invalid
+     */
+    protected bool isCorrect(Object value) {
+        errorMessage = null;
+        if (validator is null) {
+            return true;
+        }
+
+        errorMessage = validator.isValid(value);
+        return (errorMessage is null || errorMessage.equals(""));//$NON-NLS-1$
+    }
+
+    /**
+     * Returns <code>true</code> if this cell editor is able to perform the
+     * cut action.
+     * <p>
+     * This default implementation always returns <code>false</code>.
+     * </p>
+     * <p>
+     * Subclasses may override
+     * </p>
+     *
+     * @return <code>true</code> if cut is possible, <code>false</code>
+     *         otherwise
+     */
+    public bool isCutEnabled() {
+        return false;
+    }
+
+    /**
+     * Returns <code>true</code> if this cell editor is able to perform the
+     * delete action.
+     * <p>
+     * This default implementation always returns <code>false</code>.
+     * </p>
+     * <p>
+     * Subclasses may override
+     * </p>
+     *
+     * @return <code>true</code> if delete is possible, <code>false</code>
+     *         otherwise
+     */
+    public bool isDeleteEnabled() {
+        return false;
+    }
+
+    /**
+     * Returns whether the value of this cell editor has changed since the last
+     * call to <code>setValue</code>.
+     *
+     * @return <code>true</code> if the value has changed, and
+     *         <code>false</code> if unchanged
+     */
+    public bool isDirty() {
+        return dirty;
+    }
+
+    /**
+     * Marks this cell editor as dirty.
+     *
+     * @since 2.1
+     */
+    protected void markDirty() {
+        dirty = true;
+    }
+
+    /**
+     * Returns <code>true</code> if this cell editor is able to perform the
+     * find action.
+     * <p>
+     * This default implementation always returns <code>false</code>.
+     * </p>
+     * <p>
+     * Subclasses may override
+     * </p>
+     *
+     * @return <code>true</code> if find is possible, <code>false</code>
+     *         otherwise
+     */
+    public bool isFindEnabled() {
+        return false;
+    }
+
+    /**
+     * Returns <code>true</code> if this cell editor is able to perform the
+     * paste action.
+     * <p>
+     * This default implementation always returns <code>false</code>.
+     * </p>
+     * <p>
+     * Subclasses may override
+     * </p>
+     *
+     * @return <code>true</code> if paste is possible, <code>false</code>
+     *         otherwise
+     */
+    public bool isPasteEnabled() {
+        return false;
+    }
+
+    /**
+     * Returns <code>true</code> if this cell editor is able to perform the
+     * redo action.
+     * <p>
+     * This default implementation always returns <code>false</code>.
+     * </p>
+     * <p>
+     * Subclasses may override
+     * </p>
+     *
+     * @return <code>true</code> if redo is possible, <code>false</code>
+     *         otherwise
+     */
+    public bool isRedoEnabled() {
+        return false;
+    }
+
+    /**
+     * Returns <code>true</code> if this cell editor is able to perform the
+     * select all action.
+     * <p>
+     * This default implementation always returns <code>false</code>.
+     * </p>
+     * <p>
+     * Subclasses may override
+     * </p>
+     *
+     * @return <code>true</code> if select all is possible, <code>false</code>
+     *         otherwise
+     */
+    public bool isSelectAllEnabled() {
+        return false;
+    }
+
+    /**
+     * Returns <code>true</code> if this cell editor is able to perform the
+     * undo action.
+     * <p>
+     * This default implementation always returns <code>false</code>.
+     * </p>
+     * <p>
+     * Subclasses may override
+     * </p>
+     *
+     * @return <code>true</code> if undo is possible, <code>false</code>
+     *         otherwise
+     */
+    public bool isUndoEnabled() {
+        return false;
+    }
+
+    /**
+     * Returns whether this cell editor has a valid value. The default value is
+     * false.
+     *
+     * @return <code>true</code> if the value is valid, and <code>false</code>
+     *         if invalid
+     *
+     * @see #setValueValid(bool)
+     */
+    public bool isValueValid() {
+        return valid;
+    }
+
+    /**
+     * Processes a key release event that occurred in this cell editor.
+     * <p>
+     * The default implementation of this framework method cancels editing when
+     * the ESC key is pressed. When the RETURN key is pressed the current value
+     * is applied and the cell editor deactivates. Subclasses should call this
+     * method at appropriate times. Subclasses may also extend or reimplement.
+     * </p>
+     *
+     * @param keyEvent
+     *            the key event
+     */
+    protected void keyReleaseOccured(KeyEvent keyEvent) {
+        if (keyEvent.character is '\u001b') { // Escape character
+            fireCancelEditor();
+        } else if (keyEvent.character is '\r') { // Return key
+            fireApplyEditorValue();
+            deactivate();
+        }
+    }
+
+    /**
+     * Processes a focus lost event that occurred in this cell editor.
+     * <p>
+     * The default implementation of this framework method applies the current
+     * value and deactivates the cell editor. Subclasses should call this method
+     * at appropriate times. Subclasses may also extend or reimplement.
+     * </p>
+     */
+    protected void focusLost() {
+        if (isActivated()) {
+            fireApplyEditorValue();
+            deactivate();
+        }
+    }
+
+    /**
+     * Performs the copy action. This default implementation does nothing.
+     * <p>
+     * Subclasses may override
+     * </p>
+     */
+    public void performCopy() {
+    }
+
+    /**
+     * Performs the cut action. This default implementation does nothing.
+     * <p>
+     * Subclasses may override
+     * </p>
+     */
+    public void performCut() {
+    }
+
+    /**
+     * Performs the delete action. This default implementation does nothing.
+     * <p>
+     * Subclasses may override
+     * </p>
+     */
+    public void performDelete() {
+    }
+
+    /**
+     * Performs the find action. This default implementation does nothing.
+     * <p>
+     * Subclasses may override
+     * </p>
+     */
+    public void performFind() {
+    }
+
+    /**
+     * Performs the paste action. This default implementation does nothing.
+     * <p>
+     * Subclasses may override
+     * </p>
+     */
+    public void performPaste() {
+    }
+
+    /**
+     * Performs the redo action. This default implementation does nothing.
+     * <p>
+     * Subclasses may override
+     * </p>
+     */
+    public void performRedo() {
+    }
+
+    /**
+     * Performs the select all action. This default implementation does nothing.
+     * <p>
+     * Subclasses may override
+     * </p>
+     */
+    public void performSelectAll() {
+    }
+
+    /**
+     * Performs the undo action. This default implementation does nothing.
+     * <p>
+     * Subclasses may override
+     * </p>
+     */
+    public void performUndo() {
+    }
+
+    /**
+     * Removes the given listener from this cell editor. Has no affect if an
+     * identical listener is not registered.
+     *
+     * @param listener
+     *            a cell editor listener
+     */
+    public void removeListener(ICellEditorListener listener) {
+        listeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Removes the given property change listener from this cell editor. Has no
+     * affect if an identical property change listener is not registered.
+     *
+     * @param listener
+     *            a property change listener
+     */
+    public void removePropertyChangeListener(IPropertyChangeListener listener) {
+        propertyChangeListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Sets or clears the current error message for this cell editor.
+     * <p>
+     * No formatting is done here, the message to be set is expected to be fully
+     * formatted before being passed in.
+     * </p>
+     *
+     * @param message
+     *            the error message, or <code>null</code> to clear
+     */
+    protected void setErrorMessage(String message) {
+        errorMessage = message;
+    }
+
+    /**
+     * Sets the focus to the cell editor's control.
+     */
+    public void setFocus() {
+        doSetFocus();
+    }
+
+    /**
+     * Sets the input validator for this cell editor.
+     *
+     * @param validator
+     *            the input validator, or <code>null</code> if none
+     */
+    public void setValidator(ICellEditorValidator validator) {
+        this.validator = validator;
+    }
+
+    /**
+     * Sets this cell editor's value.
+     *
+     * @param value
+     *            the value of this cell editor
+     */
+    public final void setValue(Object value) {
+        valid = isCorrect(value);
+        dirty = false;
+        doSetValue(value);
+    }
+
+    /**
+     * Sets the valid state of this cell editor. The default value is false.
+     * Subclasses should call this method on construction.
+     *
+     * @param valid
+     *            <code>true</code> if the current value is valid, and
+     *            <code>false</code> if invalid
+     *
+     * @see #isValueValid
+     */
+    protected void setValueValid(bool valid) {
+        this.valid = valid;
+    }
+
+    /**
+     * The value has changed. Updates the valid state flag, marks this cell
+     * editor as dirty, and notifies all registered cell editor listeners of a
+     * value change.
+     *
+     * @param oldValidState
+     *            the valid state before the end user changed the value
+     * @param newValidState
+     *            the current valid state
+     * @see ICellEditorListener#editorValueChanged
+     */
+    protected void valueChanged(bool oldValidState, bool newValidState) {
+        valid = newValidState;
+        dirty = true;
+        fireEditorValueChanged(oldValidState, newValidState);
+    }
+
+    /**
+     * Activate the editor but also inform the editor which event triggered its
+     * activation. <b>The default implementation simply calls
+     * {@link #activate()}</b>
+     *
+     * @param activationEvent
+     *            the editor activation event
+     * @since 3.3
+     */
+    public void activate(ColumnViewerEditorActivationEvent activationEvent) {
+        activate();
+    }
+
+    /**
+     * The default implementation of this method returns true. Subclasses that
+     * hook their own focus listener should override this method and return
+     * false. See also bug 58777.
+     *
+     * @return <code>true</code> to indicate that a focus listener has to be
+     *         attached
+     * @since 3.4
+     */
+    protected bool dependsOnExternalFocusListener() {
+        return true;
+    }
+    package bool dependsOnExternalFocusListener_package() {
+        return dependsOnExternalFocusListener();
+    }
+
+    /**
+     * @param event
+     *            deactivation event
+     * @since 3.4
+     *
+     */
+    protected void deactivate(ColumnViewerEditorDeactivationEvent event) {
+        deactivate();
+    }
+    package void deactivate_package(ColumnViewerEditorDeactivationEvent event) {
+        deactivate(event);
+    }
+
+    /**
+     * Returns the duration, in milliseconds, between the mouse button click
+     * that activates the cell editor and a subsequent mouse button click that
+     * will be considered a <em>double click</em> on the underlying control.
+     * Clients may override, in particular, clients can return 0 to denote that
+     * two subsequent mouse clicks in a cell should not be interpreted as a
+     * double click.
+     *
+     * @return the timeout or <code>0</code>
+     * @since 3.4
+     */
+    protected int getDoubleClickTimeout() {
+        return Display.getCurrent().getDoubleClickTime();
+    }
+    package int getDoubleClickTimeout_package() {
+        return getDoubleClickTimeout();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/CellLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,281 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *     Tom Shindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                              - bug fixes for 182443
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.CellLabelProvider;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.BaseLabelProvider;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.ITableColorProvider;
+import org.eclipse.jface.viewers.ITableFontProvider;
+import org.eclipse.jface.viewers.TableColumnViewerLabelProvider;
+import org.eclipse.jface.viewers.ViewerColumn;
+import org.eclipse.jface.viewers.WrappedViewerLabelProvider;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The CellLabelProvider is an abstract implementation of a label provider for
+ * structured viewers.
+ *
+ * <p><b>This class is intended to be subclassed</b></p>
+ *
+ * @since 3.3
+ * @see ColumnLabelProvider as a concrete implementation
+ */
+public abstract class CellLabelProvider : BaseLabelProvider {
+
+    alias BaseLabelProvider.dispose dispose;
+
+    /**
+     * Create a new instance of the receiver.
+     */
+    public this() {
+    }
+
+    /**
+     * Create a ViewerLabelProvider for the column at index
+     *
+     * @param labelProvider
+     *            The labelProvider to convert
+     * @return ViewerLabelProvider
+     */
+    /* package */static CellLabelProvider createViewerLabelProvider(
+            ColumnViewer viewer, IBaseLabelProvider labelProvider) {
+
+        bool noColumnTreeViewer = ( null !is cast(AbstractTreeViewer)viewer ) && viewer
+                .doGetColumnCount_package() is 0;
+
+        if (!noColumnTreeViewer &&
+          (  null !is cast(ITableLabelProvider) labelProvider
+          || null !is cast(ITableColorProvider) labelProvider
+          || null !is cast(ITableFontProvider)labelProvider )){
+            auto res = new TableColumnViewerLabelProvider(labelProvider);
+            return res;
+        }
+        if ( cast(CellLabelProvider)labelProvider ){
+            auto res = cast(CellLabelProvider) labelProvider;
+            return res;
+        }
+
+        auto res = new WrappedViewerLabelProvider(labelProvider);
+        return res;
+
+    }
+
+    /**
+     * Get the image displayed in the tool tip for object.
+     *
+     * <p>
+     * <b>If {@link #getToolTipText(Object)} and
+     * {@link #getToolTipImage(Object)} both return <code>null</code> the
+     * control is set back to standard behavior</b>
+     * </p>
+     *
+     * @param object
+     *            the element for which the tool tip is shown
+     * @return {@link Image} or <code>null</code> if there is not image.
+     */
+
+    public Image getToolTipImage(Object object) {
+        return null;
+    }
+
+    /**
+     * Get the text displayed in the tool tip for object.
+     *
+     * <p>
+     * <b>If {@link #getToolTipText(Object)} and
+     * {@link #getToolTipImage(Object)} both return <code>null</code> the
+     * control is set back to standard behavior</b>
+     * </p>
+     *
+     * @param element
+     *            the element for which the tool tip is shown
+     * @return the {@link String} or <code>null</code> if there is not text to
+     *         display
+     */
+    public String getToolTipText(Object element) {
+        return null;
+    }
+
+    /**
+     * Return the background color used for the tool tip
+     *
+     * @param object
+     *            the {@link Object} for which the tool tip is shown
+     *
+     * @return the {@link Color} used or <code>null</code> if you want to use
+     *         the default color {@link SWT#COLOR_INFO_BACKGROUND}
+     * @see SWT#COLOR_INFO_BACKGROUND
+     */
+    public Color getToolTipBackgroundColor(Object object) {
+        return null;
+    }
+
+    /**
+     * The foreground color used to display the the text in the tool tip
+     *
+     * @param object
+     *            the {@link Object} for which the tool tip is shown
+     * @return the {@link Color} used or <code>null</code> if you want to use
+     *         the default color {@link SWT#COLOR_INFO_FOREGROUND}
+     * @see SWT#COLOR_INFO_FOREGROUND
+     */
+    public Color getToolTipForegroundColor(Object object) {
+        return null;
+    }
+
+    /**
+     * Get the {@link Font} used to display the tool tip
+     *
+     * @param object
+     *            the element for which the tool tip is shown
+     * @return {@link Font} or <code>null</code> if the default font is to be
+     *         used.
+     */
+    public Font getToolTipFont(Object object) {
+        return null;
+    }
+
+    /**
+     * Return the amount of pixels in x and y direction you want the tool tip to
+     * pop up from the mouse pointer. The default shift is 10px right and 0px
+     * below your mouse cursor. Be aware of the fact that you should at least
+     * position the tool tip 1px right to your mouse cursor else click events
+     * may not get propagated properly.
+     *
+     * @param object
+     *            the element for which the tool tip is shown
+     * @return {@link Point} to shift of the tool tip or <code>null</code> if the
+     *         default shift should be used.
+     */
+    public Point getToolTipShift(Object object) {
+        return null;
+    }
+
+    /**
+     * Return whether or not to use the native tool tip. If you switch to native
+     * tool tips only the value from {@link #getToolTipText(Object)} is used all
+     * other features from custom tool tips are not supported.
+     *
+     * <p>
+     * To reset the control to native behavior you should return
+     * <code>true</code> from this method and <code>null</code> from
+     * {@link #getToolTipText(Object)} or <code>null</code> from
+     * {@link #getToolTipText(Object)} and {@link #getToolTipImage(Object)} at
+     * the same time
+     * </p>
+     *
+     * @param object
+     *            the {@link Object} for which the tool tip is shown
+     * @return <code>true</code> if native tool tips should be used
+     */
+    public bool useNativeToolTip(Object object) {
+        return false;
+    }
+
+    /**
+     * The time in milliseconds the tool tip is shown for.
+     *
+     * @param object
+     *            the {@link Object} for which the tool tip is shown
+     * @return time in milliseconds the tool tip is shown for
+     */
+    public int getToolTipTimeDisplayed(Object object) {
+        return 0;
+    }
+
+    /**
+     * The time in milliseconds until the tool tip is displayed.
+     *
+     * @param object
+     *            the {@link Object} for which the tool tip is shown
+     * @return time in milliseconds until the tool tip is displayed
+     */
+    public int getToolTipDisplayDelayTime(Object object) {
+        return 0;
+    }
+
+    /**
+     * The {@link SWT} style used to create the {@link CLabel} (see there for
+     * supported styles). By default {@link SWT#SHADOW_NONE} is used.
+     *
+     * @param object
+     *            the element for which the tool tip is shown
+     * @return the style used to create the label
+     * @see CLabel
+     */
+    public int getToolTipStyle(Object object) {
+        return SWT.SHADOW_NONE;
+    }
+
+    /**
+     * Update the label for cell.
+     *
+     * @param cell
+     *            {@link ViewerCell}
+     */
+    public abstract void update(ViewerCell cell);
+
+    /**
+     * Initialize this label provider for use with the given column viewer for
+     * the given column. Subclasses may extend but should call the super
+     * implementation (which at this time is empty but may be changed in the
+     * future).
+     *
+     * @param viewer
+     *            the viewer
+     * @param column
+     *            the column, or <code>null</code> if a column is not
+     *            available.
+     *
+     * @since 3.4
+     */
+    protected void initialize(ColumnViewer viewer, ViewerColumn column) {
+    }
+    package void initialize_package(ColumnViewer viewer, ViewerColumn column) {
+        initialize(viewer,column);
+    }
+
+    /**
+     * Dispose of this label provider which was used with the given column
+     * viewer and column. Subclasses may extend but should call the super
+     * implementation (which calls {@link #dispose()}).
+     *
+     * @param viewer
+     *            the viewer
+     * @param column
+     *            the column, or <code>null</code> if a column is not
+     *            available.
+     *
+     * @since 3.4
+     */
+    public void dispose(ColumnViewer viewer, ViewerColumn column) {
+        dispose();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/CellNavigationStrategy.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.CellNavigationStrategy;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ViewerCell;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Event;
+
+/**
+ * This class implementation the strategy how the table is navigated using the
+ * keyboard.
+ *
+ * <p>
+ * <b>Subclasses can implement their custom navigation algorithms</b>
+ * </p>
+ *
+ * @since 3.3
+ *
+ */
+public class CellNavigationStrategy {
+    /**
+     * is the given event an event which moves the selection to another cell
+     *
+     * @param viewer
+     *            the viewer we are working for
+     * @param event
+     *            the key event
+     * @return <code>true</code> if a new cell is searched
+     */
+    public bool isNavigationEvent(ColumnViewer viewer, Event event) {
+        switch (event.keyCode) {
+        case SWT.ARROW_UP:
+        case SWT.ARROW_DOWN:
+        case SWT.ARROW_LEFT:
+        case SWT.ARROW_RIGHT:
+        case SWT.HOME:
+        case SWT.PAGE_DOWN:
+        case SWT.PAGE_UP:
+        case SWT.END:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    /**
+     * @param viewer
+     *            the viewer we are working for
+     * @param cellToCollapse
+     *            the cell to collapse
+     * @param event
+     *            the key event
+     * @return <code>true</code> if this event triggers collapsing of a node
+     */
+    public bool isCollapseEvent(ColumnViewer viewer,
+            ViewerCell cellToCollapse, Event event) {
+        return false;
+    }
+
+    /**
+     * @param viewer
+     *            the viewer we are working for
+     * @param cellToExpand
+     *            the cell to expand
+     * @param event
+     *            the key event
+     * @return <code>true</code> if this event triggers expanding of a node
+     */
+    public bool isExpandEvent(ColumnViewer viewer, ViewerCell cellToExpand,
+            Event event) {
+        return false;
+    }
+
+    /**
+     * @param viewer
+     *            the viewer working for
+     * @param cellToExpand
+     *            the cell the user wants to expand
+     * @param event
+     *            the event triggering the expansion
+     */
+    public void expand(ColumnViewer viewer, ViewerCell cellToExpand, Event event) {
+
+    }
+
+    /**
+     * @param viewer
+     *            the viewer working for
+     * @param cellToCollapse
+     *            the cell the user wants to collapse
+     * @param event
+     *            the event triggering the expansion
+     */
+    public void collapse(ColumnViewer viewer, ViewerCell cellToCollapse,
+            Event event) {
+
+    }
+
+    /**
+     * @param viewer
+     *            the viewer we are working for
+     * @param currentSelectedCell
+     *            the cell currently selected
+     * @param event
+     *            the key event
+     * @return the cell which is highlighted next or <code>null</code> if the
+     *         default implementation is taken. E.g. it's fairly impossible to
+     *         react on PAGE_DOWN requests
+     */
+    public ViewerCell findSelectedCell(ColumnViewer viewer,
+            ViewerCell currentSelectedCell, Event event) {
+
+        switch (event.keyCode) {
+        case SWT.ARROW_UP:
+            if (currentSelectedCell !is null) {
+                return currentSelectedCell.getNeighbor(ViewerCell.ABOVE, false);
+            }
+            break;
+        case SWT.ARROW_DOWN:
+            if (currentSelectedCell !is null) {
+                return currentSelectedCell.getNeighbor(ViewerCell.BELOW, false);
+            }
+            break;
+        case SWT.ARROW_LEFT:
+            if (currentSelectedCell !is null) {
+                return currentSelectedCell.getNeighbor(ViewerCell.LEFT, true);
+            }
+            break;
+        case SWT.ARROW_RIGHT:
+            if (currentSelectedCell !is null) {
+                return currentSelectedCell.getNeighbor(ViewerCell.RIGHT, true);
+            }
+            break;
+        default:
+        }
+
+        return null;
+    }
+
+    /**
+     * This method is consulted to decide whether an event has to be canceled or
+     * not. By default events who collapse/expand tree-nodes are canceled
+     *
+     * @param viewer
+     *            the viewer working for
+     * @param event
+     *            the event
+     * @return <code>true</code> if the event has to be canceled
+     */
+    public bool shouldCancelEvent(ColumnViewer viewer, Event event) {
+        return event.keyCode is SWT.ARROW_LEFT
+                || event.keyCode is SWT.ARROW_RIGHT;
+    }
+
+    /**
+     * This method is called by the framework to initialize this navigation
+     * strategy object. Subclasses may extend.
+     */
+    protected void init() {
+    }
+    package void init_package() {
+        init();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/CheckStateChangedEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.CheckStateChangedEvent;
+
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ICheckable;
+
+import java.lang.all;
+import java.util.EventObject;
+
+/**
+ * Event object describing a change to the checked state
+ * of a viewer element.
+ *
+ * @see ICheckStateListener
+ */
+public class CheckStateChangedEvent : EventObject {
+
+    /**
+     * Generated serial version UID for this class.
+     * @since 3.1
+     */
+    private static const long serialVersionUID = 3256443603340244789L;
+
+    /**
+     * The viewer element.
+     */
+    private Object element;
+
+    /**
+     * The checked state.
+     */
+    private bool state;
+
+    /**
+     * Creates a new event for the given source, element, and checked state.
+     *
+     * @param source the source
+     * @param element the element
+     * @param state the checked state
+     */
+    public this(ICheckable source, Object element,
+            bool state) {
+        super(cast(Object)source);
+        this.element = element;
+        this.state = state;
+    }
+
+    /**
+     * Returns the checkable that is the source of this event.
+     *
+     * @return the originating checkable
+     */
+    public ICheckable getCheckable() {
+        return cast(ICheckable) source;
+    }
+
+    /**
+     * Returns the checked state of the element.
+     *
+     * @return the checked state
+     */
+    public bool getChecked() {
+        return state;
+    }
+
+    /**
+     * Returns the element whose check state changed.
+     *
+     * @return the element
+     */
+    public Object getElement() {
+        return element;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/CheckboxCellEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.CheckboxCellEditor;
+
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A cell editor that manages a checkbox.
+ * The cell editor's value is a bool.
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.
+ * </p>
+ * <p>
+ * Note that this implementation simply fakes it and does does not create
+ * any new controls. The mere activation of this editor means that the value
+ * of the check box is being toggled by the end users; the listener method
+ * <code>applyEditorValue</code> is immediately called to signal the change.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class CheckboxCellEditor : CellEditor {
+
+    /**
+     * The checkbox value.
+     */
+    /* package */
+    bool value = false;
+
+    /**
+     * Default CheckboxCellEditor style
+     */
+    private static const int defaultStyle = SWT.NONE;
+
+    /**
+     * Creates a new checkbox cell editor with no control
+     * @since 2.1
+     */
+    public this() {
+        setStyle(defaultStyle);
+    }
+
+    /**
+     * Creates a new checkbox cell editor parented under the given control.
+     * The cell editor value is a bool value, which is initially <code>false</code>.
+     * Initially, the cell editor has no cell validator.
+     *
+     * @param parent the parent control
+     */
+    public this(Composite parent) {
+        this(parent, defaultStyle);
+    }
+
+    /**
+     * Creates a new checkbox cell editor parented under the given control.
+     * The cell editor value is a bool value, which is initially <code>false</code>.
+     * Initially, the cell editor has no cell validator.
+     *
+     * @param parent the parent control
+     * @param style the style bits
+     * @since 2.1
+     */
+    public this(Composite parent, int style) {
+        super(parent, style);
+    }
+
+    /**
+     * The <code>CheckboxCellEditor</code> implementation of
+     * this <code>CellEditor</code> framework method simulates
+     * the toggling of the checkbox control and notifies
+     * listeners with <code>ICellEditorListener.applyEditorValue</code>.
+     */
+    public override void activate() {
+        value = !value;
+        fireApplyEditorValue();
+    }
+
+    /**
+     * The <code>CheckboxCellEditor</code> implementation of
+     * this <code>CellEditor</code> framework method does
+     * nothing and returns <code>null</code>.
+     */
+    protected override Control createControl(Composite parent) {
+        return null;
+    }
+
+    /**
+     * The <code>CheckboxCellEditor</code> implementation of
+     * this <code>CellEditor</code> framework method returns
+     * the checkbox setting wrapped as a <code>Boolean</code>.
+     *
+     * @return the Boolean checkbox value
+     */
+    protected override Object doGetValue() {
+        return value ? Boolean.TRUE : Boolean.FALSE;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     */
+    protected override void doSetFocus() {
+        // Ignore
+    }
+
+    /**
+     * The <code>CheckboxCellEditor</code> implementation of
+     * this <code>CellEditor</code> framework method accepts
+     * a value wrapped as a <code>Boolean</code>.
+     *
+     * @param value a Boolean value
+     */
+    protected override void doSetValue(Object value) {
+        Assert.isTrue( null !is cast(Boolean)value );
+        this.value = (cast(Boolean) value).booleanValue();
+    }
+
+    public override void activate(ColumnViewerEditorActivationEvent activationEvent) {
+        if (activationEvent.eventType !is ColumnViewerEditorActivationEvent.TRAVERSAL) {
+            super.activate(activationEvent);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/CheckboxTableViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,454 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.CheckboxTableViewer;
+
+import org.eclipse.jface.viewers.ICheckable;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.TableLayout;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.CustomHashtable;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.util.SafeRunnable;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * A concrete viewer based on an SWT <code>Table</code>
+ * control with checkboxes on each node.
+ * <p>
+ * This class is not intended to be subclassed outside the viewer framework.
+ * It is designed to be instantiated with a pre-existing SWT table control and configured
+ * with a domain-specific content provider, label provider, element filter (optional),
+ * and element sorter (optional).
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class CheckboxTableViewer : TableViewer, ICheckable {
+    alias TableViewer.preservingSelection preservingSelection;
+
+    /**
+     * List of check state listeners (element type: <code>ICheckStateListener</code>).
+     */
+    private ListenerList checkStateListeners;
+
+    /**
+     * Creates a table viewer on a newly-created table control under the given parent.
+     * The table control is created using the SWT style bits:
+     * <code>SWT.CHECK</code> and <code>SWT.BORDER</code>.
+     * The table has one column.
+     * The viewer has no input, no content provider, a default label provider,
+     * no sorter, and no filters.
+     * <p>
+     * This is equivalent to calling <code>new CheckboxTableViewer(parent, SWT.BORDER)</code>.
+     * See that constructor for more details.
+     * </p>
+     *
+     * @param parent the parent control
+     *
+     * @deprecated use newCheckList(Composite, int) or new CheckboxTableViewer(Table)
+     *   instead (see below for details)
+     */
+    public this(Composite parent) {
+        this(parent, SWT.BORDER);
+    }
+
+    /**
+     * Creates a table viewer on a newly-created table control under the given parent.
+     * The table control is created using the given SWT style bits, plus the
+     * <code>SWT.CHECK</code> style bit.
+     * The table has one column.
+     * The viewer has no input, no content provider, a default label provider,
+     * no sorter, and no filters.
+     * <p>
+     * This also adds a <code>TableColumn</code> for the single column,
+     * and sets a <code>TableLayout</code> on the table which sizes the column to fill
+     * the table for its initial sizing, but does nothing on subsequent resizes.
+     * </p>
+     * <p>
+     * If the caller just needs to show a single column with no header,
+     * it is preferable to use the <code>newCheckList</code> factory method instead,
+     * since SWT properly handles the initial sizing and subsequent resizes in this case.
+     * </p>
+     * <p>
+     * If the caller adds its own columns, uses <code>Table.setHeadersVisible(true)</code>,
+     * or needs to handle dynamic resizing of the table, it is recommended to
+     * create the <code>Table</code> itself, specifying the <code>SWT.CHECK</code> style bit
+     * (along with any other style bits needed), and use <code>new CheckboxTableViewer(Table)</code>
+     * rather than this constructor.
+     * </p>
+     *
+     * @param parent the parent control
+     * @param style SWT style bits
+     *
+     * @deprecated use newCheckList(Composite, int) or new CheckboxTableViewer(Table)
+     *   instead (see above for details)
+     */
+    public this(Composite parent, int style) {
+        this(createTable(parent, style));
+    }
+
+    /**
+     * Creates a table viewer on a newly-created table control under the given parent.
+     * The table control is created using the given SWT style bits, plus the
+     * <code>SWT.CHECK</code> style bit.
+     * The table shows its contents in a single column, with no header.
+     * The viewer has no input, no content provider, a default label provider,
+     * no sorter, and no filters.
+     * <p>
+     * No <code>TableColumn</code> is added. SWT does not require a
+     * <code>TableColumn</code> if showing only a single column with no header.
+     * SWT correctly handles the initial sizing and subsequent resizes in this case.
+     *
+     * @param parent the parent control
+     * @param style SWT style bits
+     *
+     * @since 2.0
+     * @return CheckboxTableViewer
+     */
+    public static CheckboxTableViewer newCheckList(Composite parent, int style) {
+        Table table = new Table(parent, SWT.CHECK | style);
+        return new CheckboxTableViewer(table);
+    }
+
+    /**
+     * Creates a table viewer on the given table control.
+     * The <code>SWT.CHECK</code> style bit must be set on the given table control.
+     * The viewer has no input, no content provider, a default label provider,
+     * no sorter, and no filters.
+     *
+     * @param table the table control
+     */
+    public this(Table table) {
+        super(table);
+        checkStateListeners = new ListenerList();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ICheckable.
+     */
+    public void addCheckStateListener(ICheckStateListener listener) {
+        checkStateListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Creates a new table control with one column.
+     *
+     * @param parent the parent control
+     * @param style style bits
+     * @return a new table control
+     */
+    protected static Table createTable(Composite parent, int style) {
+        Table table = new Table(parent, SWT.CHECK | style);
+
+        // Although this table column is not needed, and can cause resize problems,
+        // it can't be removed since this would be a breaking change against R1.0.
+        // See bug 6643 for more details.
+        new TableColumn(table, SWT.NONE);
+        TableLayout layout = new TableLayout();
+        layout.addColumnData(new ColumnWeightData(100));
+        table.setLayout(layout);
+
+        return table;
+    }
+
+    /**
+     * Notifies any check state listeners that a check state changed  has been received.
+     * Only listeners registered at the time this method is called are notified.
+     *
+     * @param event a check state changed event
+     *
+     * @see ICheckStateListener#checkStateChanged
+     */
+    private void fireCheckStateChanged(CheckStateChangedEvent event) {
+        Object[] array = checkStateListeners.getListeners();
+        for (int i = 0; i < array.length; i++) {
+            SafeRunnable.run( dgSafeRunnable( (ICheckStateListener l, CheckStateChangedEvent event_) {
+                l.checkStateChanged(event_);
+            }, cast(ICheckStateListener) array[i], event ));
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ICheckable.
+     */
+    public bool getChecked(Object element) {
+        Widget widget = findItem(element);
+        if ( auto ti = cast(TableItem) widget ) {
+            return ti.getChecked();
+        }
+        return false;
+    }
+
+    /**
+     * Returns a list of elements corresponding to checked table items in this
+     * viewer.
+     * <p>
+     * This method is typically used when preserving the interesting
+     * state of a viewer; <code>setCheckedElements</code> is used during the restore.
+     * </p>
+     *
+     * @return the array of checked elements
+     * @see #setCheckedElements
+     */
+    public Object[] getCheckedElements() {
+        TableItem[] children = getTable().getItems();
+        ArrayList v = new ArrayList(children.length);
+        for (int i = 0; i < children.length; i++) {
+            TableItem item = children[i];
+            if (item.getChecked()) {
+                v.add(item.getData());
+            }
+        }
+        return v.toArray();
+    }
+
+    /**
+     * Returns the grayed state of the given element.
+     *
+     * @param element the element
+     * @return <code>true</code> if the element is grayed,
+     *   and <code>false</code> if not grayed
+     */
+    public bool getGrayed(Object element) {
+        Widget widget = findItem(element);
+        if ( auto ti = cast(TableItem) widget ) {
+            return ti.getGrayed();
+        }
+        return false;
+    }
+
+    /**
+     * Returns a list of elements corresponding to grayed nodes in this
+     * viewer.
+     * <p>
+     * This method is typically used when preserving the interesting
+     * state of a viewer; <code>setGrayedElements</code> is used during the restore.
+     * </p>
+     *
+     * @return the array of grayed elements
+     * @see #setGrayedElements
+     */
+    public Object[] getGrayedElements() {
+        TableItem[] children = getTable().getItems();
+        ArrayList v = new ArrayList(children.length);
+        for (int i = 0; i < children.length; i++) {
+            TableItem item = children[i];
+            if (item.getGrayed()) {
+                v.add(item.getData());
+            }
+        }
+        return v.toArray();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     */
+    public override void handleSelect(SelectionEvent event) {
+        if (event.detail is SWT.CHECK) {
+            super.handleSelect(event); // this will change the current selection
+
+            TableItem item = cast(TableItem) event.item;
+            Object data = item.getData();
+            if (data !is null) {
+                fireCheckStateChanged(new CheckStateChangedEvent(this, data,
+                        item.getChecked()));
+            }
+        } else {
+            super.handleSelect(event);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on Viewer.
+     */
+    protected override void preservingSelection(Runnable updateCode) {
+
+        TableItem[] children = getTable().getItems();
+        CustomHashtable checked = newHashtable(children.length * 2 + 1);
+        CustomHashtable grayed = newHashtable(children.length * 2 + 1);
+
+        for (int i = 0; i < children.length; i++) {
+            TableItem item = children[i];
+            Object data = item.getData();
+            if (data !is null) {
+                if (item.getChecked()) {
+                    checked.put(data, data);
+                }
+                if (item.getGrayed()) {
+                    grayed.put(data, data);
+                }
+            }
+        }
+
+        super.preservingSelection(updateCode);
+
+        children = getTable().getItems();
+        for (int i = 0; i < children.length; i++) {
+            TableItem item = children[i];
+            Object data = item.getData();
+            if (data !is null) {
+                item.setChecked(checked.containsKey(data));
+                item.setGrayed(grayed.containsKey(data));
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ICheckable.
+     */
+    public void removeCheckStateListener(ICheckStateListener listener) {
+        checkStateListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Sets to the given value the checked state for all elements in this viewer.
+     * Does not fire events to check state listeners.
+     *
+     * @param state <code>true</code> if the element should be checked,
+     *  and <code>false</code> if it should be unchecked
+     */
+    public void setAllChecked(bool state) {
+        TableItem[] children = getTable().getItems();
+        for (int i = 0; i < children.length; i++) {
+            TableItem item = children[i];
+            item.setChecked(state);
+        }
+    }
+
+    /**
+     * Sets to the given value the grayed state for all elements in this viewer.
+     *
+     * @param state <code>true</code> if the element should be grayed,
+     *  and <code>false</code> if it should be ungrayed
+     */
+    public void setAllGrayed(bool state) {
+        TableItem[] children = getTable().getItems();
+        for (int i = 0; i < children.length; i++) {
+            TableItem item = children[i];
+            item.setGrayed(state);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ICheckable.
+     */
+    public bool setChecked(Object element, bool state) {
+        Assert.isNotNull(element);
+        Widget widget = findItem(element);
+        if ( auto ti = cast(TableItem) widget ) {
+            ti.setChecked(state);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Sets which nodes are checked in this viewer.
+     * The given list contains the elements that are to be checked;
+     * all other nodes are to be unchecked.
+     * Does not fire events to check state listeners.
+     * <p>
+     * This method is typically used when restoring the interesting
+     * state of a viewer captured by an earlier call to <code>getCheckedElements</code>.
+     * </p>
+     *
+     * @param elements the list of checked elements (element type: <code>Object</code>)
+     * @see #getCheckedElements
+     */
+    public void setCheckedElements(Object[] elements) {
+        assertElementsNotNull(elements);
+        CustomHashtable set = newHashtable(elements.length * 2 + 1);
+        for (int i = 0; i < elements.length; ++i) {
+            set.put(elements[i], elements[i]);
+        }
+        TableItem[] items = getTable().getItems();
+        for (int i = 0; i < items.length; ++i) {
+            TableItem item = items[i];
+            Object element = item.getData();
+            if (element !is null) {
+                bool check = set.containsKey(element);
+                // only set if different, to avoid flicker
+                if (item.getChecked() !is check) {
+                    item.setChecked(check);
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets the grayed state for the given element in this viewer.
+     *
+     * @param element the element
+     * @param state <code>true</code> if the item should be grayed,
+     *  and <code>false</code> if it should be ungrayed
+     * @return <code>true</code> if the element is visible and the gray
+     *  state could be set, and <code>false</code> otherwise
+     */
+    public bool setGrayed(Object element, bool state) {
+        Assert.isNotNull(element);
+        Widget widget = findItem(element);
+        if ( auto ti = cast(TableItem) widget ) {
+            ti.setGrayed(state);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Sets which nodes are grayed in this viewer.
+     * The given list contains the elements that are to be grayed;
+     * all other nodes are to be ungrayed.
+     * <p>
+     * This method is typically used when restoring the interesting
+     * state of a viewer captured by an earlier call to <code>getGrayedElements</code>.
+     * </p>
+     *
+     * @param elements the array of grayed elements
+     *
+     * @see #getGrayedElements
+     */
+    public void setGrayedElements(Object[] elements) {
+        assertElementsNotNull(elements);
+        CustomHashtable set = newHashtable(elements.length * 2 + 1);
+        for (int i = 0; i < elements.length; ++i) {
+            set.put(elements[i], elements[i]);
+        }
+        TableItem[] items = getTable().getItems();
+        for (int i = 0; i < items.length; ++i) {
+            TableItem item = items[i];
+            Object element = item.getData();
+            if (element !is null) {
+                bool gray = set.containsKey(element);
+                // only set if different, to avoid flicker
+                if (item.getGrayed() !is gray) {
+                    item.setGrayed(gray);
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/CheckboxTreeViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,606 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.CheckboxTreeViewer;
+
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.ICheckable;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.CustomHashtable;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.util.SafeRunnable;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * A concrete tree-structured viewer based on an SWT <code>Tree</code>
+ * control with checkboxes on each node.
+ * <p>
+ * This class is not intended to be subclassed outside the viewer framework.
+ * It is designed to be instantiated with a pre-existing SWT tree control and configured
+ * with a domain-specific content provider, label provider, element filter (optional),
+ * and element sorter (optional).
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class CheckboxTreeViewer : TreeViewer, ICheckable {
+    alias TreeViewer.preservingSelection preservingSelection;
+
+    /**
+     * List of check state listeners (element type: <code>ICheckStateListener</code>).
+     */
+    private ListenerList checkStateListeners;
+
+    /**
+     * Last item clicked on, or <code>null</code> if none.
+     */
+    private TreeItem lastClickedItem = null;
+
+    /**
+     * Creates a tree viewer on a newly-created tree control under the given parent.
+     * The tree control is created using the SWT style bits: <code>CHECK</code> and <code>BORDER</code>.
+     * The viewer has no input, no content provider, a default label provider,
+     * no sorter, and no filters.
+     *
+     * @param parent the parent control
+     */
+    public this(Composite parent) {
+        this(parent, SWT.BORDER);
+    }
+
+    /**
+     * Creates a tree viewer on a newly-created tree control under the given parent.
+     * The tree control is created using the given SWT style bits, plus the <code>CHECK</code> style bit.
+     * The viewer has no input, no content provider, a default label provider,
+     * no sorter, and no filters.
+     *
+     * @param parent the parent control
+     * @param style the SWT style bits
+     */
+    public this(Composite parent, int style) {
+        this(new Tree(parent, SWT.CHECK | style));
+    }
+
+    /**
+     * Creates a tree viewer on the given tree control.
+     * The <code>SWT.CHECK</code> style bit must be set on the given tree control.
+     * The viewer has no input, no content provider, a default label provider,
+     * no sorter, and no filters.
+     *
+     * @param tree the tree control
+     */
+    public this(Tree tree) {
+        checkStateListeners = new ListenerList();
+        super(tree);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ICheckable.
+     */
+    public void addCheckStateListener(ICheckStateListener listener) {
+        checkStateListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Applies the checked and grayed states of the given widget and its
+     * descendents.
+     *
+     * @param checked a set of elements (element type: <code>Object</code>)
+     * @param grayed a set of elements (element type: <code>Object</code>)
+     * @param widget the widget
+     */
+    private void applyState(CustomHashtable checked, CustomHashtable grayed,
+            Widget widget) {
+        Item[] items = getChildren(widget);
+        for (int i = 0; i < items.length; i++) {
+            Item item = items[i];
+            if ( auto ti = cast(TreeItem) item ) {
+                Object data = item.getData();
+                if (data !is null) {
+                    ti.setChecked(checked.containsKey(data));
+                    ti.setGrayed(grayed.containsKey(data));
+                }
+            }
+            applyState(checked, grayed, item);
+        }
+    }
+
+    /**
+     * Notifies any check state listeners that the check state of an element has changed.
+     * Only listeners registered at the time this method is called are notified.
+     *
+     * @param event a check state changed event
+     *
+     * @see ICheckStateListener#checkStateChanged
+     */
+    protected void fireCheckStateChanged(CheckStateChangedEvent event) {
+        Object[] array = checkStateListeners.getListeners();
+        for (int i = 0; i < array.length; i++) {
+            SafeRunnable.run( dgSafeRunnable( (ICheckStateListener l){
+                l.checkStateChanged(event);
+            }, cast(ICheckStateListener) array[i]));
+        }
+
+    }
+
+    /**
+     * Gathers the checked and grayed states of the given widget and its
+     * descendents.
+     *
+     * @param checked a writable set of elements (element type: <code>Object</code>)
+     * @param grayed a writable set of elements (element type: <code>Object</code>)
+     * @param widget the widget
+     */
+    private void gatherState(CustomHashtable checked, CustomHashtable grayed,
+            Widget widget) {
+        Item[] items = getChildren(widget);
+        for (int i = 0; i < items.length; i++) {
+            Item item = items[i];
+            if ( auto ti = cast(TreeItem) item ) {
+                Object data = item.getData();
+                if (data !is null) {
+                    if (ti.getChecked()) {
+                        checked.put(data, data);
+                    }
+                    if (ti.getGrayed()) {
+                        grayed.put(data, data);
+                    }
+                }
+            }
+            gatherState(checked, grayed, item);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ICheckable.
+     */
+    public bool getChecked(Object element) {
+        Widget widget = findItem(element);
+        if ( auto ti = cast(TreeItem) widget ) {
+            return ti.getChecked();
+        }
+        return false;
+    }
+
+    /**
+     * Returns a list of checked elements in this viewer's tree,
+     * including currently hidden ones that are marked as
+     * checked but are under a collapsed ancestor.
+     * <p>
+     * This method is typically used when preserving the interesting
+     * state of a viewer; <code>setCheckedElements</code> is used during the restore.
+     * </p>
+     *
+     * @return the array of checked elements
+     *
+     * @see #setCheckedElements
+     */
+    public Object[] getCheckedElements() {
+        ArrayList v = new ArrayList();
+        Control tree = getControl();
+        internalCollectChecked(v, tree);
+        return v.toArray();
+    }
+
+    /**
+     * Returns the grayed state of the given element.
+     *
+     * @param element the element
+     * @return <code>true</code> if the element is grayed,
+     *   and <code>false</code> if not grayed
+     */
+    public bool getGrayed(Object element) {
+        Widget widget = findItem(element);
+        if ( auto ti = cast(TreeItem) widget ) {
+            return ti.getGrayed();
+        }
+        return false;
+    }
+
+    /**
+     * Returns a list of grayed elements in this viewer's tree,
+     * including currently hidden ones that are marked as
+     * grayed but are under a collapsed ancestor.
+     * <p>
+     * This method is typically used when preserving the interesting
+     * state of a viewer; <code>setGrayedElements</code> is used during the restore.
+     * </p>
+     *
+     * @return the array of grayed elements
+     *
+     * @see #setGrayedElements
+     */
+    public Object[] getGrayedElements() {
+        List result = new ArrayList();
+        internalCollectGrayed(result, getControl());
+        return result.toArray();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     */
+    protected override void handleDoubleSelect(SelectionEvent event) {
+
+        if (lastClickedItem !is null) {
+            TreeItem item = lastClickedItem;
+            Object data = item.getData();
+            if (data !is null) {
+                bool state = item.getChecked();
+                setChecked(data, !state);
+                fireCheckStateChanged(new CheckStateChangedEvent(this, data,
+                        !state));
+            }
+            lastClickedItem = null;
+        } else {
+            super.handleDoubleSelect(event);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     */
+    protected override void handleSelect(SelectionEvent event) {
+
+        lastClickedItem = null;
+        if (event.detail is SWT.CHECK) {
+            TreeItem item = cast(TreeItem) event.item;
+            lastClickedItem = item;
+            super.handleSelect(event);
+
+            Object data = item.getData();
+            if (data !is null) {
+                fireCheckStateChanged(new CheckStateChangedEvent(this, data,
+                        item.getChecked()));
+            }
+        } else {
+            super.handleSelect(event);
+        }
+    }
+
+    /**
+     * Gathers the checked states of the given widget and its
+     * descendents, following a pre-order traversal of the tree.
+     *
+     * @param result a writable list of elements (element type: <code>Object</code>)
+     * @param widget the widget
+     */
+    private void internalCollectChecked(List result, Widget widget) {
+        Item[] items = getChildren(widget);
+        for (int i = 0; i < items.length; i++) {
+            Item item = items[i];
+            if ( null !is cast(TreeItem)item && (cast(TreeItem) item).getChecked()) {
+                Object data = item.getData();
+                if (data !is null) {
+                    result.add(data);
+                }
+            }
+            internalCollectChecked(result, item);
+        }
+    }
+
+    /**
+     * Gathers the grayed states of the given widget and its
+     * descendents, following a pre-order traversal of the tree.
+     *
+     * @param result a writable list of elements (element type: <code>Object</code>)
+     * @param widget the widget
+     */
+    private void internalCollectGrayed(List result, Widget widget) {
+        Item[] items = getChildren(widget);
+        for (int i = 0; i < items.length; i++) {
+            Item item = items[i];
+            if (null !is cast(TreeItem)item && (cast(TreeItem) item).getGrayed()) {
+                Object data = item.getData();
+                if (data !is null) {
+                    result.add(data);
+                }
+            }
+            internalCollectGrayed(result, item);
+        }
+    }
+
+    /**
+     * Sets the checked state of all items to correspond to the given set of checked elements.
+     *
+     * @param checkedElements the set (element type: <code>Object</code>) of elements which are checked
+     * @param widget the widget
+     */
+    private void internalSetChecked(CustomHashtable checkedElements,
+            Widget widget) {
+        Item[] items = getChildren(widget);
+        for (int i = 0; i < items.length; i++) {
+            TreeItem item = cast(TreeItem) items[i];
+            Object data = item.getData();
+            if (data !is null) {
+                bool checked = checkedElements.containsKey(data);
+                if (checked !is item.getChecked()) {
+                    item.setChecked(checked);
+                }
+            }
+            internalSetChecked(checkedElements, item);
+        }
+    }
+
+    /**
+     * Sets the grayed state of all items to correspond to the given set of grayed elements.
+     *
+     * @param grayedElements the set (element type: <code>Object</code>) of elements which are grayed
+     * @param widget the widget
+     */
+    private void internalSetGrayed(CustomHashtable grayedElements, Widget widget) {
+        Item[] items = getChildren(widget);
+        for (int i = 0; i < items.length; i++) {
+            TreeItem item = cast(TreeItem) items[i];
+            Object data = item.getData();
+            if (data !is null) {
+                bool grayed = grayedElements.containsKey(data);
+                if (grayed !is item.getGrayed()) {
+                    item.setGrayed(grayed);
+                }
+            }
+            internalSetGrayed(grayedElements, item);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on Viewer.
+     */
+    protected override void preservingSelection(Runnable updateCode) {
+
+        int n = getItemCount(getControl());
+        CustomHashtable checkedNodes = newHashtable(n * 2 + 1);
+        CustomHashtable grayedNodes = newHashtable(n * 2 + 1);
+
+        gatherState(checkedNodes, grayedNodes, getControl());
+
+        super.preservingSelection(updateCode);
+
+        applyState(checkedNodes, grayedNodes, getControl());
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ICheckable.
+     */
+    public void removeCheckStateListener(ICheckStateListener listener) {
+        checkStateListeners.remove(cast(Object)listener);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ICheckable.
+     */
+    public bool setChecked(Object element, bool state) {
+        Assert.isNotNull(element);
+        Widget widget = internalExpand(element, false);
+        if ( auto ti = cast(TreeItem) widget ) {
+            ti.setChecked(state);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Sets the checked state for the children of the given item.
+     *
+     * @param item the item
+     * @param state <code>true</code> if the item should be checked,
+     *  and <code>false</code> if it should be unchecked
+     */
+    private void setCheckedChildren(Item item, bool state) {
+        createChildren(item);
+        Item[] items = getChildren(item);
+        if (items !is null) {
+            for (int i = 0; i < items.length; i++) {
+                Item it = items[i];
+                if (it.getData() !is null && (null !is cast(TreeItem)it )) {
+                    TreeItem treeItem = cast(TreeItem) it;
+                    treeItem.setChecked(state);
+                    setCheckedChildren(treeItem, state);
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets which elements are checked in this viewer's tree.
+     * The given list contains the elements that are to be checked;
+     * all other elements are to be unchecked.
+     * Does not fire events to check state listeners.
+     * <p>
+     * This method is typically used when restoring the interesting
+     * state of a viewer captured by an earlier call to <code>getCheckedElements</code>.
+     * </p>
+     *
+     * @param elements the array of checked elements
+     * @see #getCheckedElements
+     */
+    public void setCheckedElements(Object[] elements) {
+        assertElementsNotNull(elements);
+        CustomHashtable checkedElements = newHashtable(elements.length * 2 + 1);
+        for (int i = 0; i < elements.length; ++i) {
+            Object element = elements[i];
+            // Ensure item exists for element
+            internalExpand(element, false);
+            checkedElements.put(element, element);
+        }
+        Control tree = getControl();
+        tree.setRedraw(false);
+        internalSetChecked(checkedElements, tree);
+        tree.setRedraw(true);
+    }
+
+    /**
+     * Sets the grayed state for the given element in this viewer.
+     *
+     * @param element the element
+     * @param state <code>true</code> if the item should be grayed,
+     *  and <code>false</code> if it should be ungrayed
+     * @return <code>true</code> if the gray state could be set,
+     *  and <code>false</code> otherwise
+     */
+    public bool setGrayed(Object element, bool state) {
+        Assert.isNotNull(element);
+        Widget widget = internalExpand(element, false);
+        if ( auto ti = cast(TreeItem) widget ) {
+            ti.setGrayed(state);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Check and gray the selection rather than calling both
+     * setGrayed and setChecked as an optimization.
+     * Does not fire events to check state listeners.
+     * @param element the item being checked
+     * @param state a bool indicating selection or deselection
+     * @return bool indicating success or failure.
+     */
+    public bool setGrayChecked(Object element, bool state) {
+        Assert.isNotNull(element);
+        Widget widget = internalExpand(element, false);
+        if (auto item = cast(TreeItem)widget ) {
+            item.setChecked(state);
+            item.setGrayed(state);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Sets which elements are grayed in this viewer's tree.
+     * The given list contains the elements that are to be grayed;
+     * all other elements are to be ungrayed.
+     * <p>
+     * This method is typically used when restoring the interesting
+     * state of a viewer captured by an earlier call to <code>getGrayedElements</code>.
+     * </p>
+     *
+     * @param elements the array of grayed elements
+     *
+     * @see #getGrayedElements
+     */
+    public void setGrayedElements(Object[] elements) {
+        assertElementsNotNull(elements);
+        CustomHashtable grayedElements = newHashtable(elements.length * 2 + 1);
+        for (int i = 0; i < elements.length; ++i) {
+            Object element = elements[i];
+            // Ensure item exists for element
+            internalExpand(element, false);
+            grayedElements.put(element, element);
+        }
+        Control tree = getControl();
+        tree.setRedraw(false);
+        internalSetGrayed(grayedElements, tree);
+        tree.setRedraw(true);
+    }
+
+    /**
+     * Sets the grayed state for the given element and its parents
+     * in this viewer.
+     *
+     * @param element the element
+     * @param state <code>true</code> if the item should be grayed,
+     *  and <code>false</code> if it should be ungrayed
+     * @return <code>true</code> if the element is visible and the gray
+     *  state could be set, and <code>false</code> otherwise
+     * @see #setGrayed
+     */
+    public bool setParentsGrayed(Object element, bool state) {
+        Assert.isNotNull(element);
+        Widget widget = internalExpand(element, false);
+        if (auto item = cast(TreeItem) widget ) {
+            item.setGrayed(state);
+            item = item.getParentItem();
+            while (item !is null) {
+                item.setGrayed(state);
+                item = item.getParentItem();
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Sets the checked state for the given element and its visible
+     * children in this viewer.
+     * Assumes that the element has been expanded before. To enforce
+     * that the item is expanded, call <code>expandToLevel</code>
+     * for the element.
+     * Does not fire events to check state listeners.
+     *
+     * @param element the element
+     * @param state <code>true</code> if the item should be checked,
+     *  and <code>false</code> if it should be unchecked
+     * @return <code>true</code> if the checked state could be set,
+     *  and <code>false</code> otherwise
+     */
+    public bool setSubtreeChecked(Object element, bool state) {
+        Widget widget = internalExpand(element, false);
+        if (auto item = cast(TreeItem) widget ) {
+            item.setChecked(state);
+            setCheckedChildren(item, state);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Sets to the given value the checked state for all elements in this viewer.
+     * Does not fire events to check state listeners.
+     * Assumes that the element has been expanded before. To enforce
+     * that the item is expanded, call <code>expandToLevel</code>
+     * for the element.
+     *
+     * @param state <code>true</code> if the element should be checked,
+     *  and <code>false</code> if it should be unchecked
+     * @deprecated as this method only checks or unchecks visible items
+     * is is recommended that {@link #setSubtreeChecked(Object, bool)}
+     * is used instead.
+     * @see #setSubtreeChecked(Object, bool)
+     *
+     *  @since 3.2
+     */
+    public void setAllChecked(bool state) {
+        setAllChecked(state,  getTree().getItems());
+
+    }
+
+    /**
+     * Set the checked state of the visible items and their children to state.
+     * @param state
+     * @param items
+     * @deprecated
+     * @see #setAllChecked(bool)
+     */
+    private void setAllChecked(bool state, TreeItem[] items) {
+        for (int i = 0; i < items.length; i++) {
+            items[i].setChecked(state);
+            TreeItem[] children = items[i].getItems();
+            setAllChecked(state, children);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ColorCellEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.ColorCellEditor;
+
+import org.eclipse.jface.viewers.DialogCellEditor;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.TableTree;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.ColorDialog;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Tree;
+
+import java.lang.all;
+import java.util.Set;
+import tango.text.convert.Format;
+
+/**
+ * A cell editor that manages a color field.
+ * The cell editor's value is the color (an SWT <code>RBG</code>).
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ColorCellEditor : DialogCellEditor {
+
+    /**
+     * The default extent in pixels.
+     */
+    private static const int DEFAULT_EXTENT = 16;
+
+    /**
+     * Gap between between image and text in pixels.
+     */
+    private static const int GAP = 6;
+
+    /**
+     * The composite widget containing the color and RGB label widgets
+     */
+    private Composite composite;
+
+    /**
+     * The label widget showing the current color.
+     */
+    private Label colorLabel;
+
+    /**
+     * The label widget showing the RGB values.
+     */
+    private Label rgbLabel;
+
+    /**
+     * The image.
+     */
+    private Image image;
+
+    /**
+     * Internal class for laying out this cell editor.
+     */
+    private class ColorCellLayout : Layout {
+        public override Point computeSize(Composite editor, int wHint, int hHint,
+                bool force) {
+            if (wHint !is SWT.DEFAULT && hHint !is SWT.DEFAULT) {
+                return new Point(wHint, hHint);
+            }
+            Point colorSize = colorLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT,
+                    force);
+            Point rgbSize = rgbLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT,
+                    force);
+            return new Point(colorSize.x + GAP + rgbSize.x, Math.max(
+                    colorSize.y, rgbSize.y));
+        }
+
+        public override void layout(Composite editor, bool force) {
+            Rectangle bounds = editor.getClientArea();
+            Point colorSize = colorLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT,
+                    force);
+            Point rgbSize = rgbLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT,
+                    force);
+            int ty = (bounds.height - rgbSize.y) / 2;
+            if (ty < 0) {
+                ty = 0;
+            }
+            colorLabel.setBounds(-1, 0, colorSize.x, colorSize.y);
+            rgbLabel.setBounds(colorSize.x + GAP - 1, ty, bounds.width
+                    - colorSize.x - GAP, bounds.height);
+        }
+    }
+
+    /**
+     * Creates a new color cell editor parented under the given control.
+     * The cell editor value is black (<code>RGB(0,0,0)</code>) initially, and has no
+     * validator.
+     *
+     * @param parent the parent control
+     */
+    public this(Composite parent) {
+        this(parent, SWT.NONE);
+    }
+
+    /**
+     * Creates a new color cell editor parented under the given control.
+     * The cell editor value is black (<code>RGB(0,0,0)</code>) initially, and has no
+     * validator.
+     *
+     * @param parent the parent control
+     * @param style the style bits
+     * @since 2.1
+     */
+    public this(Composite parent, int style) {
+        super(parent, style);
+        doSetValue(new RGB(0, 0, 0));
+    }
+
+    /**
+     * Creates and returns the color image data for the given control
+     * and RGB value. The image's size is either the control's item extent
+     * or the cell editor's default extent, which is 16 pixels square.
+     *
+     * @param w the control
+     * @param color the color
+     */
+    private ImageData createColorImage(Control w, RGB color) {
+
+        GC gc = new GC(w);
+        FontMetrics fm = gc.getFontMetrics();
+        int size = fm.getAscent();
+        gc.dispose();
+
+        int indent = 6;
+        int extent = DEFAULT_EXTENT;
+        if ( auto i = cast(Table)w ) {
+            extent = i.getItemHeight() - 1;
+        } else if ( auto i = cast(Tree)w ) {
+            extent = i.getItemHeight() - 1;
+        } else if ( auto i = cast(TableTree)w ) {
+            extent = i.getItemHeight() - 1;
+        }
+
+        if (size > extent) {
+            size = extent;
+        }
+
+        int width = indent + size;
+        int height = extent;
+
+        int xoffset = indent;
+        int yoffset = (height - size) / 2;
+
+        RGB black = new RGB(0, 0, 0);
+        PaletteData dataPalette = new PaletteData([ black, black,
+                color ]);
+        ImageData data = new ImageData(width, height, 4, dataPalette);
+        data.transparentPixel = 0;
+
+        int end = size - 1;
+        for (int y = 0; y < size; y++) {
+            for (int x = 0; x < size; x++) {
+                if (x is 0 || y is 0 || x is end || y is end) {
+                    data.setPixel(x + xoffset, y + yoffset, 1);
+                } else {
+                    data.setPixel(x + xoffset, y + yoffset, 2);
+                }
+            }
+        }
+
+        return data;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on DialogCellEditor.
+     */
+    protected override Control createContents(Composite cell) {
+        Color bg = cell.getBackground();
+        composite = new Composite(cell, getStyle());
+        composite.setBackground(bg);
+        composite.setLayout(new ColorCellLayout());
+        colorLabel = new Label(composite, SWT.LEFT);
+        colorLabel.setBackground(bg);
+        rgbLabel = new Label(composite, SWT.LEFT);
+        rgbLabel.setBackground(bg);
+        rgbLabel.setFont(cell.getFont());
+        return composite;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     */
+    public override void dispose() {
+        if (image !is null) {
+            image.dispose();
+            image = null;
+        }
+        super.dispose();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on DialogCellEditor.
+     */
+    protected override Object openDialogBox(Control cellEditorWindow) {
+        ColorDialog dialog = new ColorDialog(cellEditorWindow.getShell());
+        Object value = getValue();
+        if (value !is null) {
+            dialog.setRGB(cast(RGB) value);
+        }
+        value = dialog.open();
+        return dialog.getRGB();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on DialogCellEditor.
+     */
+    protected override void updateContents(Object value) {
+        RGB rgb = cast(RGB) value;
+        // XXX: We don't have a value the first time this method is called".
+        if (rgb is null) {
+            rgb = new RGB(0, 0, 0);
+        }
+        // XXX: Workaround for 1FMQ0P3: SWT:ALL - TableItem.setImage doesn't work if using the identical image."
+        if (image !is null) {
+            image.dispose();
+        }
+
+        ImageData id = createColorImage(colorLabel.getParent().getParent(), rgb);
+        ImageData mask = id.getTransparencyMask();
+        image = new Image(colorLabel.getDisplay(), id, mask);
+        colorLabel.setImage(image);
+
+        rgbLabel
+                .setText( Format( "({},{},{})", rgb.red, rgb.green, rgb.blue));//$NON-NLS-4$//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.viewers.ColumnLabelProvider;
+
+import org.eclipse.jface.viewers.CellLabelProvider;
+import org.eclipse.jface.viewers.IFontProvider;
+import org.eclipse.jface.viewers.IColorProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ViewerCell;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The ColumnLabelProvider is the label provider for viewers
+ * that have column support such as {@link TreeViewer} and
+ * {@link TableViewer}
+ *
+ * <p><b>This classes is intended to be subclassed</b></p>
+ *
+ * @since 3.3
+ *
+ */
+public class ColumnLabelProvider : CellLabelProvider,
+        IFontProvider, IColorProvider, ILabelProvider {
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.CellLabelProvider#update(org.eclipse.jface.viewers.ViewerCell)
+     */
+    public override void update(ViewerCell cell) {
+        Object element = cell.getElement();
+        cell.setText(getText(element));
+        Image image = getImage(element);
+        cell.setImage(image);
+        cell.setBackground(getBackground(element));
+        cell.setForeground(getForeground(element));
+        cell.setFont(getFont(element));
+
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.IFontProvider#getFont(java.lang.Object)
+     */
+    public Font getFont(Object element) {
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.IColorProvider#getBackground(java.lang.Object)
+     */
+    public Color getBackground(Object element) {
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.IColorProvider#getForeground(java.lang.Object)
+     */
+    public Color getForeground(Object element) {
+        return null;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object)
+     */
+    public Image getImage(Object element) {
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
+     */
+    public String getText(Object element) {
+        return element is null ? "" : element.toString();//$NON-NLS-1$
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnLayoutData.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.ColumnLayoutData;
+
+import java.lang.all;
+
+/**
+ * An abstract column layout data describing the information needed
+ * (by <code>TableLayout</code>) to properly lay out a table.
+ * <p>
+ * This class is not intended to be subclassed outside the framework.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public abstract class ColumnLayoutData {
+
+    /**
+     * Indicates whether the column is resizable.
+     */
+    public bool resizable;
+
+    /**
+     * Creates a new column layout data object.
+     *
+     * @param resizable <code>true</code> if the column is resizable, and <code>false</code> if not
+     */
+    protected this(bool resizable) {
+        this.resizable = resizable;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnPixelData.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.ColumnPixelData;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.ColumnLayoutData;
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * Describes the width of a table column in pixels, and
+ * whether the column is resizable.
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ColumnPixelData : ColumnLayoutData {
+
+   /**
+     * The column's width in pixels.
+     */
+    public int width;
+
+    /**
+     * Whether to allocate extra width to the column to account for
+     * trim taken by the column itself.
+     * The default is <code>false</code> for backwards compatibility, but
+     * the recommended practice is to specify <code>true</code>, and
+     * specify the desired width for the content of the column, rather
+     * than adding a fudge factor to the specified width.
+     *
+     * @since 3.1
+     */
+    public bool addTrim = false;
+
+    /**
+     * Creates a resizable column width of the given number of pixels.
+     *
+     * @param widthInPixels the width of column in pixels
+     */
+    public this(int widthInPixels) {
+        this(widthInPixels, true, false);
+    }
+
+    /**
+     * Creates a column width of the given number of pixels.
+     *
+     * @param widthInPixels the width of column in pixels
+     * @param resizable <code>true</code> if the column is resizable,
+     *   and <code>false</code> if size of the column is fixed
+     */
+    public this(int widthInPixels, bool resizable) {
+        this(widthInPixels, resizable, false);
+    }
+
+    /**
+     * Creates a column width of the given number of pixels.
+     *
+     * @param widthInPixels
+     *            the width of column in pixels
+     * @param resizable
+     *            <code>true</code> if the column is resizable, and
+     *            <code>false</code> if size of the column is fixed
+     * @param addTrim
+     *            <code>true</code> to allocate extra width to the column to
+     *            account for trim taken by the column itself,
+     *            <code>false</code> to use the given width exactly
+     * @since 3.1
+     */
+    public this(int widthInPixels, bool resizable, bool addTrim) {
+        super(resizable);
+        Assert.isTrue(widthInPixels >= 0);
+        this.width = widthInPixels;
+        this.addTrim = addTrim;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,866 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation; bug 153993
+ *                                                 fix in bug 163317, 151295, 167323, 167858, 184346, 187826, 201905
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.ColumnViewer;
+
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.ICellModifier;
+import org.eclipse.jface.viewers.ColumnViewerEditor;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ViewerRow;
+import org.eclipse.jface.viewers.ViewerColumn;
+import org.eclipse.jface.viewers.CellLabelProvider;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
+import org.eclipse.jface.viewers.EditingSupport;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.internal.InternalPolicy;
+import org.eclipse.jface.util.Policy;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The ColumnViewer is the abstract superclass of viewers that have columns
+ * (e.g., AbstractTreeViewer and AbstractTableViewer). Concrete subclasses of
+ * {@link ColumnViewer} should implement a matching concrete subclass of {@link
+ * ViewerColumn}.
+ *
+ * <strong> This class is not intended to be subclassed outside of the JFace
+ * viewers framework.</strong>
+ *
+ * @since 3.3
+ *
+ */
+public abstract class ColumnViewer : StructuredViewer {
+    alias StructuredViewer.getLabelProvider getLabelProvider;
+    alias StructuredViewer.refresh refresh;
+    alias StructuredViewer.update update;
+
+    private CellEditor[] cellEditors;
+
+    private ICellModifier cellModifier;
+
+    private String[] columnProperties;
+
+    /**
+     * The cell is a cached viewer cell used for refreshing.
+     */
+    private ViewerCell cell;
+
+    private ColumnViewerEditor viewerEditor;
+
+    private bool busy;
+    private bool logWhenBusy = true; // initially true, set to false
+
+    // after logging for the first
+    // time
+
+    /**
+     * Create a new instance of the receiver.
+     */
+    public this() {
+        cell = new ViewerCell(null, 0, null);
+    }
+
+    protected override void hookControl(Control control) {
+        super.hookControl(control);
+        viewerEditor = createViewerEditor();
+        hookEditingSupport(control);
+    }
+
+    /**
+     * Hook up the editing support. Subclasses may override.
+     *
+     * @param control
+     *      the control you want to hook on
+     */
+    protected void hookEditingSupport(Control control) {
+        // Needed for backwards comp with AbstractTreeViewer and TableTreeViewer
+        // who are not hooked this way others may already overwrite and provide
+        // their
+        // own impl
+        if (viewerEditor !is null) {
+            control.addMouseListener(new class MouseAdapter {
+                public void mouseDown(MouseEvent e) {
+                    // Workaround for bug 185817
+                    if (e.count !is 2) {
+                        handleMouseDown(e);
+                    }
+                }
+
+                public void mouseDoubleClick(MouseEvent e) {
+                    handleMouseDown(e);
+                }
+            });
+        }
+    }
+
+    /**
+     * Creates the viewer editor used for editing cell contents. To be
+     * implemented by subclasses.
+     *
+     * @return the editor, or <code>null</code> if this viewer does not support
+     *  editing cell contents.
+     */
+    protected abstract ColumnViewerEditor createViewerEditor();
+
+    /**
+     * Returns the viewer cell at the given widget-relative coordinates, or
+     * <code>null</code> if there is no cell at that location
+     *
+     * @param point
+     *      the widget-relative coordinates
+     * @return the cell or <code>null</code> if no cell is found at the given
+     *  point
+     *
+     * @since 3.4
+     */
+    public ViewerCell getCell(Point point) {
+        ViewerRow row = getViewerRow(point);
+        if (row !is null) {
+            return row.getCell(point);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the viewer row at the given widget-relative coordinates.
+     *
+     * @param point
+     *      the widget-relative coordinates of the viewer row
+     * @return ViewerRow the row or <code>null</code> if no row is found at the
+     *  given coordinates
+     */
+    protected ViewerRow getViewerRow(Point point) {
+        Item item = getItemAt(point);
+
+        if (item !is null) {
+            return getViewerRowFromItem(item);
+        }
+
+        return null;
+    }
+    package ViewerRow getViewerRow_package(Point point) {
+        return getViewerRow(point);
+    }
+
+    /**
+     * Returns a {@link ViewerRow} associated with the given row widget.
+     * Implementations may re-use the same instance for different row widgets;
+     * callers can only use the viewer row locally and until the next call to
+     * this method.
+     *
+     * @param item
+     *      the row widget
+     * @return ViewerRow a viewer row object
+     */
+    protected abstract ViewerRow getViewerRowFromItem(Widget item);
+    package ViewerRow getViewerRowFromItem_package(Widget item){
+        return getViewerRowFromItem(item);
+    }
+
+    /**
+     * Returns the column widget at the given column index.
+     *
+     * @param columnIndex
+     *      the column index
+     * @return Widget the column widget
+     */
+    protected abstract Widget getColumnViewerOwner(int columnIndex);
+
+    /**
+     * Returns the viewer column for the given column index.
+     *
+     * @param columnIndex
+     *      the column index
+     * @return the viewer column at the given index, or <code>null</code> if
+     *  there is none for the given index
+     */
+    /* package */ViewerColumn getViewerColumn(int columnIndex) {
+
+        ViewerColumn viewer;
+        Widget columnOwner = getColumnViewerOwner(columnIndex);
+
+        if (columnOwner is null || columnOwner.isDisposed()) {
+            return null;
+        }
+
+        viewer = cast(ViewerColumn) columnOwner.getData(ViewerColumn.COLUMN_VIEWER_KEY);
+
+        if (viewer is null) {
+            viewer = createViewerColumn(columnOwner, CellLabelProvider
+                    .createViewerLabelProvider(this, getLabelProvider()));
+            setupEditingSupport(columnIndex, viewer);
+        }
+
+        if (viewer.getEditingSupport() is null && getCellModifier() !is null) {
+            setupEditingSupport(columnIndex, viewer);
+        }
+
+        return viewer;
+    }
+
+    /**
+     * Sets up editing support for the given column based on the "old" cell
+     * editor API.
+     *
+     * @param columnIndex
+     * @param viewer
+     */
+    private void setupEditingSupport(int columnIndex, ViewerColumn viewer) {
+        if (getCellModifier() !is null) {
+            viewer.setEditingSupport(new class(this,columnIndex) EditingSupport {
+                int columnIndex_;
+                this(ColumnViewer cv, int ci){
+                    super(cv);
+                    columnIndex_=ci;
+                }
+                /*
+                 * (non-Javadoc)
+                 *
+                 * @see
+                 * org.eclipse.jface.viewers.EditingSupport#canEdit(java.lang
+                 * .Object)
+                 */
+                public bool canEdit(Object element) {
+                    Object[] properties = getColumnProperties();
+
+                    if (columnIndex_ < properties.length ) {
+                        return getCellModifier().canModify(element,
+                                (cast(ArrayWrapperString) getColumnProperties()[columnIndex_]).array);
+                    }
+
+                    return false;
+                }
+
+                /*
+                 * (non-Javadoc)
+                 *
+                 * @see
+                 * org.eclipse.jface.viewers.EditingSupport#getCellEditor(java
+                 * .lang.Object)
+                 */
+                public CellEditor getCellEditor(Object element) {
+                    CellEditor[] editors = getCellEditors();
+                    if (columnIndex_ < editors.length ) {
+                        return getCellEditors()[columnIndex_];
+                    }
+                    return null;
+                }
+
+                /*
+                 * (non-Javadoc)
+                 *
+                 * @see
+                 * org.eclipse.jface.viewers.EditingSupport#getValue(java.lang
+                 * .Object)
+                 */
+                public Object getValue(Object element) {
+                    Object[] properties = getColumnProperties();
+
+                    if (columnIndex_ < properties.length) {
+                        return getCellModifier().getValue(element,
+                                (cast(ArrayWrapperString) getColumnProperties()[columnIndex_]).array);
+                    }
+
+                    return null;
+                }
+
+                /*
+                 * (non-Javadoc)
+                 *
+                 * @see
+                 * org.eclipse.jface.viewers.EditingSupport#setValue(java.lang
+                 * .Object, java.lang.Object)
+                 */
+                public void setValue(Object element, Object value) {
+                    Object[] properties = getColumnProperties();
+
+                    if (columnIndex_ < properties.length) {
+                        getCellModifier().modify(findItem(element),
+                                (cast(ArrayWrapperString) getColumnProperties()[columnIndex_]).array,
+                                value);
+                    }
+                }
+
+                bool isLegacySupport() {
+                    return true;
+                }
+            });
+        }
+    }
+
+    /**
+     * Creates a generic viewer column for the given column widget, based on the
+     * given label provider.
+     *
+     * @param columnOwner
+     *      the column widget
+     * @param labelProvider
+     *      the label provider to use for the column
+     * @return ViewerColumn the viewer column
+     */
+    private ViewerColumn createViewerColumn(Widget columnOwner,
+            CellLabelProvider labelProvider) {
+        ViewerColumn column = new class(this, columnOwner) ViewerColumn {
+            this( ColumnViewer cv, Widget co ){
+                super(cv,co);
+            }
+        };
+        column.setLabelProvider(labelProvider, false);
+        return column;
+    }
+
+    /**
+     * Update the cached cell object with the given row and column.
+     *
+     * @param rowItem
+     * @param column
+     * @return ViewerCell
+     */
+    /* package */ViewerCell updateCell(ViewerRow rowItem, int column,
+            Object element) {
+        cell.update(rowItem, column, element);
+        return cell;
+    }
+
+    /**
+     * Returns the {@link Item} at the given widget-relative coordinates, or
+     * <code>null</code> if there is no item at the given coordinates.
+     *
+     * @param point
+     *      the widget-relative coordinates
+     * @return the {@link Item} at the coordinates or <code>null</code> if there
+     *  is no item at the given coordinates
+     */
+    protected abstract Item getItemAt(Point point);
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StructuredViewer#getItem(int, int)
+     */
+    protected override Item getItem(int x, int y) {
+        return getItemAt(getControl().toControl(x, y));
+    }
+
+    /**
+     * The column viewer implementation of this <code>Viewer</code> framework
+     * method ensures that the given label provider is an instance of
+     * <code>ITableLabelProvider</code>, <code>ILabelProvider</code>, or
+     * <code>CellLabelProvider</code>.
+     * <p>
+     * If the label provider is an {@link ITableLabelProvider} , then it
+     * provides a separate label text and image for each column. Implementers of
+     * <code>ITableLabelProvider</code> may also implement {@link
+     * ITableColorProvider} and/or {@link ITableFontProvider} to provide colors
+     * and/or fonts.
+     * </p>
+     * <p>
+     * If the label provider is an <code>ILabelProvider</code> , then it
+     * provides only the label text and image for the first column, and any
+     * remaining columns are blank. Implementers of <code>ILabelProvider</code>
+     * may also implement {@link IColorProvider} and/or {@link IFontProvider} to
+     * provide colors and/or fonts.
+     * </p>
+     *
+     */
+    public override void setLabelProvider(IBaseLabelProvider labelProvider) {
+        Assert.isTrue( null !is cast(ITableLabelProvider)labelProvider
+                || null !is cast(ILabelProvider)labelProvider
+                || null !is cast(CellLabelProvider)labelProvider );
+        updateColumnParts(labelProvider);// Reset the label providers in the
+        // columns
+        super.setLabelProvider(labelProvider);
+        if ( null !is cast(CellLabelProvider)labelProvider ) {
+            (cast(CellLabelProvider) labelProvider).initialize_package(this, null);
+        }
+    }
+
+    void internalDisposeLabelProvider(IBaseLabelProvider oldProvider) {
+        if (null !is cast(CellLabelProvider)oldProvider ) {
+            (cast(CellLabelProvider) oldProvider).dispose(this, null);
+        } else {
+            super.internalDisposeLabelProvider(oldProvider);
+        }
+    }
+
+    /**
+     * Clear the viewer parts for the columns
+     */
+    private void updateColumnParts(IBaseLabelProvider labelProvider) {
+        ViewerColumn column;
+        int i = 0;
+
+        while ((column = getViewerColumn(i++)) !is null) {
+            column.setLabelProvider(CellLabelProvider
+                    .createViewerLabelProvider(this, labelProvider), false);
+        }
+    }
+
+    /**
+     * Cancels a currently active cell editor if one is active. All changes
+     * already done in the cell editor are lost.
+     *
+     * @since 3.1 (in subclasses, added in 3.3 to abstract class)
+     */
+    public void cancelEditing() {
+        if (viewerEditor !is null) {
+            viewerEditor.cancelEditing();
+        }
+    }
+
+    /**
+     * Apply the value of the active cell editor if one is active.
+     *
+     * @since 3.3
+     */
+    protected void applyEditorValue() {
+        if (viewerEditor !is null) {
+            viewerEditor.applyEditorValue();
+        }
+    }
+
+    /**
+     * Starts editing the given element at the given column index.
+     *
+     * @param element
+     *      the model element
+     * @param column
+     *      the column index
+     * @since 3.1 (in subclasses, added in 3.3 to abstract class)
+     */
+    public void editElement(Object element, int column) {
+        if (viewerEditor !is null) {
+            try {
+                getControl().setRedraw(false);
+                // Set the selection at first because in Tree's
+                // the element might not be materialized
+                setSelection(new StructuredSelection(element), true);
+
+                Widget item = findItem(element);
+                if (item !is null) {
+                    ViewerRow row = getViewerRowFromItem(item);
+                    if (row !is null) {
+                        ViewerCell cell = row.getCell(column);
+                        if (cell !is null) {
+                            triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent(
+                                    cell));
+                        }
+                    }
+                }
+            } finally {
+                getControl().setRedraw(true);
+            }
+        }
+    }
+
+    /**
+     * Return the CellEditors for the receiver, or <code>null</code> if no cell
+     * editors are set.
+     * <p>
+     * Since 3.3, an alternative API is available, see {@link
+     * ViewerColumn#setEditingSupport(EditingSupport)} for a more flexible way
+     * of editing values in a column viewer.
+     * </p>
+     *
+     *
+     * @return CellEditor[]
+     * @since 3.1 (in subclasses, added in 3.3 to abstract class)
+     * @see ViewerColumn#setEditingSupport(EditingSupport)
+     * @see EditingSupport
+     */
+    public CellEditor[] getCellEditors() {
+        return cellEditors;
+    }
+
+    /**
+     * Returns the cell modifier of this viewer, or <code>null</code> if none
+     * has been set.
+     *
+     * <p>
+     * Since 3.3, an alternative API is available, see {@link
+     * ViewerColumn#setEditingSupport(EditingSupport)} for a more flexible way
+     * of editing values in a column viewer.
+     * </p>
+     *
+     * @return the cell modifier, or <code>null</code>
+     * @since 3.1 (in subclasses, added in 3.3 to abstract class)
+     * @see ViewerColumn#setEditingSupport(EditingSupport)
+     * @see EditingSupport
+     */
+    public ICellModifier getCellModifier() {
+        return cellModifier;
+    }
+
+    /**
+     * Returns the column properties of this table viewer. The properties must
+     * correspond with the columns of the table control. They are used to
+     * identify the column in a cell modifier.
+     *
+     * <p>
+     * Since 3.3, an alternative API is available, see {@link
+     * ViewerColumn#setEditingSupport(EditingSupport)} for a more flexible way
+     * of editing values in a column viewer.
+     * </p>
+     *
+     * @return the list of column properties
+     * @since 3.1 (in subclasses, added in 3.3 to abstract class)
+     * @see ViewerColumn#setEditingSupport(EditingSupport)
+     * @see EditingSupport
+     */
+    public Object[] getColumnProperties() {
+        Object[] res;
+        foreach( prop; columnProperties ){
+            res ~= new ArrayWrapperString( prop );
+        }
+        return res;
+    }
+
+    /**
+     * Returns whether there is an active cell editor.
+     *
+     * <p>
+     * Since 3.3, an alternative API is available, see {@link
+     * ViewerColumn#setEditingSupport(EditingSupport)} for a more flexible way
+     * of editing values in a column viewer.
+     * </p>
+     *
+     * @return <code>true</code> if there is an active cell editor, and
+     *  <code>false</code> otherwise
+     * @since 3.1 (in subclasses, added in 3.3 to abstract class)
+     * @see ViewerColumn#setEditingSupport(EditingSupport)
+     * @see EditingSupport
+     */
+    public bool isCellEditorActive() {
+        if (viewerEditor !is null) {
+            return viewerEditor.isCellEditorActive();
+        }
+        return false;
+    }
+
+    public override void refresh(Object element) {
+        if (checkBusy())
+            return;
+
+        if (isCellEditorActive()) {
+            cancelEditing();
+        }
+
+        super.refresh(element);
+    }
+
+    public override void refresh(Object element, bool updateLabels) {
+        if (checkBusy())
+            return;
+
+        if (isCellEditorActive()) {
+            cancelEditing();
+        }
+
+        super.refresh(element, updateLabels);
+    }
+
+    public override void update(Object element, String[] properties) {
+        if (checkBusy())
+            return;
+        super.update(element, properties);
+    }
+
+    /**
+     * Sets the cell editors of this column viewer. If editing is not supported
+     * by this viewer the call simply has no effect.
+     *
+     * <p>
+     * Since 3.3, an alternative API is available, see {@link
+     * ViewerColumn#setEditingSupport(EditingSupport)} for a more flexible way
+     * of editing values in a column viewer.
+     * </p>
+     * <p>
+     * Users setting up an editable {@link TreeViewer} or {@link TableViewer} with more than 1 column <b>have</b>
+     * to pass the SWT.FULL_SELECTION style bit
+     * </p>
+     * @param editors
+     *      the list of cell editors
+     * @since 3.1 (in subclasses, added in 3.3 to abstract class)
+     * @see ViewerColumn#setEditingSupport(EditingSupport)
+     * @see EditingSupport
+     */
+    public void setCellEditors(CellEditor[] editors) {
+        this.cellEditors = editors;
+    }
+
+    /**
+     * Sets the cell modifier for this column viewer. This method does nothing
+     * if editing is not supported by this viewer.
+     *
+     * <p>
+     * Since 3.3, an alternative API is available, see {@link
+     * ViewerColumn#setEditingSupport(EditingSupport)} for a more flexible way
+     * of editing values in a column viewer.
+     * </p>
+     * <p>
+     * Users setting up an editable {@link TreeViewer} or {@link TableViewer} with more than 1 column <b>have</b>
+     * to pass the SWT.FULL_SELECTION style bit
+     * </p>
+     * @param modifier
+     *      the cell modifier
+     * @since 3.1 (in subclasses, added in 3.3 to abstract class)
+     * @see ViewerColumn#setEditingSupport(EditingSupport)
+     * @see EditingSupport
+     */
+    public void setCellModifier(ICellModifier modifier) {
+        this.cellModifier = modifier;
+    }
+
+    /**
+     * Sets the column properties of this column viewer. The properties must
+     * correspond with the columns of the control. They are used to identify the
+     * column in a cell modifier. If editing is not supported by this viewer the
+     * call simply has no effect.
+     *
+     * <p>
+     * Since 3.3, an alternative API is available, see {@link
+     * ViewerColumn#setEditingSupport(EditingSupport)} for a more flexible way
+     * of editing values in a column viewer.
+     * </p>
+     * <p>
+     * Users setting up an editable {@link TreeViewer} or {@link TableViewer} with more than 1 column <b>have</b>
+     * to pass the SWT.FULL_SELECTION style bit
+     * </p>
+     * @param columnProperties
+     *      the list of column properties
+     * @since 3.1 (in subclasses, added in 3.3 to abstract class)
+     * @see ViewerColumn#setEditingSupport(EditingSupport)
+     * @see EditingSupport
+     */
+    public void setColumnProperties(String[] columnProperties) {
+        this.columnProperties = columnProperties;
+    }
+
+    /**
+     * Returns the number of columns contained in the receiver. If no columns
+     * were created by the programmer, this value is zero, despite the fact that
+     * visually, one column of items may be visible. This occurs when the
+     * programmer uses the column viewer like a list, adding elements but never
+     * creating a column.
+     *
+     * @return the number of columns
+     *
+     * @since 3.3
+     */
+    protected abstract int doGetColumnCount();
+    package int doGetColumnCount_package(){
+        return doGetColumnCount();
+    }
+
+    /**
+     * Returns the label provider associated with the column at the given index
+     * or <code>null</code> if no column with this index is known.
+     *
+     * @param columnIndex
+     *      the column index
+     * @return the label provider associated with the column or
+     *  <code>null</code> if no column with this index is known
+     *
+     * @since 3.3
+     */
+    public CellLabelProvider getLabelProvider(int columnIndex) {
+        ViewerColumn column = getViewerColumn(columnIndex);
+        if (column !is null) {
+            auto res = column.getLabelProvider();
+            return res;
+        }
+        return null;
+    }
+
+    private void handleMouseDown(MouseEvent e) {
+        ViewerCell cell = getCell(new Point(e.x, e.y));
+
+        if (cell !is null) {
+            triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent(
+                    cell, e));
+        }
+    }
+
+    /**
+     * Invoking this method fires an editor activation event which tries to
+     * enable the editor but before this event is passed to {@link
+     * ColumnViewerEditorActivationStrategy} to see if this event should really
+     * trigger editor activation
+     *
+     * @param event
+     *      the activation event
+     */
+    protected void triggerEditorActivationEvent(
+            ColumnViewerEditorActivationEvent event) {
+        viewerEditor.handleEditorActivationEvent(event);
+    }
+    package void triggerEditorActivationEvent_package(
+            ColumnViewerEditorActivationEvent event) {
+        triggerEditorActivationEvent(event);
+    }
+
+    /**
+     * @param columnViewerEditor
+     *      the new column viewer editor
+     */
+    public void setColumnViewerEditor(ColumnViewerEditor columnViewerEditor) {
+        Assert.isNotNull(columnViewerEditor);
+        this.viewerEditor = columnViewerEditor;
+    }
+
+    /**
+     * @return the currently attached viewer editor
+     */
+    public ColumnViewerEditor getColumnViewerEditor() {
+        return viewerEditor;
+    }
+
+    protected override Object[] getRawChildren(Object parent) {
+        bool oldBusy = isBusy();
+        setBusy(true);
+        try {
+            return super.getRawChildren(parent);
+        } finally {
+            setBusy(oldBusy);
+        }
+    }
+
+    void clearLegacyEditingSetup() {
+        if (!getControl().isDisposed() && getCellEditors() !is null) {
+            int count = doGetColumnCount();
+
+            for (int i = 0; i < count || i is 0; i++) {
+                Widget owner = getColumnViewerOwner(i);
+                if (owner !is null && !owner.isDisposed()) {
+                ViewerColumn column = cast(ViewerColumn) owner
+                            .getData(ViewerColumn.COLUMN_VIEWER_KEY);
+                    if (column !is null) {
+                        EditingSupport e = column.getEditingSupport();
+                        // Ensure that only EditingSupports are wiped that are
+                        // setup
+                        // for Legacy reasons
+                        if (e !is null && e.isLegacySupport()) {
+                            column.setEditingSupport(null);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Checks if this viewer is currently busy, logging a warning and returning
+     * <code>true</code> if it is busy. A column viewer is busy when it is
+     * processing a refresh, add, remove, insert, replace, setItemCount,
+     * expandToLevel, update, setExpandedElements, or similar method that may
+     * make calls to client code. Column viewers are not designed to handle
+     * reentrant calls while they are busy. The method returns <code>true</code>
+     * if the viewer is busy. It is recommended that this method be used by
+     * subclasses to determine whether the viewer is busy to return early from
+     * state-changing methods.
+     *
+     * <p>
+     * This method is not intended to be overridden by subclasses.
+     * </p>
+     *
+     * @return <code>true</code> if the viewer is busy.
+     *
+     * @since 3.4
+     */
+    protected bool checkBusy() {
+        if (isBusy()) {
+            if (logWhenBusy) {
+                String message = "Ignored reentrant call while viewer is busy."; //$NON-NLS-1$
+                if (!InternalPolicy.DEBUG_LOG_REENTRANT_VIEWER_CALLS) {
+                    // stop logging after the first
+                    logWhenBusy = false;
+                    message ~= " This is only logged once per viewer instance," ~ //$NON-NLS-1$
+                            " but similar calls will still be ignored."; //$NON-NLS-1$
+                }
+                Policy.getLog().log(
+                        new Status(IStatus.WARNING, Policy.JFACE, message,
+                                new RuntimeException()));
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Sets the busy state of this viewer. Subclasses MUST use <code>try</code>
+     * ...<code>finally</code> as follows to ensure that the busy flag is reset
+     * to its original value:
+     *
+     * <pre>
+     * bool oldBusy = isBusy();
+     * setBusy(true);
+     * try {
+     *  // do work
+     * } finally {
+     *  setBusy(oldBusy);
+     * }
+     * </pre>
+     *
+     * <p>
+     * This method is not intended to be overridden by subclasses.
+     * </p>
+     *
+     * @param busy
+     *      the new value of the busy flag
+     *
+     * @since 3.4
+     */
+    protected void setBusy(bool busy) {
+        this.busy = busy;
+    }
+
+    /**
+     * Returns <code>true</code> if this viewer is currently busy processing a
+     * refresh, add, remove, insert, replace, setItemCount, expandToLevel,
+     * update, setExpandedElements, or similar method that may make calls to
+     * client code. Column viewers are not designed to handle reentrant calls
+     * while they are busy. It is recommended that clients avoid using this
+     * method if they can ensure by other means that they will not make
+     * reentrant calls to methods like the ones listed above. See bug 184991 for
+     * background discussion.
+     *
+     * <p>
+     * This method is not intended to be overridden by subclasses.
+     * </p>
+     *
+     * @return Returns whether this viewer is busy.
+     *
+     * @since 3.4
+     */
+    public bool isBusy() {
+        return busy;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewerEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,751 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - refactoring (bug 153993)
+ *                                                 fix in bug: 151295,178946,166500,195908,201906,207676,180504,216706,218336
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.ColumnViewerEditor;
+
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.ICellEditorListener;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ViewerColumn;
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationListener;
+import org.eclipse.jface.viewers.ColumnViewerEditorDeactivationEvent;
+import org.eclipse.jface.viewers.ViewerRow;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.OpenEvent;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.core.runtime.ListenerList;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * This is the base for all editor implementations of Viewers. ColumnViewer
+ * implementors have to subclass this class and implement the missing methods
+ *
+ * @since 3.3
+ * @see TableViewerEditor
+ * @see TreeViewerEditor
+ */
+public abstract class ColumnViewerEditor {
+    private CellEditor cellEditor;
+
+    private ICellEditorListener cellEditorListener;
+
+    private FocusListener focusListener;
+
+    private MouseListener mouseListener;
+
+    private ColumnViewer viewer;
+
+    private TraverseListener tabeditingListener;
+
+    private ViewerCell cell;
+
+    private ListenerList editorActivationListener;
+
+    private ColumnViewerEditorActivationStrategy editorActivationStrategy;
+
+    private bool inEditorDeactivation;
+
+    private DisposeListener disposeListener;
+
+    /**
+     * Tabbing from cell to cell is turned off
+     */
+    public static const int DEFAULT = 1;
+
+    /**
+     * Should if the end of the row is reach started from the start/end of the
+     * row below/above
+     */
+    public static const int TABBING_MOVE_TO_ROW_NEIGHBOR = 1 << 1;
+
+    /**
+     * Should if the end of the row is reach started from the beginning in the
+     * same row
+     */
+    public static const int TABBING_CYCLE_IN_ROW = 1 << 2;
+
+    /**
+     * Support tabbing to Cell above/below the current cell
+     */
+    public static const int TABBING_VERTICAL = 1 << 3;
+
+    /**
+     * Should tabbing from column to column with in one row be supported
+     */
+    public static const int TABBING_HORIZONTAL = 1 << 4;
+
+    /**
+     * Style mask used to enable keyboard activation
+     */
+    public static const int KEYBOARD_ACTIVATION = 1 << 5;
+
+    /**
+     * Style mask used to turn <b>off</b> the feature that an editor activation
+     * is canceled on double click. It is also possible to turn off this feature
+     * per cell-editor using {@link CellEditor#getDoubleClickTimeout()}
+     * @since 3.4
+     */
+    public static final int KEEP_EDITOR_ON_DOUBLE_CLICK = 1 << 6;
+
+    private int feature;
+
+    /**
+     * @param viewer
+     *            the viewer this editor is attached to
+     * @param editorActivationStrategy
+     *            the strategy used to decide about editor activation
+     * @param feature
+     *            bit mask controlling the editor
+     *            <ul>
+     *            <li>{@link ColumnViewerEditor#DEFAULT}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li>
+     *            </ul>
+     */
+    protected this(ColumnViewer viewer,
+            ColumnViewerEditorActivationStrategy editorActivationStrategy,
+            int feature) {
+        this.viewer = viewer;
+        this.editorActivationStrategy = editorActivationStrategy;
+        if ((feature & KEYBOARD_ACTIVATION) is KEYBOARD_ACTIVATION) {
+            this.editorActivationStrategy
+                    .setEnableEditorActivationWithKeyboard(true);
+        }
+        this.feature = feature;
+        this.disposeListener = new class(viewer) DisposeListener {
+            ColumnViewer viewer_;
+            this(ColumnViewer a){
+                viewer_=a;
+            }
+            public void widgetDisposed(DisposeEvent e) {
+                if( viewer_.isCellEditorActive() ) {
+                    cancelEditing();
+                }
+            }
+
+        };
+        initCellEditorListener();
+    }
+
+    private void initCellEditorListener() {
+        cellEditorListener = new class ICellEditorListener {
+            public void editorValueChanged(bool oldValidState,
+                    bool newValidState) {
+                // Ignore.
+            }
+
+            public void cancelEditor() {
+                this.outer.cancelEditing();
+            }
+
+            public void applyEditorValue() {
+                this.outer.applyEditorValue();
+            }
+        };
+    }
+
+    private bool activateCellEditor(ColumnViewerEditorActivationEvent activationEvent) {
+
+        ViewerColumn part = viewer.getViewerColumn(cell.getColumnIndex());
+        Object element = cell.getElement();
+
+        if (part !is null && part.getEditingSupport() !is null
+                && part.getEditingSupport().canEdit_package(element)) {
+            cellEditor = part.getEditingSupport().getCellEditor_package(element);
+            if (cellEditor !is null) {
+                int timeout = cellEditor.getDoubleClickTimeout_package();
+
+                int activationTime;
+
+                if (timeout !is 0) {
+                    activationTime = activationEvent.time + timeout;
+                } else {
+                    activationTime = 0;
+                }
+
+                if (editorActivationListener !is null
+                        && !editorActivationListener.isEmpty()) {
+                    Object[] ls = editorActivationListener.getListeners();
+                    for (int i = 0; i < ls.length; i++) {
+                        (cast(ColumnViewerEditorActivationListener) ls[i])
+                                .beforeEditorActivated(activationEvent);
+
+                        // Was the activation canceled ?
+                        if (activationEvent.cancel) {
+                            return false;
+                        }
+                    }
+                }
+
+                updateFocusCell(cell, activationEvent);
+
+                cellEditor.addListener(cellEditorListener);
+                part.getEditingSupport().initializeCellEditorValue_package(cellEditor,
+                        cell);
+
+                // Tricky flow of control here:
+                // activate() can trigger callback to cellEditorListener which
+                // will clear cellEditor
+                // so must get control first, but must still call activate()
+                // even if there is no control.
+                Control control = cellEditor.getControl();
+                cellEditor.activate(activationEvent);
+                if (control is null) {
+                    return false;
+                }
+                setLayoutData(cellEditor.getLayoutData());
+                setEditor(control, cast(Item) cell.getItem(), cell.getColumnIndex());
+                cellEditor.setFocus();
+
+                if (cellEditor.dependsOnExternalFocusListener_package()) {
+                    if (focusListener is null) {
+                        focusListener = new class FocusAdapter {
+                            public void focusLost(FocusEvent e) {
+                                applyEditorValue();
+                            }
+                        };
+                    }
+                    control.addFocusListener(focusListener);
+                }
+
+                mouseListener = new class(control, activationEvent, activationTime) MouseAdapter {
+                    Control control_;
+                    ColumnViewerEditorActivationEvent activationEvent_;
+                    int activationTime_;
+                    this(Control a, ColumnViewerEditorActivationEvent b, int c){
+                        control_=a;
+                        activationEvent_=b;
+                        activationTime_=c;
+                    }
+                    public void mouseDown(MouseEvent e) {
+                        // time wrap?
+                        // check for expiration of doubleClickTime
+                        if (shouldFireDoubleClick(activationTime_, e.time, activationEvent_) && e.button is 1) {
+                            control_.removeMouseListener(mouseListener);
+                            cancelEditing();
+                            handleDoubleClickEvent();
+                        } else if (mouseListener !is null) {
+                            control_.removeMouseListener(mouseListener);
+                        }
+                    }
+                };
+
+                if (activationTime !is 0
+                        && (feature & KEEP_EDITOR_ON_DOUBLE_CLICK) is 0) {
+                    control.addMouseListener(mouseListener);
+                }
+
+                if (tabeditingListener is null) {
+                    tabeditingListener = new class TraverseListener {
+
+                        public void keyTraversed(TraverseEvent e) {
+                            if ((feature & DEFAULT) !is DEFAULT) {
+                                processTraverseEvent(cell.getColumnIndex(),
+                                        viewer.getViewerRowFromItem_package(cell
+                                                .getItem()), e);
+                            }
+                        }
+                    };
+                }
+
+                control.addTraverseListener(tabeditingListener);
+
+                if (editorActivationListener !is null
+                        && !editorActivationListener.isEmpty()) {
+                    Object[] ls = editorActivationListener.getListeners();
+                    for (int i = 0; i < ls.length; i++) {
+                        (cast(ColumnViewerEditorActivationListener) ls[i])
+                                .afterEditorActivated(activationEvent);
+                    }
+                }
+
+                this.cell.getItem().addDisposeListener(disposeListener);
+
+                return true;
+            }
+
+        }
+
+        return false;
+    }
+
+    private bool shouldFireDoubleClick(int activationTime, int mouseTime,
+            ColumnViewerEditorActivationEvent activationEvent) {
+        return mouseTime <= activationTime
+                && activationEvent.eventType !is ColumnViewerEditorActivationEvent.KEY_PRESSED
+                && activationEvent.eventType !is ColumnViewerEditorActivationEvent.PROGRAMMATIC
+                && activationEvent.eventType !is ColumnViewerEditorActivationEvent.TRAVERSAL;
+    }
+
+    /**
+     * Applies the current value and deactivates the currently active cell
+     * editor.
+     */
+    void applyEditorValue() {
+        // avoid re-entering
+        if (!inEditorDeactivation) {
+            try {
+                inEditorDeactivation = true;
+                CellEditor c = this.cellEditor;
+                if (c !is null && this.cell !is null) {
+                    ColumnViewerEditorDeactivationEvent tmp = new ColumnViewerEditorDeactivationEvent(
+                            cell);
+                    tmp.eventType = ColumnViewerEditorDeactivationEvent.EDITOR_SAVED;
+                    if (editorActivationListener !is null
+                            && !editorActivationListener.isEmpty()) {
+                        Object[] ls = editorActivationListener.getListeners();
+                        for (int i = 0; i < ls.length; i++) {
+
+                            (cast(ColumnViewerEditorActivationListener) ls[i])
+                                    .beforeEditorDeactivated(tmp);
+                        }
+                    }
+
+                    Item t = cast(Item) this.cell.getItem();
+
+                    // don't null out table item -- same item is still selected
+                    if (t !is null && !t.isDisposed()) {
+                        saveEditorValue(c);
+                    }
+                    if (!viewer.getControl().isDisposed()) {
+                        setEditor(null, null, 0);
+                    }
+
+                    c.removeListener(cellEditorListener);
+                    Control control = c.getControl();
+                    if (control !is null && !control.isDisposed()) {
+                        if (mouseListener !is null) {
+                            control.removeMouseListener(mouseListener);
+                            // Clear the instance not needed any more
+                            mouseListener = null;
+                        }
+                        if (focusListener !is null) {
+                            control.removeFocusListener(focusListener);
+                        }
+
+                        if (tabeditingListener !is null) {
+                            control.removeTraverseListener(tabeditingListener);
+                        }
+                    }
+                    c.deactivate_package(tmp);
+
+                    if (editorActivationListener !is null
+                            && !editorActivationListener.isEmpty()) {
+                        Object[] ls = editorActivationListener.getListeners();
+                        for (int i = 0; i < ls.length; i++) {
+                            (cast(ColumnViewerEditorActivationListener) ls[i])
+                                    .afterEditorDeactivated(tmp);
+                        }
+                    }
+
+                    if( ! this.cell.getItem().isDisposed() ) {
+                        this.cell.getItem().removeDisposeListener(disposeListener);
+                    }
+                }
+
+                this.cellEditor = null;
+                this.cell = null;
+            } finally {
+                inEditorDeactivation = false;
+            }
+        }
+    }
+
+    /**
+     * Cancel editing
+     */
+    void cancelEditing() {
+        // avoid re-entering
+        if (!inEditorDeactivation) {
+            try {
+                inEditorDeactivation = true;
+                if (cellEditor !is null) {
+                    ColumnViewerEditorDeactivationEvent tmp = new ColumnViewerEditorDeactivationEvent(
+                            cell);
+                    tmp.eventType = ColumnViewerEditorDeactivationEvent.EDITOR_CANCELED;
+                    if (editorActivationListener !is null
+                            && !editorActivationListener.isEmpty()) {
+                        Object[] ls = editorActivationListener.getListeners();
+                        for (int i = 0; i < ls.length; i++) {
+
+                            (cast(ColumnViewerEditorActivationListener) ls[i])
+                                    .beforeEditorDeactivated(tmp);
+                        }
+                    }
+
+                    if (!viewer.getControl().isDisposed()) {
+                        setEditor(null, null, 0);
+                    }
+
+                    cellEditor.removeListener(cellEditorListener);
+
+                    Control control = cellEditor.getControl();
+                    if (control !is null && !viewer.getControl().isDisposed()) {
+                        if (mouseListener !is null) {
+                            control.removeMouseListener(mouseListener);
+                            // Clear the instance not needed any more
+                            mouseListener = null;
+                        }
+                        if (focusListener !is null) {
+                            control.removeFocusListener(focusListener);
+                        }
+
+                        if (tabeditingListener !is null) {
+                            control.removeTraverseListener(tabeditingListener);
+                        }
+                    }
+
+                    CellEditor oldEditor = cellEditor;
+                    oldEditor.deactivate_package(tmp);
+
+                    if (editorActivationListener !is null
+                            && !editorActivationListener.isEmpty()) {
+                        Object[] ls = editorActivationListener.getListeners();
+                        for (int i = 0; i < ls.length; i++) {
+                            (cast(ColumnViewerEditorActivationListener) ls[i])
+                                    .afterEditorDeactivated(tmp);
+                        }
+                    }
+
+                    if( ! this.cell.getItem().isDisposed() ) {
+                        this.cell.getItem().addDisposeListener(disposeListener);
+                    }
+
+                    this.cellEditor = null;
+                    this.cell = null;
+
+                }
+            } finally {
+                inEditorDeactivation = false;
+            }
+        }
+    }
+
+    /**
+     * Enable the editor by mouse down
+     *
+     * @param event
+     */
+    void handleEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
+
+        // Only activate if the event isn't tagged as canceled
+        if (!event.cancel
+                && editorActivationStrategy.isEditorActivationEvent_package(event)) {
+            if (cellEditor !is null) {
+                applyEditorValue();
+            }
+
+            this.cell = cast(ViewerCell) event.getSource();
+
+            if( ! activateCellEditor(event) ) {
+                this.cell = null;
+                this.cellEditor = null;
+            }
+        }
+    }
+
+    private void saveEditorValue(CellEditor cellEditor) {
+        ViewerColumn part = viewer.getViewerColumn(cell.getColumnIndex());
+
+        if (part !is null && part.getEditingSupport() !is null) {
+            part.getEditingSupport().saveCellEditorValue_package(cellEditor, cell);
+        }
+    }
+
+    /**
+     * Return whether there is an active cell editor.
+     *
+     * @return <code>true</code> if there is an active cell editor; otherwise
+     *         <code>false</code> is returned.
+     */
+    bool isCellEditorActive() {
+        return cellEditor !is null;
+    }
+
+    void handleDoubleClickEvent() {
+        viewer.fireDoubleClick_package(new DoubleClickEvent(viewer, viewer
+                .getSelection()));
+        viewer.fireOpen_package(new OpenEvent(viewer, viewer.getSelection()));
+    }
+
+    /**
+     * Adds the given listener, it is to be notified when the cell editor is
+     * activated or deactivated.
+     *
+     * @param listener
+     *            the listener to add
+     */
+    public void addEditorActivationListener(
+            ColumnViewerEditorActivationListener listener) {
+        if (editorActivationListener is null) {
+            editorActivationListener = new ListenerList();
+        }
+        editorActivationListener.add(listener);
+    }
+
+    /**
+     * Removes the given listener.
+     *
+     * @param listener
+     *            the listener to remove
+     */
+    public void removeEditorActivationListener(
+            ColumnViewerEditorActivationListener listener) {
+        if (editorActivationListener !is null) {
+            editorActivationListener.remove(listener);
+        }
+    }
+
+    /**
+     * Process the traverse event and opens the next available editor depending
+     * of the implemented strategy. The default implementation uses the style
+     * constants
+     * <ul>
+     * <li>{@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li>
+     * <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li>
+     * <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li>
+     * <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li>
+     * </ul>
+     *
+     * <p>
+     * Subclasses may overwrite to implement their custom logic to edit the next
+     * cell
+     * </p>
+     *
+     * @param columnIndex
+     *            the index of the current column
+     * @param row
+     *            the current row - may only be used for the duration of this
+     *            method call
+     * @param event
+     *            the traverse event
+     */
+    protected void processTraverseEvent(int columnIndex, ViewerRow row,
+            TraverseEvent event) {
+
+        ViewerCell cell2edit = null;
+
+        if (event.detail is SWT.TRAVERSE_TAB_PREVIOUS) {
+            event.doit = false;
+
+            if ((event.stateMask & SWT.CTRL) is SWT.CTRL
+                    && (feature & TABBING_VERTICAL) is TABBING_VERTICAL) {
+                cell2edit = searchCellAboveBelow(row, viewer, columnIndex, true);
+            } else if ((feature & TABBING_HORIZONTAL) is TABBING_HORIZONTAL) {
+                cell2edit = searchPreviousCell(row, row.getCell(columnIndex),
+                        row.getCell(columnIndex), viewer);
+            }
+        } else if (event.detail is SWT.TRAVERSE_TAB_NEXT) {
+            event.doit = false;
+
+            if ((event.stateMask & SWT.CTRL) is SWT.CTRL
+                    && (feature & TABBING_VERTICAL) is TABBING_VERTICAL) {
+                cell2edit = searchCellAboveBelow(row, viewer, columnIndex,
+                        false);
+            } else if ((feature & TABBING_HORIZONTAL) is TABBING_HORIZONTAL) {
+                cell2edit = searchNextCell(row, row.getCell(columnIndex), row
+                        .getCell(columnIndex), viewer);
+            }
+        }
+
+        if (cell2edit !is null) {
+
+            viewer.getControl().setRedraw(false);
+            ColumnViewerEditorActivationEvent acEvent = new ColumnViewerEditorActivationEvent(
+                    cell2edit, event);
+            viewer.triggerEditorActivationEvent_package(acEvent);
+            viewer.getControl().setRedraw(true);
+        }
+    }
+
+    private ViewerCell searchCellAboveBelow(ViewerRow row, ColumnViewer viewer,
+            int columnIndex, bool above) {
+        ViewerCell rv = null;
+
+        ViewerRow newRow = null;
+
+        if (above) {
+            newRow = row.getNeighbor(ViewerRow.ABOVE, false);
+        } else {
+            newRow = row.getNeighbor(ViewerRow.BELOW, false);
+        }
+
+        if (newRow !is null) {
+            ViewerColumn column = viewer.getViewerColumn(columnIndex);
+            if (column !is null
+                    && column.getEditingSupport() !is null
+                    && column.getEditingSupport().canEdit_package(
+                            newRow.getItem().getData())) {
+                rv = newRow.getCell(columnIndex);
+            } else {
+                rv = searchCellAboveBelow(newRow, viewer, columnIndex, above);
+            }
+        }
+
+        return rv;
+    }
+
+    private bool isCellEditable(ColumnViewer viewer, ViewerCell cell) {
+        ViewerColumn column = viewer.getViewerColumn(cell.getColumnIndex());
+        return column !is null && column.getEditingSupport() !is null
+                && column.getEditingSupport().canEdit_package(cell.getElement());
+    }
+
+    private ViewerCell searchPreviousCell(ViewerRow row,
+            ViewerCell currentCell, ViewerCell originalCell, ColumnViewer viewer) {
+        ViewerCell rv = null;
+        ViewerCell previousCell;
+
+        if (currentCell !is null) {
+            previousCell = currentCell.getNeighbor(ViewerCell.LEFT, true);
+        } else {
+            if (row.getColumnCount() !is 0) {
+                previousCell = row.getCell(row.getCreationIndex_package(row
+                        .getColumnCount() - 1));
+            } else {
+                previousCell = row.getCell(0);
+            }
+
+        }
+
+        // No endless loop
+        if (originalCell.opEquals(previousCell)) {
+            return null;
+        }
+
+        if (previousCell !is null) {
+            if (isCellEditable(viewer, previousCell)) {
+                rv = previousCell;
+            } else {
+                rv = searchPreviousCell(row, previousCell, originalCell, viewer);
+            }
+        } else {
+            if ((feature & TABBING_CYCLE_IN_ROW) is TABBING_CYCLE_IN_ROW) {
+                rv = searchPreviousCell(row, null, originalCell, viewer);
+            } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) is TABBING_MOVE_TO_ROW_NEIGHBOR) {
+                ViewerRow rowAbove = row.getNeighbor(ViewerRow.ABOVE, false);
+                if (rowAbove !is null) {
+                    rv = searchPreviousCell(rowAbove, null, originalCell,
+                            viewer);
+                }
+            }
+        }
+
+        return rv;
+    }
+
+    private ViewerCell searchNextCell(ViewerRow row, ViewerCell currentCell,
+            ViewerCell originalCell, ColumnViewer viewer) {
+        ViewerCell rv = null;
+
+        ViewerCell nextCell;
+
+        if (currentCell !is null) {
+            nextCell = currentCell.getNeighbor(ViewerCell.RIGHT, true);
+        } else {
+            nextCell = row.getCell(row.getCreationIndex_package(0));
+        }
+
+        // No endless loop
+        if (originalCell.opEquals(nextCell)) {
+            return null;
+        }
+
+        if (nextCell !is null) {
+            if (isCellEditable(viewer, nextCell)) {
+                rv = nextCell;
+            } else {
+                rv = searchNextCell(row, nextCell, originalCell, viewer);
+            }
+        } else {
+            if ((feature & TABBING_CYCLE_IN_ROW) is TABBING_CYCLE_IN_ROW) {
+                rv = searchNextCell(row, null, originalCell, viewer);
+            } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) is TABBING_MOVE_TO_ROW_NEIGHBOR) {
+                ViewerRow rowBelow = row.getNeighbor(ViewerRow.BELOW, false);
+                if (rowBelow !is null) {
+                    rv = searchNextCell(rowBelow, null, originalCell, viewer);
+                }
+            }
+        }
+
+        return rv;
+    }
+
+    /**
+     * Position the editor inside the control
+     *
+     * @param w
+     *            the editor control
+     * @param item
+     *            the item (row) in which the editor is drawn in
+     * @param fColumnNumber
+     *            the column number in which the editor is shown
+     */
+    protected abstract void setEditor(Control w, Item item, int fColumnNumber);
+
+    /**
+     * set the layout data for the editor
+     *
+     * @param layoutData
+     *            the layout data used when editor is displayed
+     */
+    protected abstract void setLayoutData(LayoutData layoutData);
+
+    /**
+     * @param focusCell
+     *            updates the cell with the current input focus
+     * @param event
+     *            the event requesting to update the focusCell
+     */
+    protected abstract void updateFocusCell(ViewerCell focusCell,
+            ColumnViewerEditorActivationEvent event);
+
+    /**
+     * @return the cell currently holding the focus if no cell has the focus or
+     *         the viewer implementation doesn't support <code>null</code> is
+     *         returned
+     *
+     */
+    public ViewerCell getFocusCell() {
+        return null;
+    }
+
+    /**
+     * @return the viewer working for
+     */
+    protected ColumnViewer getViewer() {
+        return viewer;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewerEditorActivationEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,171 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 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 org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
+
+import org.eclipse.jface.viewers.ViewerCell;
+// import java.util.EventObject;
+
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.TraverseEvent;
+
+import java.lang.all;
+import java.util.EventObject;
+import java.util.Set;
+
+/**
+ * This event is passed on when a cell-editor is going to be activated
+ *
+ * @since 3.3
+ *
+ */
+public class ColumnViewerEditorActivationEvent : EventObject {
+    /**
+     *
+     */
+    private static const long serialVersionUID = 1L;
+
+    /**
+     * if a key is pressed on a selected cell
+     */
+    public static const int KEY_PRESSED = 1;
+
+    /**
+     * if a cell is selected using a single click of the mouse
+     */
+    public static const int MOUSE_CLICK_SELECTION = 2;
+
+    /**
+     * if a cell is selected using double clicking of the mouse
+     */
+    public static const int MOUSE_DOUBLE_CLICK_SELECTION = 3;
+
+    /**
+     * if a cell is activated using code like e.g
+     * {@link ColumnViewer#editElement(Object, int)}
+     */
+    public static const int PROGRAMMATIC = 4;
+
+    /**
+     * is a cell is activated by traversing
+     */
+    public static const int TRAVERSAL = 5;
+
+    /**
+     * the original event triggered
+     */
+    public EventObject sourceEvent;
+
+    /**
+     * The time the event is triggered
+     */
+    public int time;
+
+    /**
+     * The event type triggered:
+     * <ul>
+     * <li>{@link #KEY_PRESSED} if a key is pressed on a selected cell</li>
+     * <li>{@link #MOUSE_CLICK_SELECTION} if a cell is selected using a single
+     * click of the mouse</li>
+     * <li>{@link #MOUSE_DOUBLE_CLICK_SELECTION} if a cell is selected using
+     * double clicking of the mouse</li>
+     * </ul>
+     */
+    public int eventType;
+
+    /**
+     * <b>Only set for {@link #KEY_PRESSED}</b>
+     */
+    public int keyCode;
+
+    /**
+     * <b>Only set for {@link #KEY_PRESSED}</b>
+     */
+    public char character;
+
+    /**
+     * The statemask
+     */
+    public int stateMask;
+
+    /**
+     * Cancel the event (=> editor is not activated)
+     */
+    public bool cancel = false;
+
+    /**
+     * This constructor can be used when no event exists. The type set is
+     * {@link #PROGRAMMATIC}
+     *
+     * @param cell
+     *            the cell
+     */
+    public this(ViewerCell cell) {
+        super(cell);
+        eventType = PROGRAMMATIC;
+    }
+
+    /**
+     * This constructor is used for all types of mouse events. Currently the
+     * type is can be {@link #MOUSE_CLICK_SELECTION} and
+     * {@link #MOUSE_DOUBLE_CLICK_SELECTION}
+     *
+     * @param cell
+     *            the cell source of the event
+     * @param event
+     *            the event
+     */
+    public this(ViewerCell cell, MouseEvent event) {
+        super(cell);
+
+        if (event.count >= 2) {
+            eventType = MOUSE_DOUBLE_CLICK_SELECTION;
+        } else {
+            eventType = MOUSE_CLICK_SELECTION;
+        }
+
+        this.sourceEvent = event;
+        this.time = event.time;
+    }
+
+    /**
+     * @param cell
+     *            the cell source of the event
+     * @param event
+     *            the event
+     */
+    public this(ViewerCell cell, KeyEvent event) {
+        super(cell);
+        this.eventType = KEY_PRESSED;
+        this.sourceEvent = event;
+        this.time = event.time;
+        this.keyCode = event.keyCode;
+        this.character = event.character;
+        this.stateMask = event.stateMask;
+    }
+
+    /**
+     * This constructor is used to mark the activation triggered by a traversal
+     *
+     * @param cell
+     *            the cell source of the event
+     * @param event
+     *            the event
+     */
+    public this(ViewerCell cell, TraverseEvent event) {
+        super(cell);
+        this.eventType = TRAVERSAL;
+        this.sourceEvent = event;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewerEditorActivationListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.ColumnViewerEditorActivationListener;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
+import org.eclipse.jface.viewers.ColumnViewerEditorDeactivationEvent;
+
+/**
+ * Parties interested in activation and deactivation of editors extend this
+ * class and implement any or all of the methods
+ *
+ * @since 3.3
+ *
+ */
+public abstract class ColumnViewerEditorActivationListener {
+    /**
+     * Called before an editor is activated
+     *
+     * @param event
+     *            the event
+     */
+    public abstract void beforeEditorActivated(ColumnViewerEditorActivationEvent event);
+
+    /**
+     * Called after an editor has been activated
+     *
+     * @param event the event
+     */
+    public abstract void afterEditorActivated(ColumnViewerEditorActivationEvent event);
+
+    /**
+     * Called before an editor is deactivated
+     *
+     * @param event
+     *            the event
+     */
+    public abstract void beforeEditorDeactivated(ColumnViewerEditorDeactivationEvent event);
+
+
+    /**
+     * Called after an editor is deactivated
+     *
+     * @param event the event
+     */
+    public abstract void afterEditorDeactivated(ColumnViewerEditorDeactivationEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewerEditorActivationStrategy.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                               - fix for bug 187817
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
+
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
+import org.eclipse.jface.viewers.IStructuredSelection;
+
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * This class is responsible to determine if a cell selection event is triggers
+ * an editor activation. Implementors can extend and overwrite to implement
+ * custom editing behavior
+ *
+ * @since 3.3
+ */
+public class ColumnViewerEditorActivationStrategy {
+    private ColumnViewer viewer;
+
+    private KeyListener keyboardActivationListener;
+
+    /**
+     * @param viewer
+     *            the viewer the editor support is attached to
+     */
+    public this(ColumnViewer viewer) {
+        this.viewer = viewer;
+    }
+
+    /**
+     * @param event
+     *            the event triggering the action
+     * @return <code>true</code> if this event should open the editor
+     */
+    protected bool isEditorActivationEvent(
+            ColumnViewerEditorActivationEvent event) {
+        bool singleSelect = (cast(IStructuredSelection)viewer.getSelection()).size() is 1;
+        bool isLeftMouseSelect = event.eventType is ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION && (cast(MouseEvent)event.sourceEvent).button is 1;
+
+        return singleSelect && (isLeftMouseSelect
+                || event.eventType is ColumnViewerEditorActivationEvent.PROGRAMMATIC
+                || event.eventType is ColumnViewerEditorActivationEvent.TRAVERSAL);
+    }
+    package bool isEditorActivationEvent_package(ColumnViewerEditorActivationEvent event){
+        return isEditorActivationEvent(event);
+    }
+
+    /**
+     * @return the cell holding the current focus
+     */
+    private ViewerCell getFocusCell() {
+        return viewer.getColumnViewerEditor().getFocusCell();
+    }
+
+    /**
+     * @return the viewer
+     */
+    public ColumnViewer getViewer() {
+        return viewer;
+    }
+
+    /**
+     * Enable activation of cell editors by keyboard
+     *
+     * @param enable
+     *            <code>true</code> to enable
+     */
+    public void setEnableEditorActivationWithKeyboard(bool enable) {
+        if (enable) {
+            if (keyboardActivationListener is null) {
+                keyboardActivationListener = new class KeyListener {
+
+                    public void keyPressed(KeyEvent e) {
+                        ViewerCell cell = getFocusCell();
+
+                        if (cell !is null) {
+                            viewer
+                                    .triggerEditorActivationEvent_package(new ColumnViewerEditorActivationEvent(
+                                            cell, e));
+                        }
+                    }
+
+                    public void keyReleased(KeyEvent e) {
+
+                    }
+
+                };
+                viewer.getControl().addKeyListener(keyboardActivationListener);
+            }
+        } else {
+            if (keyboardActivationListener !is null) {
+                viewer.getControl().removeKeyListener(
+                        keyboardActivationListener);
+                keyboardActivationListener = null;
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewerEditorDeactivationEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                                 fixes in bug: 178946
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.ColumnViewerEditorDeactivationEvent;
+
+import java.lang.all;
+import java.util.EventObject;
+
+/**
+ * This event is fired when an editor deactivated
+ *
+ * @since 3.3
+ * @noextend This class is not intended to be subclassed by clients.
+ *
+ */
+public class ColumnViewerEditorDeactivationEvent : EventObject {
+
+    /**
+     *
+     */
+    private static const long serialVersionUID = 1L;
+
+    /**
+     * The event type
+     * @since 3.4
+     */
+    public int eventType;
+
+    /**
+     * Event when editor is canceled
+     * @since 3.4
+     */
+    public static final int EDITOR_CANCELED = 1;
+
+    /**
+     * Event when editor is saved
+     * @since 3.4
+     */
+    public static final int EDITOR_SAVED = 2;
+
+    /**
+     * @param source
+     */
+    public this(Object source) {
+        super(source);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewerToolTipSupport.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                                 bugfix in: 195137, 198089
+ *     Fredy Dobler <fredy@dobler.net> - bug 159600
+ *     Brock Janiczak <brockj@tpg.com.au> - bug 182443
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
+
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ViewerRow;
+import org.eclipse.jface.viewers.ViewerColumn;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.CellLabelProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.jface.util.Policy;
+import org.eclipse.jface.window.DefaultToolTip;
+import org.eclipse.jface.window.ToolTip;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The ColumnViewerTooltipSupport is the class that provides tool tips for
+ * ColumnViewers.
+ *
+ * @since 3.3
+ *
+ */
+public class ColumnViewerToolTipSupport : DefaultToolTip {
+    private ColumnViewer viewer;
+
+    private static const String VIEWER_CELL_KEY = Policy.JFACE
+            ~ "_VIEWER_CELL_KEY"; //$NON-NLS-1$
+
+    private static const int DEFAULT_SHIFT_X = 10;
+
+    private static const int DEFAULT_SHIFT_Y = 0;
+
+    /**
+     * Enable ToolTip support for the viewer by creating an instance from this
+     * class. To get all necessary informations this support class consults the
+     * {@link CellLabelProvider}.
+     *
+     * @param viewer
+     *            the viewer the support is attached to
+     * @param style
+     *            style passed to control tool tip behavior
+     *
+     * @param manualActivation
+     *            <code>true</code> if the activation is done manually using
+     *            {@link #show(Point)}
+     */
+    protected this(ColumnViewer viewer, int style,
+            bool manualActivation) {
+        super(viewer.getControl(), style, manualActivation);
+        this.viewer = viewer;
+    }
+
+    /**
+     * Enable ToolTip support for the viewer by creating an instance from this
+     * class. To get all necessary informations this support class consults the
+     * {@link CellLabelProvider}.
+     *
+     * @param viewer
+     *            the viewer the support is attached to
+     */
+    public static void enableFor(ColumnViewer viewer) {
+        new ColumnViewerToolTipSupport(viewer, ToolTip.NO_RECREATE, false);
+    }
+
+    /**
+     * Enable ToolTip support for the viewer by creating an instance from this
+     * class. To get all necessary informations this support class consults the
+     * {@link CellLabelProvider}.
+     *
+     * @param viewer
+     *            the viewer the support is attached to
+     * @param style
+     *            style passed to control tool tip behavior
+     *
+     * @see ToolTip#RECREATE
+     * @see ToolTip#NO_RECREATE
+     */
+    public static void enableFor(ColumnViewer viewer, int style) {
+        new ColumnViewerToolTipSupport(viewer, style, false);
+    }
+
+    protected override Object getToolTipArea(Event event) {
+        return viewer.getCell(new Point(event.x, event.y));
+    }
+
+    /**
+     * Instead of overwriting this method subclasses should overwrite
+     * {@link #createViewerToolTipContentArea(Event, ViewerCell, Composite)}
+     */
+    protected Composite createToolTipContentArea(Event event, Composite parent) {
+        ViewerCell cell = cast(ViewerCell) getData(VIEWER_CELL_KEY);
+        setData(VIEWER_CELL_KEY, null);
+
+        return createViewerToolTipContentArea(event, cell, parent);
+    }
+
+    /**
+     * Creates the content area of the tool tip giving access to the cell the
+     * tip is shown for. Subclasses can overload this method to implement their
+     * own tool tip design.
+     *
+     * <p>
+     * This method is called from
+     * {@link #createToolTipContentArea(Event, Composite)} and by default calls
+     * the {@link DefaultToolTip#createToolTipContentArea(Event, Composite)}.
+     * </p>
+     *
+     * @param event
+     *            the event that which
+     * @param cell
+     *            the cell the tool tip is shown for
+     * @param parent
+     *            the parent of the control to create
+     * @return the control to be displayed in the tool tip area
+     * @since 3.4
+     */
+    protected Composite createViewerToolTipContentArea(Event event,
+            ViewerCell cell, Composite parent) {
+        return super.createToolTipContentArea(event, parent);
+    }
+
+    protected override bool shouldCreateToolTip(Event event) {
+        if (!super.shouldCreateToolTip(event)) {
+            return false;
+        }
+
+        bool rv = false;
+
+        ViewerRow row = viewer.getViewerRow_package(new Point(event.x, event.y));
+
+        viewer.getControl().setToolTipText(""); //$NON-NLS-1$
+        Point point = new Point(event.x, event.y);
+
+        if (row !is null) {
+            Object element = row.getItem().getData();
+
+            ViewerCell cell = row.getCell(point);
+            ViewerColumn viewPart = viewer.getViewerColumn(cell
+                    .getColumnIndex());
+
+            if (viewPart is null) {
+                return false;
+            }
+
+            CellLabelProvider labelProvider = viewPart.getLabelProvider();
+            bool useNative = labelProvider.useNativeToolTip(element);
+
+            String text = labelProvider.getToolTipText(element);
+            Image img = null;
+
+            if (!useNative) {
+                img = labelProvider.getToolTipImage(element);
+            }
+
+            if (useNative || (text is null && img is null)) {
+                viewer.getControl().setToolTipText(text);
+                rv = false;
+            } else {
+                setPopupDelay(labelProvider.getToolTipDisplayDelayTime(element));
+                setHideDelay(labelProvider.getToolTipTimeDisplayed(element));
+
+                Point shift = labelProvider.getToolTipShift(element);
+
+                if (shift is null) {
+                    setShift(new Point(DEFAULT_SHIFT_X, DEFAULT_SHIFT_Y));
+                } else {
+                    setShift(new Point(shift.x, shift.y));
+                }
+
+                setData(VIEWER_CELL_KEY, cell);
+
+                setText(text);
+                setImage(img);
+                setStyle(labelProvider.getToolTipStyle(element));
+                setForegroundColor(labelProvider
+                        .getToolTipForegroundColor(element));
+                setBackgroundColor(labelProvider
+                        .getToolTipBackgroundColor(element));
+                setFont(labelProvider.getToolTipFont(element));
+
+                // Check if at least one of the values is set
+                rv = getText(event) !is null || getImage(event) !is null;
+            }
+        }
+
+        return rv;
+    }
+
+    protected override void afterHideToolTip(Event event) {
+        super.afterHideToolTip(event);
+        // Clear the restored value else this could be a source of a leak
+        setData(VIEWER_CELL_KEY, null);
+        if (event !is null && event.widget !is viewer.getControl()) {
+            viewer.getControl().setFocus();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnWeightData.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.ColumnWeightData;
+
+import org.eclipse.jface.viewers.ColumnLayoutData;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+
+/**
+ * Describes the width of a table column in terms of a weight,
+ * a minimum width, and whether the column is resizable.
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ColumnWeightData : ColumnLayoutData {
+
+    /**
+     * Default width of a column (in pixels).
+     */
+    public static const int MINIMUM_WIDTH = 20;
+
+    /**
+     * The column's minimum width in pixels.
+     */
+    public int minimumWidth;
+
+    /**
+     * The column's weight.
+     */
+    public int weight;
+
+    /**
+     * Creates a resizable column width with the given weight and a default
+     * minimum width.
+     *
+     * @param weight the weight of the column
+     */
+    public this(int weight) {
+        this(weight, true);
+    }
+
+    /**
+     * Creates a resizable column width with the given weight and minimum width.
+     *
+     * @param weight the weight of the column
+     * @param minimumWidth the minimum width of the column in pixels
+     */
+    public this(int weight, int minimumWidth) {
+        this(weight, minimumWidth, true);
+    }
+
+    /**
+     * Creates a column width with the given weight and minimum width.
+     *
+     * @param weight the weight of the column
+     * @param minimumWidth the minimum width of the column in pixels
+     * @param resizable <code>true</code> if the column is resizable,
+     *   and <code>false</code> if size of the column is fixed
+     */
+    public this(int weight, int minimumWidth, bool resizable) {
+        super(resizable);
+        Assert.isTrue(weight >= 0);
+        Assert.isTrue(minimumWidth >= 0);
+        this.weight = weight;
+        this.minimumWidth = minimumWidth;
+    }
+
+    /**
+     * Creates a column width with the given weight and a default
+     * minimum width.
+     *
+     * @param weight the weight of the column
+     * @param resizable <code>true</code> if the column is resizable,
+     *   and <code>false</code> if size of the column is fixed
+     */
+    public this(int weight, bool resizable) {
+        this(weight, MINIMUM_WIDTH, resizable);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ComboBoxCellEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,306 @@
+/*******************************************************************************
+ * 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - bugfix in 174739
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.ComboBoxCellEditor;
+
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.AbstractComboBoxCellEditor;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Set;
+import tango.text.convert.Format;
+
+/**
+ * A cell editor that presents a list of items in a combo box. The cell editor's
+ * value is the zero-based index of the selected item.
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ComboBoxCellEditor : AbstractComboBoxCellEditor {
+
+    /**
+     * The list of items to present in the combo box.
+     */
+    private String[] items;
+
+    /**
+     * The zero-based index of the selected item.
+     */
+    int selection;
+
+    /**
+     * The custom combo box control.
+     */
+    CCombo comboBox;
+
+    /**
+     * Default ComboBoxCellEditor style
+     */
+    private static const int defaultStyle = SWT.NONE;
+
+    /**
+     * Creates a new cell editor with no control and no st of choices.
+     * Initially, the cell editor has no cell validator.
+     *
+     * @since 2.1
+     * @see CellEditor#setStyle
+     * @see CellEditor#create
+     * @see ComboBoxCellEditor#setItems
+     * @see CellEditor#dispose
+     */
+    public this() {
+        setStyle(defaultStyle);
+    }
+
+    /**
+     * Creates a new cell editor with a combo containing the given list of
+     * choices and parented under the given control. The cell editor value is
+     * the zero-based index of the selected item. Initially, the cell editor has
+     * no cell validator and the first item in the list is selected.
+     *
+     * @param parent
+     *            the parent control
+     * @param items
+     *            the list of strings for the combo box
+     */
+    public this(Composite parent, String[] items) {
+        this(parent, items, defaultStyle);
+    }
+
+    /**
+     * Creates a new cell editor with a combo containing the given list of
+     * choices and parented under the given control. The cell editor value is
+     * the zero-based index of the selected item. Initially, the cell editor has
+     * no cell validator and the first item in the list is selected.
+     *
+     * @param parent
+     *            the parent control
+     * @param items
+     *            the list of strings for the combo box
+     * @param style
+     *            the style bits
+     * @since 2.1
+     */
+    public this(Composite parent, String[] items, int style) {
+        super(parent, style);
+        setItems(items);
+    }
+
+    /**
+     * Returns the list of choices for the combo box
+     *
+     * @return the list of choices for the combo box
+     */
+    public String[] getItems() {
+        return this.items;
+    }
+
+    /**
+     * Sets the list of choices for the combo box
+     *
+     * @param items
+     *            the list of choices for the combo box
+     */
+    public void setItems(String[] items) {
+//         Assert.isNotNull(items);
+        this.items = items;
+        populateComboBoxItems();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on CellEditor.
+     */
+    protected override Control createControl(Composite parent) {
+
+        comboBox = new CCombo(parent, getStyle());
+        comboBox.setFont(parent.getFont());
+
+        populateComboBoxItems();
+
+        comboBox.addKeyListener(new class KeyAdapter {
+            // hook key pressed - see PR 14201
+            public void keyPressed(KeyEvent e) {
+                keyReleaseOccured(e);
+            }
+        });
+
+        comboBox.addSelectionListener(new class SelectionAdapter {
+            public void widgetDefaultSelected(SelectionEvent event) {
+                applyEditorValueAndDeactivate();
+            }
+
+            public void widgetSelected(SelectionEvent event) {
+                selection = comboBox.getSelectionIndex();
+            }
+        });
+
+        comboBox.addTraverseListener(new class TraverseListener {
+            public void keyTraversed(TraverseEvent e) {
+                if (e.detail is SWT.TRAVERSE_ESCAPE
+                        || e.detail is SWT.TRAVERSE_RETURN) {
+                    e.doit = false;
+                }
+            }
+        });
+
+        comboBox.addFocusListener(new class FocusAdapter {
+            public void focusLost(FocusEvent e) {
+                this.outer.focusLost();
+            }
+        });
+        return comboBox;
+    }
+
+    /**
+     * The <code>ComboBoxCellEditor</code> implementation of this
+     * <code>CellEditor</code> framework method returns the zero-based index
+     * of the current selection.
+     *
+     * @return the zero-based index of the current selection wrapped as an
+     *         <code>Integer</code>
+     */
+    protected override Object doGetValue() {
+        return new ValueWrapperInt(selection);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on CellEditor.
+     */
+    protected override void doSetFocus() {
+        comboBox.setFocus();
+    }
+
+    /**
+     * The <code>ComboBoxCellEditor</code> implementation of this
+     * <code>CellEditor</code> framework method sets the minimum width of the
+     * cell. The minimum width is 10 characters if <code>comboBox</code> is
+     * not <code>null</code> or <code>disposed</code> else it is 60 pixels
+     * to make sure the arrow button and some text is visible. The list of
+     * CCombo will be wide enough to show its longest item.
+     */
+    public override LayoutData getLayoutData() {
+        LayoutData layoutData = super.getLayoutData();
+        if ((comboBox is null) || comboBox.isDisposed()) {
+            layoutData.minimumWidth = 60;
+        } else {
+            // make the comboBox 10 characters wide
+            GC gc = new GC(comboBox);
+            layoutData.minimumWidth = (gc.getFontMetrics()
+                    .getAverageCharWidth() * 10) + 10;
+            gc.dispose();
+        }
+        return layoutData;
+    }
+
+    /**
+     * The <code>ComboBoxCellEditor</code> implementation of this
+     * <code>CellEditor</code> framework method accepts a zero-based index of
+     * a selection.
+     *
+     * @param value
+     *            the zero-based index of the selection wrapped as an
+     *            <code>Integer</code>
+     */
+    protected override void doSetValue(Object value) {
+        Assert.isTrue(comboBox !is null && (cast(ValueWrapperInt)value ));
+        selection = (cast(ValueWrapperInt) value).value;
+        comboBox.select(selection);
+    }
+
+    /**
+     * Updates the list of choices for the combo box for the current control.
+     */
+    private void populateComboBoxItems() {
+        if (comboBox !is null && items !is null) {
+            comboBox.removeAll();
+            for (int i = 0; i < items.length; i++) {
+                comboBox.add(items[i], i);
+            }
+
+            setValueValid(true);
+            selection = 0;
+        }
+    }
+
+    /**
+     * Applies the currently selected value and deactivates the cell editor
+     */
+    void applyEditorValueAndDeactivate() {
+        // must set the selection before getting value
+        selection = comboBox.getSelectionIndex();
+        Object newValue = doGetValue();
+        markDirty();
+        bool isValid = isCorrect(newValue);
+        setValueValid(isValid);
+
+        if (!isValid) {
+            // Only format if the 'index' is valid
+            if (items.length > 0 && selection >= 0 && selection < items.length) {
+                // try to insert the current value into the error message.
+                setErrorMessage(Format(getErrorMessage(),
+                        [ items[selection] ]));
+            } else {
+                // Since we don't have a valid index, assume we're using an
+                // 'edit'
+                // combo so format using its text value
+                setErrorMessage(Format(getErrorMessage(),
+                        [ comboBox.getText() ]));
+            }
+        }
+
+        fireApplyEditorValue();
+        deactivate();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.CellEditor#focusLost()
+     */
+    protected override void focusLost() {
+        if (isActivated()) {
+            applyEditorValueAndDeactivate();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.CellEditor#keyReleaseOccured(org.eclipse.swt.events.KeyEvent)
+     */
+    protected override void keyReleaseOccured(KeyEvent keyEvent) {
+        if (keyEvent.character is '\u001b') { // Escape character
+            fireCancelEditor();
+        } else if (keyEvent.character is '\t') { // tab key
+            applyEditorValueAndDeactivate();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ComboBoxViewerCellEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,279 @@
+/*******************************************************************************
+ * Copyright (c) 2006 Tom Schindl and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                                 bugfix in 174739
+ *     Eric Rizzo - bug 213315
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.ComboBoxViewerCellEditor;
+
+import org.eclipse.jface.viewers.AbstractComboBoxCellEditor;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A cell editor that presents a list of items in a combo box. In contrast to
+ * {@link ComboBoxCellEditor} it wraps the underlying {@link CCombo} using a
+ * {@link ComboViewer}
+ * @since 3.4
+ */
+public class ComboBoxViewerCellEditor : AbstractComboBoxCellEditor {
+
+    /**
+     * The custom combo box control.
+     */
+    ComboViewer viewer;
+
+    Object selectedValue;
+
+    /**
+     * Default ComboBoxCellEditor style
+     */
+    private static const int defaultStyle = SWT.NONE;
+
+    /**
+     * Creates a new cell editor with a combo viewer and a default style
+     *
+     * @param parent
+     *            the parent control
+     */
+    public this(Composite parent) {
+        this(parent, defaultStyle);
+    }
+
+    /**
+     * Creates a new cell editor with a combo viewer and the given style
+     *
+     * @param parent
+     *            the parent control
+     * @param style
+     *            the style bits
+     */
+    public this(Composite parent, int style) {
+        super(parent, style);
+        setValueValid(true);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on CellEditor.
+     */
+    protected Control createControl(Composite parent) {
+
+        CCombo comboBox = new CCombo(parent, getStyle());
+        comboBox.setFont(parent.getFont());
+        viewer = new ComboViewer(comboBox);
+
+        comboBox.addKeyListener(new class KeyAdapter {
+            // hook key pressed - see PR 14201
+            public void keyPressed(KeyEvent e) {
+                keyReleaseOccured(e);
+            }
+        });
+
+        comboBox.addSelectionListener(new class SelectionAdapter {
+            public void widgetDefaultSelected(SelectionEvent event) {
+                applyEditorValueAndDeactivate();
+            }
+
+            public void widgetSelected(SelectionEvent event) {
+                ISelection selection = viewer.getSelection();
+                if (selection.isEmpty()) {
+                    selectedValue = null;
+                } else {
+                    selectedValue = (cast(IStructuredSelection) selection)
+                            .getFirstElement();
+                }
+            }
+        });
+
+        comboBox.addTraverseListener(new class TraverseListener {
+            public void keyTraversed(TraverseEvent e) {
+                if (e.detail is SWT.TRAVERSE_ESCAPE
+                        || e.detail is SWT.TRAVERSE_RETURN) {
+                    e.doit = false;
+                }
+            }
+        });
+
+        comboBox.addFocusListener(new class FocusAdapter {
+            public void focusLost(FocusEvent e) {
+                this.outer.focusLost();
+            }
+        });
+        return comboBox;
+    }
+
+    /**
+     * The <code>ComboBoxCellEditor</code> implementation of this
+     * <code>CellEditor</code> framework method returns the zero-based index
+     * of the current selection.
+     *
+     * @return the zero-based index of the current selection wrapped as an
+     *         <code>Integer</code>
+     */
+    protected Object doGetValue() {
+        return selectedValue;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on CellEditor.
+     */
+    protected void doSetFocus() {
+        viewer.getControl().setFocus();
+    }
+
+    /**
+     * The <code>ComboBoxCellEditor</code> implementation of this
+     * <code>CellEditor</code> framework method sets the minimum width of the
+     * cell. The minimum width is 10 characters if <code>comboBox</code> is
+     * not <code>null</code> or <code>disposed</code> eles it is 60 pixels
+     * to make sure the arrow button and some text is visible. The list of
+     * CCombo will be wide enough to show its longest item.
+     */
+    public LayoutData getLayoutData() {
+        LayoutData layoutData = super.getLayoutData();
+        if ((viewer.getControl() is null) || viewer.getControl().isDisposed()) {
+            layoutData.minimumWidth = 60;
+        } else {
+            // make the comboBox 10 characters wide
+            GC gc = new GC(viewer.getControl());
+            layoutData.minimumWidth = (gc.getFontMetrics()
+                    .getAverageCharWidth() * 10) + 10;
+            gc.dispose();
+        }
+        return layoutData;
+    }
+
+    /**
+     * Set a new value
+     *
+     * @param value
+     *            the new value
+     */
+    protected void doSetValue(Object value) {
+        Assert.isTrue(viewer !is null);
+        selectedValue = value;
+        if (value is null) {
+            viewer.setSelection(StructuredSelection.EMPTY);
+        } else {
+            viewer.setSelection(new StructuredSelection(value));
+        }
+    }
+
+    /**
+     * @param labelProvider
+     *            the label provider used
+     * @see StructuredViewer#setLabelProvider(IBaseLabelProvider)
+     */
+    public void setLabelProvider(IBaseLabelProvider labelProvider) {
+        viewer.setLabelProvider(labelProvider);
+    }
+
+    /**
+     * @param provider
+     *            the content provider used
+     * @see StructuredViewer#setContentProvider(IContentProvider)
+     */
+    public void setContenProvider(IStructuredContentProvider provider) {
+        viewer.setContentProvider(provider);
+    }
+
+    /**
+     * @param input
+     *            the input used
+     * @see StructuredViewer#setInput(Object)
+     */
+    public void setInput(Object input) {
+        viewer.setInput(input);
+    }
+
+    /**
+     * @return get the viewer
+     */
+    public ComboViewer getViewer() {
+        return viewer;
+    }
+
+    /**
+     * Applies the currently selected value and deactiavates the cell editor
+     */
+    void applyEditorValueAndDeactivate() {
+        // must set the selection before getting value
+        ISelection selection = viewer.getSelection();
+        if (selection.isEmpty()) {
+            selectedValue = null;
+        } else {
+            selectedValue = (cast(IStructuredSelection) selection)
+                    .getFirstElement();
+        }
+
+        Object newValue = doGetValue();
+        markDirty();
+        bool isValid = isCorrect(newValue);
+        setValueValid(isValid);
+
+        if (!isValid) {
+        //SWT: the result is not used?
+//             MessageFormat.format(getErrorMessage(),
+//                     [ selectedValue ]);
+        }
+
+        fireApplyEditorValue();
+        deactivate();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.CellEditor#focusLost()
+     */
+    protected void focusLost() {
+        if (isActivated()) {
+            applyEditorValueAndDeactivate();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.CellEditor#keyReleaseOccured(org.eclipse.swt.events.KeyEvent)
+     */
+    protected void keyReleaseOccured(KeyEvent keyEvent) {
+        if (keyEvent.character is '\u001b') { // Escape character
+            fireCancelEditor();
+        } else if (keyEvent.character is '\t') { // tab key
+            applyEditorValueAndDeactivate();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ComboViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * 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
+ *     Sebastian Davids - bug 69254
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.ComboViewer;
+
+import org.eclipse.jface.viewers.AbstractListViewer;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A concrete viewer based either on an SWT <code>Combo</code> control or <code>CCombo</code>
+ * control. This class is intended as an alternative to the JFace <code>ListViewer</code>, which displays
+ * its content in a combo box rather than a list. Wherever possible, this class attempts to behave
+ * like ListViewer. <p>
+ *
+ * This class is designed to be instantiated with a pre-existing SWT combo control
+ * and configured with a domain-specific content provider, label provider, element
+ * filter (optional), and element sorter (optional).
+ * </p>
+ *
+ * @see org.eclipse.jface.viewers.ListViewer
+ * @since 3.0 (made non-final in 3.4)
+ */
+public class ComboViewer : AbstractListViewer {
+
+    /**
+     * This viewer's list control if this viewer is instantiated with a combo control; otherwise
+     * <code>null</code>.
+     *
+     * @see #ComboViewer(Combo)
+     */
+    private Combo combo;
+
+    /**
+     * This viewer's list control if this viewer is instantiated with a CCombo control; otherwise
+     * <code>null</code>.
+     *
+     * @see #ComboViewer(CCombo)
+     * @since 3.3
+     */
+    private CCombo ccombo;
+
+    /**
+     * Creates a combo viewer on a newly-created combo control under the given parent.
+     * The viewer has no input, no content provider, a default label provider,
+     * no sorter, and no filters.
+     *
+     * @param parent the parent control
+     */
+    public this(Composite parent) {
+        this(parent, SWT.READ_ONLY | SWT.BORDER);
+    }
+
+    /**
+     * Creates a combo viewer on a newly-created combo control under the given parent.
+     * The combo control is created using the given SWT style bits.
+     * The viewer has no input, no content provider, a default label provider,
+     * no sorter, and no filters.
+     *
+     * @param parent the parent control
+     * @param style the SWT style bits
+     */
+    public this(Composite parent, int style) {
+        this(new Combo(parent, style));
+    }
+
+    /**
+     * Creates a combo viewer on the given combo control.
+     * The viewer has no input, no content provider, a default label provider,
+     * no sorter, and no filters.
+     *
+     * @param list the combo control
+     */
+    public this(Combo list) {
+        this.combo = list;
+        hookControl(list);
+    }
+
+    /**
+     * Creates a combo viewer on the given CCombo control.
+     * The viewer has no input, no content provider, a default label provider,
+     * no sorter, and no filters.
+     *
+     * @param list the CCombo control
+     * @since 3.3
+     */
+    public this(CCombo list) {
+        this.ccombo = list;
+        hookControl(list);
+    }
+
+    protected override void listAdd(String string, int index) {
+        if (combo is null) {
+            ccombo.add(string, index);
+        } else {
+            combo.add(string, index);
+        }
+    }
+
+    protected override void listSetItem(int index, String string) {
+        if (combo is null) {
+            ccombo.setItem(index, string);
+        } else {
+            combo.setItem(index, string);
+        }
+    }
+
+    protected override int[] listGetSelectionIndices() {
+        if (combo is null) {
+            return [ ccombo.getSelectionIndex() ];
+        } else {
+            return [ combo.getSelectionIndex() ];
+        }
+    }
+
+    protected override int listGetItemCount() {
+        if (combo is null) {
+            return ccombo.getItemCount();
+        } else {
+            return combo.getItemCount();
+        }
+    }
+
+    protected override void listSetItems(String[] labels) {
+        if (combo is null) {
+            ccombo.setItems(labels);
+        } else {
+            combo.setItems(labels);
+        }
+    }
+
+    protected override void listRemoveAll() {
+        if (combo is null) {
+            ccombo.removeAll();
+        } else {
+            combo.removeAll();
+        }
+    }
+
+    protected override void listRemove(int index) {
+        if (combo is null) {
+            ccombo.remove(index);
+        } else {
+            combo.remove(index);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on Viewer.
+     */
+    public override Control getControl() {
+        if (combo is null) {
+            return ccombo;
+        } else {
+            return combo;
+        }
+    }
+
+    /**
+     * Returns this list viewer's list control. If the viewer was not created on
+     * a CCombo control, some kind of unchecked exception is thrown.
+     *
+     * @return the list control
+     * @since 3.3
+     */
+    public CCombo getCCombo() {
+        Assert.isNotNull(ccombo);
+        return ccombo;
+    }
+
+    /**
+     * Returns this list viewer's list control. If the viewer was not created on
+     * a Combo control, some kind of unchecked exception is thrown.
+     *
+     * @return the list control
+     */
+    public Combo getCombo() {
+        Assert.isNotNull(combo);
+        return combo;
+    }
+
+    /*
+     * Do nothing -- combos only display the selected element, so there is no way
+     * we can ensure that the given element is visible without changing the selection.
+     * Method defined on StructuredViewer.
+     */
+    public override void reveal(Object element) {
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#listSetSelection(int[])
+     */
+    protected override void listSetSelection(int[] ixs) {
+        if (combo is null) {
+            for (int idx = 0; idx < ixs.length; idx++) {
+                ccombo.select(ixs[idx]);
+            }
+        } else {
+            for (int idx = 0; idx < ixs.length; idx++) {
+                combo.select(ixs[idx]);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#listDeselectAll()
+     */
+    protected override void listDeselectAll() {
+        if (combo is null) {
+            ccombo.deselectAll();
+            ccombo.clearSelection();
+        } else {
+            combo.deselectAll();
+            combo.clearSelection();
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#listShowSelection()
+     */
+    protected override void listShowSelection() {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ContentViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,311 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.ContentViewer;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.LabelProviderChangedEvent;
+import org.eclipse.jface.viewers.LabelProvider;
+
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A content viewer is a model-based adapter on a widget which accesses its
+ * model by means of a content provider and a label provider.
+ * <p>
+ * A viewer's model consists of elements, represented by objects.
+ * A viewer defines and implements generic infrastructure for handling model
+ * input, updates, and selections in terms of elements.
+ * Input is obtained by querying an <code>IContentProvider</code> which returns
+ * elements. The elements themselves are not displayed directly.  They are
+ * mapped to labels, containing text and/or an image, using the viewer's
+ * <code>ILabelProvider</code>.
+ * </p>
+ * <p>
+ * Implementing a concrete content viewer typically involves the following steps:
+ * <ul>
+ * <li>
+ * create SWT controls for viewer (in constructor) (optional)
+ * </li>
+ * <li>
+ * initialize SWT controls from input (inputChanged)
+ * </li>
+ * <li>
+ * define viewer-specific update methods
+ * </li>
+ * <li>
+ * support selections (<code>setSelection</code>, <code>getSelection</code>)
+ * </ul>
+ * </p>
+ */
+public abstract class ContentViewer : Viewer {
+
+    /**
+     * This viewer's content provider, or <code>null</code> if none.
+     */
+    private IContentProvider contentProvider = null;
+
+    /**
+     * This viewer's input, or <code>null</code> if none.
+     * The viewer's input provides the "model" for the viewer's content.
+     */
+    private Object input = null;
+
+    /**
+     * This viewer's label provider. Initially <code>null</code>, but
+     * lazily initialized (to a <code>SimpleLabelProvider</code>).
+     */
+    private IBaseLabelProvider labelProvider = null;
+
+    /**
+     * This viewer's label provider listener.
+     * Note: Having a viewer register a label provider listener with
+     * a label provider avoids having to define public methods
+     * for internal events.
+     */
+    private const ILabelProviderListener labelProviderListener;
+
+    /**
+     * Creates a content viewer with no input, no content provider, and a
+     * default label provider.
+     */
+    protected this() {
+        labelProviderListener = new class ILabelProviderListener {
+            public void labelProviderChanged(LabelProviderChangedEvent event) {
+                this.outer.handleLabelProviderChanged(event);
+            }
+        };
+    }
+
+    /**
+     * Returns the content provider used by this viewer,
+     * or <code>null</code> if this view does not yet have a content
+     * provider.
+     * <p>
+     * The <code>ContentViewer</code> implementation of this method returns the content
+     * provider recorded is an internal state variable.
+     * Overriding this method is generally not required;
+     * however, if overriding in a subclass,
+     * <code>super.getContentProvider</code> must be invoked.
+     * </p>
+     *
+     * @return the content provider, or <code>null</code> if none
+     */
+    public IContentProvider getContentProvider() {
+        return contentProvider;
+    }
+
+    /**
+     * The <code>ContentViewer</code> implementation of this <code>IInputProvider</code>
+     * method returns the current input of this viewer, or <code>null</code>
+     * if none. The viewer's input provides the "model" for the viewer's
+     * content.
+     */
+    public override Object getInput() {
+        return input;
+    }
+
+    /**
+     * Returns the label provider used by this viewer.
+     * <p>
+     * The <code>ContentViewer</code> implementation of this method returns the label
+     * provider recorded in an internal state variable; if none has been
+     * set (with <code>setLabelProvider</code>) a default label provider
+     * will be created, remembered, and returned.
+     * Overriding this method is generally not required;
+     * however, if overriding in a subclass,
+     * <code>super.getLabelProvider</code> must be invoked.
+     * </p>
+     *
+     * @return a label provider
+     */
+    public IBaseLabelProvider getLabelProvider() {
+        if (labelProvider is null) {
+            labelProvider = new LabelProvider();
+        }
+        return labelProvider;
+    }
+
+    /**
+     * Handles a dispose event on this viewer's control.
+     * <p>
+     * The <code>ContentViewer</code> implementation of this method disposes of this
+     * viewer's label provider and content provider (if it has one).
+     * Subclasses should override this method to perform any additional
+     * cleanup of resources; however, overriding methods must invoke
+     * <code>super.handleDispose</code>.
+     * </p>
+     *
+     * @param event a dispose event
+     */
+    protected void handleDispose(DisposeEvent event) {
+        if (contentProvider !is null) {
+            contentProvider.inputChanged(this, getInput(), null);
+            contentProvider.dispose();
+            contentProvider = null;
+        }
+        if (labelProvider !is null) {
+            labelProvider.removeListener(labelProviderListener);
+            labelProvider.dispose();
+            labelProvider = null;
+        }
+        input = null;
+    }
+
+    /**
+     * Handles a label provider changed event.
+     * <p>
+     * The <code>ContentViewer</code> implementation of this method calls <code>labelProviderChanged()</code>
+     * to cause a complete refresh of the viewer.
+     * Subclasses may reimplement or extend.
+     * </p>
+     * @param event the change event
+     */
+    protected void handleLabelProviderChanged(LabelProviderChangedEvent event) {
+        labelProviderChanged();
+    }
+
+    /**
+     * Adds event listener hooks to the given control.
+     * <p>
+     * All subclasses must call this method when their control is
+     * first established.
+     * </p>
+     * <p>
+     * The <code>ContentViewer</code> implementation of this method hooks
+     * dispose events for the given control.
+     * Subclasses may override if they need to add other control hooks;
+     * however, <code>super.hookControl</code> must be invoked.
+     * </p>
+     *
+     * @param control the control
+     */
+    protected void hookControl(Control control) {
+        control.addDisposeListener(new class DisposeListener {
+            public void widgetDisposed(DisposeEvent event) {
+                handleDispose(event);
+            }
+        });
+    }
+
+    /**
+     * Notifies that the label provider has changed.
+     * <p>
+     * The <code>ContentViewer</code> implementation of this method calls <code>refresh()</code>.
+     * Subclasses may reimplement or extend.
+     * </p>
+     */
+    protected void labelProviderChanged() {
+        refresh();
+    }
+
+    /**
+     * Sets the content provider used by this viewer.
+     * <p>
+     * The <code>ContentViewer</code> implementation of this method records the
+     * content provider in an internal state variable.
+     * Overriding this method is generally not required;
+     * however, if overriding in a subclass,
+     * <code>super.setContentProvider</code> must be invoked.
+     * </p>
+     *
+     * @param contentProvider the content provider
+     * @see #getContentProvider
+     */
+    public void setContentProvider(IContentProvider contentProvider) {
+        Assert.isNotNull(cast(Object)contentProvider);
+        IContentProvider oldContentProvider = this.contentProvider;
+        this.contentProvider = contentProvider;
+        if (oldContentProvider !is null) {
+            Object currentInput = getInput();
+            oldContentProvider.inputChanged(this, currentInput, null);
+            oldContentProvider.dispose();
+            contentProvider.inputChanged(this, null, currentInput);
+            refresh();
+        }
+    }
+
+    /**
+     * The <code>ContentViewer</code> implementation of this <code>Viewer</code>
+     * method invokes <code>inputChanged</code> on the content provider and then the
+     * <code>inputChanged</code> hook method. This method fails if this viewer does
+     * not have a content provider. Subclassers are advised to override
+     * <code>inputChanged</code> rather than this method, but may extend this method
+     * if required.
+     */
+    public override void setInput(Object input) {
+        Assert
+                .isTrue(getContentProvider() !is null,
+                        "ContentViewer must have a content provider when input is set."); //$NON-NLS-1$
+
+        Object oldInput = getInput();
+        contentProvider.inputChanged(this, oldInput, input);
+        this.input = input;
+
+        // call input hook
+        inputChanged(this.input, oldInput);
+    }
+
+    /**
+     * Sets the label provider for this viewer.
+     * <p>
+     * The <code>ContentViewer</code> implementation of this method ensures that the
+     * given label provider is connected to this viewer and the
+     * former label provider is disconnected from this viewer.
+     * Overriding this method is generally not required;
+     * however, if overriding in a subclass,
+     * <code>super.setLabelProvider</code> must be invoked.
+     * </p>
+     *
+     * @param labelProvider the label provider, or <code>null</code> if none
+     */
+    public void setLabelProvider(IBaseLabelProvider labelProvider) {
+        IBaseLabelProvider oldProvider = this.labelProvider;
+        // If it hasn't changed, do nothing.
+        // This also ensures that the provider is not disposed
+        // if set a second time.
+        if (labelProvider is oldProvider) {
+            return;
+        }
+        if (oldProvider !is null) {
+            oldProvider.removeListener(this.labelProviderListener);
+        }
+        this.labelProvider = labelProvider;
+        if (labelProvider !is null) {
+            labelProvider.addListener(this.labelProviderListener);
+        }
+        refresh();
+
+        // Dispose old provider after refresh, so that items never refer to stale images.
+        if (oldProvider !is null) {
+            internalDisposeLabelProvider(oldProvider);
+        }
+    }
+
+    /**
+     * @param oldProvider
+     * 
+     * @since 3.4
+     */
+    void internalDisposeLabelProvider(IBaseLabelProvider oldProvider) {
+        oldProvider.dispose();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/CustomHashtable.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,445 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Peter Shipton - original hashtable implementation
+ *     Nick Edgar - added element comparer support
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.CustomHashtable;
+
+import org.eclipse.jface.viewers.IElementComparer;
+// import java.util.Enumeration;
+// import java.util.NoSuchElementException;
+
+import java.lang.all;
+import java.util.Enumeration;
+import tango.core.Exception;
+static import tango.text.Text;
+alias tango.text.Text.Text!(char) StringBuffer;
+
+/**
+ * CustomHashtable associates keys with values. Keys and values cannot be null.
+ * The size of the Hashtable is the number of key/value pairs it contains.
+ * The capacity is the number of key/value pairs the Hashtable can hold.
+ * The load factor is a float value which determines how full the Hashtable
+ * gets before expanding the capacity. If the load factor of the Hashtable
+ * is exceeded, the capacity is doubled.
+ * <p>
+ * CustomHashtable allows a custom comparator and hash code provider.
+ */
+/* package */final class CustomHashtable {
+    alias Object.toHash toHash;
+
+    /**
+     * HashMapEntry is an internal class which is used to hold the entries of a Hashtable.
+     */
+    private static class HashMapEntry {
+        Object key, value;
+
+        HashMapEntry next;
+
+        this(Object theKey, Object theValue) {
+            key = theKey;
+            value = theValue;
+        }
+    }
+
+    private static final class EmptyEnumerator : Enumeration {
+        public bool hasMoreElements() {
+            return false;
+        }
+
+        public Object nextElement() {
+            throw new NoSuchElementException(null);
+        }
+    }
+
+    private class HashEnumerator : Enumeration {
+        bool key;
+
+        int start;
+
+        HashMapEntry entry;
+
+        this(bool isKey) {
+            key = isKey;
+            start = firstSlot;
+        }
+
+        public bool hasMoreElements() {
+            if (entry !is null) {
+                return true;
+            }
+            while (start <= lastSlot) {
+                if (elementData[start++] !is null) {
+                    entry = elementData[start - 1];
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public Object nextElement() {
+            if (hasMoreElements()) {
+                Object result = key ? entry.key : entry.value;
+                entry = entry.next;
+                return result;
+            } else {
+                throw new NoSuchElementException(null);
+            }
+        }
+    }
+
+    /+transient+/ int elementCount;
+
+    /+transient+/ HashMapEntry[] elementData;
+
+    private float loadFactor;
+
+    private int threshold;
+
+    /+transient+/ int firstSlot = 0;
+
+    /+transient+/ int lastSlot = -1;
+
+    /+transient+/ private IElementComparer comparer;
+
+    private static const EmptyEnumerator emptyEnumerator;
+    static this(){
+        emptyEnumerator = new EmptyEnumerator();
+    }
+
+    /**
+     * The default capacity used when not specified in the constructor.
+     */
+    public static const int DEFAULT_CAPACITY = 13;
+
+    /**
+     * Constructs a new Hashtable using the default capacity
+     * and load factor.
+     */
+    public this() {
+        this(13);
+    }
+
+    /**
+     * Constructs a new Hashtable using the specified capacity
+     * and the default load factor.
+     *
+     * @param capacity the initial capacity
+     */
+    public this(int capacity) {
+        this(capacity, null);
+    }
+
+    /**
+     * Constructs a new hash table with the default capacity and the given
+     * element comparer.
+     *
+     * @param comparer the element comparer to use to compare keys and obtain
+     *   hash codes for keys, or <code>null</code>  to use the normal
+     *   <code>equals</code> and <code>hashCode</code> methods
+     */
+    public this(IElementComparer comparer) {
+        this(DEFAULT_CAPACITY, comparer);
+    }
+
+    /**
+     * Constructs a new hash table with the given capacity and the given
+     * element comparer.
+     *
+     * @param capacity the maximum number of elements that can be added without
+     *   rehashing
+     * @param comparer the element comparer to use to compare keys and obtain
+     *   hash codes for keys, or <code>null</code>  to use the normal
+     *   <code>equals</code> and <code>hashCode</code> methods
+     */
+    public this(int capacity, IElementComparer comparer) {
+        if (capacity >= 0) {
+            elementCount = 0;
+            elementData = new HashMapEntry[capacity is 0 ? 1 : capacity];
+            firstSlot = elementData.length;
+            loadFactor = 0.75f;
+            computeMaxSize();
+        } else {
+            throw new IllegalArgumentException(null);
+        }
+        this.comparer = comparer;
+    }
+
+    /**
+     * Constructs a new hash table with enough capacity to hold all keys in the
+     * given hash table, then adds all key/value pairs in the given hash table
+     * to the new one, using the given element comparer.
+     * @param table the original hash table to copy from
+     *
+     * @param comparer the element comparer to use to compare keys and obtain
+     *   hash codes for keys, or <code>null</code>  to use the normal
+     *   <code>equals</code> and <code>hashCode</code> methods
+     */
+    public this(CustomHashtable table, IElementComparer comparer) {
+        this(table.size() * 2, comparer);
+        for (int i = table.elementData.length; --i >= 0;) {
+            HashMapEntry entry = table.elementData[i];
+            while (entry !is null) {
+                put(entry.key, entry.value);
+                entry = entry.next;
+            }
+        }
+    }
+
+    /**
+     * Returns the element comparer used  to compare keys and to obtain
+     * hash codes for keys, or <code>null</code> if no comparer has been
+     * provided.
+     *
+     * @return the element comparer or <code>null</code>
+     *
+     * @since 3.2
+     */
+    public IElementComparer getComparer() {
+        return comparer;
+    }
+
+    private void computeMaxSize() {
+        threshold = cast(int) (elementData.length * loadFactor);
+    }
+
+    /**
+     * Answers if this Hashtable contains the specified object as a key
+     * of one of the key/value pairs.
+     *
+     * @param       key the object to look for as a key in this Hashtable
+     * @return      true if object is a key in this Hashtable, false otherwise
+     */
+    public bool containsKey(Object key) {
+        return getEntry(key) !is null;
+    }
+
+    /**
+     * Answers an Enumeration on the values of this Hashtable. The
+     * results of the Enumeration may be affected if the contents
+     * of this Hashtable are modified.
+     *
+     * @return      an Enumeration of the values of this Hashtable
+     */
+    public Enumeration elements() {
+        if (elementCount is 0) {
+            return emptyEnumerator;
+        }
+        return new HashEnumerator(false);
+    }
+
+    /**
+     * Answers the value associated with the specified key in
+     * this Hashtable.
+     *
+     * @param       key the key of the value returned
+     * @return      the value associated with the specified key, null if the specified key
+     *              does not exist
+     */
+    public Object get(Object key) {
+        int index = (toHash(key) & 0x7FFFFFFF) % elementData.length;
+        HashMapEntry entry = elementData[index];
+        while (entry !is null) {
+            if (keyEquals(key, entry.key)) {
+                return entry.value;
+            }
+            entry = entry.next;
+        }
+        return null;
+    }
+
+    private HashMapEntry getEntry(Object key) {
+        int index = (toHash(key) & 0x7FFFFFFF) % elementData.length;
+        HashMapEntry entry = elementData[index];
+        while (entry !is null) {
+            if (keyEquals(key, entry.key)) {
+                return entry;
+            }
+            entry = entry.next;
+        }
+        return null;
+    }
+
+    /**
+     * Answers the hash code for the given key.
+     */
+    private hash_t toHash(Object key) {
+        if (comparer is null) {
+            return key.toHash();
+        } else {
+            return comparer.toHash(key);
+        }
+    }
+
+    /**
+     * Compares two keys for equality.
+     */
+    private bool keyEquals(Object a, Object b) {
+        if (comparer is null) {
+            return a.opEquals(b) !is 0;
+        } else {
+            return comparer.opEquals(a, b) !is 0;
+        }
+    }
+
+    /**
+     * Answers an Enumeration on the keys of this Hashtable. The
+     * results of the Enumeration may be affected if the contents
+     * of this Hashtable are modified.
+     *
+     * @return      an Enumeration of the keys of this Hashtable
+     */
+    public Enumeration keys() {
+        if (elementCount is 0) {
+            return emptyEnumerator;
+        }
+        return new HashEnumerator(true);
+    }
+
+    /**
+     * Associate the specified value with the specified key in this Hashtable.
+     * If the key already exists, the old value is replaced. The key and value
+     * cannot be null.
+     *
+     * @param       key the key to add
+     * @param       value   the value to add
+     * @return      the old value associated with the specified key, null if the key did
+     *              not exist
+     */
+    public Object put(Object key, Object value) {
+        if (key !is null && value !is null) {
+            int index = (toHash(key) & 0x7FFFFFFF) % elementData.length;
+            HashMapEntry entry = elementData[index];
+            while (entry !is null && !keyEquals(key, entry.key)) {
+                entry = entry.next;
+            }
+            if (entry is null) {
+                if (++elementCount > threshold) {
+                    rehash();
+                    index = (toHash(key) & 0x7FFFFFFF) % elementData.length;
+                }
+                if (index < firstSlot) {
+                    firstSlot = index;
+                }
+                if (index > lastSlot) {
+                    lastSlot = index;
+                }
+                entry = new HashMapEntry(key, value);
+                entry.next = elementData[index];
+                elementData[index] = entry;
+                return null;
+            }
+            Object result = entry.value;
+            entry.key = key; // important to avoid hanging onto keys that are equal but "old" -- see bug 30607
+            entry.value = value;
+            return result;
+        } else {
+            throw new NullPointerException();
+        }
+    }
+
+    /**
+     * Increases the capacity of this Hashtable. This method is sent when
+     * the size of this Hashtable exceeds the load factor.
+     */
+    private void rehash() {
+        int length = elementData.length << 1;
+        if (length is 0) {
+            length = 1;
+        }
+        firstSlot = length;
+        lastSlot = -1;
+        HashMapEntry[] newData = new HashMapEntry[length];
+        for (int i = elementData.length; --i >= 0;) {
+            HashMapEntry entry = elementData[i];
+            while (entry !is null) {
+                int index = (toHash(entry.key) & 0x7FFFFFFF) % length;
+                if (index < firstSlot) {
+                    firstSlot = index;
+                }
+                if (index > lastSlot) {
+                    lastSlot = index;
+                }
+                HashMapEntry next = entry.next;
+                entry.next = newData[index];
+                newData[index] = entry;
+                entry = next;
+            }
+        }
+        elementData = newData;
+        computeMaxSize();
+    }
+
+    /**
+     * Remove the key/value pair with the specified key from this Hashtable.
+     *
+     * @param       key the key to remove
+     * @return      the value associated with the specified key, null if the specified key
+     *              did not exist
+     */
+    public Object remove(Object key) {
+        HashMapEntry last = null;
+        int index = (toHash(key) & 0x7FFFFFFF) % elementData.length;
+        HashMapEntry entry = elementData[index];
+        while (entry !is null && !keyEquals(key, entry.key)) {
+            last = entry;
+            entry = entry.next;
+        }
+        if (entry !is null) {
+            if (last is null) {
+                elementData[index] = entry.next;
+            } else {
+                last.next = entry.next;
+            }
+            elementCount--;
+            return entry.value;
+        }
+        return null;
+    }
+
+    /**
+     * Answers the number of key/value pairs in this Hashtable.
+     *
+     * @return      the number of key/value pairs in this Hashtable
+     */
+    public int size() {
+        return elementCount;
+    }
+
+    /**
+     * Answers the string representation of this Hashtable.
+     *
+     * @return      the string representation of this Hashtable
+     */
+    public override String toString() {
+        if (size() is 0) {
+            return "{}"; //$NON-NLS-1$
+        }
+
+        StringBuffer buffer = new StringBuffer();
+        buffer.append('{');
+        for (int i = elementData.length; --i >= 0;) {
+            HashMapEntry entry = elementData[i];
+            while (entry !is null) {
+                if( buffer.length > 1 ){
+                    buffer.append(", "); //$NON-NLS-1$
+                }
+                buffer.append(entry.key.toString);
+                buffer.append('=');
+                buffer.append(entry.value.toString);
+                entry = entry.next;
+            }
+        }
+        buffer.append('}');
+        return buffer.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/DecoratingLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,424 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.DecoratingLabelProvider;
+
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IViewerLabelProvider;
+import org.eclipse.jface.viewers.IColorProvider;
+import org.eclipse.jface.viewers.IFontProvider;
+import org.eclipse.jface.viewers.ITreePathLabelProvider;
+import org.eclipse.jface.viewers.ILabelDecorator;
+import org.eclipse.jface.viewers.IDecorationContext;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ViewerLabel;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.DecorationContext;
+import org.eclipse.jface.viewers.LabelDecorator;
+import org.eclipse.jface.viewers.LabelProviderChangedEvent;
+import org.eclipse.jface.viewers.IDelayedLabelDecorator;
+import org.eclipse.jface.viewers.IColorDecorator;
+import org.eclipse.jface.viewers.IFontDecorator;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A decorating label provider is a label provider which combines
+ * a nested label provider and an optional decorator.
+ * The decorator decorates the label text, image, font and colors provided by
+ * the nested label provider.
+ */
+public class DecoratingLabelProvider : LabelProvider,
+        ILabelProvider, IViewerLabelProvider, IColorProvider, IFontProvider, ITreePathLabelProvider {
+
+    private ILabelProvider provider;
+
+    private ILabelDecorator decorator;
+
+    // Need to keep our own list of listeners
+    private ListenerList listeners;
+
+    private IDecorationContext decorationContext;
+
+    /**
+     * Creates a decorating label provider which uses the given label decorator
+     * to decorate labels provided by the given label provider.
+     *
+     * @param provider the nested label provider
+     * @param decorator the label decorator, or <code>null</code> if no decorator is to be used initially
+     */
+    public this(ILabelProvider provider,
+            ILabelDecorator decorator) {
+        decorationContext = DecorationContext.DEFAULT_CONTEXT;
+        listeners = new ListenerList();
+        Assert.isNotNull(cast(Object)provider);
+        this.provider = provider;
+        this.decorator = decorator;
+    }
+
+    /**
+     * The <code>DecoratingLabelProvider</code> implementation of this <code>IBaseLabelProvider</code> method
+     * adds the listener to both the nested label provider and the label decorator.
+     *
+     * @param listener a label provider listener
+     */
+    public override void addListener(ILabelProviderListener listener) {
+        super.addListener(listener);
+        provider.addListener(listener);
+        if (decorator !is null) {
+            decorator.addListener(listener);
+        }
+        listeners.add(cast(Object)listener);
+    }
+
+    /**
+     * The <code>DecoratingLabelProvider</code> implementation of this <code>IBaseLabelProvider</code> method
+     * disposes both the nested label provider and the label decorator.
+     */
+    public override void dispose() {
+        provider.dispose();
+        if (decorator !is null) {
+            decorator.dispose();
+        }
+    }
+
+    /**
+     * The <code>DecoratingLabelProvider</code> implementation of this
+     * <code>ILabelProvider</code> method returns the image provided
+     * by the nested label provider's <code>getImage</code> method,
+     * decorated with the decoration provided by the label decorator's
+     * <code>decorateImage</code> method.
+     */
+    public override Image getImage(Object element) {
+        Image image = provider.getImage(element);
+        if (decorator !is null) {
+            if ( auto ld2 = cast(LabelDecorator)decorator ) {
+                Image decorated = ld2.decorateImage(image, element, getDecorationContext());
+                if (decorated !is null) {
+                    return decorated;
+                }
+            } else {
+                Image decorated = decorator.decorateImage(image, element);
+                if (decorated !is null) {
+                    return decorated;
+                }
+            }
+        }
+        return image;
+    }
+
+    /**
+     * Returns the label decorator, or <code>null</code> if none has been set.
+     *
+     * @return the label decorator, or <code>null</code> if none has been set.
+     */
+    public ILabelDecorator getLabelDecorator() {
+        return decorator;
+    }
+
+    /**
+     * Returns the nested label provider.
+     *
+     * @return the nested label provider
+     */
+    public ILabelProvider getLabelProvider() {
+        return provider;
+    }
+
+    /**
+     * The <code>DecoratingLabelProvider</code> implementation of this
+     * <code>ILabelProvider</code> method returns the text label provided
+     * by the nested label provider's <code>getText</code> method,
+     * decorated with the decoration provided by the label decorator's
+     * <code>decorateText</code> method.
+     */
+    public override String getText(Object element) {
+        String text = provider.getText(element);
+        if (decorator !is null) {
+            if ( auto ld2 = cast(LabelDecorator)decorator ) {
+                String decorated = ld2.decorateText(text, element, getDecorationContext());
+                if (decorated !is null) {
+                    return decorated;
+                }
+            } else {
+                String decorated = decorator.decorateText(text, element);
+                if (decorated !is null) {
+                    return decorated;
+                }
+            }
+        }
+        return text;
+    }
+
+    /**
+     * The <code>DecoratingLabelProvider</code> implementation of this
+     * <code>IBaseLabelProvider</code> method returns <code>true</code> if the corresponding method
+     * on the nested label provider returns <code>true</code> or if the corresponding method on the
+     * decorator returns <code>true</code>.
+     */
+    public override bool isLabelProperty(Object element, String property) {
+        if (provider.isLabelProperty(element, property)) {
+            return true;
+        }
+        if (decorator !is null && decorator.isLabelProperty(element, property)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * The <code>DecoratingLabelProvider</code> implementation of this <code>IBaseLabelProvider</code> method
+     * removes the listener from both the nested label provider and the label decorator.
+     *
+     * @param listener a label provider listener
+     */
+    public override void removeListener(ILabelProviderListener listener) {
+        super.removeListener(listener);
+        provider.removeListener(listener);
+        if (decorator !is null) {
+            decorator.removeListener(listener);
+        }
+        listeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Sets the label decorator.
+     * Removes all known listeners from the old decorator, and adds all known listeners to the new decorator.
+     * The old decorator is not disposed.
+     * Fires a label provider changed event indicating that all labels should be updated.
+     * Has no effect if the given decorator is identical to the current one.
+     *
+     * @param decorator the label decorator, or <code>null</code> if no decorations are to be applied
+     */
+    public void setLabelDecorator(ILabelDecorator decorator) {
+        ILabelDecorator oldDecorator = this.decorator;
+        if (oldDecorator !is decorator) {
+            Object[] listenerList = this.listeners.getListeners();
+            if (oldDecorator !is null) {
+                for (int i = 0; i < listenerList.length; ++i) {
+                    oldDecorator
+                            .removeListener(cast(ILabelProviderListener) listenerList[i]);
+                }
+            }
+            this.decorator = decorator;
+            if (decorator !is null) {
+                for (int i = 0; i < listenerList.length; ++i) {
+                    decorator
+                            .addListener(cast(ILabelProviderListener) listenerList[i]);
+                }
+            }
+            fireLabelProviderChanged(new LabelProviderChangedEvent(this));
+        }
+    }
+
+
+    /*
+     *  (non-Javadoc)
+     * @see org.eclipse.jface.viewers.IViewerLabelProvider#updateLabel(org.eclipse.jface.viewers.ViewerLabel, java.lang.Object)
+     */
+    public void updateLabel(ViewerLabel settings, Object element) {
+
+        ILabelDecorator currentDecorator = getLabelDecorator();
+        String oldText = settings.getText();
+        bool decorationReady = true;
+        if ( auto delayedDecorator = cast(IDelayedLabelDecorator)currentDecorator ) {
+            if (!delayedDecorator.prepareDecoration(element, oldText)) {
+                // The decoration is not ready but has been queued for processing
+                decorationReady = false;
+            }
+        }
+        // update icon and label
+
+        if (decorationReady || oldText is null
+                || settings.getText().length is 0) {
+            settings.setText(getText(element));
+        }
+
+        Image oldImage = settings.getImage();
+        if (decorationReady || oldImage is null) {
+            settings.setImage(getImage(element));
+        }
+
+        if(decorationReady) {
+            updateForDecorationReady(settings,element);
+        }
+
+    }
+
+    /**
+     * Decoration is ready. Update anything else for the settings.
+     * @param settings The object collecting the settings.
+     * @param element The Object being decorated.
+     * @since 3.1
+     */
+    protected void updateForDecorationReady(ViewerLabel settings, Object element) {
+
+        if( auto colorDecorator = cast(IColorDecorator) decorator ){
+            settings.setBackground(colorDecorator.decorateBackground(element));
+            settings.setForeground(colorDecorator.decorateForeground(element));
+        }
+
+        if( auto d = cast(IFontDecorator) decorator ) {
+            settings.setFont(d.decorateFont(element));
+        }
+
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.IColorProvider#getBackground(java.lang.Object)
+     */
+    public Color getBackground(Object element) {
+        if( auto p = cast(IColorProvider) provider ) {
+            return p.getBackground(element);
+        }
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.IFontProvider#getFont(java.lang.Object)
+     */
+    public Font getFont(Object element) {
+        if(auto p = cast(IFontProvider)provider ) {
+            return p.getFont(element);
+        }
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.IColorProvider#getForeground(java.lang.Object)
+     */
+    public Color getForeground(Object element) {
+        if(auto p = cast(IColorProvider)provider ) {
+            return p.getForeground(element);
+        }
+        return null;
+    }
+
+    /**
+     * Return the decoration context associated with this label provider.
+     * It will be passed to the decorator if the decorator is an
+     * instance of {@link LabelDecorator}.
+     * @return the decoration context associated with this label provider
+     *
+     * @since 3.2
+     */
+    public IDecorationContext getDecorationContext() {
+        return decorationContext;
+    }
+
+    /**
+     * Set the decoration context that will be based to the decorator
+     * for this label provider if that decorator implements {@link LabelDecorator}.
+     * @param decorationContext the decoration context.
+     *
+     * @since 3.2
+     */
+    public void setDecorationContext(IDecorationContext decorationContext) {
+        Assert.isNotNull(cast(Object)decorationContext);
+        this.decorationContext = decorationContext;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ITreePathLabelProvider#updateLabel(org.eclipse.jface.viewers.ViewerLabel, org.eclipse.jface.viewers.TreePath)
+     */
+    public void updateLabel(ViewerLabel settings, TreePath elementPath) {
+        ILabelDecorator currentDecorator = getLabelDecorator();
+        String oldText = settings.getText();
+        Object element = elementPath.getLastSegment();
+        bool decorationReady = true;
+        if ( auto labelDecorator = cast(LabelDecorator) currentDecorator ) {
+           if (!labelDecorator.prepareDecoration(element, oldText, getDecorationContext())) {
+                // The decoration is not ready but has been queued for processing
+                decorationReady = false;
+            }
+        } else if ( auto delayedDecorator = cast(IDelayedLabelDecorator) currentDecorator ) {
+            if (!delayedDecorator.prepareDecoration(element, oldText)) {
+                // The decoration is not ready but has been queued for processing
+                decorationReady = false;
+            }
+        }
+        settings.setHasPendingDecorations(!decorationReady);
+        // update icon and label
+
+        if ( auto pprov = cast(ITreePathLabelProvider) provider ) {
+            if (decorationReady || oldText is null
+                    || settings.getText().length is 0) {
+                pprov.updateLabel(settings, elementPath);
+                decorateSettings(settings, elementPath);
+            }
+        } else {
+            if (decorationReady || oldText is null
+                    || settings.getText().length is 0) {
+                settings.setText(getText(element));
+            }
+
+            Image oldImage = settings.getImage();
+            if (decorationReady || oldImage is null) {
+                settings.setImage(getImage(element));
+            }
+
+            if(decorationReady) {
+                updateForDecorationReady(settings,element);
+            }
+        }
+
+    }
+
+    /**
+     * Decorate the settings
+     * @param settings the settings obtained from the label provider
+     * @param elementPath the element path being decorated
+     */
+    private void decorateSettings(ViewerLabel settings, TreePath elementPath) {
+        Object element = elementPath.getLastSegment();
+        if (decorator !is null) {
+            if ( auto labelDecorator = cast(LabelDecorator) decorator ) {
+                String text = labelDecorator.decorateText(settings.getText(), element, getDecorationContext());
+                if (text !is null && text.length > 0)
+                    settings.setText(text);
+                Image image = labelDecorator.decorateImage(settings.getImage(), element, getDecorationContext());
+                if (image !is null)
+                    settings.setImage(image);
+
+            } else {
+                String text = decorator.decorateText(settings.getText(), element);
+                if (text !is null && text.length > 0)
+                    settings.setText(text);
+                Image image = decorator.decorateImage(settings.getImage(), element);
+                if (image !is null)
+                    settings.setImage(image);
+            }
+            if( auto colorDecorator = cast(IColorDecorator) decorator ){
+                Color background = colorDecorator.decorateBackground(element);
+                if (background !is null)
+                    settings.setBackground(background);
+                Color foreground = colorDecorator.decorateForeground(element);
+                if (foreground !is null)
+                    settings.setForeground(foreground);
+            }
+
+            if( auto fd = cast(IFontDecorator) decorator ) {
+                Font font = fd.decorateFont(element);
+                if (font !is null)
+                    settings.setFont(font);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/DecoratingStyledCellLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,333 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider;
+
+import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
+import org.eclipse.jface.viewers.ILabelDecorator;
+import org.eclipse.jface.viewers.IDecorationContext;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.DecorationContext;
+import org.eclipse.jface.viewers.LabelProviderChangedEvent;
+import org.eclipse.jface.viewers.LabelDecorator;
+import org.eclipse.jface.viewers.IDelayedLabelDecorator;
+import org.eclipse.jface.viewers.IColorDecorator;
+import org.eclipse.jface.viewers.IFontDecorator;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.viewers.StyledString;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A {@link DecoratingStyledCellLabelProvider} is a
+ * {@link DelegatingStyledCellLabelProvider} that uses a nested
+ * {@link DelegatingStyledCellLabelProvider.IStyledLabelProvider} to compute
+ * styled text label and image and takes a {@link ILabelDecorator} to decorate
+ * the label.
+ *
+ * <p>
+ * Use this label provider as a replacement for the
+ * {@link DecoratingLabelProvider} when decorating styled text labels.
+ * </p>
+ *
+ * <p>
+ * The {@link DecoratingStyledCellLabelProvider} will try to evaluate the text
+ * decoration added by the {@link ILabelDecorator} and will apply the style
+ * returned by {@link #getDecorationStyle(Object)}
+ * </p>
+ * <p>
+ * The {@link ILabelDecorator} can optionally implement {@link IColorDecorator}
+ * and {@link IFontDecorator} to provide foreground and background color and
+ * font decoration.
+ * </p>
+ *
+ * @since 3.4
+ */
+public class DecoratingStyledCellLabelProvider :
+        DelegatingStyledCellLabelProvider {
+
+    private ILabelDecorator decorator;
+    private IDecorationContext decorationContext;
+    private ILabelProviderListener labelProviderListener;
+
+    /**
+     * Creates a {@link DecoratingStyledCellLabelProvider} that delegates the
+     * requests for styled labels and for images to a
+     * {@link DelegatingStyledCellLabelProvider.IStyledLabelProvider}.
+     *
+     * @param labelProvider
+     *            the styled label provider
+     * @param decorator
+     *            a label decorator or <code>null</code> to not decorate the
+     *            label
+     * @param decorationContext
+     *            a decoration context or <code>null</code> if the no
+     *            decorator is configured or the default decorator should be
+     *            used
+     */
+    public this(
+            IStyledLabelProvider labelProvider, ILabelDecorator decorator,
+            IDecorationContext decorationContext) {
+        decorationContext= DecorationContext.DEFAULT_CONTEXT;
+        super(labelProvider);
+
+        this.decorator = decorator;
+        this.decorationContext = decorationContext !is null ? decorationContext
+                : DecorationContext.DEFAULT_CONTEXT;
+
+        this.labelProviderListener = new class ILabelProviderListener {
+            public void labelProviderChanged(LabelProviderChangedEvent event) {
+                fireLabelProviderChanged(event);
+            }
+        };
+        labelProvider.addListener(this.labelProviderListener);
+        if (decorator !is null)
+            decorator.addListener(this.labelProviderListener);
+    }
+
+    /**
+     * Returns the decoration context associated with this label provider. It
+     * will be passed to the decorator if the decorator is an instance of
+     * {@link LabelDecorator}.
+     *
+     * @return the decoration context associated with this label provider
+     */
+    public IDecorationContext getDecorationContext() {
+        return this.decorationContext;
+    }
+
+    /**
+     * Set the decoration context that will be based to the decorator for this
+     * label provider if that decorator implements {@link LabelDecorator}.
+     *
+     * @param decorationContext
+     *            the decoration context.
+     */
+    public void setDecorationContext(IDecorationContext decorationContext) {
+        Assert.isNotNull(cast(Object)decorationContext);
+        this.decorationContext = decorationContext;
+    }
+
+    private bool waitForPendingDecoration(ViewerCell cell) {
+        if (this.decorator is null)
+            return false;
+
+        Object element = cell.getElement();
+        String oldText = cell.getText();
+
+        bool isDecorationPending = false;
+        if (null !is cast(LabelDecorator)this.decorator ) {
+            isDecorationPending = !(cast(LabelDecorator) this.decorator)
+                    .prepareDecoration(element, oldText, getDecorationContext());
+        } else if (null !is cast(IDelayedLabelDecorator)this.decorator ) {
+            isDecorationPending = !(cast(IDelayedLabelDecorator) this.decorator)
+                    .prepareDecoration(element, oldText);
+        }
+        if (isDecorationPending && oldText.length is 0) {
+            // item is empty: is shown for the first time: don't wait
+            return false;
+        }
+        return isDecorationPending;
+    }
+
+    public void update(ViewerCell cell) {
+        if (waitForPendingDecoration(cell)) {
+            return; // wait until the decoration is ready
+        }
+        super.update(cell);
+    }
+
+    public Color getForeground(Object element) {
+        if (null !is cast(IColorDecorator)this.decorator ) {
+            Color foreground = (cast(IColorDecorator) this.decorator)
+                    .decorateForeground(element);
+            if (foreground !is null)
+                return foreground;
+        }
+        return super.getForeground(element);
+    }
+
+    public Color getBackground(Object element) {
+        if (null !is cast(IColorDecorator)this.decorator ) {
+            Color color = (cast(IColorDecorator) this.decorator)
+                    .decorateBackground(element);
+            if (color !is null)
+                return color;
+        }
+        return super.getBackground(element);
+    }
+
+    public Font getFont(Object element) {
+        if (null !is cast(IFontDecorator)this.decorator ) {
+            Font font = (cast(IFontDecorator) this.decorator).decorateFont(element);
+            if (font !is null)
+                return font;
+        }
+        return super.getFont(element);
+    }
+
+    public Image getImage(Object element) {
+        Image image = super.getImage(element);
+        if (this.decorator is null) {
+            return image;
+        }
+        Image decorated = null;
+        if (null !is cast(LabelDecorator)this.decorator ) {
+            decorated = (cast(LabelDecorator) this.decorator).decorateImage(image,
+                    element, getDecorationContext());
+        } else {
+            decorated = this.decorator.decorateImage(image, element);
+        }
+        if (decorated !is null)
+            return decorated;
+
+        return image;
+    }
+
+    /**
+     * Returns the styled text for the label of the given element.
+     *
+     * @param element
+     *            the element for which to provide the styled label text
+     * @return the styled text string used to label the element
+     */
+    protected StyledString getStyledText(Object element) {
+        StyledString styledString = super.getStyledText(element);
+        if (this.decorator is null) {
+            return styledString;
+        }
+
+        String label = styledString.getString();
+        String decorated;
+        if (null !is cast(LabelDecorator)this.decorator ) {
+            decorated = (cast(LabelDecorator) this.decorator).decorateText(label,
+                    element, getDecorationContext());
+        } else {
+            decorated = this.decorator.decorateText(label, element);
+        }
+        if (decorated is null)
+            return styledString;
+
+        int originalStart = decorated.indexOf(label);
+        if (originalStart is -1) {
+            return new StyledString(decorated); // the decorator did
+                                                        // something wild
+        }
+
+        if (decorated.length is label.length)
+            return styledString;
+
+        auto style = getDecorationStyle(element);
+        if (originalStart > 0) {
+            StyledString newString = new StyledString(decorated
+                    .substring(0, originalStart), style);
+            newString.append(styledString);
+            styledString = newString;
+        }
+        if (decorated.length > originalStart + label.length) { // decorator
+                                                                    // appended
+                                                                    // something
+            return styledString.append(decorated.substring(originalStart
+                    + label.length), style);
+        }
+        return styledString;
+    }
+
+    /**
+     * Sets the {@link StyledString.Styler} to be used for string
+     * decorations. By default the
+     * {@link StyledString#DECORATIONS_STYLER decoration style}. Clients
+     * can override.
+     *
+     * Note that it is the client's responsibility to react on color changes of
+     * the decoration color by refreshing the view
+     *
+     * @param element
+     *            the element that has been decorated
+     *
+     * @return return the decoration style
+     */
+    protected StyledString.Styler getDecorationStyle(Object element) {
+        return StyledString.DECORATIONS_STYLER;
+    }
+
+    /**
+     * Returns the decorator or <code>null</code> if no decorator is installed
+     *
+     * @return the decorator or <code>null</code> if no decorator is installed
+     */
+    public ILabelDecorator getLabelDecorator() {
+        return this.decorator;
+    }
+
+    /**
+     * Sets the label decorator. Removes all known listeners from the old
+     * decorator, and adds all known listeners to the new decorator. The old
+     * decorator is not disposed. Fires a label provider changed event
+     * indicating that all labels should be updated. Has no effect if the given
+     * decorator is identical to the current one.
+     *
+     * @param newDecorator
+     *            the label decorator, or <code>null</code> if no decorations
+     *            are to be applied
+     */
+    public void setLabelDecorator(ILabelDecorator newDecorator) {
+        ILabelDecorator oldDecorator = this.decorator;
+        if (oldDecorator !is newDecorator) {
+            if (oldDecorator !is null)
+                oldDecorator.removeListener(this.labelProviderListener);
+            this.decorator = newDecorator;
+            if (newDecorator !is null) {
+                newDecorator.addListener(this.labelProviderListener);
+            }
+        }
+        fireLabelProviderChanged(new LabelProviderChangedEvent(this));
+    }
+
+    public void addListener(ILabelProviderListener listener) {
+        super.addListener(listener);
+        if (this.decorator !is null) {
+            this.decorator.addListener(this.labelProviderListener);
+        }
+    }
+
+    public void removeListener(ILabelProviderListener listener) {
+        super.removeListener(listener);
+        if (this.decorator !is null) {
+            this.decorator.removeListener(this.labelProviderListener);
+        }
+    }
+
+    public bool isLabelProperty(Object element, String property) {
+        if (super.isLabelProperty(element, property)) {
+            return true;
+        }
+        return this.decorator !is null
+                && this.decorator.isLabelProperty(element, property);
+    }
+
+    public void dispose() {
+        super.dispose();
+        if (this.decorator !is null) {
+            this.decorator.removeListener(this.labelProviderListener);
+            this.decorator.dispose();
+            this.decorator = null;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/DecorationContext.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.viewers.DecorationContext;
+
+import org.eclipse.jface.viewers.IDecorationContext;
+
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+/**
+ * A concrete implementation of the {@link IDecorationContext} interface,
+ * suitable for instantiating.
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ * @since 3.2
+ */
+public class DecorationContext : IDecorationContext {
+
+    /**
+     * Constant that defines a default decoration context that has
+     * no context ids associated with it.
+     */
+    public static const IDecorationContext DEFAULT_CONTEXT;
+    static this(){
+        DEFAULT_CONTEXT = new DecorationContext();
+    }
+
+    private Map properties;
+
+    /**
+     * Create a decoration context.
+     */
+    public this() {
+        properties = new HashMap();
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.IDecorationContext#getProperty(java.lang.String)
+     */
+    public Object getProperty(String property) {
+        return properties.get(stringcast(property));
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.IDecorationContext#getProperties()
+     */
+    public String[] getProperties() {
+        return stringcast( properties.keySet().toArray() );
+    }
+
+    /**
+     * Set the given property to the given value. Setting the value of
+     * a property to <code>null</code> removes the property from
+     * the context.
+     * @param property the property
+     * @param value the value of the property or <code>null</code>
+     * if the property is to be removed.
+     */
+    public void putProperty(String property, Object value) {
+        if (value is null) {
+            properties.remove(stringcast(property));
+        } else {
+            properties.put(stringcast(property), value);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/DecorationOverlayIcon.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,204 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.viewers.DecorationOverlayIcon;
+
+import org.eclipse.jface.viewers.IDecoration;
+import org.eclipse.jface.util.Util;
+
+// import tango.util.Arrays;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.jface.resource.CompositeImageDescriptor;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import java.lang.all;
+import java.util.Arrays;
+
+/**
+ * A <code>DecorationOverlayIcon</code> is an image descriptor that can be used
+ * to overlay decoration images on to the 4 corner quadrants of a base image.
+ * The four quadrants are {@link IDecoration#TOP_LEFT}, {@link IDecoration#TOP_RIGHT},
+ * {@link IDecoration#BOTTOM_LEFT} and {@link IDecoration#BOTTOM_RIGHT}. Additionally,
+ * the overlay can be used to provide an underlay corresponding to {@link IDecoration#UNDERLAY}.
+ *
+ * @since 3.3
+ * @see IDecoration
+ */
+public class DecorationOverlayIcon : CompositeImageDescriptor {
+
+    // the base image
+    private Image base;
+
+    // the overlay images
+    private ImageDescriptor[] overlays;
+
+    // the size
+    private Point size;
+
+    /**
+     * Create the decoration overlay for the base image using the array of
+     * provided overlays. The indices of the array correspond to the values
+     * of the 5 overlay constants defined on {@link IDecoration}
+     * ({@link IDecoration#TOP_LEFT}, {@link IDecoration#TOP_RIGHT},
+     * {@link IDecoration#BOTTOM_LEFT}, {@link IDecoration#BOTTOM_RIGHT}
+     * and{@link IDecoration#UNDERLAY}).
+     *
+     * @param baseImage the base image
+     * @param overlaysArray the overlay images
+     * @param sizeValue the size of the resulting image
+     */
+    public this(Image baseImage,
+            ImageDescriptor[] overlaysArray, Point sizeValue) {
+        this.base = baseImage;
+        this.overlays = overlaysArray;
+        this.size = sizeValue;
+    }
+
+    /**
+     * Create the decoration overlay for the base image using the array of
+     * provided overlays. The indices of the array correspond to the values
+     * of the 5 overlay constants defined on {@link IDecoration}
+     * ({@link IDecoration#TOP_LEFT}, {@link IDecoration#TOP_RIGHT},
+     * {@link IDecoration#BOTTOM_LEFT}, {@link IDecoration#BOTTOM_RIGHT}
+     * and {@link IDecoration#UNDERLAY}).
+     *
+     * @param baseImage the base image
+     * @param overlaysArray the overlay images
+     */
+    public this(Image baseImage, ImageDescriptor[] overlaysArray) {
+        this(baseImage, overlaysArray, new Point(baseImage.getBounds().width, baseImage.getBounds().height));
+    }
+
+    /**
+     * Create a decoration overlay icon that will place the given overlay icon in
+     * the given quadrant of the base image.
+     * @param baseImage the base image
+     * @param overlayImage the overlay image
+     * @param quadrant the quadrant (one of {@link IDecoration}
+     * ({@link IDecoration#TOP_LEFT}, {@link IDecoration#TOP_RIGHT},
+     * {@link IDecoration#BOTTOM_LEFT}, {@link IDecoration#BOTTOM_RIGHT}
+     * or {@link IDecoration#UNDERLAY})
+     */
+    public this(Image baseImage, ImageDescriptor overlayImage, int quadrant) {
+        this(baseImage, createArrayFrom(overlayImage, quadrant));
+    }
+
+    /**
+     * Convert the given image and quadrant into the proper input array.
+     * @param overlayImage the overlay image
+     * @param quadrant the quadrant
+     * @return an array with the given image in the proper quadrant
+     */
+    private static ImageDescriptor[] createArrayFrom(
+            ImageDescriptor overlayImage, int quadrant) {
+        ImageDescriptor[] descs = [ cast(ImageDescriptor) null, null, null, null, null ];
+        descs[quadrant] = overlayImage;
+        return descs;
+    }
+
+    /**
+     * Draw the overlays for the receiver.
+     * @param overlaysArray
+     */
+    private void drawOverlays(ImageDescriptor[] overlaysArray) {
+
+        for (int i = 0; i < overlays.length; i++) {
+            ImageDescriptor overlay = overlaysArray[i];
+            if (overlay is null) {
+                continue;
+            }
+            ImageData overlayData = overlay.getImageData();
+            //Use the missing descriptor if it is not there.
+            if (overlayData is null) {
+                overlayData = ImageDescriptor.getMissingImageDescriptor()
+                        .getImageData();
+            }
+            switch (i) {
+            case IDecoration.TOP_LEFT:
+                drawImage(overlayData, 0, 0);
+                break;
+            case IDecoration.TOP_RIGHT:
+                drawImage(overlayData, size.x - overlayData.width, 0);
+                break;
+            case IDecoration.BOTTOM_LEFT:
+                drawImage(overlayData, 0, size.y - overlayData.height);
+                break;
+            case IDecoration.BOTTOM_RIGHT:
+                drawImage(overlayData, size.x - overlayData.width, size.y
+                        - overlayData.height);
+                break;
+            default:
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public override int opEquals(Object o) {
+        if (!( cast(DecorationOverlayIcon)o )) {
+            return false;
+        }
+        DecorationOverlayIcon other = cast(DecorationOverlayIcon) o;
+        return base.opEquals(other.base)
+                && Util.opEquals(overlays, other.overlays);
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    public override hash_t toHash() {
+        int code = System.identityHashCode(base);
+        for (int i = 0; i < overlays.length; i++) {
+            if (overlays[i] !is null) {
+                code ^= overlays[i].toHash();
+            }
+        }
+        return code;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.CompositeImageDescriptor#drawCompositeImage(int, int)
+     */
+    protected override void drawCompositeImage(int width, int height) {
+        if (overlays.length > IDecoration.UNDERLAY) {
+            ImageDescriptor underlay = overlays[IDecoration.UNDERLAY];
+            if (underlay !is null) {
+                drawImage(underlay.getImageData(), 0, 0);
+            }
+        }
+        if (overlays.length > IDecoration.REPLACE && overlays[IDecoration.REPLACE] !is null) {
+            drawImage(overlays[IDecoration.REPLACE].getImageData(), 0, 0);
+        } else {
+            drawImage(base.getImageData(), 0, 0);
+        }
+        drawOverlays(overlays);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.CompositeImageDescriptor#getSize()
+     */
+    protected override Point getSize() {
+        return size;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.resource.CompositeImageDescriptor#getTransparentPixel()
+     */
+    protected override int getTransparentPixel() {
+        return base.getImageData().transparentPixel;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/DelegatingStyledCellLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
+
+import org.eclipse.jface.viewers.StyledCellLabelProvider;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.StyledString;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.IColorProvider;
+import org.eclipse.jface.viewers.IFontProvider;
+
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * A {@link DelegatingStyledCellLabelProvider} is a
+ * {@link StyledCellLabelProvider} that delegates requests for the styled string
+ * and the image to a
+ * {@link DelegatingStyledCellLabelProvider.IStyledLabelProvider}.
+ *
+ * <p>
+ * Existing label providers can be enhanced by implementing
+ * {@link DelegatingStyledCellLabelProvider.IStyledLabelProvider} so they can be
+ * used in viewers with styled labels.
+ * </p>
+ *
+ * <p>
+ * The {@link DelegatingStyledCellLabelProvider.IStyledLabelProvider} can
+ * optionally implement {@link IColorProvider} and {@link IFontProvider} to
+ * provide foreground and background color and a default font.
+ * </p>
+ *
+ * @since 3.4
+ */
+public class DelegatingStyledCellLabelProvider : StyledCellLabelProvider {
+
+    /**
+     * Interface marking a label provider that provides styled text labels and
+     * images.
+     * <p>
+     * The {@link DelegatingStyledCellLabelProvider.IStyledLabelProvider} can
+     * optionally implement {@link IColorProvider} and {@link IFontProvider} to
+     * provide foreground and background color and a default font.
+     * </p>
+     */
+    public static interface IStyledLabelProvider : IBaseLabelProvider {
+
+        /**
+         * Returns the styled text label for the given element
+         *
+         * @param element
+         *            the element to evaluate the styled string for
+         *
+         * @return the styled string.
+         */
+        public StyledString getStyledText(Object element);
+
+        /**
+         * Returns the image for the label of the given element. The image is
+         * owned by the label provider and must not be disposed directly.
+         * Instead, dispose the label provider when no longer needed.
+         *
+         * @param element
+         *            the element for which to provide the label image
+         * @return the image used to label the element, or <code>null</code>
+         *         if there is no image for the given object
+         */
+        public Image getImage(Object element);
+    }
+
+    private IStyledLabelProvider styledLabelProvider;
+
+    /**
+     * Creates a {@link DelegatingStyledCellLabelProvider} that delegates the
+     * requests for the styled labels and the images to a
+     * {@link IStyledLabelProvider}.
+     *
+     * @param labelProvider
+     *            the label provider that provides the styled labels and the
+     *            images
+     */
+    public this(IStyledLabelProvider labelProvider) {
+        if (labelProvider is null)
+            throw new IllegalArgumentException(
+                    "Label provider must not be null"); //$NON-NLS-1$
+
+        this.styledLabelProvider = labelProvider;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.StyledCellLabelProvider#update(org.eclipse.jface.viewers.ViewerCell)
+     */
+    public void update(ViewerCell cell) {
+        Object element = cell.getElement();
+
+        StyledString styledString = getStyledText(element);
+        String newText= styledString.toString();
+
+        StyleRange[] oldStyleRanges= cell.getStyleRanges();
+        StyleRange[] newStyleRanges= isOwnerDrawEnabled() ? styledString.getStyleRanges() : null;
+
+        if (!Arrays.equals(oldStyleRanges, newStyleRanges)) {
+            cell.setStyleRanges(newStyleRanges);
+            if (cell.getText().equals(newText)) {
+                // make sure there will be a refresh from a change
+                cell.setText(""); //$NON-NLS-1$
+            }
+        }
+
+        cell.setText(newText);
+        cell.setImage(getImage(element));
+        cell.setFont(getFont(element));
+        cell.setForeground(getForeground(element));
+        cell.setBackground(getBackground(element));
+
+        // no super call required. changes on item will trigger the refresh.
+    }
+
+    /**
+     * Provides a foreground color for the given element.
+     *
+     * @param element
+     *            the element
+     * @return the foreground color for the element, or <code>null</code> to
+     *         use the default foreground color
+     */
+    public Color getForeground(Object element) {
+        if (null !is cast(IColorProvider)this.styledLabelProvider ) {
+            return (cast(IColorProvider) this.styledLabelProvider)
+                    .getForeground(element);
+        }
+        return null;
+    }
+
+    /**
+     * Provides a background color for the given element.
+     *
+     * @param element
+     *            the element
+     * @return the background color for the element, or <code>null</code> to
+     *         use the default background color
+     */
+    public Color getBackground(Object element) {
+        if (null !is cast(IColorProvider)this.styledLabelProvider) {
+            return (cast(IColorProvider) this.styledLabelProvider)
+                    .getBackground(element);
+        }
+        return null;
+    }
+
+    /**
+     * Provides a font for the given element.
+     *
+     * @param element
+     *            the element
+     * @return the font for the element, or <code>null</code> to use the
+     *         default font
+     */
+    public Font getFont(Object element) {
+        if (null !is cast(IFontProvider)this.styledLabelProvider ) {
+            return (cast(IFontProvider) this.styledLabelProvider).getFont(element);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the image for the label of the given element. The image is owned
+     * by the label provider and must not be disposed directly. Instead, dispose
+     * the label provider when no longer needed.
+     *
+     * @param element
+     *            the element for which to provide the label image
+     * @return the image used to label the element, or <code>null</code> if
+     *         there is no image for the given object
+     */
+    public Image getImage(Object element) {
+        return this.styledLabelProvider.getImage(element);
+    }
+
+    /**
+     * Returns the styled text for the label of the given element.
+     *
+     * @param element
+     *            the element for which to provide the styled label text
+     * @return the styled text string used to label the element
+     */
+    protected StyledString getStyledText(Object element) {
+        return this.styledLabelProvider.getStyledText(element);
+    }
+
+    /**
+     * Returns the styled string provider.
+     *
+     * @return the wrapped label provider
+     */
+    public IStyledLabelProvider getStyledStringProvider() {
+        return this.styledLabelProvider;
+    }
+
+    public void addListener(ILabelProviderListener listener) {
+        super.addListener(listener);
+        this.styledLabelProvider.addListener(listener);
+    }
+
+    public void removeListener(ILabelProviderListener listener) {
+        super.removeListener(listener);
+        this.styledLabelProvider.removeListener(listener);
+    }
+
+    public bool isLabelProperty(Object element, String property) {
+        return this.styledLabelProvider.isLabelProperty(element, property);
+    }
+
+    public void dispose() {
+        super.dispose();
+        this.styledLabelProvider.dispose();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/DialogCellEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,394 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.DialogCellEditor;
+
+import org.eclipse.jface.viewers.CellEditor;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+import tango.text.convert.Format;
+
+/**
+ * An abstract cell editor that uses a dialog.
+ * Dialog cell editors usually have a label control on the left and a button on
+ * the right. Pressing the button opens a dialog window (for example, a color dialog
+ * or a file dialog) to change the cell editor's value.
+ * The cell editor's value is the value of the dialog.
+ * <p>
+ * Subclasses may override the following methods:
+ * <ul>
+ *  <li><code>createButton</code>: creates the cell editor's button control</li>
+ *  <li><code>createContents</code>: creates the cell editor's 'display value' control</li>
+ *  <li><code>updateContents</code>: updates the cell editor's 'display value' control
+ *      after its value has changed</li>
+ *  <li><code>openDialogBox</code>: opens the dialog box when the end user presses
+ *      the button</li>
+ * </ul>
+ * </p>
+ */
+public abstract class DialogCellEditor : CellEditor {
+
+    /**
+     * Image registry key for three dot image (value <code>"cell_editor_dots_button_image"</code>).
+     */
+    public static const String CELL_EDITOR_IMG_DOTS_BUTTON = "cell_editor_dots_button_image";//$NON-NLS-1$
+
+    /**
+     * The editor control.
+     */
+    private Composite editor;
+
+    /**
+     * The current contents.
+     */
+    private Control contents;
+
+    /**
+     * The label that gets reused by <code>updateLabel</code>.
+     */
+    private Label defaultLabel;
+
+    /**
+     * The button.
+     */
+    private Button button;
+
+    /**
+     * Listens for 'focusLost' events and  fires the 'apply' event as long
+     * as the focus wasn't lost because the dialog was opened.
+     */
+    private FocusListener buttonFocusListener;
+
+    /**
+     * The value of this cell editor; initially <code>null</code>.
+     */
+    private Object value = null;
+
+    static this() {
+        ImageRegistry reg = JFaceResources.getImageRegistry();
+        reg.put(CELL_EDITOR_IMG_DOTS_BUTTON, ImageDescriptor.createFromFile(
+                getImportData!("org.eclipse.jface.images.dots_button.gif")));//$NON-NLS-1$
+    }
+
+    /**
+     * Internal class for laying out the dialog.
+     */
+    private class DialogCellLayout : Layout {
+        public override void layout(Composite editor, bool force) {
+            Rectangle bounds = editor.getClientArea();
+            Point size = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, force);
+            if (contents !is null) {
+                contents.setBounds(0, 0, bounds.width - size.x, bounds.height);
+            }
+            button.setBounds(bounds.width - size.x, 0, size.x, bounds.height);
+        }
+
+        public override Point computeSize(Composite editor, int wHint, int hHint,
+                bool force) {
+            if (wHint !is SWT.DEFAULT && hHint !is SWT.DEFAULT) {
+                return new Point(wHint, hHint);
+            }
+            Point contentsSize = contents.computeSize(SWT.DEFAULT, SWT.DEFAULT,
+                    force);
+            Point buttonSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT,
+                    force);
+            // Just return the button width to ensure the button is not clipped
+            // if the label is long.
+            // The label will just use whatever extra width there is
+            Point result = new Point(buttonSize.x, Math.max(contentsSize.y,
+                    buttonSize.y));
+            return result;
+        }
+    }
+
+    /**
+     * Default DialogCellEditor style
+     */
+    private static const int defaultStyle = SWT.NONE;
+
+    /**
+     * Creates a new dialog cell editor with no control
+     * @since 2.1
+     */
+    public this() {
+        setStyle(defaultStyle);
+    }
+
+    /**
+     * Creates a new dialog cell editor parented under the given control.
+     * The cell editor value is <code>null</code> initially, and has no
+     * validator.
+     *
+     * @param parent the parent control
+     */
+    protected this(Composite parent) {
+        this(parent, defaultStyle);
+    }
+
+    /**
+     * Creates a new dialog cell editor parented under the given control.
+     * The cell editor value is <code>null</code> initially, and has no
+     * validator.
+     *
+     * @param parent the parent control
+     * @param style the style bits
+     * @since 2.1
+     */
+    protected this(Composite parent, int style) {
+        super(parent, style);
+    }
+
+    /**
+     * Creates the button for this cell editor under the given parent control.
+     * <p>
+     * The default implementation of this framework method creates the button
+     * display on the right hand side of the dialog cell editor. Subclasses
+     * may extend or reimplement.
+     * </p>
+     *
+     * @param parent the parent control
+     * @return the new button control
+     */
+    protected Button createButton(Composite parent) {
+        Button result = new Button(parent, SWT.DOWN);
+        result.setText("..."); //$NON-NLS-1$
+        return result;
+    }
+
+    /**
+     * Creates the controls used to show the value of this cell editor.
+     * <p>
+     * The default implementation of this framework method creates
+     * a label widget, using the same font and background color as the parent control.
+     * </p>
+     * <p>
+     * Subclasses may reimplement.  If you reimplement this method, you
+     * should also reimplement <code>updateContents</code>.
+     * </p>
+     *
+     * @param cell the control for this cell editor
+     * @return the underlying control
+     */
+    protected Control createContents(Composite cell) {
+        defaultLabel = new Label(cell, SWT.LEFT);
+        defaultLabel.setFont(cell.getFont());
+        defaultLabel.setBackground(cell.getBackground());
+        return defaultLabel;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     */
+    protected override Control createControl(Composite parent) {
+
+        Font font = parent.getFont();
+        Color bg = parent.getBackground();
+
+        editor = new Composite(parent, getStyle());
+        editor.setFont(font);
+        editor.setBackground(bg);
+        editor.setLayout(new DialogCellLayout());
+
+        contents = createContents(editor);
+        updateContents(value);
+
+        button = createButton(editor);
+        button.setFont(font);
+
+        button.addKeyListener(new class KeyAdapter {
+            /* (non-Javadoc)
+             * @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent)
+             */
+            public void keyReleased(KeyEvent e) {
+                if (e.character is '\u001b') { // Escape
+                    fireCancelEditor();
+                }
+            }
+        });
+
+        button.addFocusListener(getButtonFocusListener());
+
+        button.addSelectionListener(new class SelectionAdapter {
+            /* (non-Javadoc)
+             * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+             */
+            public void widgetSelected(SelectionEvent event) {
+                // Remove the button's focus listener since it's guaranteed
+                // to lose focus when the dialog opens
+                button.removeFocusListener(getButtonFocusListener());
+
+                Object newValue = openDialogBox(editor);
+
+                // Re-add the listener once the dialog closes
+                button.addFocusListener(getButtonFocusListener());
+
+                if (newValue !is null) {
+                    bool newValidState = isCorrect(newValue);
+                    if (newValidState) {
+                        markDirty();
+                        doSetValue(newValue);
+                    } else {
+                        // try to insert the current value into the error message.
+                        setErrorMessage(Format(getErrorMessage(),
+                                newValue.toString() ));
+                    }
+                    fireApplyEditorValue();
+                }
+            }
+        });
+
+        setValueValid(true);
+
+        return editor;
+    }
+
+    /* (non-Javadoc)
+     *
+     * Override in order to remove the button's focus listener if the celleditor
+     * is deactivating.
+     *
+     * @see org.eclipse.jface.viewers.CellEditor#deactivate()
+     */
+    public override void deactivate() {
+        if (button !is null && !button.isDisposed()) {
+            button.removeFocusListener(getButtonFocusListener());
+        }
+
+        super.deactivate();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     */
+    protected override Object doGetValue() {
+        return value;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     * The focus is set to the cell editor's button.
+     */
+    protected override void doSetFocus() {
+        button.setFocus();
+
+        // add a FocusListener to the button
+        button.addFocusListener(getButtonFocusListener());
+    }
+
+    /**
+     * Return a listener for button focus.
+     * @return FocusListener
+     */
+    private FocusListener getButtonFocusListener() {
+        if (buttonFocusListener is null) {
+            buttonFocusListener = new class FocusListener {
+
+                /* (non-Javadoc)
+                 * @see org.eclipse.swt.events.FocusListener#focusGained(org.eclipse.swt.events.FocusEvent)
+                 */
+                public void focusGained(FocusEvent e) {
+                    // Do nothing
+                }
+
+                /* (non-Javadoc)
+                 * @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent)
+                 */
+                public void focusLost(FocusEvent e) {
+                    this.outer.focusLost();
+                }
+            };
+        }
+
+        return buttonFocusListener;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     */
+    protected override void doSetValue(Object value) {
+        this.value = value;
+        updateContents(value);
+    }
+
+    /**
+     * Returns the default label widget created by <code>createContents</code>.
+     *
+     * @return the default label widget
+     */
+    protected Label getDefaultLabel() {
+        return defaultLabel;
+    }
+
+    /**
+     * Opens a dialog box under the given parent control and returns the
+     * dialog's value when it closes, or <code>null</code> if the dialog
+     * was canceled or no selection was made in the dialog.
+     * <p>
+     * This framework method must be implemented by concrete subclasses.
+     * It is called when the user has pressed the button and the dialog
+     * box must pop up.
+     * </p>
+     *
+     * @param cellEditorWindow the parent control cell editor's window
+     *   so that a subclass can adjust the dialog box accordingly
+     * @return the selected value, or <code>null</code> if the dialog was
+     *   canceled or no selection was made in the dialog
+     */
+    protected abstract Object openDialogBox(Control cellEditorWindow);
+
+    /**
+     * Updates the controls showing the value of this cell editor.
+     * <p>
+     * The default implementation of this framework method just converts
+     * the passed object to a string using <code>toString</code> and
+     * sets this as the text of the label widget.
+     * </p>
+     * <p>
+     * Subclasses may reimplement.  If you reimplement this method, you
+     * should also reimplement <code>createContents</code>.
+     * </p>
+     *
+     * @param value the new value of this cell editor
+     */
+    protected void updateContents(Object value) {
+        if (defaultLabel is null) {
+            return;
+        }
+
+        String text = "";//$NON-NLS-1$
+        if (value !is null) {
+            text = value.toString();
+        }
+        defaultLabel.setText(text);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/DoubleClickEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.DoubleClickEvent;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.EventObject;
+
+/**
+ * Event object describing a double-click. The source of these
+ * events is a viewer.
+ *
+ * @see IDoubleClickListener
+ */
+public class DoubleClickEvent : EventObject {
+
+    /**
+     * Generated serial version UID for this class.
+     * @since 3.1
+     */
+    private static const long serialVersionUID = 3258408443605038133L;
+
+    /**
+     * The selection.
+     */
+    protected ISelection selection;
+
+    /**
+     * Creates a new event for the given source and selection.
+     *
+     * @param source the viewer
+     * @param selection the selection
+     */
+    public this(Viewer source, ISelection selection) {
+        super(source);
+        Assert.isNotNull(cast(Object)selection);
+        this.selection = selection;
+    }
+
+    /**
+     * Returns the selection.
+     *
+     * @return the selection
+     */
+    public ISelection getSelection() {
+        return selection;
+    }
+
+    /**
+     * Returns the viewer that is the source of this event.
+     *
+     * @return the originating viewer
+     */
+    public Viewer getViewer() {
+        return cast(Viewer) getSource();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/EditingSupport.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                                 fix in bug 151295,167325,201905
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.EditingSupport;
+
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.ViewerCell;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * EditingSupport is the abstract superclass of the support for cell editing.
+ *
+ * @since 3.3
+ *
+ */
+public abstract class EditingSupport {
+
+    private ColumnViewer viewer;
+
+    /**
+     * @param viewer
+     *            a new viewer
+     */
+    public this(ColumnViewer viewer) {
+        Assert.isNotNull(viewer, "Viewer is not allowed to be null"); //$NON-NLS-1$
+        this.viewer = viewer;
+    }
+
+    /**
+     * The editor to be shown
+     *
+     * @param element
+     *            the model element
+     * @return the CellEditor
+     */
+    protected abstract CellEditor getCellEditor(Object element);
+    package CellEditor getCellEditor_package(Object element){
+        return getCellEditor(element);
+    }
+
+    /**
+     * Is the cell editable
+     *
+     * @param element
+     *            the model element
+     * @return true if editable
+     */
+    protected abstract bool canEdit(Object element);
+    package bool canEdit_package(Object element){
+        return canEdit(element);
+    }
+
+    /**
+     * Get the value to set to the editor
+     *
+     * @param element
+     *            the model element
+     * @return the value shown
+     */
+    protected abstract Object getValue(Object element);
+
+    /**
+     * Restore the value from the CellEditor
+     *
+     * <p>
+     * <b>Subclasses should overwrite!</b>
+     * </p>
+     *
+     * @param element
+     *            the model element
+     * @param value
+     *            the new value
+     */
+    protected abstract void setValue(Object element, Object value);
+
+    /**
+     * @return the viewer this editing support works for
+     */
+    public ColumnViewer getViewer() {
+        return viewer;
+    }
+
+    /**
+     * Initialize the editor. Frameworks like Databinding can hook in here and provide
+     * a customized implementation. <p><b>Standard customers should not overwrite this method but {@link #getValue(Object)}</b></p>
+     *
+     * @param cellEditor
+     *            the cell editor
+     * @param cell
+     *            the cell the editor is working for
+     */
+    protected void initializeCellEditorValue(CellEditor cellEditor, ViewerCell cell) {
+        Object value = getValue(cell.getElement());
+        cellEditor.setValue(value);
+    }
+    package void initializeCellEditorValue_package(CellEditor cellEditor, ViewerCell cell) {
+        initializeCellEditorValue(cellEditor, cell);
+    }
+
+    /**
+     * Save the value of the cell editor back to the model. Frameworks like Databinding can hook in here and provide
+     * a customized implementation. <p><b>Standard customers should not overwrite this method but {@link #setValue(Object, Object)} </b></p>
+     * @param cellEditor
+     *            the cell-editor
+     * @param cell
+     *            the cell the editor is working for
+     */
+    protected void saveCellEditorValue(CellEditor cellEditor, ViewerCell cell) {
+        Object value = cellEditor.getValue();
+        setValue(cell.getElement(), value);
+    }
+    package void saveCellEditorValue_package(CellEditor cellEditor, ViewerCell cell) {
+        saveCellEditorValue(cellEditor, cell);
+    }
+
+    bool isLegacySupport() {
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/FocusCellHighlighter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                                 bugfix in: 182800
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module org.eclipse.jface.viewers.FocusCellHighlighter;
+
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ViewerCell;
+
+import java.lang.all;
+
+/**
+ * @since 3.3
+ *
+ */
+public abstract class FocusCellHighlighter {
+    private ColumnViewer viewer;
+
+    /**
+     * @param viewer
+     */
+    public this(ColumnViewer viewer) {
+        this.viewer = viewer;
+    }
+
+    /**
+     * @return the focus cell
+     */
+    public ViewerCell getFocusCell() {
+        return viewer.getColumnViewerEditor().getFocusCell();
+    }
+
+    /**
+     * Called by the framework when the focus cell has changed. Subclasses may
+     * extend.
+     *
+     * @param cell
+     *            the new focus cell
+     * @deprecated use {@link #focusCellChanged(ViewerCell, ViewerCell)} instead
+     */
+    protected void focusCellChanged(ViewerCell cell) {
+    }
+    package void focusCellChanged_package(ViewerCell cell){
+        focusCellChanged(cell);
+    }
+
+    /**
+     * Called by the framework when the focus cell has changed. Subclasses may
+     * extend.
+     * <p>
+     * <b>The default implementation for this method calls
+     * focusCellChanged(ViewerCell). Subclasses should override this method
+     * rather than {@link #focusCellChanged(ViewerCell)} .</b>
+     *
+     * @param newCell
+     *            the new focus cell or <code>null</code> if no new cell
+     *            receives the focus
+     * @param oldCell
+     *            the old focus cell or <code>null</code> if no cell has been
+     *            focused before
+     * @since 3.4
+     */
+    protected void focusCellChanged(ViewerCell newCell, ViewerCell oldCell) {
+        focusCellChanged(newCell);
+    }
+    package void focusCellChanged_package(ViewerCell newCell, ViewerCell oldCell){
+        focusCellChanged(newCell,oldCell);
+    }
+
+    /**
+     * This method is called by the framework to initialize this cell
+     * highlighter object. Subclasses may extend.
+     */
+    protected void init() {
+    }
+
+    package void init_package() {
+        init();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/FocusCellOwnerDrawHighlighter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,223 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                               - fix for bug 183850, 182652, 182800, 215069
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter;
+
+import org.eclipse.jface.viewers.FocusCellHighlighter;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ViewerRow;
+import org.eclipse.jface.viewers.ColumnViewer;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A concrete implementation of {@link FocusCellHighlighter} using by setting
+ * the control into owner draw mode and highlighting the currently selected
+ * cell. To make the use this class you should create the control with the
+ * {@link SWT#FULL_SELECTION} bit set
+ * 
+ * This class can be subclassed to configure how the coloring of the selected
+ * cell.
+ * 
+ * @since 3.3
+ *
+ */
+public class FocusCellOwnerDrawHighlighter : FocusCellHighlighter {
+    /**
+     * Create a new instance which can be passed to a
+     * {@link TreeViewerFocusCellManager}
+     * 
+     * @param viewer
+     *            the viewer
+     */
+    public this(ColumnViewer viewer) {
+        super(viewer);
+        hookListener(viewer);
+    }
+
+    private void markFocusedCell(Event event, ViewerCell cell) {
+        Color background = (cell.getControl().isFocusControl()) ? getSelectedCellBackgroundColor(cell)
+                : getSelectedCellBackgroundColorNoFocus(cell);
+        Color foreground = (cell.getControl().isFocusControl()) ? getSelectedCellForegroundColor(cell)
+                : getSelectedCellForegroundColorNoFocus(cell);
+
+        if (foreground !is null || background !is null || onlyTextHighlighting(cell)) {
+            GC gc = event.gc;
+
+            if (background is null) {
+                background = cell.getItem().getDisplay().getSystemColor(
+                        SWT.COLOR_LIST_SELECTION);
+            }
+
+            if (foreground is null) {
+                foreground = cell.getItem().getDisplay().getSystemColor(
+                        SWT.COLOR_LIST_SELECTION_TEXT);
+            }
+
+            gc.setBackground(background);
+            gc.setForeground(foreground);
+            
+            if (onlyTextHighlighting(cell)) {
+                Rectangle area = event.getBounds();
+                Rectangle rect = cell.getTextBounds();
+                if( rect !is null ) {
+                    area.x = rect.x;
+                }
+                gc.fillRectangle(area);
+            } else {
+                gc.fillRectangle(event.getBounds());
+            }
+            
+            event.detail &= ~SWT.SELECTED;
+        }
+    }
+
+    private void removeSelectionInformation(Event event, ViewerCell cell) {
+        GC gc = event.gc;
+        gc.setBackground(cell.getViewerRow().getBackground(
+                cell.getColumnIndex()));
+        gc.setForeground(cell.getViewerRow().getForeground(
+                cell.getColumnIndex()));
+        gc.fillRectangle(cell.getBounds());
+        event.detail &= ~SWT.SELECTED;
+    }
+
+    private void hookListener(ColumnViewer viewer) {
+
+        Listener listener = new class(viewer) Listener {
+            ColumnViewer viewer_;
+            this(ColumnViewer a){
+                viewer_ = a;
+            }
+            public void handleEvent(Event event) {
+                if ((event.detail & SWT.SELECTED) > 0) {
+                    ViewerCell focusCell = getFocusCell();
+                    ViewerRow row = viewer_.getViewerRowFromItem_package(event.item);
+
+                    Assert.isNotNull(row,
+                        "Internal structure invalid. Item without associated row is not possible."); //$NON-NLS-1$
+
+                    ViewerCell cell = row.getCell(event.index);
+
+                    if (focusCell is null || !cell.opEquals(focusCell)) {
+                        removeSelectionInformation(event, cell);
+                    } else {
+                        markFocusedCell(event, cell);
+                    }
+                }
+            }
+
+        };
+        viewer.getControl().addListener(SWT.EraseItem, listener);
+    }
+
+    /**
+     * The color to use when rendering the background of the selected cell when
+     * the control has the input focus
+     * 
+     * @param cell
+     *            the cell which is colored
+     * @return the color or <code>null</code> to use the default
+     */
+    protected Color getSelectedCellBackgroundColor(ViewerCell cell) {
+        return null;
+    }
+
+    /**
+     * The color to use when rendering the foreground (=text) of the selected
+     * cell when the control has the input focus
+     * 
+     * @param cell
+     *            the cell which is colored
+     * @return the color or <code>null</code> to use the default
+     */
+    protected Color getSelectedCellForegroundColor(ViewerCell cell) {
+        return null;
+    }
+
+    /**
+     * The color to use when rendering the foreground (=text) of the selected
+     * cell when the control has <b>no</b> input focus
+     * 
+     * @param cell
+     *            the cell which is colored
+     * @return the color or <code>null</code> to use the same used when
+     *         control has focus
+     * @since 3.4
+     */
+    protected Color getSelectedCellForegroundColorNoFocus(ViewerCell cell) {
+        return null;
+    }
+
+    /**
+     * The color to use when rendering the background of the selected cell when
+     * the control has <b>no</b> input focus
+     * 
+     * @param cell
+     *            the cell which is colored
+     * @return the color or <code>null</code> to use the same used when
+     *         control has focus
+     * @since 3.4
+     */
+    protected Color getSelectedCellBackgroundColorNoFocus(ViewerCell cell) {
+        return null;
+    }
+
+    /**
+     * Controls whether the whole cell or only the text-area is highlighted
+     * 
+     * @param cell
+     *            the cell which is highlighted
+     * @return <code>true</code> if only the text area should be highlighted
+     * @since 3.4
+     */
+    protected bool onlyTextHighlighting(ViewerCell cell) {
+        return false;
+    }
+
+    protected void focusCellChanged(ViewerCell newCell, ViewerCell oldCell) {
+        super.focusCellChanged(newCell, oldCell);
+
+        // Redraw new area
+        if (newCell !is null) {
+            Rectangle rect = newCell.getBounds();
+            int x = newCell.getColumnIndex() is 0 ? 0 : rect.x;
+            int width = newCell.getColumnIndex() is 0 ? rect.x + rect.width
+                    : rect.width;
+            // 1 is a fix for Linux-GTK
+            newCell.getControl().redraw(x, rect.y - 1, width, rect.height + 1,
+                    true);
+        }
+
+        if (oldCell !is null) {
+            Rectangle rect = oldCell.getBounds();
+            int x = oldCell.getColumnIndex() is 0 ? 0 : rect.x;
+            int width = oldCell.getColumnIndex() is 0 ? rect.x + rect.width
+                    : rect.width;
+            // 1 is a fix for Linux-GTK
+            oldCell.getControl().redraw(x, rect.y - 1, width, rect.height + 1,
+                    true);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IBaseLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.IBaseLabelProvider;
+
+import org.eclipse.jface.viewers.ILabelProviderListener;
+
+import java.lang.all;
+
+/**
+ * A label provider maps an element of the viewer's model to
+ * an optional image and optional text string used to display
+ * the element in the viewer's control.  Certain label providers
+ * may allow multiple labels per element.
+ * This is an "abstract interface", defining methods common
+ * to all label providers, but does not actually define the methods
+ * to get the label(s) for an element.  This interface should never
+ * be directly implemented.
+ * Most viewers will take either an <code>ILabelProvider</code> or
+ * an <code>ITableLabelProvider</code>.
+ * <p>
+ * A label provider must not be shared between viewers
+ * since a label provider generally manages SWT resources (images),
+ * which must be disposed when the viewer is disposed.
+ * To simplify life cycle management, the current label provider
+ * of a viewer is disposed when the viewer is disposed.
+ * </p>
+ * <p>
+ * Label providers can be used outside the context of viewers wherever
+ * images are needed.  When label providers are used in this fashion
+ * it is the responsibility of the user to ensure <code>dispose</code>
+ * is called when the provider is no longer needed.
+ * </p>
+ *
+ * @see ILabelProvider
+ * @see ITableLabelProvider
+ */
+public interface IBaseLabelProvider {
+    /**
+     * Adds a listener to this label provider.
+     * Has no effect if an identical listener is already registered.
+     * <p>
+     * Label provider listeners are informed about state changes
+     * that affect the rendering of the viewer that uses this label provider.
+     * </p>
+     *
+     * @param listener a label provider listener
+     */
+    public void addListener(ILabelProviderListener listener);
+
+    /**
+     * Disposes of this label provider.  When a label provider is
+     * attached to a viewer, the viewer will automatically call
+     * this method when the viewer is being closed.  When label providers
+     * are used outside of the context of a viewer, it is the client's
+     * responsibility to ensure that this method is called when the
+     * provider is no longer needed.
+     */
+    public void dispose();
+
+    /**
+     * Returns whether the label would be affected
+     * by a change to the given property of the given element.
+     * This can be used to optimize a non-structural viewer update.
+     * If the property mentioned in the update does not affect the label,
+     * then the viewer need not update the label.
+     *
+     * @param element the element
+     * @param property the property
+     * @return <code>true</code> if the label would be affected,
+     *    and <code>false</code> if it would be unaffected
+     */
+    public bool isLabelProperty(Object element, String property);
+
+    /**
+     * Removes a listener to this label provider.
+     * Has no affect if an identical listener is not registered.
+     *
+     * @param listener a label provider listener
+     */
+    public void removeListener(ILabelProviderListener listener);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IBasicPropertyConstants.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.IBasicPropertyConstants;
+
+import java.lang.all;
+
+/**
+ * Predefined property names used for elements displayed in viewers.
+ *
+ * @see StructuredViewer#update(Object, String[])
+ * @see StructuredViewer#update(Object[], String[])
+ * @see IBaseLabelProvider#isLabelProperty
+ * @see ViewerComparator#isSorterProperty
+ * @see ViewerFilter#isFilterProperty
+ */
+public interface IBasicPropertyConstants {
+
+    /**
+     * Property name constant (value <code>"org.eclipse.jface.text"</code>)
+     * for an element's label text.
+     *
+     * @see org.eclipse.jface.viewers.ILabelProvider#getText
+     */
+    public static final String P_TEXT = "org.eclipse.jface.text"; //$NON-NLS-1$
+
+    /**
+     * Property name constant (value <code>"org.eclipse.jface.image"</code>)
+     * for an element's label image.
+     *
+     * @see org.eclipse.jface.viewers.ILabelProvider#getImage
+     */
+    public static final String P_IMAGE = "org.eclipse.jface.image"; //$NON-NLS-1$
+
+    /**
+     * Property name constant (value <code>"org.eclipse.jface.children"</code>)
+     * for an element's children.
+     */
+    public static final String P_CHILDREN = "org.eclipse.jface.children"; //$NON-NLS-1$
+
+    /**
+     * Property name constant (value <code>"org.eclipse.jface.parent"</code>)
+     * for an element's parent object.
+     */
+    public static final String P_PARENT = "org.eclipse.jface.parent"; //$NON-NLS-1$
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ICellEditorListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.ICellEditorListener;
+
+import java.lang.all;
+
+/**
+ * A listener which is notified of significant events in the
+ * life of a cell editor.
+ * <p>
+ * This interface should be implemented by classes that wish to
+ * react to cell editor activity.
+ * </p>
+ * <p>
+ * Note: the cell editor is not passed as a parameter to any
+ * of these methods; so the assumption is that the listener
+ * knows which cell editor is talking to it.
+ * </p>
+ */
+public interface ICellEditorListener {
+    /**
+     * Notifies that the end user has requested applying a value.
+     * All cell editors send this notification.
+     * <p>
+     * The normal reaction is to update the model with the current cell editor value.
+     * However, if the value is not valid, it should not be applied. 
+     * A typical text-based cell editor would send this message
+     * when the end user hits Return, whereas other editors would
+     * send it whenever their value changes.
+     * </p>
+     */
+    public void applyEditorValue();
+
+    /**
+     * Notifies that the end user has canceled editing.
+     * All cell editors send this notification.
+     * A listener should <b>not</b> update the model based on this
+     * notification; see <code>applyEditorValue</code>.
+     */
+    public void cancelEditor();
+
+    /**
+     * Notifies that the end user is changing the value in the cell editor. This
+     * notification is normally sent only by text-based editors in response to a
+     * keystroke, so that the listener may show an error message reflecting the
+     * current valid state. This notification is sent while the value is being
+     * actively edited, before the value is applied or canceled.  A listener should
+     * <b>not</b> update the model based on this notification; see
+     * <code>applyEditorValue</code>.
+     * <p>
+     * If the <code>newValidState</code> parameter is <code>true</code>,
+     * the new value may be retrieved by calling <code>ICellEditor.getValue</code>
+     * on the appropriate cell editor.
+     * </p>
+     * 
+     * @param oldValidState the valid state before the end user changed the value
+     * @param newValidState the current valid state
+     */
+    public void editorValueChanged(bool oldValidState, bool newValidState);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ICellEditorValidator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ICellEditorValidator;
+
+import java.lang.all;
+
+/**
+ * An interface for validating a cell editor's input.
+ * <p>
+ * This interface should be implemented by classes that wish to
+ * act as cell editor validators.
+ * </p>
+ */
+public interface ICellEditorValidator {
+    /**
+     * Returns a string indicating whether the given value is valid;
+     * <code>null</code> means valid, and non-<code>null</code> means
+     * invalid, with the result being the error message to display
+     * to the end user.
+     * <p>
+     * It is the responsibility of the implementor to fully format the
+     * message before returning it.
+     * </p>
+     *
+     * @param value the value to be validated
+     * @return the error message, or <code>null</code> indicating
+     *  that the value is valid
+     */
+    public String isValid(Object value);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ICellModifier.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ICellModifier;
+
+import java.lang.all;
+
+/**
+ * A cell modifier is used to access the data model from a cell
+ * editor in an abstract way. It offers methods to:
+ * <ul>
+ *  <li>to check if a a model element's property can be edited or not</li>
+ *  <li>retrieve a value a model element's property</li>
+ *  <li>to store a cell editor's value back into the model
+ *    element's property</li>
+ * </ul>
+ * <p>
+ * This interface should be implemented by classes that wish to
+ * act as cell modifiers.
+ * </p>
+ */
+public interface ICellModifier {
+    /**
+     * Checks whether the given property of the given element can be
+     * modified.
+     *
+     * @param element the element
+     * @param property the property
+     * @return <code>true</code> if the property can be modified,
+     *   and <code>false</code> if it is not modifiable
+     */
+    public bool canModify(Object element, String property);
+
+    /**
+     * Returns the value for the given property of the given element.
+     * Returns <code>null</code> if the element does not have the given property.
+     *
+     * @param element the element
+     * @param property the property
+     * @return the property value
+     */
+    public Object getValue(Object element, String property);
+
+    /**
+     * Modifies the value for the given property of the given element.
+     * Has no effect if the element does not have the given property,
+     * or if the property cannot be modified.
+     * <p>
+     * Note that it is possible for an SWT Item to be passed instead of
+     * the model element. To handle this case in a safe way, use:
+     * <pre>
+     *     if (element instanceof Item) {
+     *         element = ((Item) element).getData();
+     *     }
+     *     // modify the element's property here
+     * </pre>
+     * </p>
+     *
+     * @param element the model element or SWT Item (see above)
+     * @param property the property
+     * @param value the new property value
+     *
+     * @see org.eclipse.swt.widgets.Item
+     */
+    public void modify(Object element, String property, Object value);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ICheckStateListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ICheckStateListener;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+
+/**
+ * A listener which is notified of changes to the checked
+ * state of items in checkbox viewers.
+ *
+ * @see CheckStateChangedEvent
+ */
+public interface ICheckStateListener {
+    /**
+     * Notifies of a change to the checked state of an element.
+     *
+     * @param event event object describing the change
+     */
+    void checkStateChanged(CheckStateChangedEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ICheckable.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.ICheckable;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.ICheckStateListener;
+
+/**
+ * Interface for objects that support elements with a checked state.
+ *
+ * @see ICheckStateListener
+ * @see CheckStateChangedEvent
+ */
+public interface ICheckable {
+    /**
+     * Adds a listener for changes to the checked state of elements
+     * in this viewer.
+     * Has no effect if an identical listener is already registered.
+     *
+     * @param listener a check state listener
+     */
+    public void addCheckStateListener(ICheckStateListener listener);
+
+    /**
+     * Returns the checked state of the given element.
+     *
+     * @param element the element
+     * @return <code>true</code> if the element is checked,
+     *   and <code>false</code> if not checked
+     */
+    public bool getChecked(Object element);
+
+    /**
+     * Removes the given check state listener from this viewer.
+     * Has no effect if an identical listener is not registered.
+     *
+     * @param listener a check state listener
+     */
+    public void removeCheckStateListener(ICheckStateListener listener);
+
+    /**
+     * Sets the checked state for the given element in this viewer.
+     * Does not fire events to check state listeners.
+     *
+     * @param element the element
+     * @param state <code>true</code> if the item should be checked,
+     *  and <code>false</code> if it should be unchecked
+     * @return <code>true</code> if the checked state could be set,
+     *  and <code>false</code> otherwise
+     */
+    public bool setChecked(Object element, bool state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IColorDecorator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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 org.eclipse.jface.viewers.IColorDecorator;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Color;
+
+/**
+ * The IColorDecorator is an interface for objects that return a color to
+ * decorate either the foreground and background colors for displaying an
+ * an object.
+ * 
+ * If an IColorDecorator decorates a foreground or background in an object 
+ * that also has an IColorProvider the IColorDecorator will take precedence.
+ * @see IColorProvider
+ * 
+ * @since 3.1
+ */
+public interface IColorDecorator {
+    
+    /**
+     * Return the foreground Color for element or <code>null</code> if there
+     * is not one.
+     * @param element
+     * @return Color or <code>null</code>
+     */
+    public Color decorateForeground(Object element);
+    
+    /**
+     * Return the background Color for element or <code>null</code> if there
+     * is not one.
+     * @param element
+     * @return Color or <code>null</code>
+     */
+    public Color decorateBackground(Object element);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IColorProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.IColorProvider;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Color;
+
+/**
+ * Interface to provide color representation for a given element.
+ * @see org.eclipse.jface.viewers.IColorDecorator
+ */
+public interface IColorProvider {
+
+    /**
+     * Provides a foreground color for the given element.
+     * 
+     * @param element the element
+     * @return  the foreground color for the element, or <code>null</code> 
+     *   to use the default foreground color
+     */
+    Color getForeground(Object element);
+
+    /**
+     * Provides a background color for the given element.
+     * 
+     * @param element the element
+     * @return  the background color for the element, or <code>null</code> 
+     *   to use the default background color
+     */
+    Color getBackground(Object element);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IContentProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.IContentProvider;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * A content provider mediates between the viewer's model
+ * and the viewer itself.
+ *
+ * @see org.eclipse.jface.viewers.ContentViewer#setContentProvider(IContentProvider)
+ */
+public interface IContentProvider {
+    /**
+     * Disposes of this content provider.
+     * This is called by the viewer when it is disposed.
+     * <p>
+     * The viewer should not be updated during this call, as it is in the process
+     * of being disposed.
+     * </p>
+     */
+    public void dispose();
+
+    /**
+     * Notifies this content provider that the given viewer's input
+     * has been switched to a different element.
+     * <p>
+     * A typical use for this method is registering the content provider as a listener
+     * to changes on the new input (using model-specific means), and deregistering the viewer
+     * from the old input. In response to these change notifications, the content provider
+     * should update the viewer (see the add, remove, update and refresh methods on the viewers).
+     * </p>
+     * <p>
+     * The viewer should not be updated during this call, as it might be in the process
+     * of being disposed.
+     * </p>
+     *
+     * @param viewer the viewer
+     * @param oldInput the old input element, or <code>null</code> if the viewer
+     *   did not previously have an input
+     * @param newInput the new input element, or <code>null</code> if the viewer
+     *   does not have an input
+     */
+    public void inputChanged(Viewer viewer, Object oldInput, Object newInput);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IDecoration.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.IDecoration;
+
+import org.eclipse.jface.viewers.IDecorationContext;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Defines the result of decorating an element.
+ *
+ * This interface is not meant to be implemented and will be provided to
+ * instances of <code>ILightweightLabelDecorator</code>.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IDecoration{
+
+    /**
+     * Constants for placement of image decorations.
+     */
+    public static const int TOP_LEFT = 0;
+
+    /**
+     * Constant for the top right quadrant.
+     */
+    public static const int TOP_RIGHT = 1;
+
+    /**
+     * Constant for the bottom left quadrant.
+     */
+    public static const int BOTTOM_LEFT = 2;
+
+    /**
+     * Constant for the bottom right quadrant.
+     */
+    public static const int BOTTOM_RIGHT = 3;
+
+    /**
+     * Constant for the underlay.
+     */
+    public static const int UNDERLAY = 4;
+
+    /**
+     * Constant for replacing the original image. Note that for this to have an
+     * effect on the resulting decorated image, {@link #ENABLE_REPLACE} has to
+     * be set to {@link Boolean#TRUE} in the {@link IDecorationContext} (opt-in
+     * model). If replacement behavior is enabled, the resulting decorated image
+     * will be constructed by first painting the underlay, then the replacement
+     * image, and then the regular quadrant images.
+     * 
+     * @since 3.4
+     */
+    public static final int REPLACE = 5;
+    
+    /**
+     * Constant that is used as the property key on an
+     * {@link IDecorationContext}. To enable image replacement, set to
+     * {@link Boolean#TRUE}.
+     * 
+     * @since 3.4
+     * @see IDecorationContext
+     */
+    public static final String ENABLE_REPLACE = "org.eclipse.jface.viewers.IDecoration.disableReplace"; //$NON-NLS-1$
+    
+    /**
+     * Adds a prefix to the element's label.
+     *
+     * @param prefix
+     *            the prefix
+     */
+    public void addPrefix(String prefix);
+
+    /**
+     * Adds a suffix to the element's label.
+     *
+     * @param suffix
+     *            the suffix
+     */
+    public void addSuffix(String suffix);
+
+    /**
+     * Adds an overlay to the element's image.
+     *
+     * @param overlay
+     *            the overlay image descriptor
+     */
+    public void addOverlay(ImageDescriptor overlay);
+
+    /**
+     * Adds an overlay to the element's image.
+     *
+     * @param overlay
+     *            the overlay image descriptor
+     * @param quadrant
+     *            The constant for the quadrant to draw the image on.
+     */
+    public void addOverlay(ImageDescriptor overlay, int quadrant);
+
+    /**
+     * Set the foreground color for this decoration.
+     * @param color the color to be set for the foreground
+     *
+     * @since 3.1
+     */
+    public void setForegroundColor(Color color);
+
+    /**
+     * Set the background color for this decoration.
+     * @param color the color to be set for the background
+     *
+     * @since 3.1
+     */
+    public void setBackgroundColor(Color color);
+
+    /**
+     * Set the font for this decoration.
+     * @param font the font to use in this decoration
+     *
+     * @since 3.1
+     */
+    public void setFont(Font font);
+
+    /**
+     * Return the decoration context in which this decoration
+     * will be applied.
+     * @return the decoration context
+     *
+     * @since 3.2
+     */
+    public IDecorationContext getDecorationContext();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IDecorationContext.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.viewers.IDecorationContext;
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * A decoration context provides additional information to
+ * a label decorator.
+ * <p>
+ * This interface is not intended to be implemented by clients
+ *
+ * @see LabelDecorator
+ *
+ * @since 3.2
+ */
+public interface IDecorationContext {
+
+    /**
+     * Get the value of the given property or <code>null</code>
+     * if the property does not exist in this context.
+     * @param property the property
+     * @return the value of the given property or <code>null</code>
+     */
+    Object getProperty(String property);
+
+    /**
+     * Return the properties that exist in this context
+     * (i.e. the set of properties that have values associated
+     * with them.
+     * @return the properties that exist in this context
+     */
+    String[] getProperties();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IDelayedLabelDecorator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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 org.eclipse.jface.viewers.IDelayedLabelDecorator;
+
+import org.eclipse.jface.viewers.ILabelDecorator;
+
+import java.lang.all;
+
+/**
+ * A delayed label decorator is a label decorator that may not have a
+ * decoration available immediately. This interface defines the methods for
+ * requesting the preparation of a decorator for an object and for querying
+ * if the decorator is ready. Interested parties should register an
+ * ILabelProviderListener with a delayed label decorator in order to be informed
+ * when the decoration is ready.
+ * @since 3.0
+ */
+public interface IDelayedLabelDecorator : ILabelDecorator {
+
+    /**
+     * Prepare the element for decoration. If it is already decorated and ready for update
+     * return true. If decoration is pending return false.
+     * @param element The element to be decorated
+     * @param originalText The starting text.
+     * @return bool <code>true</code> if the decoration is ready for this element
+     */
+
+    public bool prepareDecoration(Object element, String originalText);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IDoubleClickListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.IDoubleClickListener;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.DoubleClickEvent;
+
+/**
+ * A listener which is notified of double-click events on viewers.
+ */
+public interface IDoubleClickListener {
+    /**
+     * Notifies of a double click.
+     *
+     * @param event event object describing the double-click
+     */
+    public void doubleClick(DoubleClickEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IElementComparer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.IElementComparer;
+
+import java.lang.all;
+
+/**
+ * This interface is used to compare elements in a viewer for equality,
+ * and to provide the hash code for an element.
+ * This allows the client of the viewer to specify different equality criteria
+ * and a different hash code implementation than the
+ * <code>equals</code> and <code>hashCode</code> implementations of the
+ * elements themselves.
+ *
+ * @see StructuredViewer#setComparer
+ */
+public interface IElementComparer {
+
+    /**
+     * Compares two elements for equality
+     *
+     * @param a the first element
+     * @param b the second element
+     * @return whether a is equal to b
+     */
+    int opEquals(Object a, Object b);
+
+    /**
+     * Returns the hash code for the given element.
+     * @param element the element the hash code is calculated for
+     *
+     * @return the hash code for the given element
+     */
+    hash_t toHash(Object element);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IFilter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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 org.eclipse.jface.viewers.IFilter;
+
+import java.lang.all;
+
+/**
+ * Interface for filters. Can accept or reject items.
+ * 
+ * @since 3.1
+ */
+public interface IFilter {
+    /**
+     * Determines if the given object passes this filter.
+     * 
+     * @param toTest object to compare against the filter 
+     * 
+     * @return <code>true</code> if the object is accepted by the filter.
+     */
+    public bool select(Object toTest);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IFontDecorator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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 org.eclipse.jface.viewers.IFontDecorator;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Font;
+
+/**
+ * The IFontDecorator is an interface for objects that return a font to
+ * decorate an object.
+ * 
+ * If an IFontDecorator decorates a font in an object that also has
+ * an IFontProvider the IFontDecorator will take precedence.
+ * @see IFontProvider
+ * 
+ * @since 3.1
+ */
+public interface IFontDecorator {
+    
+    /**
+     * Return the font for element or <code>null</code> if there
+     * is not one.
+     * 
+     * @param element
+     * @return Font or <code>null</code>
+     */
+    public Font decorateFont(Object element);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IFontProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.IFontProvider;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Font;
+
+/**
+ * Interface to provide font representation for a given element.
+ * @see org.eclipse.jface.viewers.IFontDecorator
+ * 
+ * @since 3.0
+ */
+public interface IFontProvider {
+
+    /**
+     * Provides a font for the given element.
+     * 
+     * @param element the element
+     * @return the font for the element, or <code>null</code> 
+     *   to use the default font
+     */
+    public Font getFont(Object element);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IInputProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.IInputProvider;
+
+import java.lang.all;
+
+/**
+ * Interface common to all objects that provide an input.
+ */
+public interface IInputProvider {
+    /**
+     * Returns the input.
+     *
+     * @return the input object
+     */
+    public Object getInput();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IInputSelectionProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.IInputSelectionProvider;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.IInputProvider;
+import org.eclipse.jface.viewers.ISelectionProvider;
+
+/**
+ * Interface common to all objects that provide both an input and
+ * a selection.
+ */
+public interface IInputSelectionProvider : IInputProvider,
+        ISelectionProvider {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ILabelDecorator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ILabelDecorator;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+
+import org.eclipse.swt.graphics.Image;
+
+import java.lang.all;
+
+/**
+ * A label decorator decorates the label text and image for some element.
+ * The original label text and image are obtained by some other means,
+ * for example by a label provider.
+ *
+ * @see ILabelProvider
+ */
+public interface ILabelDecorator : IBaseLabelProvider {
+    /**
+     * Returns an image that is based on the given image,
+     * but decorated with additional information relating to the state
+     * of the provided element.
+     *
+     * Text and image decoration updates can occur as a result of other updates
+     * within the workbench including deferred decoration by background processes.
+     * Clients should handle labelProviderChangedEvents for the given element to get
+     * the complete decoration.
+     * @see LabelProviderChangedEvent
+     * @see IBaseLabelProvider#addListener
+     *
+     * @param image the input image to decorate, or <code>null</code> if the element has no image
+     * @param element the element whose image is being decorated
+     * @return the decorated image, or <code>null</code> if no decoration is to be applied
+     *
+     * @see org.eclipse.jface.resource.CompositeImageDescriptor
+     */
+    public Image decorateImage(Image image, Object element);
+
+    /**
+     * Returns a text label that is based on the given text label,
+     * but decorated with additional information relating to the state
+     * of the provided element.
+     *
+     * Text and image decoration updates can occur as a result of other updates
+     * within the workbench including deferred decoration by background processes.
+     * Clients should handle labelProviderChangedEvents for the given element to get
+     * the complete decoration.
+     * @see LabelProviderChangedEvent
+     * @see IBaseLabelProvider#addListener
+     *
+     * @param text the input text label to decorate
+     * @param element the element whose image is being decorated
+     * @return the decorated text label, or <code>null</code> if no decoration is to be applied
+     */
+    public String decorateText(String text, Object element);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ILabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ILabelProvider;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+
+import org.eclipse.swt.graphics.Image;
+
+import java.lang.all;
+
+/**
+ * Extends <code>IBaseLabelProvider</code> with the methods
+ * to provide the text and/or image for the label of a given element.
+ * Used by most structured viewers, except table viewers.
+ */
+public interface ILabelProvider : IBaseLabelProvider {
+    /**
+     * Returns the image for the label of the given element.  The image
+     * is owned by the label provider and must not be disposed directly.
+     * Instead, dispose the label provider when no longer needed.
+     *
+     * @param element the element for which to provide the label image
+     * @return the image used to label the element, or <code>null</code>
+     *   if there is no image for the given object
+     */
+    public Image getImage(Object element);
+
+    /**
+     * Returns the text for the label of the given element.
+     *
+     * @param element the element for which to provide the label text
+     * @return the text string used to label the element, or <code>null</code>
+     *   if there is no text label for the given object
+     */
+    public String getText(Object element);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ILabelProviderListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ILabelProviderListener;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.LabelProviderChangedEvent;
+
+/**
+ * A listener which is notified when a label provider's state changes.
+ *
+ * @see IBaseLabelProvider#addListener
+ * @see IBaseLabelProvider#removeListener
+ */
+public interface ILabelProviderListener {
+    /**
+     * Notifies this listener that the state of the label provider
+     * has changed in a way that affects the labels it computes.
+     * <p>
+     * A typical response would be to refresh all labels by
+     * re-requesting them from the label provider.
+     * </p>
+     *
+     * @param event the label provider change event
+     */
+    public void labelProviderChanged(LabelProviderChangedEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ILazyContentProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.jface.viewers.ILazyContentProvider;
+
+import org.eclipse.jface.viewers.IContentProvider;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The ILazyContentProvider is the content provider
+ * for table viewers created using the SWT.VIRTUAL flag that
+ * only wish to return their contents as they are queried.
+ *
+ * <strong>NOTE:</strong> As the ILazyContentProvider does
+ * not have API for determining the total item count any
+ * changes to the number of items for this object while
+ * require a call to <code>#setItemCount</code> on the
+ * viewer that uses it.
+ */
+public interface ILazyContentProvider : IContentProvider {
+    /**
+     * Called when a previously-blank item becomes visible in the
+     * TableViewer. If the content provider knows the element
+     * at this row, it should respond by calling
+     * TableViewer#replace(Object, int).
+     *
+     * <strong>NOTE</strong> #updateElement(int index) can be used to determine selection
+     * values. TableViewer#replace(Object, int) is not called before
+     * returning from this method selections may have missing or stale elements.
+     * In this situation it is suggested that the selection is asked
+     * for again after he update.
+     *
+     * @param index The index that is being updated in the
+     * table.
+     */
+    public void updateElement(int index);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ILazyTreeContentProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.ILazyTreeContentProvider;
+
+import org.eclipse.jface.viewers.IContentProvider;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The ILazyTreeContentProvider is the content provider for tree viewers created
+ * using the SWT.VIRTUAL flag that only wish to return their contents as they
+ * are queried.
+ *
+ * @since 3.2
+ */
+public interface ILazyTreeContentProvider : IContentProvider {
+    /**
+     * Called when a previously-blank item becomes visible in the TreeViewer. If
+     * the content provider knows the child element for the given parent at this
+     * index, it should respond by calling
+     * {@link TreeViewer#replace(Object, int, Object)}. The content provider
+     * should also update the child count for any replaced element by calling
+     * {@link TreeViewer#setChildCount(Object, int)}. If the given current child
+     * count is already correct, setChildCount does not have to be called since
+     * a call to replace will not change the child count.
+     *
+     * <strong>NOTE</strong> #updateElement(int index) can be used to determine
+     * selection values. If TableViewer#replace(Object, int) is not called
+     * before returning from this method, selections may have missing or stale
+     * elements. In this situation it is suggested that the selection is asked
+     * for again after replace() has been called.
+     *
+     * @param parent
+     *            The parent of the element, or the viewer's input if the
+     *            element to update is a root element
+     * @param index
+     *            The index of the element to update in the tree
+     */
+    public void updateElement(Object parent, int index);
+
+    /**
+     * Called when the TreeViewer needs an up-to-date child count for the given
+     * element, for example from {@link TreeViewer#refresh()} and
+     * {@link TreeViewer#setInput(Object)}. If the content provider knows the
+     * given element, it should respond by calling
+     * {@link TreeViewer#setChildCount(Object, int)}. If the given current
+     * child count is already correct, no action has to be taken by this content
+     * provider.
+     *
+     * @param element
+     *            The element for which an up-to-date child count is needed, or
+     *            the viewer's input if the number of root elements is requested
+     * @param currentChildCount
+     *            The current child count for the element that needs updating
+     */
+    public void updateChildCount(Object element, int currentChildCount);
+
+    /**
+     * Returns the parent for the given element, or <code>null</code>
+     * indicating that the parent can't be computed.
+     * In this case the tree-structured viewer can't expand
+     * a given node correctly if requested.
+     *
+     * @param element the element
+     * @return the parent element, or <code>null</code> if it
+     *   has none or if the parent cannot be computed
+     */
+    public Object getParent(Object element);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ILazyTreePathContentProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.ILazyTreePathContentProvider;
+
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.TreePath;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The ILazyTreePathContentProvider is a tree path-based content provider for
+ * tree viewers created using the SWT.VIRTUAL flag that only wish to return
+ * their contents as they are queried.
+ *
+ * @since 3.3
+ */
+public interface ILazyTreePathContentProvider : IContentProvider {
+    /**
+     * Called when a previously-blank item becomes visible in the TreeViewer. If
+     * the content provider knows the child element for the given parent at this
+     * index, it should respond by calling
+     * {@link TreeViewer#replace(Object, int, Object)}. The content provider
+     * should also update the child count for any replaced element by calling
+     * {@link TreeViewer#setChildCount(Object, int)}. If the given current child
+     * count is already correct, setChildCount does not have to be called since
+     * a call to replace will not change the child count.
+     *
+     * <strong>NOTE</strong> #updateElement(int index) can be used to determine
+     * selection values. If TableViewer#replace(Object, int) is not called
+     * before returning from this method, selections may have missing or stale
+     * elements. In this situation it is suggested that the selection is asked
+     * for again after replace() has been called.
+     *
+     * @param parentPath
+     *            The tree path of parent of the element, or if the
+     *            element to update is a root element, an empty tree path
+     * @param index
+     *            The index of the element to update in the tree
+     */
+    public void updateElement(TreePath parentPath, int index);
+
+    /**
+     * Called when the TreeViewer needs an up-to-date child count for the given
+     * tree path, for example from {@link TreeViewer#refresh()} and
+     * {@link TreeViewer#setInput(Object)}. If the content provider knows the
+     * element at the given tree path, it should respond by calling
+     * {@link TreeViewer#setChildCount(Object, int)}. If the given current
+     * child count is already correct, no action has to be taken by this content
+     * provider.
+     *
+     * @param treePath
+     *            The tree path for which an up-to-date child count is needed, or
+     *            if the number of root elements is requested, the empty tree path
+     * @param currentChildCount
+     *            The current child count for the element that needs updating
+     */
+    public void updateChildCount(TreePath treePath, int currentChildCount);
+
+    /**
+     * Called when the TreeViewer needs up-to-date information whether the node
+     * at the given tree path can be expanded. If the content provider knows the
+     * element at the given tree path, it should respond by calling
+     * {@link TreeViewer#setHasChildren(Object, bool)}. The content provider
+     * may also choose to call {@link TreeViewer#setChildCount(Object, int)}
+     * instead if it knows the number of children.
+     *
+     * <p>
+     * Intended as an optimization for when the viewer does not need the actual
+     * children. Clients may be able to implement this more efficiently than
+     * <code>updateChildCount</code>.
+     * </p>
+     *
+     * @param path
+     *            The tree path for which up-to-date information about children
+     *            is needed
+     */
+    public void updateHasChildren(TreePath path);
+
+    /**
+     * Return the possible parent paths for the given element. An empty array
+     * can be returned if the paths cannot be computed. In this case the
+     * tree-structured viewer can't expand a given node correctly if requested.
+     * If the element is a potential child of the input of the viewer, an empty
+     * tree path should be an entry in the returned array.
+     *
+     * @param element
+     *            the element
+     * @return the possible parent paths for the given element
+     */
+    public TreePath[] getParents(Object element);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ILightweightLabelDecorator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ILightweightLabelDecorator;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.IDecoration;
+
+/**
+ * The <code>ILightweightLabelDecorator</code> is a decorator that decorates
+ * using a prefix, suffix and overlay image rather than doing all
+ * of the image and text management itself like an <code>ILabelDecorator</code>.
+ */
+public interface ILightweightLabelDecorator : IBaseLabelProvider {
+
+    /**
+     * Calculates decorations based on element.
+     *
+     * @param element the element to decorate
+     * @param decoration the decoration to set
+     */
+    public void decorate(Object element, IDecoration decoration);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IOpenListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.IOpenListener;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.OpenEvent;
+
+/**
+ * A listener which is notified of open events on viewers.
+ */
+public interface IOpenListener {
+    /**
+     * Notifies of an open event.
+     *
+     * @param event event object describing the open event
+     */
+    public void open(OpenEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IPostSelectionProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.IPostSelectionProvider;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+
+/**
+ * Selection provider extension interface to allow providers
+ * to notify about post selection changed events.
+ * A post selection changed event is equivalent to selection changed event
+ * if the selection change was triggered by the mouse, but it has a delay
+ * if the selection change is triggered by keyboard navigation.
+ *
+ * @see ISelectionProvider
+ *
+ * @since 3.0
+ */
+public interface IPostSelectionProvider : ISelectionProvider {
+
+    /**
+     * Adds a listener for post selection changes in this selection provider.
+     * Has no effect if an identical listener is already registered.
+     *
+     * @param listener a selection changed listener
+     */
+    public void addPostSelectionChangedListener(
+            ISelectionChangedListener listener);
+
+    /**
+     * Removes the given listener for post selection changes from this selection
+     * provider.
+     * Has no affect if an identical listener is not registered.
+     *
+     * @param listener a selection changed listener
+     */
+    public void removePostSelectionChangedListener(
+            ISelectionChangedListener listener);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ISelection.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ISelection;
+
+import java.lang.all;
+
+/**
+ * Interface for a selection.
+ *
+ * @see ISelectionProvider
+ * @see ISelectionChangedListener
+ * @see SelectionChangedEvent
+ */
+public interface ISelection {
+
+    /**
+     * Returns whether this selection is empty.
+     *
+     * @return <code>true</code> if this selection is empty,
+     *   and <code>false</code> otherwise
+     */
+    public bool isEmpty();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ISelectionChangedListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ISelectionChangedListener;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+
+/**
+ * A listener which is notified when a viewer's selection changes.
+ *
+ * @see ISelection
+ * @see ISelectionProvider
+ * @see SelectionChangedEvent
+ */
+public interface ISelectionChangedListener {
+    /**
+     * Notifies that the selection has changed.
+     *
+     * @param event event object describing the change
+     */
+    public void selectionChanged(SelectionChangedEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ISelectionProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ISelectionProvider;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelection;
+
+/**
+ * Interface common to all objects that provide a selection.
+ *
+ * @see ISelection
+ * @see ISelectionChangedListener
+ * @see SelectionChangedEvent
+ */
+public interface ISelectionProvider {
+    /**
+     * Adds a listener for selection changes in this selection provider.
+     * Has no effect if an identical listener is already registered.
+     *
+     * @param listener a selection changed listener
+     */
+    public void addSelectionChangedListener(ISelectionChangedListener listener);
+
+    /**
+     * Returns the current selection for this provider.
+     *
+     * @return the current selection
+     */
+    public ISelection getSelection();
+
+    /**
+     * Removes the given selection change listener from this selection provider.
+     * Has no affect if an identical listener is not registered.
+     *
+     * @param listener a selection changed listener
+     */
+    public void removeSelectionChangedListener(
+            ISelectionChangedListener listener);
+
+    /**
+     * Sets the current selection for this selection provider.
+     *
+     * @param selection the new selection
+     */
+    public void setSelection(ISelection selection);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IStructuredContentProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.IStructuredContentProvider;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.IContentProvider;
+
+/**
+ * An interface to content providers for structured viewers.
+ *
+ * @see StructuredViewer
+ */
+public interface IStructuredContentProvider : IContentProvider {
+    /**
+     * Returns the elements to display in the viewer
+     * when its input is set to the given element.
+     * These elements can be presented as rows in a table, items in a list, etc.
+     * The result is not modified by the viewer.
+     * <p>
+     * <b>NOTE:</b> For instances where the viewer is displaying a tree
+     * containing a single 'root' element it is still necessary that the
+     * 'input' does not return <i>itself</i> from this method. This leads
+     * to recursion issues (see bug 9262).
+     * </p>
+     * @param inputElement the input element
+     * @return the array of elements to display in the viewer
+     */
+    public Object[] getElements(Object inputElement);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IStructuredSelection.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.IStructuredSelection;
+
+import java.lang.all;
+import java.util.List;
+import java.util.Iterator;
+
+import org.eclipse.jface.viewers.ISelection;
+
+/**
+ * A selection containing elements.
+ */
+public interface IStructuredSelection : ISelection {
+    /**
+     * Returns the first element in this selection, or <code>null</code>
+     * if the selection is empty.
+     *
+     * @return an element, or <code>null</code> if none
+     */
+    public Object getFirstElement();
+
+    /**
+     * Returns an iterator over the elements of this selection.
+     *
+     * @return an iterator over the selected elements
+     */
+    public Iterator iterator();
+
+    /**
+     * Returns the number of elements selected in this selection.
+     *
+     * @return the number of elements selected
+     */
+    public int size();
+
+    /**
+     * Returns the elements in this selection as an array.
+     *
+     * @return the selected elements as an array
+     */
+    public Object[] toArray();
+
+    /**
+     * Returns the elements in this selection as a <code>List</code>.
+     * <strong>Note</strong> In the default implementation of {@link #toList()} in
+     * {@link StructuredSelection} the returned list is not a copy of the elements of the
+     * receiver and modifying it will modify the contents of the selection.
+     *
+     * @return the selected elements as a list
+     */
+    public List toList();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ITableColorProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Initial implementation - Gunnar Ahlberg (IBS AB, www.ibs.net)
+ *     IBM Corporation - further revisions
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.ITableColorProvider;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Color;
+
+/**
+ * Interface to provide color representation for a given cell within
+ * the row for an element in a table.
+ * @since 3.1
+ */
+public interface ITableColorProvider {
+
+    /**
+     * Provides a foreground color for the given element.
+     * 
+     * @param element the element
+     * @param columnIndex the zero-based index of the column in which
+     *  the color appears
+     * @return the foreground color for the element, or <code>null</code> to
+     *         use the default foreground color
+     */
+    Color getForeground(Object element, int columnIndex);
+
+    /**
+     * Provides a background color for the given element at the specified index
+     * 
+     * @param element the element
+     * @param columnIndex the zero-based index of the column in which the color appears
+     * @return the background color for the element, or <code>null</code> to
+     *         use the default background color
+     *  
+     */
+    Color getBackground(Object element, int columnIndex);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ITableFontProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.ITableFontProvider;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.Font;
+
+/**
+ * The ITableFontProvider is a font provider that provides fonts to 
+ * individual cells within tables.
+ * @since 3.1
+ */
+public interface ITableFontProvider {
+    
+    /**
+     * Provides a font for the given element at index
+     * columnIndex.
+     * @param element The element being displayed
+     * @param columnIndex The index of the column being displayed
+     * @return Font
+     */
+    public Font getFont(Object element, int columnIndex);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ITableLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ITableLabelProvider;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+
+import org.eclipse.swt.graphics.Image;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Extends <code>IBaseLabelProvider</code> with the methods
+ * to provide the text and/or image for each column of a given element.
+ * Used by table viewers.
+ *
+ * @see TableViewer
+ */
+public interface ITableLabelProvider : IBaseLabelProvider {
+    /**
+     * Returns the label image for the given column of the given element.
+     *
+     * @param element the object representing the entire row, or
+     *    <code>null</code> indicating that no input object is set
+     *    in the viewer
+     * @param columnIndex the zero-based index of the column in which
+     *   the label appears
+     * @return Image or <code>null</code> if there is no image for the
+     *  given object at columnIndex
+     */
+    public Image getColumnImage(Object element, int columnIndex);
+
+    /**
+     * Returns the label text for the given column of the given element.
+     *
+     * @param element the object representing the entire row, or
+     *   <code>null</code> indicating that no input object is set
+     *   in the viewer
+     * @param columnIndex the zero-based index of the column in which the label appears
+     * @return String or or <code>null</code> if there is no text for the
+     *  given object at columnIndex
+     */
+    public String getColumnText(Object element, int columnIndex);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ITreeContentProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ITreeContentProvider;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+
+/**
+ * An interface to content providers for tree-structure-oriented
+ * viewers.
+ *
+ * @see AbstractTreeViewer
+ */
+public interface ITreeContentProvider : IStructuredContentProvider {
+    /**
+     * Returns the child elements of the given parent element.
+     * <p>
+     * The difference between this method and <code>IStructuredContentProvider.getElements</code>
+     * is that <code>getElements</code> is called to obtain the
+     * tree viewer's root elements, whereas <code>getChildren</code> is used
+     * to obtain the children of a given parent element in the tree (including a root).
+     * </p>
+     * The result is not modified by the viewer.
+     *
+     * @param parentElement the parent element
+     * @return an array of child elements
+     */
+    public Object[] getChildren(Object parentElement);
+
+    /**
+     * Returns the parent for the given element, or <code>null</code>
+     * indicating that the parent can't be computed.
+     * In this case the tree-structured viewer can't expand
+     * a given node correctly if requested.
+     *
+     * @param element the element
+     * @return the parent element, or <code>null</code> if it
+     *   has none or if the parent cannot be computed
+     */
+    public Object getParent(Object element);
+
+    /**
+     * Returns whether the given element has children.
+     * <p>
+     * Intended as an optimization for when the viewer does not
+     * need the actual children.  Clients may be able to implement
+     * this more efficiently than <code>getChildren</code>.
+     * </p>
+     *
+     * @param element the element
+     * @return <code>true</code> if the given element has children,
+     *  and <code>false</code> if it has no children
+     */
+    public bool hasChildren(Object element);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ITreePathContentProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.viewers.ITreePathContentProvider;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TreePath;
+
+/**
+ * An interface to content providers for tree-structure-oriented viewers that
+ * provides content based on the path of elements in the tree viewer..
+ *
+ * @see AbstractTreeViewer
+ * @since 3.2
+ */
+public interface ITreePathContentProvider : IStructuredContentProvider {
+
+    /**
+     * Returns the child elements of the last element in the given path.
+     * Implementors may want to use the additional context of the complete path
+     * of a parent element in order to decide which children to return.
+     * <p>
+     * The provided path is relative to the input. The root elements must
+     * be obtained by calling
+     * {@link IStructuredContentProvider#getElements(Object)}.
+     * </p>
+     * The result is not modified by the viewer.
+     *
+     * @param parentPath
+     *            the path of the parent element
+     * @return an array of child elements
+     */
+    public Object[] getChildren(TreePath parentPath);
+
+    /**
+     * Returns whether the last element of the given path has children.
+     * <p>
+     * Intended as an optimization for when the viewer does not need the actual
+     * children. Clients may be able to implement this more efficiently than
+     * <code>getChildren</code>.
+     * </p>
+     *
+     * @param path
+     *            the path
+     * @return <code>true</code> if the lat element of the path has children,
+     *         and <code>false</code> if it has no children
+     */
+    public bool hasChildren(TreePath path);
+
+    /**
+     * Return the possible parent paths for the given element. An empty array
+     * can be returned if the paths cannot be computed. If the element is
+     * a potential child of the input of the viewer, an empty tree path
+     * should be an entry in the returned array.
+     *
+     * @param element
+     *            the element
+     * @return the possible parent paths for the given element
+     */
+    public TreePath[] getParents(Object element);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ITreePathLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.viewers.ITreePathLabelProvider;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ViewerLabel;
+import org.eclipse.jface.viewers.TreePath;
+
+/**
+ * An extension to {@link ILabelProvider} that is given the
+ * path of the element being decorated, when it is available.
+ * @since 3.2
+ */
+public interface ITreePathLabelProvider : IBaseLabelProvider {
+
+    /**
+     * Updates the label for the given element.
+     *
+     * @param label the label to update
+     * @param elementPath the path of the element being decorated
+     */
+    public void updateLabel(ViewerLabel label, TreePath elementPath);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ITreeSelection.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.jface.viewers.ITreeSelection;
+
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TreePath;
+
+import java.lang.all;
+
+/**
+ * A selection containing tree paths.
+ * <p>
+ * It is recommended that clients do not implement this interface but instead
+ * use the standard implementation of this interface, {@link TreeSelection}.
+ * <code>TreeSelection</code> adds API for getting the {@link IElementComparer}
+ * of a selection (if available). This is important for clients who want to
+ * create a slightly modified tree selection based on an existing tree selection.
+ * The recommended coding pattern in this case is as follows:
+ * <pre>
+ * ITreeSelection selection = (ITreeSelection)treeViewer.getSelection();
+ * TreePath[] paths = selection.getPaths();
+ * IElementComparer comparer = null;
+ * if (selection instanceof TreeSelection) {
+ *   comparer = ((TreeSelection)selection).getElementComparer();
+ * }
+ * TreePath[] modifiedPaths = ... // modify as required
+ * TreeSelection modifiedSelection = new TreeSelection(modifiedPaths, comparer);
+ * </pre>
+ * See bugs 135818 and 133375 for details.
+ * </p>
+ *
+ * @since 3.2
+ *
+ */
+public interface ITreeSelection : IStructuredSelection {
+
+    /**
+     * Returns the paths in this selection
+     *
+     * @return the paths in this selection
+     */
+    public TreePath[] getPaths();
+
+    /**
+     * Returns the paths in this selection whose last segment is equal
+     * to the given element
+     *
+     * @param element the element to get the tree paths for
+     *
+     * @return the array of tree paths
+     */
+    public TreePath[] getPathsFor(Object element);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ITreeViewerListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ITreeViewerListener;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.TreeExpansionEvent;
+
+/**
+ * A listener which is notified when a tree viewer expands or collapses
+ * a node.
+ */
+public interface ITreeViewerListener {
+    /**
+     * Notifies that a node in the tree has been collapsed.
+     *
+     * @param event event object describing details
+     */
+    public void treeCollapsed(TreeExpansionEvent event);
+
+    /**
+     * Notifies that a node in the tree has been expanded.
+     *
+     * @param event event object describing details
+     */
+    public void treeExpanded(TreeExpansionEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/IViewerLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.IViewerLabelProvider;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ViewerLabel;
+
+/**
+ * Extends <code>IBaseLabelProvider</code> with the methods
+ * to update the label for a given element.  The label is represented by a
+ * <code>ViewerLabel</code>.
+ * Unlike <code>ILabelProvider</code>, this allows the text and image to be
+ * set in the same request, rather than via separate requests.
+ * <p>
+ * It also allows the current values for the text and image to be considered by
+ * the label provider, allowing for potential optimizations.
+ * For example, decorating label providers that run in the background can hold off
+ * applying an update to a previously populated label until the decoration is ready,
+ * thereby reducing flicker.
+ * </p>
+ *
+ * @see IDelayedLabelDecorator
+ * @since 3.0
+ */
+public interface IViewerLabelProvider : IBaseLabelProvider {
+
+    /**
+     * Updates the label for the given element.
+     *
+     * @param label the label to update
+     * @param element the element
+     */
+    public void updateLabel(ViewerLabel label, Object element);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/LabelDecorator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.viewers.LabelDecorator;
+
+import org.eclipse.jface.viewers.ILabelDecorator;
+import org.eclipse.jface.viewers.IDecorationContext;
+
+import org.eclipse.swt.graphics.Image;
+
+import java.lang.all;
+
+/**
+ * The LabelDecorator is an abstract superclass of ILabelDecorators
+ * that support IDecorationContext.
+ * @see IDecorationContext
+ * @since 3.2
+ *
+ */
+public abstract class LabelDecorator : ILabelDecorator {
+
+     /**
+     * Returns an image that is based on the given image,
+     * but decorated with additional information relating to the state
+     * of the provided element taking into account the provided context.
+     *
+     * Text and image decoration updates can occur as a result of other updates
+     * within the workbench including deferred decoration by background processes.
+     * Clients should handle labelProviderChangedEvents for the given element to get
+     * the complete decoration.
+     * @see LabelProviderChangedEvent
+     * @see IBaseLabelProvider#addListener
+     *
+     * @param image the input image to decorate, or <code>null</code> if the element has no image
+     * @param element the element whose image is being decorated
+     * @param context additional context information about the element being decorated
+     * @return the decorated image, or <code>null</code> if no decoration is to be applied
+     *
+     * @see org.eclipse.jface.resource.CompositeImageDescriptor
+     */
+    public abstract Image decorateImage(Image image, Object element, IDecorationContext context);
+
+    /**
+     * Returns a text label that is based on the given text label,
+     * but decorated with additional information relating to the state
+     * of the provided element taking into account the provided context.
+     *
+     * Text and image decoration updates can occur as a result of other updates
+     * within the workbench including deferred decoration by background processes.
+     * Clients should handle labelProviderChangedEvents for the given element to get
+     * the complete decoration.
+     * @see LabelProviderChangedEvent
+     * @see IBaseLabelProvider#addListener
+     *
+     * @param text the input text label to decorate
+     * @param element the element whose image is being decorated
+     * @param context additional context information about the element being decorated
+     * @return the decorated text label, or <code>null</code> if no decoration is to be applied
+     */
+    public abstract String decorateText(String text, Object element, IDecorationContext context);
+
+    /**
+     * Prepare the element for decoration. If it is already decorated and ready for update
+     * return true. If decoration is pending return false.
+     * @param element The element to be decorated
+     * @param originalText The starting text.
+     * @param context The decoration context
+     * @return bool <code>true</code> if the decoration is ready for this element
+     */
+    public abstract bool prepareDecoration(Object element, String originalText, IDecorationContext context);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/LabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.LabelProvider;
+
+import org.eclipse.jface.viewers.BaseLabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+
+import org.eclipse.swt.graphics.Image;
+
+import java.lang.all;
+
+/**
+ * A label provider implementation which, by default, uses an element's
+ * <code>toString</code> value for its text and <code>null</code> for its
+ * image.
+ * <p>
+ * This class may be used as is, or subclassed to provide richer labels.
+ * Subclasses may override any of the following methods:
+ * <ul>
+ * <li><code>isLabelProperty</code></li>
+ * <li><code>getImage</code></li>
+ * <li><code>getText</code></li>
+ * <li><code>dispose</code></li>
+ * </ul>
+ * </p>
+ */
+public class LabelProvider : BaseLabelProvider, ILabelProvider {
+
+    /**
+     * Creates a new label provider.
+     */
+    public this() {
+    }
+
+
+    /**
+     * The <code>LabelProvider</code> implementation of this
+     * <code>ILabelProvider</code> method returns <code>null</code>.
+     * Subclasses may override.
+     */
+    public Image getImage(Object element) {
+        return null;
+    }
+
+    /**
+     * The <code>LabelProvider</code> implementation of this
+     * <code>ILabelProvider</code> method returns the element's
+     * <code>toString</code> string. Subclasses may override.
+     */
+    public String getText(Object element) {
+        return element is null ? "" : element.toString();//$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/LabelProviderChangedEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.LabelProviderChangedEvent;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+// import java.util.EventObject;
+
+import java.lang.all;
+import java.util.EventObject;
+
+/**
+ * Event object describing a label provider state change.
+ *
+ * @see ILabelProviderListener
+ */
+public class LabelProviderChangedEvent : EventObject {
+
+    /**
+     * Generated serial version UID for this class.
+     * @since 3.1
+     */
+    private static const long serialVersionUID = 3258410612479309878L;
+
+    /**
+     * The elements whose labels need to be updated or <code>null</code>.
+     */
+    private Object[] elements;
+
+    /**
+     * Creates a new event for the given source, indicating that all labels
+     * provided by the source are no longer valid and should be updated.
+     *
+     * @param source the label provider
+     */
+    public this(IBaseLabelProvider source) {
+        super(cast(Object)source);
+    }
+
+    /**
+     * Creates a new event for the given source, indicating that the label
+     * provided by the source for the given elements is no longer valid and should be updated.
+     *
+     * @param source the label provider
+     * @param elements the element whose labels have changed
+     */
+    public this(IBaseLabelProvider source,
+            Object[] elements) {
+        super(cast(Object)source);
+        this.elements = elements;
+    }
+
+    /**
+     * Creates a new event for the given source, indicating that the label
+     * provided by the source for the given element is no longer valid and should be updated.
+     *
+     * @param source the label provider
+     * @param element the element whose label needs to be updated
+     */
+    public this(IBaseLabelProvider source, Object element) {
+        super(cast(Object)source);
+        this.elements = new Object[1];
+        this.elements[0] = element;
+    }
+
+    /**
+     * Returns the first element whose label needs to be updated,
+     * or <code>null</code> if all labels need to be updated.
+     *
+     * @return the element whose label needs to be updated or <code>null</code>
+     */
+    public Object getElement() {
+        if (this.elements is null || this.elements.length is 0) {
+            return null;
+        } else {
+            return this.elements[0];
+        }
+    }
+
+    /**
+     * Returns the elements whose labels need to be updated,
+     * or <code>null</code> if all labels need to be updated.
+     *
+     * @return the element whose labels need to be updated or <code>null</code>
+     */
+    public Object[] getElements() {
+        if (this.elements is null) {
+            return null;
+        } else {
+            return this.elements;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ListViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,251 @@
+/*******************************************************************************
+ * 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
+ *     Brad Reynolds - bug 141435
+ *     Tom Schindl <tom.schindl@bestsolution.at> - bug 157309, 177619
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.ListViewer;
+
+import org.eclipse.jface.viewers.AbstractListViewer;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+static import org.eclipse.swt.widgets.List;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A concrete viewer based on an SWT <code>List</code> control.
+ * <p>
+ * This class is not intended to be subclassed. It is designed to be
+ * instantiated with a pre-existing SWT <code>List</code> control and configured
+ * with a domain-specific content provider, label provider, element filter (optional),
+ * and element sorter (optional).
+ * <p>
+ * Note that the SWT <code>List</code> control only supports the display of strings, not icons.
+ * If you need to show icons for items, use <code>TableViewer</code> instead.
+ * </p>
+ *
+ * @see TableViewer
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ListViewer : AbstractListViewer {
+    alias AbstractListViewer.setSelectionToWidget setSelectionToWidget;
+
+    /**
+     * This viewer's list control.
+     */
+    private org.eclipse.swt.widgets.List.List list;
+
+    /**
+     * Creates a list viewer on a newly-created list control under the given parent.
+     * The list control is created using the SWT style bits <code>MULTI, H_SCROLL, V_SCROLL,</code> and <code>BORDER</code>.
+     * The viewer has no input, no content provider, a default label provider,
+     * no sorter, and no filters.
+     *
+     * @param parent the parent control
+     */
+    public this(Composite parent) {
+        this(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
+    }
+
+    /**
+     * Creates a list viewer on a newly-created list control under the given parent.
+     * The list control is created using the given SWT style bits.
+     * The viewer has no input, no content provider, a default label provider,
+     * no sorter, and no filters.
+     *
+     * @param parent the parent control
+     * @param style the SWT style bits
+     */
+    public this(Composite parent, int style) {
+        this(new org.eclipse.swt.widgets.List.List(parent, style));
+    }
+
+    /**
+     * Creates a list viewer on the given list control.
+     * The viewer has no input, no content provider, a default label provider,
+     * no sorter, and no filters.
+     *
+     * @param list the list control
+     */
+    public this(org.eclipse.swt.widgets.List.List list) {
+        this.list = list;
+        hookControl(list);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on Viewer.
+     */
+    public override Control getControl() {
+        return list;
+    }
+
+    /**
+     * Returns this list viewer's list control.
+     *
+     * @return the list control
+     */
+    public org.eclipse.swt.widgets.List.List getList() {
+        return list;
+    }
+
+    /*
+     * Non-Javadoc.
+     * Method defined on StructuredViewer.
+     */
+    public override void reveal(Object element) {
+        Assert.isNotNull(element);
+        int index = getElementIndex(element);
+        if (index is -1) {
+            return;
+        }
+        // algorithm patterned after List.showSelection()
+        int count = list.getItemCount();
+        if (count is 0) {
+            return;
+        }
+        int height = list.getItemHeight();
+        Rectangle rect = list.getClientArea();
+        int topIndex = list.getTopIndex();
+        int visibleCount = Math.max(rect.height / height, 1);
+        int bottomIndex = Math.min(topIndex + visibleCount, count) - 1;
+        if ((topIndex <= index) && (index <= bottomIndex)) {
+            return;
+        }
+        int newTop = Math.min(Math.max(index - (visibleCount / 2), 0),
+                count - 1);
+        list.setTopIndex(newTop);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#listAdd(java.lang.String, int)
+     */
+    protected override void listAdd(String string, int index) {
+        list.add(string, index);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#listSetItem(int, java.lang.String)
+     */
+    protected override void listSetItem(int index, String string) {
+        list.setItem(index, string);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#listGetSelectionIndices()
+     */
+    protected override int[] listGetSelectionIndices() {
+        return list.getSelectionIndices();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#listGetItemCount()
+     */
+    protected override int listGetItemCount() {
+        return list.getItemCount();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#listSetItems(java.lang.String[])
+     */
+    protected override void listSetItems(String[] labels) {
+        list.setItems(labels);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#listRemoveAll()
+     */
+    protected override void listRemoveAll() {
+        list.removeAll();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#listRemove(int)
+     */
+    protected override void listRemove(int index) {
+        list.remove(index);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#listSelectAndShow(int[])
+     */
+    protected override void listSetSelection(int[] ixs) {
+        list.setSelection(ixs);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#listDeselectAll()
+     */
+    protected override void listDeselectAll() {
+        list.deselectAll();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#listShowSelection()
+     */
+    protected override void listShowSelection() {
+        list.showSelection();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#listGetTopIndex()
+     */
+    protected override int listGetTopIndex() {
+        return list.getTopIndex();
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#listSetTopIndex(int)
+     */
+    protected override void listSetTopIndex(int index) {
+        list.setTopIndex(index);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractListViewer#setSelectionToWidget(java.util.List, bool)
+     */
+    protected override void setSelectionToWidget(List in_, bool reveal) {
+        if( reveal ) {
+            super.setSelectionToWidget(in_, reveal);
+        } else {
+            if (in_ is null || in_.size() is 0) { // clear selection
+                list.deselectAll();
+            } else {
+                int n = in_.size();
+                int[] ixs = new int[n];
+                int count = 0;
+                for (int i = 0; i < n; ++i) {
+                    Object el = in_.get(i);
+                    int ix = getElementIndex(el);
+                    if (ix >= 0) {
+                        ixs[count++] = ix;
+                    }
+                }
+                if (count < n) {
+                    System.arraycopy(ixs, 0, ixs = new int[count], 0, count);
+                }
+                list.deselectAll();
+                list.select(ixs);
+            }
+        }
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/NamedHandleObjectLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.jface.viewers.NamedHandleObjectLabelProvider;
+
+import org.eclipse.jface.viewers.DialogCellEditor;
+import org.eclipse.jface.viewers.LabelProvider;
+
+
+import org.eclipse.core.commands.common.NamedHandleObject;
+import org.eclipse.core.commands.common.NotDefinedException;
+
+import java.lang.all;
+
+/**
+ * A label provider for instances of <code>NamedHandlerObject</code>, which
+ * exposes the name as the label.
+ *
+ * @since 3.2
+ */
+public final class NamedHandleObjectLabelProvider : LabelProvider {
+    
+    /**
+     * The text of the element is simply the name of the element if its a
+     * defined instance of <code>NamedHandleObject</code>. Otherwise, this
+     * method just returns <code>null</code>.
+     * 
+     * @param element
+     *            The element for which the text should be retrieved; may be
+     *            <code>null</code>.
+     * @return the name of the handle object; <code>null</code> if there is no
+     *         name or if the element is not a named handle object.
+     */
+    public override final String getText(Object element) {
+        if ( cast(NamedHandleObject)element ) {
+            try {
+                return (cast(NamedHandleObject) element).getName();
+            } catch (NotDefinedException e) {
+                return null;
+            }
+        }
+
+        return null;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/OpenEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.OpenEvent;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.Viewer;
+
+// import java.util.EventObject;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.EventObject;
+
+/**
+ * Event object describing an open which may be generated from a
+ * selection or default selection event. The source of these
+ * events is a viewer.
+ *
+ * @see IOpenListener
+ */
+public class OpenEvent : EventObject {
+
+    /**
+     * Generated serial version UID for this class.
+     * @since 3.1
+     */
+    private static const long serialVersionUID = 3761972652973176117L;
+
+    /**
+     * The selection.
+     */
+    protected ISelection selection;
+
+    /**
+     * Creates a new event for the given source and selection.
+     *
+     * @param source the viewer
+     * @param selection the selection
+     */
+    public this(Viewer source, ISelection selection) {
+        super(source);
+        Assert.isNotNull(cast(Object)selection);
+        this.selection = selection;
+    }
+
+    /**
+     * Returns the selection.
+     *
+     * @return the selection
+     */
+    public ISelection getSelection() {
+        return selection;
+    }
+
+    /**
+     * Returns the viewer that is the source of this event.
+     *
+     * @return the originating viewer
+     */
+    public Viewer getViewer() {
+        return cast(Viewer) getSource();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/OwnerDrawLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,291 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.viewers.OwnerDrawLabelProvider;
+
+import org.eclipse.jface.viewers.CellLabelProvider;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ViewerColumn;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+
+import java.lang.all;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * OwnerDrawLabelProvider is an abstract implementation of a label provider that
+ * handles custom draw.
+ *
+ * <p>
+ * <b>This class is intended to be subclassed by implementors.</b>
+ * </p>
+ *
+ * @since 3.3
+ *
+ */
+public abstract class OwnerDrawLabelProvider : CellLabelProvider {
+
+    alias CellLabelProvider.dispose dispose;
+
+    static class OwnerDrawListener : Listener {
+        Set enabledColumns;
+        int enabledGlobally = 0;
+        private ColumnViewer viewer;
+
+        this(ColumnViewer viewer) {
+            enabledColumns = new HashSet();
+            this.viewer = viewer;
+        }
+
+        public void handleEvent(Event event) {
+            CellLabelProvider provider = viewer.getViewerColumn(event.index)
+                    .getLabelProvider();
+            ViewerColumn column = viewer.getViewerColumn(event.index);
+            if (enabledGlobally > 0 || enabledColumns.contains(column)) {
+                if (null !is cast(OwnerDrawLabelProvider)provider ) {
+                    Object element = event.item.getData();
+                    OwnerDrawLabelProvider ownerDrawProvider = cast(OwnerDrawLabelProvider) provider;
+                    switch (event.type) {
+                    case SWT.MeasureItem:
+                        ownerDrawProvider.measure(event, element);
+                        break;
+                    case SWT.PaintItem:
+                        ownerDrawProvider.paint(event, element);
+                        break;
+                    case SWT.EraseItem:
+                        ownerDrawProvider.erase(event, element);
+                        break;
+                    default:
+                    }
+                }
+            }
+        }
+    }
+
+    private static const String OWNER_DRAW_LABEL_PROVIDER_LISTENER = "owner_draw_label_provider_listener"; //$NON-NLS-1$
+
+    /**
+     * Set up the owner draw callbacks for the viewer.
+     *
+     * @param viewer
+     *            the viewer the owner draw is set up
+     *
+     * @deprecated Since 3.4, the default implementation of
+     *             {@link CellLabelProvider#initialize(ColumnViewer, ViewerColumn)}
+     *             in this class will set up the necessary owner draw callbacks
+     *             automatically. Calls to this method can be removed.
+     */
+    public static void setUpOwnerDraw(ColumnViewer viewer) {
+        getOrCreateOwnerDrawListener(viewer).enabledGlobally++;
+    }
+
+    /**
+     * @param viewer
+     * @param control
+     * @return
+     */
+    private static OwnerDrawListener getOrCreateOwnerDrawListener(
+            ColumnViewer viewer) {
+        Control control = viewer.getControl();
+        OwnerDrawListener listener = cast(OwnerDrawListener) control
+                .getData(OWNER_DRAW_LABEL_PROVIDER_LISTENER);
+        if (listener is null) {
+            listener = new OwnerDrawListener(viewer);
+            control.setData(OWNER_DRAW_LABEL_PROVIDER_LISTENER, listener);
+            control.addListener(SWT.MeasureItem, listener);
+            control.addListener(SWT.EraseItem, listener);
+            control.addListener(SWT.PaintItem, listener);
+        }
+        return listener;
+    }
+
+    /**
+     * Create a new instance of the receiver based on a column viewer.
+     *
+     */
+    public this() {
+
+    }
+
+    public void dispose(ColumnViewer viewer, ViewerColumn column) {
+        if (!viewer.getControl().isDisposed()) {
+            setOwnerDrawEnabled(viewer, column, false);
+        }
+        super.dispose(viewer, column);
+    }
+
+    /**
+     * This implementation of
+     * {@link CellLabelProvider#initialize(ColumnViewer, ViewerColumn)}
+     * delegates to {@link #initialize(ColumnViewer, ViewerColumn, bool)}
+     * with a value of <code>true</code> for <code>enableOwnerDraw</code>.
+     * Subclasses may override this method but should either call the super
+     * implementation or, alternatively,
+     * {@link #initialize(ColumnViewer, ViewerColumn, bool)}.
+     */
+    protected void initialize(ColumnViewer viewer, ViewerColumn column) {
+        this.initialize(viewer, column, true);
+    }
+
+    /**
+     * May be called from subclasses that override
+     * {@link #initialize(ColumnViewer, ViewerColumn)} but want to customize
+     * whether owner draw will be enabled. This method calls
+     * <code>super.initialize(ColumnViewer, ViewerColumn)</code>, and then
+     * enables or disables owner draw by calling
+     * {@link #setOwnerDrawEnabled(ColumnViewer, ViewerColumn, bool)}.
+     *
+     * @param viewer
+     *            the viewer
+     * @param column
+     *            the column, or <code>null</code> if a column is not
+     *            available.
+     * @param enableOwnerDraw
+     *            <code>true</code> if owner draw should be enabled for the
+     *            given viewer and column, <code>false</code> otherwise.
+     *
+     * @since 3.4
+     */
+    final protected void initialize(ColumnViewer viewer, ViewerColumn column,
+            bool enableOwnerDraw) {
+        super.initialize(viewer, column);
+        setOwnerDrawEnabled(viewer, column, enableOwnerDraw);
+    }
+
+    public void update(ViewerCell cell) {
+        // Force a redraw
+        Rectangle cellBounds = cell.getBounds();
+        cell.getControl().redraw(cellBounds.x, cellBounds.y, cellBounds.width,
+                cellBounds.height, true);
+
+    }
+
+    /**
+     * Handle the erase event. The default implementation colors the background
+     * of selected areas with {@link SWT#COLOR_LIST_SELECTION} and foregrounds
+     * with {@link SWT#COLOR_LIST_SELECTION_TEXT}. Note that this
+     * implementation causes non-native behavior on some platforms. Subclasses
+     * should override this method and <b>not</b> call the super
+     * implementation.
+     *
+     * @param event
+     *            the erase event
+     * @param element
+     *            the model object
+     * @see SWT#EraseItem
+     * @see SWT#COLOR_LIST_SELECTION
+     * @see SWT#COLOR_LIST_SELECTION_TEXT
+     */
+    protected void erase(Event event, Object element) {
+
+        Rectangle bounds = event.getBounds();
+        if ((event.detail & SWT.SELECTED) !is 0) {
+
+            Color oldForeground = event.gc.getForeground();
+            Color oldBackground = event.gc.getBackground();
+
+            event.gc.setBackground(event.item.getDisplay().getSystemColor(
+                    SWT.COLOR_LIST_SELECTION));
+            event.gc.setForeground(event.item.getDisplay().getSystemColor(
+                    SWT.COLOR_LIST_SELECTION_TEXT));
+            event.gc.fillRectangle(bounds);
+            /* restore the old GC colors */
+            event.gc.setForeground(oldForeground);
+            event.gc.setBackground(oldBackground);
+            /* ensure that default selection is not drawn */
+            event.detail &= ~SWT.SELECTED;
+
+        }
+
+    }
+
+    /**
+     * Handle the measure event.
+     *
+     * @param event
+     *            the measure event
+     * @param element
+     *            the model element
+     * @see SWT#MeasureItem
+     */
+    protected abstract void measure(Event event, Object element);
+
+    /**
+     * Handle the paint event.
+     *
+     * @param event
+     *            the paint event
+     * @param element
+     *            the model element
+     * @see SWT#PaintItem
+     */
+    protected abstract void paint(Event event, Object element);
+
+    /**
+     * Enables or disables owner draw for the given viewer and column. This
+     * method will attach or remove a listener to the underlying control as
+     * necessary. This method is called from
+     * {@link #initialize(ColumnViewer, ViewerColumn)} and
+     * {@link #dispose(ColumnViewer, ViewerColumn)} but may be called from
+     * subclasses to enable or disable owner draw dynamically.
+     *
+     * @param viewer
+     *            the viewer
+     * @param column
+     *            the column, or <code>null</code> if a column is not
+     *            available
+     * @param enabled
+     *            <code>true</code> if owner draw should be enabled,
+     *            <code>false</code> otherwise
+     *
+     * @since 3.4
+     */
+    protected void setOwnerDrawEnabled(ColumnViewer viewer,
+            ViewerColumn column, bool enabled) {
+        if (enabled) {
+            OwnerDrawListener listener = getOrCreateOwnerDrawListener(viewer);
+            if (column is null) {
+                listener.enabledGlobally++;
+            } else {
+                listener.enabledColumns.add(column);
+            }
+        } else {
+            OwnerDrawListener listener = cast(OwnerDrawListener) viewer
+                    .getControl().getData(OWNER_DRAW_LABEL_PROVIDER_LISTENER);
+            if (listener !is null) {
+                if (column is null) {
+                    listener.enabledGlobally--;
+                } else {
+                    listener.enabledColumns.remove(column);
+                }
+                if (listener.enabledColumns.isEmpty()
+                        && listener.enabledGlobally <= 0) {
+                    viewer.getControl().removeListener(SWT.MeasureItem,
+                            listener);
+                    viewer.getControl().removeListener(SWT.EraseItem, listener);
+                    viewer.getControl().removeListener(SWT.PaintItem, listener);
+                    viewer.getControl().setData(
+                            OWNER_DRAW_LABEL_PROVIDER_LISTENER, null);
+                }
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/SWTFocusCellManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                               - bug fix for bug 187189, 182800, 215069
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.SWTFocusCellManager;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.CellNavigationStrategy;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ViewerRow;
+import org.eclipse.jface.viewers.FocusCellHighlighter;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * This class is responsible to provide cell management base features for the
+ * SWT-Controls {@link org.eclipse.swt.widgets.Table} and
+ * {@link org.eclipse.swt.widgets.Tree}.
+ *
+ * @since 3.3
+ *
+ */
+abstract class SWTFocusCellManager {
+
+    private CellNavigationStrategy navigationStrategy;
+
+    private ColumnViewer viewer;
+
+    private ViewerCell focusCell;
+
+    private FocusCellHighlighter cellHighlighter;
+
+    private DisposeListener itemDeletionListener;
+
+
+    /**
+     * @param viewer
+     * @param focusDrawingDelegate
+     * @param navigationDelegate
+     */
+    public this(ColumnViewer viewer,
+            FocusCellHighlighter focusDrawingDelegate,
+            CellNavigationStrategy navigationDelegate) {
+
+        itemDeletionListener = new class DisposeListener {
+            public void widgetDisposed(DisposeEvent e) {
+                setFocusCell(null);
+            }
+        };
+
+        this.viewer = viewer;
+        this.cellHighlighter = focusDrawingDelegate;
+        this.navigationStrategy = navigationDelegate;
+        hookListener(viewer);
+    }
+
+    /**
+     * This method is called by the framework to initialize this cell manager.
+     */
+    void init() {
+        this.cellHighlighter.init_package();
+        this.navigationStrategy.init_package();
+    }
+
+    private void handleMouseDown(Event event) {
+        ViewerCell cell = viewer.getCell(new Point(event.x, event.y));
+        if (cell !is null) {
+
+            if (!cell.opEquals(focusCell)) {
+                setFocusCell(cell);
+            }
+        }
+    }
+
+    private void handleKeyDown(Event event) {
+        ViewerCell tmp = null;
+
+        if (navigationStrategy.isCollapseEvent(viewer, focusCell, event)) {
+            navigationStrategy.collapse(viewer, focusCell, event);
+        } else if (navigationStrategy.isExpandEvent(viewer, focusCell, event)) {
+            navigationStrategy.expand(viewer, focusCell, event);
+        } else if (navigationStrategy.isNavigationEvent(viewer, event)) {
+            tmp = navigationStrategy.findSelectedCell(viewer, focusCell, event);
+
+            if (tmp !is null) {
+                if (!tmp.opEquals(focusCell)) {
+                    setFocusCell(tmp);
+                }
+            }
+        }
+
+        if (navigationStrategy.shouldCancelEvent(viewer, event)) {
+            event.doit = false;
+        }
+    }
+
+    private void handleSelection(Event event) {
+        if ((event.detail & SWT.CHECK) is 0 && focusCell !is null && focusCell.getItem() !is event.item
+                && event.item !is null ) {
+            ViewerRow row = viewer.getViewerRowFromItem_package(event.item);
+            Assert
+                    .isNotNull(row,
+                            "Internal Structure invalid. Row item has no row ViewerRow assigned"); //$NON-NLS-1$
+            ViewerCell tmp = row.getCell(focusCell.getColumnIndex());
+            if (!focusCell.opEquals(tmp)) {
+                setFocusCell(tmp);
+            }
+        }
+    }
+
+    private void handleFocusIn(Event event) {
+        if (focusCell is null) {
+            setFocusCell(getInitialFocusCell());
+        }
+    }
+
+    abstract ViewerCell getInitialFocusCell();
+
+    private void hookListener(ColumnViewer viewer) {
+        Listener listener = new class Listener {
+
+            public void handleEvent(Event event) {
+                switch (event.type) {
+                case SWT.MouseDown:
+                    handleMouseDown(event);
+                    break;
+                case SWT.KeyDown:
+                    handleKeyDown(event);
+                    break;
+                case SWT.Selection:
+                    handleSelection(event);
+                    break;
+                case SWT.FocusIn:
+                    handleFocusIn(event);
+                    break;
+                default:
+                }
+            }
+        };
+
+        viewer.getControl().addListener(SWT.MouseDown, listener);
+        viewer.getControl().addListener(SWT.KeyDown, listener);
+        viewer.getControl().addListener(SWT.Selection, listener);
+        viewer.addSelectionChangedListener(new class ISelectionChangedListener {
+
+            public void selectionChanged(SelectionChangedEvent event) {
+                if( event.getSelection_package.isEmpty() ) {
+                    setFocusCell(null);
+                }
+            }
+
+        });
+        viewer.getControl().addListener(SWT.FocusIn, listener);
+    }
+
+    /**
+     * @return the cell with the focus
+     *
+     */
+    public ViewerCell getFocusCell() {
+        return focusCell;
+    }
+
+    void setFocusCell(ViewerCell focusCell) {
+        ViewerCell oldCell = this.focusCell;
+
+        if( this.focusCell !is null && ! this.focusCell.getItem().isDisposed() ) {
+            this.focusCell.getItem().removeDisposeListener(itemDeletionListener);
+        }
+
+        this.focusCell = focusCell;
+
+        if( this.focusCell !is null && ! this.focusCell.getItem().isDisposed() ) {
+            this.focusCell.getItem().addDisposeListener(itemDeletionListener);
+        }
+
+        this.cellHighlighter.focusCellChanged_package(focusCell,oldCell);
+    }
+
+    ColumnViewer getViewer() {
+        return viewer;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/SelectionChangedEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.SelectionChangedEvent;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionProvider;
+// import java.util.EventObject;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.EventObject;
+
+/**
+ * Event object describing a selection change. The source of these
+ * events is a selection provider.
+ *
+ * @see ISelection
+ * @see ISelectionProvider
+ * @see ISelectionChangedListener
+ */
+public class SelectionChangedEvent : EventObject {
+
+    /**
+     * Generated serial version UID for this class.
+     * @since 3.1
+     */
+    private static const long serialVersionUID = 3835149545519723574L;
+
+    /**
+     * The selection.
+     */
+    protected ISelection selection;
+    package ISelection getSelection_package(){
+        return selection;
+    }
+    /**
+     * Creates a new event for the given source and selection.
+     *
+     * @param source the selection provider
+     * @param selection the selection
+     */
+    public this(ISelectionProvider source, ISelection selection) {
+        super(cast(Object)source);
+        Assert.isNotNull(cast(Object)selection);
+        this.selection = selection;
+    }
+
+    /**
+     * Returns the selection.
+     *
+     * @return the selection
+     */
+    public ISelection getSelection() {
+        return selection;
+    }
+
+    /**
+     * Returns the selection provider that is the source of this event.
+     *
+     * @return the originating selection provider
+     */
+    public ISelectionProvider getSelectionProvider() {
+        return cast(ISelectionProvider) getSource();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/StructuredSelection.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,211 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.StructuredSelection;
+
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.IElementComparer;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Iterator;
+
+/**
+ * A concrete implementation of the <code>IStructuredSelection</code> interface,
+ * suitable for instantiating.
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class StructuredSelection : IStructuredSelection {
+
+    /**
+     * The element that make up this structured selection.
+     */
+    private Object[] elements;
+
+    /**
+     * The element comparer, or <code>null</code>
+     */
+    private IElementComparer comparer;
+
+    /**
+     * The canonical empty selection. This selection should be used instead of
+     * <code>null</code>.
+     */
+    public static const StructuredSelection EMPTY;
+    static this(){
+        EMPTY = new StructuredSelection();
+    }
+    /**
+     * Creates a new empty selection.
+     * See also the static field <code>EMPTY</code> which contains an empty selection singleton.
+     *
+     * @see #EMPTY
+     */
+    public this() {
+    }
+
+    /**
+     * Creates a structured selection from the given elements.
+     *
+     * @param elements an array of elements
+     */
+    public this(Object[] elements) {
+        this.elements = new Object[elements.length];
+        System.arraycopy(elements, 0, this.elements, 0, elements.length);
+    }
+
+    /**
+     * Creates a structured selection containing a single object.
+     * The object must not be <code>null</code>.
+     *
+     * @param element the element
+     */
+    public this(Object element) {
+        Assert.isNotNull(element);
+        elements = [ element ];
+    }
+
+    /**
+     * Creates a structured selection from the given <code>List</code>.
+     * @param elements list of selected elements
+     */
+    public this(List elements) {
+        this(elements, null);
+    }
+
+    /**
+     * Creates a structured selection from the given <code>List</code> and
+     * element comparer. If an element comparer is provided, it will be used to
+     * determine equality between structured selection objects provided that
+     * they both are based on the same (identical) comparer. See bug
+     *
+     * @param elements
+     *            list of selected elements
+     * @param comparer
+     *            the comparer, or null
+     * @since 3.4
+     */
+    public this(List elements, IElementComparer comparer) {
+        Assert.isNotNull(cast(Object)elements);
+        this.elements = elements.toArray();
+        this.comparer = comparer;
+    }
+
+    /**
+     * Returns whether this structured selection is equal to the given object.
+     * Two structured selections are equal if they contain the same elements
+     * in the same order.
+     *
+     * @param o the other object
+     * @return <code>true</code> if they are equal, and <code>false</code> otherwise
+     */
+    public override int opEquals(Object o) {
+        if (this is o) {
+            return true;
+        }
+        //null and other classes
+        if (!(cast(StructuredSelection)o )) {
+            return false;
+        }
+        StructuredSelection s2 = cast(StructuredSelection) o;
+
+        // either or both empty?
+        if (isEmpty()) {
+            return s2.isEmpty();
+        }
+        if (s2.isEmpty()) {
+            return false;
+        }
+
+        bool useComparer = comparer !is null && comparer is s2.comparer;
+
+        //size
+        int myLen = elements.length;
+        if (myLen !is s2.elements.length) {
+            return false;
+        }
+        //element comparison
+        for (int i = 0; i < myLen; i++) {
+            if (useComparer) {
+                if (!comparer.opEquals(elements[i], s2.elements[i])) {
+                    return false;
+                }
+            } else {
+                if (!elements[i].opEquals(s2.elements[i])) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * Method declared in IStructuredSelection.
+     */
+    public Object getFirstElement() {
+        return isEmpty() ? null : elements[0];
+    }
+
+    /* (non-Javadoc)
+     * Method declared in ISelection.
+     */
+    public bool isEmpty() {
+        return elements is null || elements.length is 0;
+    }
+
+    /* (non-Javadoc)
+     * Method declared in IStructuredSelection.
+     */
+    public Iterator iterator() {
+        return Arrays.asList(elements is null ? null : elements)
+                .iterator();
+    }
+
+    /* (non-Javadoc)
+     * Method declared in IStructuredSelection.
+     */
+    public int size() {
+        return elements is null ? 0 : elements.length;
+    }
+
+    /* (non-Javadoc)
+     * Method declared in IStructuredSelection.
+     */
+    public Object[] toArray() {
+        return elements is null ? new Object[0] : elements.dup;
+    }
+
+    /* (non-Javadoc)
+     * Method declared in IStructuredSelection.
+     */
+    public List toList() {
+        return Arrays.asList(elements is null ? null : elements);
+    }
+
+    /**
+     * Internal method which returns a string representation of this
+     * selection suitable for debug purposes only.
+     *
+     * @return debug string
+     */
+    public override String toString() {
+        return isEmpty() ? JFaceResources.getString("<empty_selection>") : (cast(Object)toList()).toString(); //$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/StructuredViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,2246 @@
+/*******************************************************************************
+ * 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
+ *     Tom Schindl - bug 151205
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.StructuredViewer;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ContentViewer;
+import org.eclipse.jface.viewers.CustomHashtable;
+import org.eclipse.jface.viewers.IElementComparer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.IOpenListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.OpenEvent;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.IColorProvider;
+import org.eclipse.jface.viewers.IFontProvider;
+import org.eclipse.jface.viewers.LabelProviderChangedEvent;
+import org.eclipse.jface.viewers.ViewerLabel;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.ITreePathLabelProvider;
+import org.eclipse.jface.viewers.IViewerLabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+
+
+import org.eclipse.swt.custom.TableTreeItem;
+import org.eclipse.swt.dnd.DragSource;
+import org.eclipse.swt.dnd.DragSourceListener;
+import org.eclipse.swt.dnd.DropTarget;
+import org.eclipse.swt.dnd.DropTargetListener;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.util.IOpenEventListener;
+import org.eclipse.jface.util.OpenStrategy;
+import org.eclipse.jface.util.SafeRunnable;
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.EventListener;
+
+/**
+ * Abstract base implementation for structure-oriented viewers (trees, lists,
+ * tables). Supports custom sorting, filtering, and rendering.
+ * <p>
+ * Any number of viewer filters can be added to this viewer (using
+ * <code>addFilter</code>). When the viewer receives an update, it asks each
+ * of its filters if it is out of date, and refilters elements as required.
+ * </p>
+ *
+ * @see ViewerFilter
+ * @see ViewerComparator
+ */
+public abstract class StructuredViewer : ContentViewer, IPostSelectionProvider {
+    alias ContentViewer.opEquals opEquals;
+    alias ContentViewer.setSelection setSelection;
+    /**
+     * A map from the viewer's model elements to SWT widgets. (key type:
+     * <code>Object</code>, value type: <code>Widget</code>, or <code>Widget[]</code>).
+     * <code>null</code> means that the element map is disabled.
+     */
+    private CustomHashtable elementMap;
+
+    /**
+     * The comparer to use for comparing elements, or <code>null</code> to use
+     * the default <code>equals</code> and <code>hashCode</code> methods on
+     * the element itself.
+     */
+    private IElementComparer comparer;
+
+    /**
+     * This viewer's comparator used for sorting. <code>null</code> means there is no comparator.
+     */
+    private ViewerComparator sorter;
+
+    /**
+     * This viewer's filters (element type: <code>ViewerFilter</code>).
+     * <code>null</code> means there are no filters.
+     */
+    private List filters;
+
+    /**
+     * Indicates whether a selection change is in progress on this viewer.
+     *
+     * @see #setSelection(ISelection, bool)
+     */
+    private bool inChange;
+
+    /**
+     * Used while a selection change is in progress on this viewer to indicates
+     * whether the selection should be restored.
+     *
+     * @see #setSelection(ISelection, bool)
+     */
+    private bool restoreSelection;
+
+    /**
+     * List of double-click state listeners (element type:
+     * <code>IDoubleClickListener</code>).
+     *
+     * @see #fireDoubleClick
+     */
+    private ListenerList doubleClickListeners;
+
+    /**
+     * List of open listeners (element type:
+     * <code>ISelectionActivateListener</code>).
+     *
+     * @see #fireOpen
+     */
+    private ListenerList openListeners;
+
+    /**
+     * List of post selection listeners (element type:
+     * <code>ISelectionActivateListener</code>).
+     *
+     * @see #firePostSelectionChanged
+     */
+    private ListenerList postSelectionChangedListeners;
+
+    /**
+     * The colorAndFontCollector is an object used by viewers that
+     * support the IColorProvider, the IFontProvider and/or the
+     * IViewerLabelProvider for color and font updates.
+     * Initialize it to have no color or font providing
+     * initially.
+     * @since 3.1
+     */
+    private ColorAndFontCollector colorAndFontCollector;
+
+    /**
+     * Empty array of widgets.
+     */
+    private static Widget[] NO_WIDGETS = null;
+
+    /**
+     * The ColorAndFontCollector is a helper class for viewers
+     * that have color and font support ad optionally decorators.
+     * @see IColorDecorator
+     * @see IFontDecorator
+     * @see IColorProvider
+     * @see IFontProvider
+     * @see IDecoration
+     */
+    protected class ColorAndFontCollectorWithProviders : ColorAndFontCollector{
+
+        IColorProvider colorProvider;
+
+        IFontProvider fontProvider;
+
+        /**
+         * Create a new instance of the receiver using the supplied
+         * label provider. If it is an IColorProvider or IFontProvider
+         * set these values up.
+         * @param provider IBaseLabelProvider
+         * @see IColorProvider
+         * @see IFontProvider
+         */
+        public this(IBaseLabelProvider provider) {
+            super();
+            if ( auto cp = cast(IColorProvider) provider ) {
+                colorProvider = cp;
+            }
+            if ( auto fp = cast(IFontProvider) provider ) {
+                fontProvider = fp;
+            }
+        }
+
+
+        /* (non-Javadoc)
+         * @see org.eclipse.jface.viewers.StructuredViewer.ColorAndFontManager#setFontsAndColors(java.lang.Object)
+         */
+        public override void setFontsAndColors(Object element){
+
+            if(fontProvider !is null){
+                if(font is null) {
+                    font = fontProvider.getFont(element);
+                }
+            }
+
+            if(colorProvider is null) {
+                return;
+            }
+            //Set the colors if they are not set yet
+            if(background is null) {
+                background = colorProvider.getBackground(element);
+            }
+
+            if(foreground is null) {
+                foreground = colorProvider.getForeground(element);
+            }
+        }
+
+        /**
+         * Apply the fonts and colors to the control if
+         * required.
+         * @param control
+         */
+        public override void applyFontsAndColors(TableItem control) {
+
+            if(colorProvider is null){
+                if(usedDecorators){
+                    //If there is no provider only apply set values
+                    if(background !is null) {
+                        control.setBackground(background);
+                    }
+
+                    if(foreground !is null) {
+                        control.setForeground(foreground);
+                    }
+                }
+            }
+            else{
+                //Always set the value if there is a provider
+                control.setBackground(background);
+                control.setForeground(foreground);
+            }
+
+            if(fontProvider is null){
+                if(usedDecorators && font !is null) {
+                    control.setFont(font);
+                }
+            } else {
+                control.setFont(font);
+            }
+
+            clear();
+        }
+
+
+
+        /* (non-Javadoc)
+         * @see org.eclipse.jface.viewers.StructuredViewer.ColorAndFontManager#applyFontsAndColors(org.eclipse.swt.widgets.TreeItem)
+         */
+        public override void applyFontsAndColors(TreeItem control) {
+
+            if(colorProvider is null){
+                if(usedDecorators){
+                    //If there is no provider only apply set values
+                    if(background !is null) {
+                        control.setBackground(background);
+                    }
+
+                    if(foreground !is null) {
+                        control.setForeground(foreground);
+                    }
+                }
+            }
+            else{
+                //Always set the value if there is a provider
+                control.setBackground(background);
+                control.setForeground(foreground);
+            }
+
+            if(fontProvider is null){
+                if(usedDecorators && font !is null) {
+                    control.setFont(font);
+                }
+            } else {
+                control.setFont(font);
+            }
+
+            clear();
+        }
+
+
+        /* (non-Javadoc)
+         * @see org.eclipse.jface.viewers.StructuredViewer.ColorAndFontManager#applyFontsAndColors(org.eclipse.swt.custom.TableTreeItem)
+         */
+        public override void applyFontsAndColors(TableTreeItem control) {
+
+            if(colorProvider is null){
+                if(usedDecorators){
+                    //If there is no provider only apply set values
+                    if(background !is null) {
+                        control.setBackground(background);
+                    }
+
+                    if(foreground !is null) {
+                        control.setForeground(foreground);
+                    }
+                }
+            }
+            else{
+                //Always set the value if there is a provider
+                control.setBackground(background);
+                control.setForeground(foreground);
+            }
+
+            if(fontProvider is null){
+                if(usedDecorators && font !is null) {
+                    control.setFont(font);
+                }
+            } else {
+                control.setFont(font);
+            }
+
+            clear();
+        }
+
+
+    }
+
+    /**
+     * The ColorAndFontCollector collects fonts and colors without a
+     * a color or font provider.
+     *
+     */
+    protected class ColorAndFontCollector {
+
+        Color foreground = null;
+
+        Color background = null;
+
+        Font font = null;
+
+        bool usedDecorators = false;
+
+        /**
+         * Create a new instance of the receiver with
+         * no color and font provider.
+         */
+        public this(){
+        }
+
+
+        /**
+         * Clear all of the results.
+         */
+        public void clear() {
+            foreground = null;
+            background = null;
+            font = null;
+            usedDecorators = false;
+        }
+
+
+        /**
+         * Set the initial fonts and colors for the element from the
+         * content providers.
+         * @param element Object
+         */
+        public void setFontsAndColors(Object element){
+            //Do nothing if there are no providers
+        }
+
+        /**
+         * Set that decorators were applied.
+         */
+        public void setUsedDecorators() {
+            this.usedDecorators = true;
+        }
+
+        /**
+         * Apply the fonts and colors to the control if
+         * required.
+         * @param control
+         */
+        public void applyFontsAndColors(TableItem control) {
+
+            if(usedDecorators){
+                //If there is no provider only apply set values
+                if(background !is null) {
+                    control.setBackground(background);
+                }
+
+                if(foreground !is null) {
+                    control.setForeground(foreground);
+                }
+
+                if(font !is null) {
+                    control.setFont(font);
+                }
+            }
+            clear();
+        }
+
+        /**
+         * Apply the fonts and colors to the control if
+         * required.
+         * @param control
+         */
+        public void applyFontsAndColors(TreeItem control) {
+            if(usedDecorators){
+                //If there is no provider only apply set values
+                if(background !is null) {
+                    control.setBackground(background);
+                }
+
+                if(foreground !is null) {
+                    control.setForeground(foreground);
+                }
+
+                if(font !is null) {
+                    control.setFont(font);
+                }
+            }
+            clear();
+        }
+
+        /**
+         * Apply the fonts and colors to the control if
+         * required.
+         * @param control
+         */
+        public void applyFontsAndColors(TableTreeItem control) {
+            if(usedDecorators){
+                //If there is no provider only apply set values
+                if(background !is null) {
+                    control.setBackground(background);
+                }
+
+                if(foreground !is null) {
+                    control.setForeground(foreground);
+                }
+
+                if(font !is null) {
+                    control.setFont(font);
+                }
+            }
+            clear();
+        }
+
+        /**
+         * Set the background color.
+         * @param background
+         */
+        public void setBackground(Color background) {
+            this.background = background;
+        }
+        /**
+         * Set the font.
+         * @param font
+         */
+        public void setFont(Font font) {
+            this.font = font;
+        }
+        /**
+         * Set the foreground color.
+         * @param foreground
+         */
+        public void setForeground(Color foreground) {
+            this.foreground = foreground;
+        }
+
+
+    }
+
+    /**
+     * The safe runnable used to update an item.
+     */
+    class UpdateItemSafeRunnable : SafeRunnable {
+        private Widget widget;
+
+        private Object element;
+
+        private bool fullMap;
+
+        this(Widget widget, Object element, bool fullMap) {
+            this.widget = widget;
+            this.element = element;
+            this.fullMap = fullMap;
+        }
+
+        public void run() {
+            doUpdateItem(widget, element, fullMap);
+        }
+    }
+
+    /**
+     * Creates a structured element viewer. The viewer has no input, no content
+     * provider, a default label provider, no sorter, and no filters.
+     */
+    protected this() {
+        doubleClickListeners = new ListenerList();
+        openListeners = new ListenerList();
+        postSelectionChangedListeners = new ListenerList();
+        colorAndFontCollector = new ColorAndFontCollector();
+        // do nothing
+    }
+
+    /**
+     * Adds a listener for double-clicks in this viewer. Has no effect if an
+     * identical listener is already registered.
+     *
+     * @param listener
+     *            a double-click listener
+     */
+    public void addDoubleClickListener(IDoubleClickListener listener) {
+        doubleClickListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Adds a listener for selection-open in this viewer. Has no effect if an
+     * identical listener is already registered.
+     *
+     * @param listener
+     *            a double-click listener
+     */
+    public void addOpenListener(IOpenListener listener) {
+        openListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPostSelectionProvider.
+     */
+    public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
+        postSelectionChangedListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Adds support for dragging items out of this viewer via a user
+     * drag-and-drop operation.
+     *
+     * @param operations
+     *            a bitwise OR of the supported drag and drop operation types (
+     *            <code>DROP_COPY</code>,<code>DROP_LINK</code>, and
+     *            <code>DROP_MOVE</code>)
+     * @param transferTypes
+     *            the transfer types that are supported by the drag operation
+     * @param listener
+     *            the callback that will be invoked to set the drag data and to
+     *            cleanup after the drag and drop operation finishes
+     * @see org.eclipse.swt.dnd.DND
+     */
+    public void addDragSupport(int operations, Transfer[] transferTypes, DragSourceListener listener) {
+
+        Control myControl = getControl();
+        final DragSource dragSource = new DragSource(myControl, operations);
+        dragSource.setTransfer(transferTypes);
+        dragSource.addDragListener(listener);
+    }
+
+    /**
+     * Adds support for dropping items into this viewer via a user drag-and-drop
+     * operation.
+     *
+     * @param operations
+     *            a bitwise OR of the supported drag and drop operation types (
+     *            <code>DROP_COPY</code>,<code>DROP_LINK</code>, and
+     *            <code>DROP_MOVE</code>)
+     * @param transferTypes
+     *            the transfer types that are supported by the drop operation
+     * @param listener
+     *            the callback that will be invoked after the drag and drop
+     *            operation finishes
+     * @see org.eclipse.swt.dnd.DND
+     */
+    public void addDropSupport(int operations, Transfer[] transferTypes,
+            DropTargetListener listener) {
+        Control control = getControl();
+        DropTarget dropTarget = new DropTarget(control, operations);
+        dropTarget.setTransfer(transferTypes);
+        dropTarget.addDropListener(listener);
+    }
+
+    /**
+     * Adds the given filter to this viewer, and triggers refiltering and
+     * resorting of the elements. If you want to add more than one filter
+     * consider using {@link StructuredViewer#setFilters(ViewerFilter[])}.
+     *
+     * @param filter
+     *            a viewer filter
+     * @see StructuredViewer#setFilters(ViewerFilter[])
+     */
+    public void addFilter(ViewerFilter filter) {
+        if (filters is null) {
+            filters = new ArrayList();
+        }
+        filters.add(filter);
+        refresh();
+    }
+
+    /**
+     * Asserts that the given array of elements is itself non- <code>null</code>
+     * and contains no <code>null</code> elements.
+     *
+     * @param elements
+     *            the array to check
+     */
+    protected void assertElementsNotNull(Object[] elements) {
+//         Assert.isNotNull(elements);
+        for (int i = 0, n = elements.length; i < n; ++i) {
+            Assert.isNotNull(elements[i]);
+        }
+    }
+
+    /**
+     * Associates the given element with the given widget. Sets the given item's
+     * data to be the element, and maps the element to the item in the element
+     * map (if enabled).
+     *
+     * @param element
+     *            the element
+     * @param item
+     *            the widget
+     */
+    protected void associate(Object element, Item item) {
+        Object data = item.getData();
+        if (data !is element) {
+            if (data !is null) {
+                disassociate(item);
+            }
+            item.setData(element);
+        }
+        // Always map the element, even if data is element,
+        // since unmapAllElements() can leave the map inconsistent
+        // See bug 2741 for details.
+        mapElement(element, item);
+    }
+
+    /**
+     * Disassociates the given SWT item from its corresponding element. Sets the
+     * item's data to <code>null</code> and removes the element from the
+     * element map (if enabled).
+     *
+     * @param item
+     *            the widget
+     */
+    protected void disassociate(Item item) {
+        Object element = item.getData();
+        Assert.isNotNull(element);
+        //Clear the map before we clear the data
+        unmapElement(element, item);
+        item.setData(null);
+    }
+
+    /**
+     * Returns the widget in this viewer's control which represents the given
+     * element if it is the viewer's input.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method.
+     * </p>
+     *
+     * @param element
+     * @return the corresponding widget, or <code>null</code> if none
+     */
+    protected abstract Widget doFindInputItem(Object element);
+
+    /**
+     * Returns the widget in this viewer's control which represent the given
+     * element. This method searches all the children of the input element.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method.
+     * </p>
+     *
+     * @param element
+     * @return the corresponding widget, or <code>null</code> if none
+     */
+    protected abstract Widget doFindItem(Object element);
+
+    /**
+     * Copies the attributes of the given element into the given SWT item. The
+     * element map is updated according to the value of <code>fullMap</code>.
+     * If <code>fullMap</code> is <code>true</code> then the current mapping
+     * from element to widgets is removed and the new mapping is added. If
+     * full map is <code>false</code> then only the new map gets installed.
+     * Installing only the new map is necessary in cases where only the order of
+     * elements changes but not the set of elements.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method.
+     * </p>
+     *
+     * @param item
+     * @param element element
+     * @param fullMap
+     *            <code>true</code> if mappings are added and removed, and
+     *            <code>false</code> if only the new map gets installed
+     */
+    protected abstract void doUpdateItem(Widget item, Object element, bool fullMap);
+
+    /**
+     * Compares two elements for equality. Uses the element comparer if one has
+     * been set, otherwise uses the default <code>equals</code> method on the
+     * elements themselves.
+     *
+     * @param elementA
+     *            the first element
+     * @param elementB
+     *            the second element
+     * @return whether elementA is equal to elementB
+     */
+    protected int opEquals(Object elementA, Object elementB) {
+        if (comparer is null) {
+            return elementA is null ? elementB is null : elementA.opEquals(elementB);
+        } else {
+            return elementA is null ? elementB is null : comparer.opEquals(elementA, elementB);
+        }
+    }
+
+    /**
+     * Returns the result of running the given elements through the filters.
+     *
+     * @param elements
+     *            the elements to filter
+     * @return only the elements which all filters accept
+     */
+    protected Object[] filter(Object[] elements) {
+        if (filters !is null) {
+            ArrayList filtered = new ArrayList(elements.length);
+            Object root = getRoot();
+            for (int i = 0; i < elements.length; i++) {
+                bool add = true;
+                for (int j = 0; j < filters.size(); j++) {
+                    add = (cast(ViewerFilter) filters.get(j)).select(this, root, elements[i]);
+                    if (!add) {
+                        break;
+                    }
+                }
+                if (add) {
+                    filtered.add(elements[i]);
+                }
+            }
+            return filtered.toArray();
+        }
+        return elements;
+    }
+
+    /**
+     * Finds the widget which represents the given element.
+     * <p>
+     * The default implementation of this method tries first to find the widget
+     * for the given element assuming that it is the viewer's input; this is
+     * done by calling <code>doFindInputItem</code>. If it is not found
+     * there, it is looked up in the internal element map provided that this
+     * feature has been enabled. If the element map is disabled, the widget is
+     * found via <code>doFindInputItem</code>.
+     * </p>
+     *
+     * @param element
+     *            the element
+     * @return the corresponding widget, or <code>null</code> if none
+     */
+    protected final Widget findItem(Object element) {
+        Widget[] result = findItems(element);
+        return result.length is 0 ? null : result[0];
+    }
+
+    /**
+     * Finds the widgets which represent the given element. The returned array
+     * must not be changed by clients; it might change upon calling other
+     * methods on this viewer.
+     * <p>
+     * This method was introduced to support multiple equal elements in a viewer
+     * (@see {@link AbstractTreeViewer}). Multiple equal elements are only
+     * supported if the element map is enabled by calling
+     * {@link #setUseHashlookup(bool)} and passing <code>true</code>.
+     * </p>
+     * <p>
+     * The default implementation of this method tries first to find the widget
+     * for the given element assuming that it is the viewer's input; this is
+     * done by calling <code>doFindInputItem</code>. If it is not found
+     * there, the widgets are looked up in the internal element map provided
+     * that this feature has been enabled. If the element map is disabled, the
+     * widget is found via <code>doFindInputItem</code>.
+     * </p>
+     *
+     * @param element
+     *            the element
+     * @return the corresponding widgets
+     *
+     * @since 3.2
+     */
+    protected final Widget[] findItems(Object element) {
+        Widget result = doFindInputItem(element);
+        if (result !is null) {
+            return [ result ];
+        }
+        // if we have an element map use it, otherwise search for the item.
+        if (usingElementMap()) {
+            Object widgetOrWidgets = elementMap.get(element);
+            if (widgetOrWidgets is null) {
+                return NO_WIDGETS;
+            } else if ( auto w = cast(Widget) widgetOrWidgets ) {
+                return [w];
+            } else {
+                return (cast(ArrayWrapperT!(Widget))widgetOrWidgets).array;
+            }
+        }
+        result = doFindItem(element);
+        return result is null ? NO_WIDGETS : [ result ];
+    }
+
+    /**
+     * Notifies any double-click listeners that a double-click has been
+     * received. Only listeners registered at the time this method is called are
+     * notified.
+     *
+     * @param event
+     *            a double-click event
+     *
+     * @see IDoubleClickListener#doubleClick
+     */
+    protected void fireDoubleClick(DoubleClickEvent event) {
+        Object[] listeners = doubleClickListeners.getListeners();
+        for (int i = 0; i < listeners.length; ++i) {
+            SafeRunnable.run( dgSafeRunnable( (IDoubleClickListener l, DoubleClickEvent event_){
+                l.doubleClick(event_);
+            }, cast(IDoubleClickListener) listeners[i], event));
+        }
+    }
+    package void fireDoubleClick_package(DoubleClickEvent event) {
+        fireDoubleClick(event);
+    }
+
+    /**
+     * Notifies any open event listeners that a open event has been received.
+     * Only listeners registered at the time this method is called are notified.
+     *
+     * @param event
+     *            a double-click event
+     *
+     * @see IOpenListener#open(OpenEvent)
+     */
+    protected void fireOpen(OpenEvent event) {
+        Object[] listeners = openListeners.getListeners();
+        for (int i = 0; i < listeners.length; ++i) {
+            SafeRunnable.run( dgSafeRunnable( (IOpenListener l,OpenEvent event_){
+                l.open(event_);
+            }, cast(IOpenListener) listeners[i],event));
+        }
+    }
+    package void fireOpen_package(OpenEvent event) {
+        fireOpen(event);
+    }
+
+    /**
+     * Notifies any post selection listeners that a post selection event has
+     * been received. Only listeners registered at the time this method is
+     * called are notified.
+     *
+     * @param event
+     *            a selection changed event
+     *
+     * @see #addPostSelectionChangedListener(ISelectionChangedListener)
+     */
+    protected void firePostSelectionChanged(SelectionChangedEvent event) {
+        Object[] listeners = postSelectionChangedListeners.getListeners();
+        for (int i = 0; i < listeners.length; ++i) {
+            SafeRunnable.run( dgSafeRunnable( (ISelectionChangedListener l, SelectionChangedEvent event_){
+                l.selectionChanged(event_);
+            },cast(ISelectionChangedListener) listeners[i],event));
+        }
+    }
+
+    /**
+     * Returns the comparer to use for comparing elements, or
+     * <code>null</code> if none has been set.  If specified,
+     * the viewer uses this to compare and hash elements rather
+     * than the elements' own equals and hashCode methods.
+     *
+     * @return the comparer to use for comparing elements or
+     *            <code>null</code>
+     */
+    public IElementComparer getComparer() {
+        return comparer;
+    }
+
+    /**
+     * Returns the filtered array of children of the given element. The
+     * resulting array must not be modified, as it may come directly from the
+     * model's internal state.
+     *
+     * @param parent
+     *            the parent element
+     * @return a filtered array of child elements
+     */
+    protected Object[] getFilteredChildren(Object parent) {
+        Object[] result = getRawChildren(parent);
+        if (filters !is null) {
+            for (Iterator iter = filters.iterator(); iter.hasNext();) {
+                ViewerFilter f = cast(ViewerFilter) iter.next();
+                result = f.filter(this, parent, result);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns this viewer's filters.
+     *
+     * @return an array of viewer filters
+     * @see StructuredViewer#setFilters(ViewerFilter[])
+     */
+    public ViewerFilter[] getFilters() {
+        if (filters is null) {
+            return new ViewerFilter[0];
+        }
+        return arraycast!(ViewerFilter)(filters.toArray());
+    }
+
+    /**
+     * Returns the item at the given display-relative coordinates, or
+     * <code>null</code> if there is no item at that location or
+     * the underlying SWT-Control is not made up of {@link Item}
+     * (e.g {@link ListViewer})
+     * <p>
+     * The default implementation of this method returns <code>null</code>.
+     * </p>
+     *
+     * @param x
+     *            horizontal coordinate
+     * @param y
+     *            vertical coordinate
+     * @return the item, or <code>null</code> if there is no item at the given
+     *         coordinates
+     * @deprecated This method is deprecated in 3.3 in favor of {@link ColumnViewer#getItemAt(org.eclipse.swt.graphics.Point)}.
+     * Viewers who are not subclasses of {@link ColumnViewer} should consider using a
+     * widget relative implementation like {@link ColumnViewer#getItemAt(org.eclipse.swt.graphics.Point)}.
+     *
+     */
+    protected Item getItem(int x, int y) {
+        return null;
+    }
+
+    /**
+     * Returns the children of the given parent without sorting and filtering
+     * them. The resulting array must not be modified, as it may come directly
+     * from the model's internal state.
+     * <p>
+     * Returns an empty array if the given parent is <code>null</code>.
+     * </p>
+     *
+     * @param parent
+     *            the parent element
+     * @return the child elements
+     */
+    protected Object[] getRawChildren(Object parent) {
+        Object[] result = null;
+        if (parent !is null) {
+            IStructuredContentProvider cp = cast(IStructuredContentProvider) getContentProvider();
+            if (cp !is null) {
+                result = cp.getElements(parent);
+                assertElementsNotNull(result);
+            }
+        }
+        return (result !is null) ? result : null;
+    }
+
+    /**
+     * Returns the root element.
+     * <p>
+     * The default implementation of this framework method forwards to
+     * <code>getInput</code>. Override if the root element is different from
+     * the viewer's input element.
+     * </p>
+     *
+     * @return the root element, or <code>null</code> if none
+     */
+    protected Object getRoot() {
+        return getInput();
+    }
+
+    /**
+     * The <code>StructuredViewer</code> implementation of this method returns
+     * the result as an <code>IStructuredSelection</code>.
+     * <p>
+     * Subclasses do not typically override this method, but implement
+     * <code>getSelectionFromWidget(List)</code> instead.
+     * <p>
+     * @return ISelection
+     */
+    public override ISelection getSelection() {
+        Control control = getControl();
+        if (control is null || control.isDisposed()) {
+            return StructuredSelection.EMPTY;
+        }
+        List list = getSelectionFromWidget();
+        return new StructuredSelection(list, comparer);
+    }
+
+    /**
+     * Retrieves the selection, as a <code>List</code>, from the underlying
+     * widget.
+     *
+     * @return the list of selected elements
+     */
+    protected abstract List getSelectionFromWidget();
+    package List getSelectionFromWidget_package(){
+        return getSelectionFromWidget();
+    }
+
+    /**
+     * Returns the sorted and filtered set of children of the given element. The
+     * resulting array must not be modified, as it may come directly from the
+     * model's internal state.
+     *
+     * @param parent
+     *            the parent element
+     * @return a sorted and filtered array of child elements
+     */
+    protected Object[] getSortedChildren(Object parent) {
+        Object[] result = getFilteredChildren(parent);
+        if (sorter !is null) {
+            // be sure we're not modifying the original array from the model
+            result = result.dup;
+            sorter.sort(this, result);
+        }
+        return result;
+    }
+
+    /**
+     * Returns this viewer's sorter, or <code>null</code> if it does not have
+     * one.  If this viewer has a comparator that was set via
+     * <code>setComparator(ViewerComparator)</code> then this method will return
+     * <code>null</code> if the comparator is not an instance of ViewerSorter.
+     * <p>
+     * It is recommended to use <code>getComparator()</code> instead.
+     * </p>
+     *
+     * @return a viewer sorter, or <code>null</code> if none or if the comparator is
+     *              not an instance of ViewerSorter
+     */
+    public ViewerSorter getSorter() {
+        if ( auto vs = cast(ViewerSorter)sorter )
+            return vs;
+        return null;
+    }
+
+    /**
+     * Return this viewer's comparator used to sort elements.
+     * This method should be used instead of <code>getSorter()</code>.
+     *
+     * @return a viewer comparator, or <code>null</code> if none
+     *
+     * @since 3.2
+     */
+    public ViewerComparator getComparator(){
+        return sorter;
+    }
+
+    /**
+     * Handles a double-click select event from the widget.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method.
+     * </p>
+     *
+     * @param event
+     *            the SWT selection event
+     */
+    protected void handleDoubleSelect(SelectionEvent event) {
+        // This method is reimplemented in AbstractTreeViewer to fix bug 108102.
+
+        // handle case where an earlier selection listener disposed the control.
+        Control control = getControl();
+        if (control !is null && !control.isDisposed()) {
+            // If the double-clicked element can be obtained from the event, use it
+            // otherwise get it from the control.  Some controls like List do
+            // not have the notion of item.
+            // For details, see bug 90161 [Navigator] DefaultSelecting folders shouldn't always expand first one
+            ISelection selection;
+            if (event.item !is null && event.item.getData() !is null) {
+                selection = new StructuredSelection(event.item.getData());
+            }
+            else {
+                selection = getSelection();
+                updateSelection(selection);
+            }
+            fireDoubleClick(new DoubleClickEvent(this, selection));
+        }
+    }
+
+    /**
+     * Handles an open event from the OpenStrategy.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method.
+     * </p>
+     *
+     * @param event
+     *            the SWT selection event
+     */
+    protected void handleOpen(SelectionEvent event) {
+        Control control = getControl();
+        if (control !is null && !control.isDisposed()) {
+            ISelection selection = getSelection();
+            fireOpen(new OpenEvent(this, selection));
+        }
+    }
+
+    /**
+     * Handles an invalid selection.
+     * <p>
+     * This framework method is called if a model change picked up by a viewer
+     * results in an invalid selection. For instance if an element contained in
+     * the selection has been removed from the viewer, the viewer is free to
+     * either remove the element from the selection or to pick another element
+     * as its new selection. The default implementation of this method calls
+     * <code>updateSelection</code>. Subclasses may override it to implement
+     * a different strategy for picking a new selection when the old selection
+     * becomes invalid.
+     * </p>
+     *
+     * @param invalidSelection
+     *            the selection before the viewer was updated
+     * @param newSelection
+     *            the selection after the update, or <code>null</code> if none
+     */
+    protected void handleInvalidSelection(ISelection invalidSelection, ISelection newSelection) {
+        updateSelection(newSelection);
+        SelectionChangedEvent event = new SelectionChangedEvent(this, newSelection);
+        firePostSelectionChanged(event);
+    }
+
+    /**
+     * The <code>StructuredViewer</code> implementation of this
+     * <code>ContentViewer</code> method calls <code>update</code> if the
+     * event specifies that the label of a given element has changed, otherwise
+     * it calls super. Subclasses may reimplement or extend.
+     * </p>
+     * @param event the event that generated this update
+     */
+    protected override void handleLabelProviderChanged(LabelProviderChangedEvent event) {
+        Object[] elements = event.getElements();
+        if (elements !is null) {
+            update(elements, null);
+        } else {
+            super.handleLabelProviderChanged(event);
+        }
+    }
+    package void handleLabelProviderChanged_package(LabelProviderChangedEvent event) {
+        handleLabelProviderChanged(event);
+    }
+
+    /**
+     * Handles a select event from the widget.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method.
+     * </p>
+     *
+     * @param event
+     *            the SWT selection event
+     */
+    protected void handleSelect(SelectionEvent event) {
+        // handle case where an earlier selection listener disposed the control.
+        Control control = getControl();
+        if (control !is null && !control.isDisposed()) {
+            updateSelection(getSelection());
+        }
+    }
+
+    /**
+     * Handles a post select event from the widget.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method.
+     * </p>
+     *
+     * @param e the SWT selection event
+     */
+    protected void handlePostSelect(SelectionEvent e) {
+        SelectionChangedEvent event = new SelectionChangedEvent(this, getSelection());
+        firePostSelectionChanged(event);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Viewer.
+     */
+    protected override void hookControl(Control control) {
+        super.hookControl(control);
+        OpenStrategy handler = new OpenStrategy(control);
+        handler.addSelectionListener(new class SelectionListener {
+            public void widgetSelected(SelectionEvent e) {
+                // On Windows, selection events may happen during a refresh.
+                // Ignore these events if we are currently in preservingSelection().
+                // See bug 184441.
+                if (!inChange) {
+                    handleSelect(e);
+                }
+            }
+
+            public void widgetDefaultSelected(SelectionEvent e) {
+                handleDoubleSelect(e);
+            }
+        });
+        handler.addPostSelectionListener(new class SelectionAdapter {
+            public void widgetSelected(SelectionEvent e) {
+                handlePostSelect(e);
+            }
+        });
+        handler.addOpenListener(new class IOpenEventListener {
+            public void handleOpen(SelectionEvent e) {
+                this.outer.handleOpen(e);
+            }
+        });
+    }
+
+    /**
+     * Returns whether this viewer has any filters.
+     * @return bool
+     */
+    protected bool hasFilters() {
+        return filters !is null && filters.size() > 0;
+    }
+
+    /**
+     * Refreshes this viewer starting at the given element.
+     *
+     * @param element
+     *            the element
+     */
+    protected abstract void internalRefresh(Object element);
+
+    /**
+     * Refreshes this viewer starting at the given element. Labels are updated
+     * as described in <code>refresh(bool updateLabels)</code>.
+     * <p>
+     * The default implementation simply calls
+     * <code>internalRefresh(element)</code>, ignoring
+     * <code>updateLabels</code>.
+     * <p>
+     * If this method is overridden to do the actual refresh, then
+     * <code>internalRefresh(Object element)</code> should simply call
+     * <code>internalRefresh(element, true)</code>.
+     *
+     * @param element
+     *            the element
+     * @param updateLabels
+     *            <code>true</code> to update labels for existing elements,
+     *            <code>false</code> to only update labels as needed, assuming
+     *            that labels for existing elements are unchanged.
+     *
+     * @since 2.0
+     */
+    protected void internalRefresh(Object element, bool updateLabels) {
+        internalRefresh(element);
+    }
+
+    /**
+     * Adds the element item pair to the element map.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method.
+     * </p>
+     *
+     * @param element
+     *            the element
+     * @param item
+     *            the corresponding widget
+     */
+    protected void mapElement(Object element, Widget item) {
+        if (elementMap !is null) {
+            Object widgetOrWidgets = elementMap.get(element);
+            if (widgetOrWidgets is null) {
+                elementMap.put(element, item);
+            } else if ( auto w = cast(Widget)widgetOrWidgets ) {
+                if (widgetOrWidgets !is item) {
+                    elementMap.put(element, new ArrayWrapperT!(Widget)( [ w, item ] ));
+                }
+            } else {
+                Widget[] widgets = (cast(ArrayWrapperT!(Widget)) widgetOrWidgets).array;
+                int indexOfItem = Arrays.asList(widgets).indexOf(item);
+                if (indexOfItem is -1) {
+                    int length_ = widgets.length;
+                    System.arraycopy(widgets, 0,
+                            widgets = new Widget[length_ + 1], 0, length_);
+                    widgets[length_] = item;
+                    elementMap.put(element, new ArrayWrapperObject(widgets));
+                }
+            }
+        }
+    }
+
+    /**
+     * Determines whether a change to the given property of the given element
+     * would require refiltering and/or resorting.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method.
+     * </p>
+     *
+     * @param element
+     *            the element
+     * @param property
+     *            the property
+     * @return <code>true</code> if refiltering is required, and
+     *         <code>false</code> otherwise
+     */
+    protected bool needsRefilter(Object element, String property) {
+        if (sorter !is null && sorter.isSorterProperty(element, property)) {
+            return true;
+        }
+
+        if (filters !is null) {
+            for (int i = 0, n = filters.size(); i < n; ++i) {
+                ViewerFilter filter = cast(ViewerFilter) filters.get(i);
+                if (filter.isFilterProperty(element, property)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns a new hashtable using the given capacity and this viewer's element comparer.
+     *
+     * @param capacity the initial capacity of the hashtable
+     * @return a new hashtable
+     *
+     * @since 3.0
+     */
+    CustomHashtable newHashtable(int capacity) {
+        return new CustomHashtable(capacity, getComparer());
+    }
+
+    /**
+     * Attempts to preserves the current selection across a run of the given
+     * code.
+     * <p>
+     * The default implementation of this method:
+     * <ul>
+     * <li>discovers the old selection (via <code>getSelection</code>)</li>
+     * <li>runs the given runnable</li>
+     * <li>attempts to restore the old selection (using
+     * <code>setSelectionToWidget</code></li>
+     * <li>rediscovers the resulting selection (via <code>getSelection</code>)
+     * </li>
+     * <li>calls <code>handleInvalidSelection</code> if the resulting selection is different from the old selection</li>
+     * </ul>
+     * </p>
+     *
+     * @param updateCode
+     *            the code to run
+     */
+    protected void preservingSelection(Runnable updateCode) {
+        preservingSelection(updateCode, false);
+    }
+
+    /**
+     * Attempts to preserves the current selection across a run of the given
+     * code, with a best effort to avoid scrolling if <code>reveal</code> is false,
+     * or to reveal the selection if <code>reveal</code> is true.
+     * <p>
+     * The default implementation of this method:
+     * <ul>
+     * <li>discovers the old selection (via <code>getSelection</code>)</li>
+     * <li>runs the given runnable</li>
+     * <li>attempts to restore the old selection (using
+     * <code>setSelectionToWidget</code></li>
+     * <li>rediscovers the resulting selection (via <code>getSelection</code>)
+     * </li>
+     * <li>calls <code>handleInvalidSelection</code> if the selection did not
+     * take</li>
+     * </ul>
+     * </p>
+     *
+     * @param updateCode
+     *            the code to run
+     * @param reveal
+     *            <code>true</code> if the selection should be made visible,
+     *            <code>false</code> if scrolling should be avoided
+     * @since 3.3
+     */
+    void preservingSelection(Runnable updateCode, bool reveal) {
+
+        ISelection oldSelection = null;
+        try {
+            // preserve selection
+            oldSelection = getSelection();
+            inChange = restoreSelection = true;
+
+            // perform the update
+            updateCode.run();
+
+        } finally {
+            inChange = false;
+
+            // restore selection
+            if (restoreSelection) {
+                setSelectionToWidget(oldSelection, reveal);
+            }
+
+            // send out notification if old and new differ
+            ISelection newSelection = getSelection();
+            if (!(cast(Object)newSelection).opEquals(cast(Object)oldSelection)) {
+                handleInvalidSelection(oldSelection, newSelection);
+            }
+        }
+    }
+
+    /*
+     * Non-Javadoc. Method declared on Viewer.
+     */
+    public override void refresh() {
+        refresh(getRoot());
+    }
+
+    /**
+     * Refreshes this viewer with information freshly obtained from this
+     * viewer's model. If <code>updateLabels</code> is <code>true</code>
+     * then labels for otherwise unaffected elements are updated as well.
+     * Otherwise, it assumes labels for existing elements are unchanged, and
+     * labels are only obtained as needed (for example, for new elements).
+     * <p>
+     * Calling <code>refresh(true)</code> has the same effect as
+     * <code>refresh()</code>.
+     * <p>
+     * Note that the implementation may still obtain labels for existing
+     * elements even if <code>updateLabels</code> is false. The intent is
+     * simply to allow optimization where possible.
+     *
+     * @param updateLabels
+     *            <code>true</code> to update labels for existing elements,
+     *            <code>false</code> to only update labels as needed, assuming
+     *            that labels for existing elements are unchanged.
+     *
+     * @since 2.0
+     */
+    public void refresh(bool updateLabels) {
+        refresh(getRoot(), updateLabels);
+    }
+
+    /**
+     * Refreshes this viewer starting with the given element.
+     * <p>
+     * Unlike the <code>update</code> methods, this handles structural changes
+     * to the given element (e.g. addition or removal of children). If only the
+     * given element needs updating, it is more efficient to use the
+     * <code>update</code> methods.
+     * </p>
+     *
+     * @param element
+     *            the element
+     */
+    public void refresh(Object element) {
+        preservingSelection( dgRunnable( (Object element_){
+            internalRefresh(element_);
+        }, element ));
+    }
+
+    /**
+     * Refreshes this viewer starting with the given element. Labels are updated
+     * as described in <code>refresh(bool updateLabels)</code>.
+     * <p>
+     * Unlike the <code>update</code> methods, this handles structural changes
+     * to the given element (e.g. addition or removal of children). If only the
+     * given element needs updating, it is more efficient to use the
+     * <code>update</code> methods.
+     * </p>
+     *
+     * @param element
+     *            the element
+     * @param updateLabels
+     *            <code>true</code> to update labels for existing elements,
+     *            <code>false</code> to only update labels as needed, assuming
+     *            that labels for existing elements are unchanged.
+     *
+     * @since 2.0
+     */
+    public void refresh(Object element, bool updateLabels) {
+        preservingSelection( dgRunnable( (Object element_, bool updateLabels_){
+            internalRefresh(element_, updateLabels_);
+        },element, updateLabels));
+    }
+
+    /**
+     *
+     * Refreshes the given item with the given element. Calls
+     * <code>doUpdateItem(..., false)</code>.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method.
+     * </p>
+     * @param widget
+     *            the widget
+     * @param element
+     *            the element
+     */
+    protected final void refreshItem(Widget widget, Object element) {
+        SafeRunnable.run(new UpdateItemSafeRunnable(widget, element, true));
+    }
+
+    /**
+     * Removes the given open listener from this viewer. Has no affect if an
+     * identical listener is not registered.
+     *
+     * @param listener
+     *            a double-click listener
+     */
+    public void removeOpenListener(IOpenListener listener) {
+        openListeners.remove(cast(Object)listener);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPostSelectionProvider.
+     */
+    public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
+        postSelectionChangedListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Removes the given double-click listener from this viewer. Has no affect
+     * if an identical listener is not registered.
+     *
+     * @param listener
+     *            a double-click listener
+     */
+    public void removeDoubleClickListener(IDoubleClickListener listener) {
+        doubleClickListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Removes the given filter from this viewer, and triggers refiltering and
+     * resorting of the elements if required. Has no effect if the identical
+     * filter is not registered. If you want to remove more than one filter
+     * consider using {@link StructuredViewer#setFilters(ViewerFilter[])}.
+     *
+     * @param filter
+     *            a viewer filter
+     * @see StructuredViewer#setFilters(ViewerFilter[])
+     */
+    public void removeFilter(ViewerFilter filter) {
+        Assert.isNotNull(filter);
+        if (filters !is null) {
+            // Note: can't use List.remove(Object). Use identity comparison
+            // instead.
+            for (Iterator i = filters.iterator(); i.hasNext();) {
+                Object o = i.next();
+                if (o is filter) {
+                    i.remove();
+                    refresh();
+                    if (filters.size() is 0) {
+                        filters = null;
+                    }
+                    return;
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets the filters, replacing any previous filters, and triggers
+     * refiltering and resorting of the elements.
+     *
+     * @param filters
+     *            an array of viewer filters
+     * @since 3.3
+     */
+    public void setFilters(ViewerFilter[] filters) {
+        if (filters.length is 0) {
+            resetFilters();
+        } else {
+            this.filters = new ArrayList(Arrays.asList(filters));
+            refresh();
+        }
+    }
+
+    /**
+     * Discards this viewer's filters and triggers refiltering and resorting of
+     * the elements.
+     */
+    public void resetFilters() {
+        if (filters !is null) {
+            filters = null;
+            refresh();
+        }
+    }
+
+    /**
+     * Ensures that the given element is visible, scrolling the viewer if
+     * necessary. The selection is unchanged.
+     *
+     * @param element
+     *            the element to reveal
+     */
+    public abstract void reveal(Object element);
+
+    /*
+     *  (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ContentViewer#setContentProvider(org.eclipse.jface.viewers.IContentProvider)
+     */
+    public override void setContentProvider(IContentProvider provider) {
+        assertContentProviderType(provider);
+        super.setContentProvider(provider);
+    }
+
+    /**
+     * Assert that the content provider is of one of the
+     * supported types.
+     * @param provider
+     */
+    protected void assertContentProviderType(IContentProvider provider) {
+        Assert.isTrue( null !is cast(IStructuredContentProvider)provider );
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see org.eclipse.jface.viewers.Viewer#setInput(java.lang.Object)
+     */
+    public override final void setInput(Object input) {
+
+        try {
+            //      fInChange= true;
+
+            unmapAllElements();
+
+            super.setInput(input);
+
+        } finally {
+            //      fInChange= false;
+        }
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection, bool)
+     */
+    public override void setSelection(ISelection selection, bool reveal) {
+        /**
+         * <p>
+         * If the new selection differs from the current selection the hook
+         * <code>updateSelection</code> is called.
+         * </p>
+         * <p>
+         * If <code>setSelection</code> is called from within
+         * <code>preserveSelection</code>, the call to
+         * <code>updateSelection</code> is delayed until the end of
+         * <code>preserveSelection</code>.
+         * </p>
+         * <p>
+         * Subclasses do not typically override this method, but implement
+         * <code>setSelectionToWidget</code> instead.
+         * </p>
+         */
+        Control control = getControl();
+        if (control is null || control.isDisposed()) {
+            return;
+        }
+        if (!inChange) {
+            setSelectionToWidget(selection, reveal);
+            ISelection sel = getSelection();
+            updateSelection(sel);
+            firePostSelectionChanged(new SelectionChangedEvent(this, sel));
+        } else {
+            restoreSelection = false;
+            setSelectionToWidget(selection, reveal);
+        }
+    }
+
+    /**
+     * Parlays the given list of selected elements into selections on this
+     * viewer's control.
+     * <p>
+     * Subclasses should override to set their selection based on the given list
+     * of elements.
+     * </p>
+     *
+     * @param l
+     *            list of selected elements (element type: <code>Object</code>)
+     *            or <code>null</code> if the selection is to be cleared
+     * @param reveal
+     *            <code>true</code> if the selection is to be made visible,
+     *            and <code>false</code> otherwise
+     */
+    protected abstract void setSelectionToWidget(List l, bool reveal);
+
+    /**
+     * Converts the selection to a <code>List</code> and calls
+     * <code>setSelectionToWidget(List, bool)</code>. The selection is
+     * expected to be an <code>IStructuredSelection</code> of elements. If
+     * not, the selection is cleared.
+     * <p>
+     * Subclasses do not typically override this method, but implement
+     * <code>setSelectionToWidget(List, bool)</code> instead.
+     *
+     * @param selection
+     *            an IStructuredSelection of elements
+     * @param reveal
+     *            <code>true</code> to reveal the first element in the
+     *            selection, or <code>false</code> otherwise
+     */
+    protected void setSelectionToWidget(ISelection selection, bool reveal) {
+        if ( auto ss = cast(IStructuredSelection) selection ) {
+            setSelectionToWidget(ss.toList(), reveal);
+        } else {
+            setSelectionToWidget(cast(List) null, reveal);
+        }
+    }
+
+    /**
+     * Sets this viewer's sorter and triggers refiltering and resorting of this
+     * viewer's element. Passing <code>null</code> turns sorting off.
+     * <p>
+     * It is recommended to use <code>setComparator()</code> instead.
+     * </p>
+     *
+     * @param sorter
+     *            a viewer sorter, or <code>null</code> if none
+     */
+    public void setSorter(ViewerSorter sorter) {
+        if (this.sorter !is sorter) {
+            this.sorter = sorter;
+            refresh();
+        }
+    }
+
+    /**
+     * Sets this viewer's comparator to be used for sorting elements, and triggers refiltering and
+     * resorting of this viewer's element.  <code>null</code> turns sorting off.
+     * To get the viewer's comparator, call <code>getComparator()</code>.
+     * <p>
+     * IMPORTANT: This method was introduced in 3.2. If a reference to this viewer object
+     * is passed to clients who call <code>getSorter()<code>, null may be returned from
+     * from that method even though the viewer is sorting its elements using the
+     * viewer's comparator.
+     * </p>
+     *
+     * @param comparator a viewer comparator, or <code>null</code> if none
+     *
+     * @since 3.2
+     */
+    public void setComparator(ViewerComparator comparator){
+        if (this.sorter !is comparator){
+            this.sorter = comparator;
+            refresh();
+        }
+    }
+
+    /**
+     * Configures whether this structured viewer uses an internal hash table to
+     * speeds up the mapping between elements and SWT items. This must be called
+     * before the viewer is given an input (via <code>setInput</code>).
+     *
+     * @param enable
+     *            <code>true</code> to enable hash lookup, and
+     *            <code>false</code> to disable it
+     */
+    public void setUseHashlookup(bool enable) {
+        Assert.isTrue(getInput() is null,
+                "Can only enable the hash look up before input has been set");//$NON-NLS-1$
+        if (enable) {
+            elementMap = newHashtable(CustomHashtable.DEFAULT_CAPACITY);
+        } else {
+            elementMap = null;
+        }
+    }
+
+    /**
+     * Sets the comparer to use for comparing elements, or <code>null</code>
+     * to use the default <code>equals</code> and <code>hashCode</code>
+     * methods on the elements themselves.
+     *
+     * @param comparer
+     *            the comparer to use for comparing elements or
+     *            <code>null</code>
+     */
+    public void setComparer(IElementComparer comparer) {
+        this.comparer = comparer;
+        if (elementMap !is null) {
+            elementMap = new CustomHashtable(elementMap, comparer);
+        }
+    }
+
+    /**
+     * Hook for testing.
+     * @param element
+     * @return Widget
+     */
+    public Widget testFindItem(Object element) {
+        return findItem(element);
+    }
+
+    /**
+     * Hook for testing.
+     * @param element
+     * @return Widget[]
+     * @since 3.2
+     */
+    public Widget[] testFindItems(Object element) {
+        return findItems(element);
+    }
+
+    /**
+     * Removes all elements from the map.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method.
+     * </p>
+     */
+    protected void unmapAllElements() {
+        if (elementMap !is null) {
+            elementMap = newHashtable(CustomHashtable.DEFAULT_CAPACITY);
+        }
+    }
+
+    /**
+     * Removes the given element from the internal element to widget map. Does
+     * nothing if mapping is disabled. If mapping is enabled, the given element
+     * must be present.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method.
+     * </p>
+     *
+     * @param element
+     *            the element
+     */
+    protected void unmapElement(Object element) {
+        if (elementMap !is null) {
+            elementMap.remove(element);
+        }
+    }
+
+    /**
+     * Removes the given association from the internal element to widget map.
+     * Does nothing if mapping is disabled, or if the given element does not map
+     * to the given item.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method.
+     * </p>
+     *
+     * @param element
+     *            the element
+     * @param item the item to unmap
+     * @since 2.0
+     */
+    protected void unmapElement(Object element, Widget item) {
+        // double-check that the element actually maps to the given item before
+        // unmapping it
+        if (elementMap !is null) {
+            Object widgetOrWidgets = elementMap.get(element);
+            if (widgetOrWidgets is null) {
+                // item was not mapped, return
+                return;
+            } else if ( auto w = cast(Widget) widgetOrWidgets ) {
+                if (item is widgetOrWidgets) {
+                    elementMap.remove(element);
+                }
+            } else {
+                Widget[] widgets = ( cast(ArrayWrapperT!(Widget)) widgetOrWidgets).array;
+                int indexOfItem = Arrays.asList(widgets).indexOf(item);
+                if (indexOfItem is -1) {
+                    return;
+                }
+                int length = widgets.length;
+                if (indexOfItem is 0) {
+                    if(length is 1) {
+                        elementMap.remove(element);
+                    } else {
+                        Widget[] updatedWidgets = new Widget[length - 1];
+                        System.arraycopy(widgets, 1, updatedWidgets, 0, length -1 );
+                        elementMap.put(element, new ArrayWrapperObject( updatedWidgets));
+                    }
+                } else {
+                    Widget[] updatedWidgets = new Widget[length - 1];
+                    System.arraycopy(widgets, 0, updatedWidgets, 0, indexOfItem);
+                    System.arraycopy(widgets, indexOfItem + 1, updatedWidgets, indexOfItem, length - indexOfItem - 1);
+                    elementMap.put(element, new ArrayWrapperObject(updatedWidgets));
+                }
+            }
+        }
+    }
+
+    // flag to indicate that a full refresh took place. See bug 102440.
+    private bool refreshOccurred;
+
+    /**
+     * Updates the given elements' presentation when one or more of their
+     * properties change. Only the given elements are updated.
+     * <p>
+     * This does not handle structural changes (e.g. addition or removal of
+     * elements), and does not update any other related elements (e.g. child
+     * elements). To handle structural changes, use the <code>refresh</code>
+     * methods instead.
+     * </p>
+     * <p>
+     * This should be called when an element has changed in the model, in order
+     * to have the viewer accurately reflect the model. This method only affects
+     * the viewer, not the model.
+     * </p>
+     * <p>
+     * Specifying which properties are affected may allow the viewer to optimize
+     * the update. For example, if the label provider is not affected by changes
+     * to any of these properties, an update may not actually be required.
+     * Specifying <code>properties</code> as <code>null</code> forces a full
+     * update of the given elements.
+     * </p>
+     * <p>
+     * If the viewer has a sorter which is affected by a change to one of the
+     * properties, the elements' positions are updated to maintain the sort
+     * order. Note that resorting does not happen if <code>properties</code>
+     * is <code>null</code>.
+     * </p>
+     * <p>
+     * If the viewer has a filter which is affected by a change to one of the
+     * properties, elements may appear or disappear if the change affects
+     * whether or not they are filtered out.
+     * </p>
+     *
+     * @param elements
+     *            the elements
+     * @param properties
+     *            the properties that have changed, or <code>null</code> to
+     *            indicate unknown
+     */
+    public void update(Object[] elements, String[] properties) {
+        bool previousValue = refreshOccurred;
+        refreshOccurred = false;
+        try {
+            for (int i = 0; i < elements.length; ++i) {
+                update(elements[i], properties);
+                if (refreshOccurred) {
+                    return;
+                }
+            }
+        } finally {
+            refreshOccurred = previousValue;
+        }
+    }
+
+    /**
+     * Updates the given element's presentation when one or more of its
+     * properties changes. Only the given element is updated.
+     * <p>
+     * This does not handle structural changes (e.g. addition or removal of
+     * elements), and does not update any other related elements (e.g. child
+     * elements). To handle structural changes, use the <code>refresh</code>
+     * methods instead.
+     * </p>
+     * <p>
+     * This should be called when an element has changed in the model, in order
+     * to have the viewer accurately reflect the model. This method only affects
+     * the viewer, not the model.
+     * </p>
+     * <p>
+     * Specifying which properties are affected may allow the viewer to optimize
+     * the update. For example, if the label provider is not affected by changes
+     * to any of these properties, an update may not actually be required.
+     * Specifying <code>properties</code> as <code>null</code> forces a full
+     * update of the element.
+     * </p>
+     * <p>
+     * If the viewer has a sorter which is affected by a change to one of the
+     * properties, the element's position is updated to maintain the sort order.
+     * Note that resorting does not happen if <code>properties</code> is
+     * <code>null</code>.
+     * </p>
+     * <p>
+     * If the viewer has a filter which is affected by a change to one of the
+     * properties, the element may appear or disappear if the change affects
+     * whether or not the element is filtered out.
+     * </p>
+     *
+     * @param element
+     *            the element
+     * @param properties
+     *            the properties that have changed, or <code>null</code> to
+     *            indicate unknown
+     */
+    public void update(Object element, String[] properties) {
+        Assert.isNotNull(element);
+        Widget[] items = findItems(element);
+
+        bool mayExitEarly = !refreshOccurred;
+        for (int i = 0; i < items.length; i++) {
+            internalUpdate(items[i], element, properties);
+            if (mayExitEarly && refreshOccurred) {
+                // detected a change from refreshOccurred is false to refreshOccurred is true
+                return;
+            }
+        }
+    }
+
+    /**
+     * Updates the given element's presentation when one or more of its
+     * properties changes. Only the given element is updated.
+     * <p>
+     * EXPERIMENTAL.  Not to be used except by JDT.
+     * This method was added to support JDT's explorations
+     * into grouping by working sets, which requires viewers to support multiple
+     * equal elements.  See bug 76482 for more details.  This support will
+     * likely be removed in Eclipse 3.3 in favor of proper support for
+     * multiple equal elements (which was implemented for AbtractTreeViewer in 3.2).
+     * </p>
+     * @param widget
+     *            the widget for the element
+     * @param element
+     *            the element
+     * @param properties
+     *            the properties that have changed, or <code>null</code> to
+     *            indicate unknown
+     */
+    protected void internalUpdate(Widget widget, Object element, String[] properties) {
+        bool needsRefilter_ = false;
+        if (properties !is null) {
+            for (int i = 0; i < properties.length; ++i) {
+                needsRefilter_ = needsRefilter(element, properties[i]);
+                if (needsRefilter_) {
+                    break;
+                }
+            }
+        }
+        if (needsRefilter_) {
+            preservingSelection(dgRunnable( {
+                internalRefresh(getRoot());
+                refreshOccurred = true;
+            }));
+            return;
+        }
+
+        bool needsUpdate;
+        if (properties is null) {
+            needsUpdate = true;
+        } else {
+            needsUpdate = false;
+            IBaseLabelProvider labelProvider = getLabelProvider();
+            for (int i = 0; i < properties.length; ++i) {
+                needsUpdate = labelProvider.isLabelProperty(element, properties[i]);
+                if (needsUpdate) {
+                    break;
+                }
+            }
+        }
+        if (needsUpdate) {
+            updateItem(widget, element);
+        }
+    }
+
+    /**
+     * Copies attributes of the given element into the given widget.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method. Calls <code>doUpdateItem(widget, element, true)</code>.
+     * </p>
+     *
+     * @param widget
+     *            the widget
+     * @param element
+     *            the element
+     */
+    protected final void updateItem(Widget widget, Object element) {
+        SafeRunnable.run(new UpdateItemSafeRunnable(widget, element, true));
+    }
+
+    /**
+     * Updates the selection of this viewer.
+     * <p>
+     * This framework method should be called when the selection in the viewer
+     * widget changes.
+     * </p>
+     * <p>
+     * The default implementation of this method notifies all selection change
+     * listeners recorded in an internal state variable. Overriding this method
+     * is generally not required; however, if overriding in a subclass,
+     * <code>super.updateSelection</code> must be invoked.
+     * </p>
+     *
+     * @param selection
+     *            the selection, or <code>null</code> if none
+     */
+    protected void updateSelection(ISelection selection) {
+        SelectionChangedEvent event = new SelectionChangedEvent(this, selection);
+        fireSelectionChanged(event);
+    }
+
+    /**
+     * Returns whether this structured viewer is configured to use an internal
+     * map to speed up the mapping between elements and SWT items.
+     * <p>
+     * The default implementation of this framework method checks whether the
+     * internal map has been initialized.
+     * </p>
+     *
+     * @return <code>true</code> if the element map is enabled, and
+     *         <code>false</code> if disabled
+     */
+    protected bool usingElementMap() {
+        return elementMap !is null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ContentViewer#setLabelProvider(org.eclipse.jface.viewers.IBaseLabelProvider)
+     */
+    public override void setLabelProvider(IBaseLabelProvider labelProvider) {
+        if ( null !is cast(IColorProvider)labelProvider  || null !is cast(IFontProvider)labelProvider ) {
+            colorAndFontCollector = new ColorAndFontCollectorWithProviders(labelProvider);
+        } else {
+            colorAndFontCollector = new ColorAndFontCollector();
+        }
+        super.setLabelProvider(labelProvider);
+
+    }
+
+    /**
+     * Build a label up for the element using the supplied label provider.
+     * @param updateLabel The ViewerLabel to collect the result in
+     * @param element The element being decorated.
+     */
+    protected void buildLabel(ViewerLabel updateLabel, Object element){
+
+        if ( auto vlp = cast(IViewerLabelProvider)getLabelProvider() ) {
+            IViewerLabelProvider itemProvider = cast(IViewerLabelProvider) getLabelProvider();
+            itemProvider.updateLabel(updateLabel, element);
+
+            colorAndFontCollector.setUsedDecorators();
+
+            if(updateLabel.hasNewBackground()) {
+                colorAndFontCollector.setBackground(updateLabel.getBackground());
+            }
+
+            if(updateLabel.hasNewForeground()) {
+                colorAndFontCollector.setForeground(updateLabel.getForeground());
+            }
+
+            if(updateLabel.hasNewFont()) {
+                colorAndFontCollector.setFont(updateLabel.getFont());
+            }
+            return;
+
+        }
+
+        if( cast(ILabelProvider) getLabelProvider() ){
+            ILabelProvider labelProvider = cast(ILabelProvider) getLabelProvider();
+            updateLabel.setText(labelProvider.getText(element));
+            updateLabel.setImage(labelProvider.getImage(element));
+        }
+
+    }
+
+    /**
+     * Build a label up for the element using the supplied label provider.
+     * @param updateLabel The ViewerLabel to collect the result in
+     * @param element The element being decorated.
+     * @param labelProvider ILabelProvider the labelProvider for the receiver.
+     */
+    void buildLabel(ViewerLabel updateLabel, Object element, IViewerLabelProvider labelProvider){
+
+            labelProvider.updateLabel(updateLabel, element);
+
+            colorAndFontCollector.setUsedDecorators();
+
+            if(updateLabel.hasNewBackground()) {
+                colorAndFontCollector.setBackground(updateLabel.getBackground());
+            }
+
+            if(updateLabel.hasNewForeground()) {
+                colorAndFontCollector.setForeground(updateLabel.getForeground());
+            }
+
+            if(updateLabel.hasNewFont()) {
+                colorAndFontCollector.setFont(updateLabel.getFont());
+            }
+
+    }
+
+    /**
+     * Build a label up for the element using the supplied label provider.
+     * @param updateLabel The ViewerLabel to collect the result in
+     * @param elementPath The path of the element being decorated.
+     * @param labelProvider ILabelProvider the labelProvider for the receiver.
+     */
+    void buildLabel(ViewerLabel updateLabel, TreePath elementPath,ITreePathLabelProvider labelProvider){
+
+            labelProvider.updateLabel(updateLabel, elementPath);
+
+            colorAndFontCollector.setUsedDecorators();
+
+            if(updateLabel.hasNewBackground()) {
+                colorAndFontCollector.setBackground(updateLabel.getBackground());
+            }
+
+            if(updateLabel.hasNewForeground()) {
+                colorAndFontCollector.setForeground(updateLabel.getForeground());
+            }
+
+            if(updateLabel.hasNewFont()) {
+                colorAndFontCollector.setFont(updateLabel.getFont());
+            }
+
+    }
+
+    /**
+     * Build a label up for the element using the supplied label provider.
+     * @param updateLabel The ViewerLabel to collect the result in
+     * @param element The element being decorated.
+     * @param labelProvider ILabelProvider the labelProvider for the receiver.
+     */
+    void buildLabel(ViewerLabel updateLabel, Object element,ILabelProvider labelProvider){
+            updateLabel.setText(labelProvider.getText(element));
+            updateLabel.setImage(labelProvider.getImage(element));
+    }
+
+    /**
+     * Get the ColorAndFontCollector for the receiver.
+     * @return ColorAndFontCollector
+     * @since 3.1
+     */
+    protected ColorAndFontCollector getColorAndFontCollector() {
+        return colorAndFontCollector;
+    }
+
+    protected override void handleDispose(DisposeEvent event) {
+        super.handleDispose(event);
+        sorter = null;
+        comparer = null;
+        if (filters !is null)
+            filters.clear();
+        elementMap = newHashtable(1);
+        openListeners.clear();
+        doubleClickListeners.clear();
+        colorAndFontCollector.clear();
+        postSelectionChangedListeners.clear();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/StyledCellLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,394 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 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
+ *     Michael Krkoska - initial API and implementation (bug 188333)
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.StyledCellLabelProvider;
+
+import org.eclipse.jface.viewers.OwnerDrawLabelProvider;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ViewerColumn;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ViewerRow;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.TextLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A {@link StyledCellLabelProvider} supports styled labels by using owner
+ * draw.
+ * Besides the styles in labels, the label provider preserves native viewer behavior:
+ * <ul>
+ * <li>similar image and label positioning</li>
+ * <li>native drawing of focus and selection</li>
+ * </ul>
+ * <p>
+ * For providing the label's styles, create a subclass and overwrite
+ * {@link StyledCellLabelProvider#update(ViewerCell)} to
+ * return set all information needed to render a element. Use
+ * {@link ViewerCell#setStyleRanges(StyleRange[])} to set style ranges
+ * on the label.
+ * </p>
+ * <p>
+ * The current version of the {@link StyledCellLabelProvider} will ignore all font settings on
+ * {@link StyleRange}. Different fonts would make labels wider, and the native
+ * selection drawing could not be reused.
+ * </p>
+ *
+ * <p><strong>NOTE:</strong> This API is experimental and may be deleted or
+ * changed before 3.4 is released.</p>
+ *
+ * @since 3.4
+ */
+public abstract class StyledCellLabelProvider : OwnerDrawLabelProvider {
+
+    alias OwnerDrawLabelProvider.setOwnerDrawEnabled setOwnerDrawEnabled;
+
+    /**
+     * Style constant for indicating that the styled colors are to be applied
+     * even it the viewer's item is selected. Default is not to apply colors.
+     */
+    public static const int COLORS_ON_SELECTION = 1 << 0;
+
+    /**
+     * Style constant for indicating to draw the focus if requested by the owner
+     * draw event. Default is to draw the focus.
+     */
+    public static const int NO_FOCUS = 1 << 1;
+
+    /**
+     * Private constant to indicate if owner draw is enabled for the
+     * label provider's column.
+     */
+    private static const int OWNER_DRAW_ENABLED = 1 << 4;
+
+    private int style;
+
+    // reused text layout
+    private TextLayout cachedTextLayout;
+
+    private ColumnViewer viewer;
+    private ViewerColumn column;
+
+    /**
+     * Creates a new StyledCellLabelProvider. By default, owner draw is enabled, focus is drawn and no
+     * colors are painted on selected elements.
+     */
+    public this() {
+        this(0);
+    }
+
+    /**
+     * Creates a new StyledCellLabelProvider. By default, owner draw is enabled.
+     *
+     * @param style
+     *            the style bits
+     * @see StyledCellLabelProvider#COLORS_ON_SELECTION
+     * @see StyledCellLabelProvider#NO_FOCUS
+     */
+    public this(int style) {
+        this.style = style & (COLORS_ON_SELECTION | NO_FOCUS)
+                            | OWNER_DRAW_ENABLED;
+    }
+
+    /**
+     * Returns <code>true</code> is the owner draw rendering is enabled for this label provider.
+     * By default owner draw rendering is enabled. If owner draw rendering is disabled, rending is
+     * done by the viewer and no styled ranges (see {@link ViewerCell#getStyleRanges()})
+     * are drawn.
+     *
+     * @return <code>true</code> is the rendering of styles is enabled.
+     */
+    public bool isOwnerDrawEnabled() {
+        return (this.style & OWNER_DRAW_ENABLED) !is 0;
+    }
+
+    /**
+     * Specifies whether owner draw rendering is enabled for this label
+     * provider. By default owner draw rendering is enabled. If owner draw
+     * rendering is disabled, rendering is done by the viewer and no styled
+     * ranges (see {@link ViewerCell#getStyleRanges()}) are drawn.
+     * It is the caller's responsibility to also call
+     * {@link StructuredViewer#refresh()} or similar methods to update the
+     * underlying widget.
+     *
+     * @param enabled
+     *            specifies if owner draw rendering is enabled
+     */
+    public void setOwnerDrawEnabled(bool enabled) {
+        bool isEnabled= isOwnerDrawEnabled();
+        if (isEnabled !is enabled) {
+            if (enabled) {
+                this.style |= OWNER_DRAW_ENABLED;
+            } else {
+                this.style &= ~OWNER_DRAW_ENABLED;
+            }
+            if (this.viewer !is null) {
+                setOwnerDrawEnabled(this.viewer, this.column, enabled);
+            }
+        }
+    }
+
+    /**
+     * Returns the viewer on which this label provider is installed on or <code>null</code> if the
+     * label provider is not installed.
+     *
+     * @return the viewer on which this label provider is installed on or <code>null</code> if the
+     * label provider is not installed.
+     */
+    protected final ColumnViewer getViewer() {
+        return this.viewer;
+    }
+
+    /**
+     * Returns the column on which this label provider is installed on or <code>null</code> if the
+     * label provider is not installed.
+     *
+     * @return the column on which this label provider is installed on or <code>null</code> if the
+     * label provider is not installed.
+     */
+    protected final ViewerColumn getColumn() {
+        return this.column;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.OwnerDrawLabelProvider#initialize(org.eclipse.jface.viewers.ColumnViewer, org.eclipse.jface.viewers.ViewerColumn)
+     */
+    public void initialize(ColumnViewer viewer, ViewerColumn column) {
+        Assert.isTrue(this.viewer is null && this.column is null, "Label provider instance already in use"); //$NON-NLS-1$
+
+        this.viewer= viewer;
+        this.column= column;
+        super.initialize(viewer, column, isOwnerDrawEnabled());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.BaseLabelProvider#dispose()
+     */
+    public void dispose() {
+        if (this.cachedTextLayout !is null) {
+            cachedTextLayout.dispose();
+            cachedTextLayout = null;
+        }
+
+        this.viewer= null;
+        this.column= null;
+
+        super.dispose();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.OwnerDrawLabelProvider#update(org.eclipse.jface.viewers.ViewerCell)
+     */
+    public void update(ViewerCell cell) {
+        // clients must override and configure the cell and call super
+        super.update(cell); // calls 'repaint' to trigger the paint listener
+    }
+
+    private TextLayout getSharedTextLayout(Display display) {
+        if (cachedTextLayout is null) {
+            int orientation = viewer.getControl().getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT);
+            cachedTextLayout = new TextLayout(display);
+            cachedTextLayout.setOrientation(orientation);
+        } else {
+            cachedTextLayout.setText(""); // make sure no previous ranges are cleared //$NON-NLS-1$
+        }
+        return cachedTextLayout;
+    }
+
+    private bool useColors(Event event) {
+        return (event.detail & SWT.SELECTED) is 0
+                || (this.style & COLORS_ON_SELECTION) !is 0;
+    }
+
+    private bool drawFocus(Event event) {
+        return (event.detail & SWT.FOCUSED) !is 0
+                && (this.style & NO_FOCUS) is 0;
+    }
+
+    /**
+     * Returns a {@link TextLayout} instance for the given cell
+     * configured with the style ranges. The text layout instance is managed by
+     * the label provider. Caller of the method must not dispose the text
+     * layout.
+     *
+     * @param diplay
+     *            the current display
+     * @param applyColors
+     *            if set, create colors in the result
+     * @param cell
+     *            the viewer cell
+     * @return a TextLayout instance
+     */
+    private TextLayout getTextLayoutForInfo(Display display, ViewerCell cell, bool applyColors) {
+        TextLayout layout = getSharedTextLayout(display);
+
+        layout.setText(cell.getText());
+        layout.setFont(cell.getFont()); // set also if null to clear previous usages
+
+        StyleRange[] styleRanges = cell.getStyleRanges();
+        if (styleRanges !is null) { // user didn't fill styled ranges
+            for (int i = 0; i < styleRanges.length; i++) {
+                StyleRange curr = prepareStyleRange(styleRanges[i], applyColors);
+                layout.setStyle(curr, curr.start, curr.start + curr.length - 1);
+            }
+        }
+
+        return layout;
+    }
+
+    /**
+     * Prepares the given style range before it is applied to the label. This method makes sure that
+     * no colors are drawn when the element is selected.
+     * The current version of the {@link StyledCellLabelProvider} will also ignore all font settings on the
+     * style range. Clients can override.
+     *
+     * @param styleRange
+     *               the style range to prepare. the style range element must not be modified
+     * @param applyColors
+     *               specifies if colors should be applied.
+     * @return
+     *               returns the style range to use on the label
+     */
+    protected StyleRange prepareStyleRange(StyleRange styleRange, bool applyColors) {
+        // if no colors apply or font is set, create a clone and clear the
+        // colors and font
+        if (styleRange.font !is null || !applyColors
+                && (styleRange.foreground !is null || styleRange.background !is null)) {
+            styleRange = cast(StyleRange) styleRange.clone();
+            styleRange.font = null; // ignore font settings until bug 168807 is resolved
+            if (!applyColors) {
+                styleRange.foreground = null;
+                styleRange.background = null;
+            }
+        }
+        return styleRange;
+    }
+
+    private ViewerCell getViewerCell(Event event, Object element) {
+        ViewerRow row= viewer.getViewerRowFromItem_package(event.item);
+        return new ViewerCell(row, event.index, element);
+    }
+
+    /**
+     * Handle the erase event. The default implementation does nothing to ensure
+     * keep native selection highlighting working.
+     *
+     * @param event
+     *            the erase event
+     * @param element
+     *            the model object
+     * @see SWT#EraseItem
+     */
+    protected void erase(Event event, Object element) {
+        // use native erase
+        if (isOwnerDrawEnabled()) {
+            // info has been set by 'update': announce that we paint ourselves
+            event.detail &= ~SWT.FOREGROUND;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.OwnerDrawLabelProvider#measure(org.eclipse.swt.widgets.Event,
+     *      java.lang.Object)
+     */
+    protected void measure(Event event, Object element) {
+        // use native measuring
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.OwnerDrawLabelProvider#paint(org.eclipse.swt.widgets.Event,
+     *      java.lang.Object)
+     */
+    protected void paint(Event event, Object element) {
+        if (!isOwnerDrawEnabled())
+            return;
+
+        ViewerCell cell= getViewerCell(event, element);
+
+        bool applyColors = useColors(event);
+        GC gc = event.gc;
+        // remember colors to restore the GC later
+        Color oldForeground = gc.getForeground();
+        Color oldBackground = gc.getBackground();
+
+        if (applyColors) {
+            Color foreground= cell.getForeground();
+            if (foreground !is null) {
+                gc.setForeground(foreground);
+            }
+
+            Color background= cell.getBackground();
+            if (background !is null) {
+                gc.setBackground(background);
+            }
+        }
+
+        Image image = cell.getImage();
+        if (image !is null) {
+            Rectangle imageBounds = cell.getImageBounds();
+            if (imageBounds !is null) {
+                Rectangle bounds = image.getBounds();
+
+                // center the image in the given space
+                int x = imageBounds.x
+                        + Math.max(0, (imageBounds.width - bounds.width) / 2);
+                int y = imageBounds.y
+                        + Math.max(0, (imageBounds.height - bounds.height) / 2);
+                gc.drawImage(image, x, y);
+            }
+        }
+
+        TextLayout textLayout = getTextLayoutForInfo(event.display, cell, applyColors);
+
+        Rectangle textBounds = cell.getTextBounds();
+        if (textBounds !is null) {
+            Rectangle layoutBounds = textLayout.getBounds();
+
+            int x = textBounds.x;
+            int y = textBounds.y
+                    + Math.max(0, (textBounds.height - layoutBounds.height) / 2);
+
+            textLayout.draw(gc, x, y);
+        }
+
+        if (drawFocus(event)) {
+            Rectangle focusBounds = cell.getViewerRow().getBounds();
+            gc.drawFocus(focusBounds.x, focusBounds.y, focusBounds.width,
+                    focusBounds.height);
+        }
+
+        if (applyColors) {
+            gc.setForeground(oldForeground);
+            gc.setBackground(oldBackground);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/StyledString.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,523 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.viewers.StyledString;
+
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.TextStyle;
+import org.eclipse.jface.preference.JFacePreferences;
+import org.eclipse.jface.resource.ColorRegistry;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import tango.text.convert.Format;
+import tango.core.Exception;
+
+
+/**
+ * A mutable string with styled ranges. All ranges mark substrings of the string
+ * and do not overlap. Styles are applied using instances of {@link Styler} to
+ * compute the result of {@link #getStyleRanges()}.
+ *
+ * The styled string can be built in the following two ways:
+ * <ul>
+ * <li>new strings with stylers can be appended</li>
+ * <li>stylers can by applied to ranges of the existing string</li>
+ * </ul>
+ *
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.
+ * </p>
+ *
+ * @since 3.4
+ */
+public class StyledString {
+
+    /**
+     * A styler will be asked to apply its styles to one ore more ranges in the
+     * {@link StyledString}.
+     *
+     */
+    public static abstract class Styler {
+
+        /**
+         * Applies the styles represented by this object to the given textStyle.
+         *
+         * @param textStyle
+         *            the {@link TextStyle} to modify
+         */
+        public abstract void applyStyles(TextStyle textStyle);
+    }
+
+    /**
+     * A built-in styler using the {@link JFacePreferences#QUALIFIER_COLOR}
+     * managed in the JFace color registry (See
+     * {@link JFaceResources#getColorRegistry()}).
+     */
+    public static const Styler QUALIFIER_STYLER;
+
+    /**
+     * A built-in styler using the {@link JFacePreferences#COUNTER_COLOR}
+     * managed in the JFace color registry (See
+     * {@link JFaceResources#getColorRegistry()}).
+     */
+    public static const Styler COUNTER_STYLER;
+
+    /**
+     * A built-in styler using the {@link JFacePreferences#DECORATIONS_COLOR}
+     * managed in the JFace color registry (See
+     * {@link JFaceResources#getColorRegistry()}).
+     */
+    public static const Styler DECORATIONS_STYLER;
+
+    /**
+     * Creates a styler that takes the given foreground and background colors
+     * from the JFace color registry.
+     *
+     * @param foregroundColorName
+     *            the color name for the foreground color
+     * @param backgroundColorName
+     *            the color name for the background color
+     *
+     * @return the created style
+     */
+    public static Styler createColorRegistryStyler(String foregroundColorName,
+            String backgroundColorName) {
+        return new DefaultStyler(foregroundColorName, backgroundColorName);
+    }
+
+    private static const StyleRange[] EMPTY;
+    private StringBuffer fBuffer;
+    private StyleRunList fStyleRuns;
+
+    static this(){
+        QUALIFIER_STYLER = createColorRegistryStyler(
+                JFacePreferences.QUALIFIER_COLOR, null);
+        COUNTER_STYLER = createColorRegistryStyler(
+                JFacePreferences.COUNTER_COLOR, null);
+        DECORATIONS_STYLER = createColorRegistryStyler(
+                JFacePreferences.DECORATIONS_COLOR, null);
+        EMPTY = new StyleRange[0];
+    }
+
+    /**
+     * Creates an empty {@link StyledString}.
+     */
+    public this() {
+        fBuffer = new StringBuffer();
+        fStyleRuns = null;
+    }
+
+    /**
+     * Creates an {@link StyledString} initialized with a string without
+     * a style associated.
+     *
+     * @param string
+     *            the string
+     */
+    public this(String string) {
+        this(string, null);
+    }
+
+    /**
+     * Creates an {@link StyledString} initialized with a string and a
+     * style.
+     *
+     * @param string
+     *            the string
+     * @param styler
+     *            the styler for the string or <code>null</code> to not
+     *            associated a styler.
+     */
+    public this(String string, Styler styler) {
+        this();
+        append(string, styler);
+    }
+
+    /**
+     * Returns the string of this {@link StyledString}.
+     *
+     * @return the current string of this {@link StyledString}.
+     */
+    public String getString() {
+        return fBuffer.toString();
+    }
+
+    /**
+     * Returns the string of this {@link StyledString}.
+     *
+     * @return the current string of this {@link StyledString}.
+     */
+    public override String toString() {
+        return getString();
+    }
+
+    /**
+     * Returns the length of the string of this {@link StyledString}.
+     *
+     * @return the length of the current string
+     */
+    public int length() {
+        return fBuffer.length();
+    }
+
+    /**
+     * Appends a string to the {@link StyledString}. The appended string
+     * will have no associated styler.
+     *
+     * @param string
+     *            the string to append
+     * @return returns a reference to this object
+     */
+    public StyledString append(String string) {
+        return append(string, null);
+    }
+
+//     /**
+//      * Appends the string representation of the given character array
+//      * to the {@link StyledString}. The appended
+//      * character array will have no associated styler.
+//      *
+//      * @param chars
+//      *            the character array to append
+//      * @return returns a reference to this object
+//      */
+//     public StyledString append(char[] chars) {
+//         return append(chars, null);
+//     }
+
+    /**
+     * Appends the string representation of the given character
+     * to the {@link StyledString}. The appended
+     * character will have no associated styler.
+     *
+     * @param ch
+     *            the character to append
+     * @return returns a reference to this object
+     */
+    public StyledString append(dchar ch) {
+        return append(dcharToString(ch), null);
+    }
+
+    /**
+     * Appends a string with styles to the {@link StyledString}.
+     *
+     * @param string
+     *            the string to append
+     * @return returns a reference to this object
+     */
+    public StyledString append(StyledString string) {
+        if (string.length() is 0) {
+            return this;
+        }
+
+        int offset = fBuffer.length();
+        fBuffer.append(string.toString());
+
+        List otherRuns = string.fStyleRuns;
+        if (otherRuns !is null && !otherRuns.isEmpty()) {
+            for (int i = 0; i < otherRuns.size(); i++) {
+                StyleRun curr = cast(StyleRun) otherRuns.get(i);
+                if (i is 0 && curr.offset !is 0) {
+                    appendStyleRun(null, offset); // appended string will
+                    // start with the default
+                    // color
+                }
+                appendStyleRun(curr.style, offset + curr.offset);
+            }
+        } else {
+            appendStyleRun(null, offset); // appended string will start with
+            // the default color
+        }
+        return this;
+    }
+
+    /**
+     * Appends the string representation of the given character
+     * with a style to the {@link StyledString}. The
+     * appended character will have the given style associated.
+     *
+     * @param ch
+     *            the character to append
+     * @param styler
+     *            the styler to use for styling the character to append or
+     *            <code>null</code> if no styler should be associated with the
+     *            appended character
+     * @return returns a reference to this object
+     */
+    public StyledString append(dchar ch, Styler styler) {
+        return append(dcharToString(ch), styler);
+    }
+
+    /**
+     * Appends a string with a style to the {@link StyledString}. The
+     * appended string will be styled using the given styler.
+     *
+     * @param string
+     *            the string to append
+     * @param styler
+     *            the styler to use for styling the string to append or
+     *            <code>null</code> if no styler should be associated with the
+     *            appended string.
+     * @return returns a reference to this object
+     */
+    public StyledString append(String string, Styler styler) {
+        if (string.length is 0)
+            return this;
+
+        int offset = fBuffer.length; // the length before appending
+        fBuffer.append(string);
+        appendStyleRun(styler, offset);
+        return this;
+    }
+
+//     /**
+//      * Appends the string representation of the given character array
+//      * with a style to the {@link StyledString}. The
+//      * appended character array will be styled using the given styler.
+//      *
+//      * @param chars
+//      *            the character array to append
+//      * @param styler
+//      *            the styler to use for styling the character array to append or
+//      *            <code>null</code> if no styler should be associated with the
+//      *            appended character array
+//      * @return returns a reference to this object
+//      */
+//     public StyledString append(char[] chars, Styler styler) {
+//         if (chars.length is 0)
+//             return this;
+//
+//         int offset = fBuffer.length(); // the length before appending
+//         fBuffer.append(chars);
+//         appendStyleRun(styler, offset);
+//         return this;
+//     }
+
+    /**
+     * Sets a styler to use for the given source range. The range must be
+     * subrange of actual string of this {@link StyledString}. Stylers
+     * previously set for that range will be overwritten.
+     *
+     * @param offset
+     *            the start offset of the range
+     * @param length
+     *            the length of the range
+     * @param styler
+     *            the styler to set
+     *
+     * @throws StringIndexOutOfBoundsException
+     *             if <code>start</code> is less than zero, or if offset plus
+     *             length is greater than the length of this object.
+     */
+    public void setStyle(int offset, int length, Styler styler) {
+        if (offset < 0 || offset + length > fBuffer.length) {
+            throw new IllegalArgumentException (
+                    Format( "Invalid offset ({}) or length ({})", offset, length )); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+        }
+        if (length is 0) {
+            return;
+        }
+        if (!hasRuns() || getLastRun().offset <= offset) {
+            appendStyleRun(styler, offset);
+            if (offset + length !is fBuffer.length()) {
+                appendStyleRun(null, offset + length);
+            }
+            return;
+        }
+
+        int endRun = findRun(offset + length);
+        if (endRun >= 0) {
+            // run with the same end index, nothing to change
+        } else {
+            endRun = -(endRun + 1);
+            if (offset + length < fBuffer.length()) {
+                Styler prevStyle = endRun > 0 ? fStyleRuns.getRun(endRun - 1).style
+                        : null;
+                fStyleRuns
+                        .add(endRun, new StyleRun(offset + length, prevStyle));
+            }
+        }
+
+        int startRun = findRun(offset);
+        if (startRun >= 0) {
+            // run with the same start index
+            StyleRun styleRun = fStyleRuns.getRun(startRun);
+            styleRun.style = styler;
+        } else {
+            startRun = -(startRun + 1);
+
+            Styler prevStyle = startRun > 0 ? fStyleRuns.getRun(startRun - 1).style
+                    : null;
+            if (isDifferentStyle(prevStyle, styler)
+                    || (startRun is 0 && styler !is null)) {
+                fStyleRuns.add(startRun, new StyleRun(offset, styler));
+                endRun++; // endrun is moved one back
+            } else {
+                startRun--; // we use the previous
+            }
+        }
+        if (startRun + 1 < endRun) {
+            fStyleRuns.removeRange(startRun + 1, endRun);
+        }
+    }
+
+    /**
+     * Returns an array of {@link StyleRange} resulting from applying all
+     * associated stylers for this string builder.
+     *
+     * @return an array of all {@link StyleRange} resulting from applying the
+     *         stored stylers to this string.
+     */
+    public StyleRange[] getStyleRanges() {
+        if (hasRuns()) {
+            ArrayList res = new ArrayList();
+
+            List styleRuns = getStyleRuns();
+            int offset = 0;
+            Styler style = null;
+            for (int i = 0; i < styleRuns.size(); i++) {
+                StyleRun curr = cast(StyleRun) styleRuns.get(i);
+                if (isDifferentStyle(curr.style, style)) {
+                    if (curr.offset > offset && style !is null) {
+                        res.add(createStyleRange(offset, curr.offset, style));
+                    }
+                    offset = curr.offset;
+                    style = curr.style;
+                }
+            }
+            if (fBuffer.length() > offset && style !is null) {
+                res.add(createStyleRange(offset, fBuffer.length(), style));
+            }
+            return arraycast!(StyleRange)(res.toArray());
+        }
+        return EMPTY;
+    }
+
+    private int findRun(int offset) {
+        // method assumes that fStyleRuns is not null
+        int low = 0;
+        int high = fStyleRuns.size() - 1;
+        while (low <= high) {
+            int mid = (low + high) / 2;
+            StyleRun styleRun = fStyleRuns.getRun(mid);
+            if (styleRun.offset < offset) {
+                low = mid + 1;
+            } else if (styleRun.offset > offset) {
+                high = mid - 1;
+            } else {
+                return mid; // key found
+            }
+        }
+        return -(low + 1); // key not found.
+    }
+
+    private StyleRange createStyleRange(int start, int end, Styler style) {
+        StyleRange styleRange = new StyleRange();
+        styleRange.start = start;
+        styleRange.length = end - start;
+        style.applyStyles(styleRange);
+        return styleRange;
+    }
+
+    private bool hasRuns() {
+        return fStyleRuns !is null && !fStyleRuns.isEmpty();
+    }
+
+    private void appendStyleRun(Styler style, int offset) {
+        StyleRun lastRun = getLastRun();
+        if (lastRun !is null && lastRun.offset is offset) {
+            lastRun.style = style;
+            return;
+        }
+
+        if (lastRun is null && style !is null || lastRun !is null
+                && isDifferentStyle(style, lastRun.style)) {
+            getStyleRuns().add(new StyleRun(offset, style));
+        }
+    }
+
+    private bool isDifferentStyle(Styler style1, Styler style2) {
+        if (style1 is null) {
+            return style2 !is null;
+        }
+        return !style1.opEquals(style2);
+    }
+
+    private StyleRun getLastRun() {
+        if (fStyleRuns is null || fStyleRuns.isEmpty()) {
+            return null;
+        }
+        return fStyleRuns.getRun(fStyleRuns.size() - 1);
+    }
+
+    private List getStyleRuns() {
+        if (fStyleRuns is null)
+            fStyleRuns = new StyleRunList();
+        return fStyleRuns;
+    }
+
+    private static class StyleRun {
+        public int offset;
+        public Styler style;
+
+        public this(int offset, Styler style) {
+            this.offset = offset;
+            this.style = style;
+        }
+
+        public override String toString() {
+            return Format("Offset {}, style: {}", offset, style ); //$NON-NLS-1$//$NON-NLS-2$
+        }
+    }
+
+    private static class StyleRunList : ArrayList {
+        private static final long serialVersionUID = 123L;
+
+        public this() {
+            super(3);
+        }
+
+        public StyleRun getRun(int index) {
+            return cast(StyleRun) get(index);
+        }
+
+        public void removeRange(int fromIndex, int toIndex) {
+            super.removeRange(fromIndex, toIndex);
+        }
+    }
+
+    private static class DefaultStyler : Styler {
+        private final String fForegroundColorName;
+        private final String fBackgroundColorName;
+
+        public this(String foregroundColorName,
+                String backgroundColorName) {
+            fForegroundColorName = foregroundColorName;
+            fBackgroundColorName = backgroundColorName;
+        }
+
+        public void applyStyles(TextStyle textStyle) {
+            ColorRegistry colorRegistry = JFaceResources.getColorRegistry();
+            if (fForegroundColorName !is null) {
+                textStyle.foreground = colorRegistry.get(fForegroundColorName);
+            }
+            if (fBackgroundColorName !is null) {
+                textStyle.background = colorRegistry.get(fBackgroundColorName);
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TableColumnViewerLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *     Tom Shindl <tom.schindl@bestsolution.at> - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.TableColumnViewerLabelProvider;
+
+import org.eclipse.jface.viewers.WrappedViewerLabelProvider;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.ITableColorProvider;
+import org.eclipse.jface.viewers.ITableFontProvider;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ViewerCell;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * TableColumnViewerLabelProvider is the mapping from the table based providers
+ * to the ViewerLabelProvider.
+ *
+ * @since 3.3
+ * @see ITableLabelProvider
+ * @see ITableColorProvider
+ * @see ITableFontProvider
+ *
+ */
+class TableColumnViewerLabelProvider : WrappedViewerLabelProvider {
+
+    private ITableLabelProvider tableLabelProvider;
+
+    private ITableColorProvider tableColorProvider;
+
+    private ITableFontProvider tableFontProvider;
+
+    /**
+     * Create a new instance of the receiver.
+     *
+     * @param labelProvider
+     *            instance of a table based label provider
+     * @see ITableLabelProvider
+     * @see ITableColorProvider
+     * @see ITableFontProvider
+     */
+    public this(IBaseLabelProvider labelProvider) {
+        super(labelProvider);
+
+        if ( auto i = cast(ITableLabelProvider)labelProvider )
+            tableLabelProvider = i;
+
+        if (auto i = cast(ITableColorProvider)labelProvider )
+            tableColorProvider = i;
+
+        if (auto i = cast(ITableFontProvider)labelProvider )
+            tableFontProvider = i;
+    }
+
+
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.WrappedViewerLabelProvider#update(org.eclipse.jface.viewers.ViewerCell)
+     */
+    public override void update(ViewerCell cell) {
+
+        Object element = cell.getElement();
+        int index = cell.getColumnIndex();
+
+        if (tableLabelProvider is null) {
+            cell.setText(getLabelProvider().getText(element));
+            cell.setImage(getLabelProvider().getImage(element));
+        } else {
+            cell.setText(tableLabelProvider.getColumnText(element, index));
+            cell.setImage(tableLabelProvider.getColumnImage(element, index));
+        }
+
+        if (tableColorProvider is null) {
+            if (getColorProvider() !is null) {
+                cell.setBackground(getColorProvider().getBackground(element));
+                cell.setForeground(getColorProvider().getForeground(element));
+            }
+
+        } else {
+            cell.setBackground(tableColorProvider
+                    .getBackground(element, index));
+            cell.setForeground(tableColorProvider
+                    .getForeground(element, index));
+
+        }
+
+        if (tableFontProvider is null) {
+            if (getFontProvider() !is null)
+                cell.setFont(getFontProvider().getFont(element));
+        } else
+            cell.setFont(tableFontProvider.getFont(element, index));
+
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TableLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,259 @@
+/*******************************************************************************
+ * 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
+ *     Florian Priester - bug 106059
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.TableLayout;
+
+import org.eclipse.jface.viewers.ColumnLayoutData;
+import org.eclipse.jface.viewers.ColumnPixelData;
+import org.eclipse.jface.viewers.ColumnWeightData;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.layout.TableColumnLayout;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * A layout for a table. Call <code>addColumnData</code> to add columns.
+ * The TableLayout {@link ColumnLayoutData} is only valid until the table
+ * is resized. To keep the proportions constant when the table is resized
+ * see {@link TableColumnLayout}
+ */
+public class TableLayout : Layout {
+
+    /**
+     * The number of extra pixels taken as horizontal trim by the table column.
+     * To ensure there are N pixels available for the content of the column,
+     * assign N+COLUMN_TRIM for the column width.
+     *
+     * @since 3.1
+     */
+    private static int COLUMN_TRIM_ = -1;
+    private static int COLUMN_TRIM(){
+        if( COLUMN_TRIM_ is -1 ){
+            synchronized( TableLayout.classinfo ){
+                if( COLUMN_TRIM_ is -1 ){
+                    if ("win32".equals(SWT.getPlatform())) { //$NON-NLS-1$
+                        COLUMN_TRIM_ = 4;
+                    } else if ("carbon".equals(SWT.getPlatform())) { //$NON-NLS-1$
+                        COLUMN_TRIM_ = 24;
+                    } else {
+                        COLUMN_TRIM_ = 3;
+                    }
+                }
+            }
+        }
+        return COLUMN_TRIM_;
+    }
+
+    /**
+     * The list of column layout data (element type:
+     * <code>ColumnLayoutData</code>).
+     */
+    private List columns;
+
+    /**
+     * Indicates whether <code>layout</code> has yet to be called.
+     */
+    private bool firstTime = true;
+
+    /**
+     * Creates a new table layout.
+     */
+    public this() {
+        columns = new ArrayList();
+    }
+
+    /**
+     * Adds a new column of data to this table layout.
+     *
+     * @param data
+     *            the column layout data
+     */
+    public void addColumnData(ColumnLayoutData data) {
+        columns.add(data);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Layout.
+     */
+    public override Point computeSize(Composite c, int wHint, int hHint, bool flush) {
+        if (wHint !is SWT.DEFAULT && hHint !is SWT.DEFAULT) {
+            return new Point(wHint, hHint);
+        }
+
+        Table table = cast(Table) c;
+        // To avoid recursions.
+        table.setLayout(null);
+        // Use native layout algorithm
+        Point result = table.computeSize(wHint, hHint, flush);
+        table.setLayout(this);
+
+        int width = 0;
+        int size = columns.size();
+        for (int i = 0; i < size; ++i) {
+            ColumnLayoutData layoutData = cast(ColumnLayoutData) columns.get(i);
+            if ( auto col = cast(ColumnPixelData) layoutData ) {
+                width += col.width;
+                if (col.addTrim) {
+                    width += COLUMN_TRIM;
+                }
+            } else if ( auto col = cast(ColumnWeightData) layoutData ) {
+                width += col.minimumWidth;
+            } else {
+                Assert.isTrue(false, "Unknown column layout data");//$NON-NLS-1$
+            }
+        }
+        if (width > result.x) {
+            result.x = width;
+        }
+        return result;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Layout.
+     */
+    public override void layout(Composite c, bool flush) {
+        // Only do initial layout. Trying to maintain proportions when resizing
+        // is too hard,
+        // causes lots of widget flicker, causes scroll bars to appear and
+        // occasionally stick around (on Windows),
+        // requires hooking column resizing as well, and may not be what the
+        // user wants anyway.
+        if (!firstTime) {
+            return;
+        }
+
+        int width = c.getClientArea().width;
+
+        // XXX: Layout is being called with an invalid value the first time
+        // it is being called on Linux. This method resets the
+        // Layout to null so we make sure we run it only when
+        // the value is OK.
+        if (width <= 1) {
+            return;
+        }
+
+        Item[] tableColumns = getColumns(c);
+        int size = Math.min(columns.size(), tableColumns.length);
+        int[] widths = new int[size];
+        int fixedWidth = 0;
+        int numberOfWeightColumns = 0;
+        int totalWeight = 0;
+
+        // First calc space occupied by fixed columns
+        for (int i = 0; i < size; i++) {
+            ColumnLayoutData col = cast(ColumnLayoutData) columns.get(i);
+            if ( auto cpd = cast(ColumnPixelData) col ) {
+                int pixels = cpd.width;
+                if (cpd.addTrim) {
+                    pixels += COLUMN_TRIM;
+                }
+                widths[i] = pixels;
+                fixedWidth += pixels;
+            } else if ( auto cw = cast(ColumnWeightData) col ) {
+                numberOfWeightColumns++;
+                // first time, use the weight specified by the column data,
+                // otherwise use the actual width as the weight
+                // int weight = firstTime ? cw.weight :
+                // tableColumns[i].getWidth();
+                int weight = cw.weight;
+                totalWeight += weight;
+            } else {
+                Assert.isTrue(false, "Unknown column layout data");//$NON-NLS-1$
+            }
+        }
+
+        // Do we have columns that have a weight
+        if (numberOfWeightColumns > 0) {
+            // Now distribute the rest to the columns with weight.
+            int rest = width - fixedWidth;
+            int totalDistributed = 0;
+            for (int i = 0; i < size; ++i) {
+                ColumnLayoutData col = cast(ColumnLayoutData) columns.get(i);
+                if (auto cw = cast(ColumnWeightData) col ) {
+                    // calculate weight as above
+                    // int weight = firstTime ? cw.weight :
+                    // tableColumns[i].getWidth();
+                    int weight = cw.weight;
+                    int pixels = totalWeight is 0 ? 0 : weight * rest
+                            / totalWeight;
+                    if (pixels < cw.minimumWidth) {
+                        pixels = cw.minimumWidth;
+                    }
+                    totalDistributed += pixels;
+                    widths[i] = pixels;
+                }
+            }
+
+            // Distribute any remaining pixels to columns with weight.
+            int diff = rest - totalDistributed;
+            for (int i = 0; diff > 0; ++i) {
+                if (i is size) {
+                    i = 0;
+                }
+                ColumnLayoutData col = cast(ColumnLayoutData) columns.get(i);
+                if (cast(ColumnWeightData)col ) {
+                    ++widths[i];
+                    --diff;
+                }
+            }
+        }
+
+        firstTime = false;
+
+        for (int i = 0; i < size; i++) {
+            setWidth(tableColumns[i], widths[i]);
+        }
+    }
+
+    /**
+     * Set the width of the item.
+     *
+     * @param item
+     * @param width
+     */
+    private void setWidth(Item item, int width) {
+        if ( cast(TreeColumn)item ) {
+            (cast(TreeColumn) item).setWidth(width);
+        } else {
+            (cast(TableColumn) item).setWidth(width);
+        }
+
+    }
+
+    /**
+     * Return the columns for the receiver.
+     *
+     * @param composite
+     * @return Item[]
+     */
+    private Item[] getColumns(Composite composite) {
+        if (cast(Tree)composite ) {
+            return (cast(Tree) composite).getColumns();
+        }
+        return (cast(Table) composite).getColumns();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TableTreeViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,886 @@
+/*******************************************************************************
+ * 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - bug 153993
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.TableTreeViewer;
+
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.ICellEditorListener;
+import org.eclipse.jface.viewers.ICellModifier;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.OpenEvent;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.ViewerLabel;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.TableTree;
+import org.eclipse.swt.custom.TableTreeEditor;
+import org.eclipse.swt.custom.TableTreeItem;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.TreeListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Widget;
+
+import java.lang.all;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A concrete viewer based on a SWT <code>TableTree</code> control.
+ * <p>
+ * This class is not intended to be subclassed outside the viewer framework. It
+ * is designed to be instantiated with a pre-existing SWT table tree control and
+ * configured with a domain-specific content provider, label provider, element
+ * filter (optional), and element sorter (optional).
+ * </p>
+ * <p>
+ * Content providers for table tree viewers must implement the
+ * <code>ITreeContentProvider</code> interface.
+ * </p>
+ * <p>
+ * Label providers for table tree viewers must implement either the
+ * <code>ITableLabelProvider</code> or the <code>ILabelProvider</code>
+ * interface (see <code>TableTreeViewer.setLabelProvider</code> for more
+ * details).
+ * </p>
+ *
+ * @deprecated As of 3.1 use {@link TreeViewer} instead
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class TableTreeViewer : AbstractTreeViewer {
+    alias AbstractTreeViewer.addTreeListener addTreeListener;
+    alias AbstractTreeViewer.doUpdateItem doUpdateItem;
+    alias AbstractTreeViewer.getLabelProvider getLabelProvider;
+    alias AbstractTreeViewer.getSelection getSelection;
+    alias AbstractTreeViewer.setSelection setSelection;
+
+    /**
+     * Internal table viewer implementation.
+     */
+    private TableTreeEditorImpl tableEditorImpl;
+
+    /**
+     * This viewer's table tree control.
+     */
+    private TableTree tableTree;
+
+    /**
+     * This viewer's table tree editor.
+     */
+    private TableTreeEditor tableTreeEditor;
+
+    /**
+     * Copied from original TableEditorImpl and moved here since refactoring
+     * completely wiped out the original implementation in 3.3
+     *
+     * @since 3.1
+     */
+    class TableTreeEditorImpl {
+
+        private CellEditor cellEditor;
+
+        private CellEditor[] cellEditors;
+
+        private ICellModifier cellModifier;
+
+        private String[] columnProperties;
+
+        private Item tableItem;
+
+        private int columnNumber;
+
+        private ICellEditorListener cellEditorListener;
+
+        private FocusListener focusListener;
+
+        private MouseListener mouseListener;
+
+        private int doubleClickExpirationTime;
+
+        private ColumnViewer viewer;
+
+        private this(ColumnViewer viewer) {
+            this.viewer = viewer;
+            initCellEditorListener();
+        }
+
+        /**
+         * Returns this <code>TableViewerImpl</code> viewer
+         *
+         * @return the viewer
+         */
+        public ColumnViewer getViewer() {
+            return viewer;
+        }
+
+        private void activateCellEditor() {
+            if( cellEditors !is null ) {
+                if( cellEditors[columnNumber] !is null && cellModifier !is null ) {
+                    Object element = tableItem.getData();
+                    String property = columnProperties[columnNumber];
+
+                    if( cellModifier.canModify(element, property) ) {
+                        cellEditor = cellEditors[columnNumber];
+
+                        cellEditor.addListener(cellEditorListener);
+
+                        Object value = cellModifier.getValue(element, property);
+                        cellEditor.setValue(value);
+                        // Tricky flow of control here:
+                        // activate() can trigger callback to cellEditorListener
+                        // which will clear cellEditor
+                        // so must get control first, but must still call activate()
+                        // even if there is no control.
+                        final Control control = cellEditor.getControl();
+                        cellEditor.activate();
+                        if (control is null) {
+                            return;
+                        }
+                        setLayoutData(cellEditor.getLayoutData());
+                        setEditor(control, tableItem, columnNumber);
+                        cellEditor.setFocus();
+                        if (focusListener is null) {
+                            focusListener = new class FocusAdapter {
+                                public void focusLost(FocusEvent e) {
+                                    applyEditorValue();
+                                }
+                            };
+                        }
+                        control.addFocusListener(focusListener);
+                        mouseListener = new class(control) MouseAdapter {
+                            Control control_;
+                            this(Control a){
+                                control_=a;
+                            }
+                            public void mouseDown(MouseEvent e) {
+                                // time wrap?
+                                // check for expiration of doubleClickTime
+                                if (e.time <= doubleClickExpirationTime) {
+                                    control_.removeMouseListener(mouseListener);
+                                    cancelEditing();
+                                    handleDoubleClickEvent();
+                                } else if (mouseListener !is null) {
+                                    control_.removeMouseListener(mouseListener);
+                                }
+                            }
+                        };
+                        control.addMouseListener(mouseListener);
+                    }
+                }
+            }
+        }
+
+        /**
+         * Activate a cell editor for the given mouse position.
+         */
+        private void activateCellEditor(MouseEvent event) {
+            if (tableItem is null || tableItem.isDisposed()) {
+                // item no longer exists
+                return;
+            }
+            int columnToEdit;
+            int columns = getColumnCount();
+            if (columns is 0) {
+                // If no TableColumn, Table acts as if it has a single column
+                // which takes the whole width.
+                columnToEdit = 0;
+            } else {
+                columnToEdit = -1;
+                for (int i = 0; i < columns; i++) {
+                    Rectangle bounds = getBounds(tableItem, i);
+                    if (bounds.contains(event.x, event.y)) {
+                        columnToEdit = i;
+                        break;
+                    }
+                }
+                if (columnToEdit is -1) {
+                    return;
+                }
+            }
+
+            columnNumber = columnToEdit;
+            activateCellEditor();
+        }
+
+        /**
+         * Deactivates the currently active cell editor.
+         */
+        public void applyEditorValue() {
+            CellEditor c = this.cellEditor;
+            if (c !is null) {
+                // null out cell editor before calling save
+                // in case save results in applyEditorValue being re-entered
+                // see 1GAHI8Z: ITPUI:ALL - How to code event notification when
+                // using cell editor ?
+                this.cellEditor = null;
+                Item t = this.tableItem;
+                // don't null out table item -- same item is still selected
+                if (t !is null && !t.isDisposed()) {
+                    saveEditorValue(c, t);
+                }
+                setEditor(null, null, 0);
+                c.removeListener(cellEditorListener);
+                Control control = c.getControl();
+                if (control !is null) {
+                    if (mouseListener !is null) {
+                        control.removeMouseListener(mouseListener);
+                    }
+                    if (focusListener !is null) {
+                        control.removeFocusListener(focusListener);
+                    }
+                }
+                c.deactivate();
+            }
+        }
+
+        /**
+         * Cancels the active cell editor, without saving the value back to the
+         * domain model.
+         */
+        public void cancelEditing() {
+            if (cellEditor !is null) {
+                setEditor(null, null, 0);
+                cellEditor.removeListener(cellEditorListener);
+                CellEditor oldEditor = cellEditor;
+                cellEditor = null;
+                oldEditor.deactivate();
+            }
+        }
+
+        /**
+         * Start editing the given element.
+         *
+         * @param element
+         * @param column
+         */
+        public void editElement(Object element, int column) {
+            if (cellEditor !is null) {
+                applyEditorValue();
+            }
+
+            setSelection(new StructuredSelection(element), true);
+            Item[] selection = getSelection();
+            if (selection.length !is 1) {
+                return;
+            }
+
+            tableItem = selection[0];
+
+            // Make sure selection is visible
+            showSelection();
+            columnNumber = column;
+            activateCellEditor();
+
+        }
+
+        /**
+         * Return the array of CellEditors used in the viewer
+         *
+         * @return the cell editors
+         */
+        public CellEditor[] getCellEditors() {
+            return cellEditors;
+        }
+
+        /**
+         * Get the cell modifier
+         *
+         * @return the cell modifier
+         */
+        public ICellModifier getCellModifier() {
+            return cellModifier;
+        }
+
+        /**
+         * Return the properties for the column
+         *
+         * @return the array of column properties
+         */
+        public Object[] getColumnProperties() {
+            return stringcast( columnProperties );
+        }
+
+        /**
+         * Handles the mouse down event; activates the cell editor.
+         *
+         * @param event
+         *            the mouse event that should be handled
+         */
+        public void handleMouseDown(MouseEvent event) {
+            if (event.button !is 1) {
+                return;
+            }
+
+            if (cellEditor !is null) {
+                applyEditorValue();
+            }
+
+            // activate the cell editor immediately. If a second mouseDown
+            // is received prior to the expiration of the doubleClick time then
+            // the cell editor will be deactivated and a doubleClick event will
+            // be processed.
+            //
+            doubleClickExpirationTime = event.time
+                    + Display.getCurrent().getDoubleClickTime();
+
+            Item[] items = getSelection();
+            // Do not edit if more than one row is selected.
+            if (items.length !is 1) {
+                tableItem = null;
+                return;
+            }
+            tableItem = items[0];
+
+            activateCellEditor(event);
+        }
+
+        private void initCellEditorListener() {
+            cellEditorListener = new class ICellEditorListener {
+                public void editorValueChanged(bool oldValidState,
+                        bool newValidState) {
+                    // Ignore.
+                }
+
+                public void cancelEditor() {
+                    this.outer.cancelEditing();
+                }
+
+                public void applyEditorValue() {
+                    this.outer.applyEditorValue();
+                }
+            };
+        }
+
+        /**
+         * Return whether there is an active cell editor.
+         *
+         * @return <code>true</code> if there is an active cell editor;
+         *         otherwise <code>false</code> is returned.
+         */
+        public bool isCellEditorActive() {
+            return cellEditor !is null;
+        }
+
+        /**
+         * Saves the value of the currently active cell editor, by delegating to
+         * the cell modifier.
+         */
+        private void saveEditorValue(CellEditor cellEditor, Item tableItem) {
+            if( cellModifier !is null ) {
+                if( ! cellEditor.isValueValid() ) {
+                    // Do what????
+                }
+            }
+            String property = null;
+
+            if( columnProperties !is null && columnNumber < columnProperties.length ) {
+                property = columnProperties[columnNumber];
+            }
+            cellModifier.modify(tableItem, property, cellEditor.getValue());
+        }
+
+        /**
+         * Set the cell editors
+         *
+         * @param editors
+         */
+        public void setCellEditors(CellEditor[] editors) {
+            this.cellEditors = editors;
+        }
+
+        /**
+         * Set the cell modifier
+         *
+         * @param modifier
+         */
+        public void setCellModifier(ICellModifier modifier) {
+            this.cellModifier = modifier;
+        }
+
+        /**
+         * Set the column properties
+         *
+         * @param columnProperties
+         */
+        public void setColumnProperties(String[] columnProperties) {
+            this.columnProperties = columnProperties;
+        }
+
+        Rectangle getBounds(Item item, int columnNumber) {
+            return (cast(TableTreeItem) item).getBounds(columnNumber);
+        }
+
+        int getColumnCount() {
+            // getColumnCount() should be a API in TableTree.
+            return getTableTree().getTable().getColumnCount();
+        }
+
+        Item[] getSelection() {
+            return getTableTree().getSelection();
+        }
+
+        void setEditor(Control w, Item item, int columnNumber) {
+            tableTreeEditor.setEditor(w, cast(TableTreeItem) item, columnNumber);
+        }
+
+        void setSelection(StructuredSelection selection, bool b) {
+            this.outer.setSelection(selection, b);
+        }
+
+        void showSelection() {
+            getTableTree().showSelection();
+        }
+
+        void setLayoutData(LayoutData layoutData) {
+            tableTreeEditor.horizontalAlignment = layoutData.horizontalAlignment;
+            tableTreeEditor.grabHorizontal = layoutData.grabHorizontal;
+            tableTreeEditor.minimumWidth = layoutData.minimumWidth;
+        }
+
+        void handleDoubleClickEvent() {
+            Viewer viewer = getViewer();
+            fireDoubleClick(new DoubleClickEvent(viewer, viewer.getSelection()));
+            fireOpen(new OpenEvent(viewer, viewer.getSelection()));
+        }
+    }
+
+    /**
+     * Creates a table tree viewer on the given table tree control. The viewer
+     * has no input, no content provider, a default label provider, no sorter,
+     * and no filters.
+     *
+     * @param tree
+     *            the table tree control
+     */
+    public this(TableTree tree) {
+        super();
+        tableTree = tree;
+        hookControl(tree);
+        tableTreeEditor = new TableTreeEditor(tableTree);
+        tableEditorImpl = new TableTreeEditorImpl(this);
+    }
+
+    /**
+     * Creates a table tree viewer on a newly-created table tree control under
+     * the given parent. The table tree control is created using the SWT style
+     * bits <code>MULTI, H_SCROLL, V_SCROLL, and BORDER</code>. The viewer
+     * has no input, no content provider, a default label provider, no sorter,
+     * and no filters.
+     *
+     * @param parent
+     *            the parent control
+     */
+    public this(Composite parent) {
+        this(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
+    }
+
+    /**
+     * Creates a table tree viewer on a newly-created table tree control under
+     * the given parent. The table tree control is created using the given SWT
+     * style bits. The viewer has no input, no content provider, a default label
+     * provider, no sorter, and no filters.
+     *
+     * @param parent
+     *            the parent control
+     * @param style
+     *            the SWT style bits
+     */
+    public this(Composite parent, int style) {
+        this(new TableTree(parent, style));
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected override void addTreeListener(Control c, TreeListener listener) {
+        (cast(TableTree) c).addTreeListener(listener);
+    }
+
+    /**
+     * Cancels a currently active cell editor. All changes already done in the
+     * cell editor are lost.
+     */
+    public override void cancelEditing() {
+        tableEditorImpl.cancelEditing();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected override void doUpdateItem(Item item, Object element) {
+        // update icon and label
+        // Similar code in TableTreeViewer.doUpdateItem()
+        IBaseLabelProvider prov = getLabelProvider();
+        ITableLabelProvider tprov = null;
+
+        if ( auto t = cast(ITableLabelProvider) prov ) {
+            tprov = t;
+        }
+
+        int columnCount = tableTree.getTable().getColumnCount();
+        TableTreeItem ti = cast(TableTreeItem) item;
+        // Also enter loop if no columns added. See 1G9WWGZ: JFUIF:WINNT -
+        // TableViewer with 0 columns does not work
+        for (int column = 0; column < columnCount || column is 0; column++) {
+            String text = "";//$NON-NLS-1$
+            Image image = null;
+            if (tprov !is null) {
+                text = tprov.getColumnText(element, column);
+                image = tprov.getColumnImage(element, column);
+            } else {
+                if (column is 0) {
+                    ViewerLabel updateLabel = new ViewerLabel(item.getText(),
+                            item.getImage());
+                    buildLabel(updateLabel, element);
+
+                    // As it is possible for user code to run the event
+                    // loop check here.
+                    if (item.isDisposed()) {
+                        unmapElement(element, item);
+                        return;
+                    }
+
+                    text = updateLabel.getText();
+                    image = updateLabel.getImage();
+                }
+            }
+
+            // Avoid setting text to null
+            if (text is null) {
+                text = ""; //$NON-NLS-1$
+            }
+
+            ti.setText(column, text);
+            // Apparently a problem to setImage to null if already null
+            if (ti.getImage(column) !is image) {
+                ti.setImage(column, image);
+            }
+
+            getColorAndFontCollector().setFontsAndColors(element);
+            getColorAndFontCollector().applyFontsAndColors(ti);
+        }
+
+    }
+
+    /**
+     * Starts editing the given element.
+     *
+     * @param element
+     *            the element
+     * @param column
+     *            the column number
+     */
+    public override void editElement(Object element, int column) {
+        tableEditorImpl.editElement(element, column);
+    }
+
+    /**
+     * Returns the cell editors of this viewer.
+     *
+     * @return the list of cell editors
+     */
+    public override CellEditor[] getCellEditors() {
+        return tableEditorImpl.getCellEditors();
+    }
+
+    /**
+     * Returns the cell modifier of this viewer.
+     *
+     * @return the cell modifier
+     */
+    public override ICellModifier getCellModifier() {
+        return tableEditorImpl.getCellModifier();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected override Item[] getChildren(Widget o) {
+        if (auto i = cast(TableTreeItem) o ) {
+            return i.getItems();
+        }
+        if (auto i = cast(TableTree) o ) {
+            return i.getItems();
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.AbstractTreeViewer#getChild(org.eclipse.swt.widgets.Widget,
+     *      int)
+     */
+    protected override Item getChild(Widget widget, int index) {
+        if (auto w = cast(TableTreeItem) widget ) {
+            return w.getItem(index);
+        }
+        if (auto w = cast(TableTree) widget ) {
+            return w.getItem(index);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the column properties of this viewer. The properties must
+     * correspond with the columns of the table control. They are used to
+     * identify the column in a cell modifier.
+     *
+     * @return the list of column properties
+     */
+    public override Object[] getColumnProperties() {
+        return tableEditorImpl.getColumnProperties();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Viewer.
+     */
+    public override Control getControl() {
+        return tableTree;
+    }
+
+    /**
+     * Returns the element with the given index from this viewer. Returns
+     * <code>null</code> if the index is out of range.
+     * <p>
+     * This method is internal to the framework.
+     * </p>
+     *
+     * @param index
+     *            the zero-based index
+     * @return the element at the given index, or <code>null</code> if the
+     *         index is out of range
+     */
+    public Object getElementAt(int index) {
+        // XXX: Workaround for 1GBCSB1: SWT:WIN2000 - TableTree should have
+        // getItem(int index)
+        TableTreeItem i = tableTree.getItems()[index];
+        if (i !is null) {
+            return i.getData();
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected override bool getExpanded(Item item) {
+        return (cast(TableTreeItem) item).getExpanded();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ColumnViewer#getItemAt(org.eclipse.swt.graphics.Point)
+     */
+    protected override Item getItemAt(Point p) {
+        return getTableTree().getTable().getItem(p);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected override int getItemCount(Control widget) {
+        return (cast(TableTree) widget).getItemCount();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected override int getItemCount(Item item) {
+        return (cast(TableTreeItem) item).getItemCount();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected override org.eclipse.swt.widgets.Item.Item[] getItems(
+            org.eclipse.swt.widgets.Item.Item item) {
+        return (cast(TableTreeItem) item).getItems();
+    }
+
+    /**
+     * The table tree viewer implementation of this <code>Viewer</code>
+     * framework method returns the label provider, which in the case of table
+     * tree viewers will be an instance of either
+     * <code>ITableLabelProvider</code> or <code>ILabelProvider</code>. If
+     * it is an <code>ITableLabelProvider</code>, then it provides a separate
+     * label text and image for each column. If it is an
+     * <code>ILabelProvider</code>, then it provides only the label text and
+     * image for the first column, and any remaining columns are blank.
+     */
+    public override IBaseLabelProvider getLabelProvider() {
+        return super.getLabelProvider();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected override Item getParentItem(Item item) {
+        return (cast(TableTreeItem) item).getParentItem();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected override Item[] getSelection(Control widget) {
+        return (cast(TableTree) widget).getSelection();
+    }
+
+    /**
+     * Returns this table tree viewer's table tree control.
+     *
+     * @return the table tree control
+     */
+    public TableTree getTableTree() {
+        return tableTree;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected override void hookControl(Control control) {
+        super.hookControl(control);
+        tableTree.getTable().addMouseListener(new class MouseAdapter {
+            public void mouseDown(MouseEvent e) {
+                /*
+                 * If user clicked on the [+] or [-], do not activate
+                 * CellEditor.
+                 */
+                // XXX: This code should not be here. SWT should either have
+                // support to see
+                // if the user clicked on the [+]/[-] or manage the table editor
+                // activation
+                org.eclipse.swt.widgets.TableItem.TableItem[] items = tableTree
+                        .getTable().getItems();
+                for (int i = 0; i < items.length; i++) {
+                    Rectangle rect = items[i].getImageBounds(0);
+                    if (rect.contains(e.x, e.y)) {
+                        return;
+                    }
+                }
+
+                tableEditorImpl.handleMouseDown(e);
+            }
+        });
+    }
+
+    /**
+     * Returns whether there is an active cell editor.
+     *
+     * @return <code>true</code> if there is an active cell editor, and
+     *         <code>false</code> otherwise
+     */
+    public override bool isCellEditorActive() {
+        return tableEditorImpl.isCellEditorActive();
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override Item newItem(Widget parent, int flags, int ix) {
+        TableTreeItem item;
+        if (ix >= 0) {
+            if (cast(TableTreeItem) parent ) {
+                item = new TableTreeItem(cast(TableTreeItem) parent, flags, ix);
+            } else {
+                item = new TableTreeItem(cast(TableTree) parent, flags, ix);
+            }
+        } else {
+            if (cast(TableTreeItem)parent ) {
+                item = new TableTreeItem(cast(TableTreeItem) parent, flags);
+            } else {
+                item = new TableTreeItem(cast(TableTree) parent, flags);
+            }
+        }
+        return item;
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override void removeAll(Control widget) {
+        (cast(TableTree) widget).removeAll();
+    }
+
+    /**
+     * Sets the cell editors of this table viewer.
+     *
+     * @param editors
+     *            the list of cell editors
+     */
+    public override void setCellEditors(CellEditor[] editors) {
+        tableEditorImpl.setCellEditors(editors);
+    }
+
+    /**
+     * Sets the cell modifier of this table viewer.
+     *
+     * @param modifier
+     *            the cell modifier
+     */
+    public override void setCellModifier(ICellModifier modifier) {
+        tableEditorImpl.setCellModifier(modifier);
+    }
+
+    /**
+     * Sets the column properties of this table viewer. The properties must
+     * correspond with the columns of the table control. They are used to
+     * identify the column in a cell modifier.
+     *
+     * @param columnProperties
+     *            the list of column properties
+     */
+    public override void setColumnProperties(String[] columnProperties) {
+        tableEditorImpl.setColumnProperties(columnProperties);
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override void setExpanded(Item node, bool expand) {
+        (cast(TableTreeItem) node).setExpanded(expand);
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override void setSelection(List items) {
+        TableTreeItem[] newItems = arraycast!(TableTreeItem)(items.toArray());
+        getTableTree().setSelection(newItems);
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override void showItem(Item item) {
+        getTableTree().showItem(cast(TableTreeItem) item);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TableViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,394 @@
+/*******************************************************************************
+ * 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - concept of ViewerRow,
+ *                                                 fix for 159597, refactoring (bug 153993),
+ *                                                 widget-independency (bug 154329), fix for 187826, 191468
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.TableViewer;
+
+import org.eclipse.jface.viewers.AbstractTableViewer;
+import org.eclipse.jface.viewers.TableViewerRow;
+import org.eclipse.jface.viewers.ColumnViewerEditor;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ViewerRow;
+import org.eclipse.jface.viewers.TableViewerEditor;
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A concrete viewer based on a SWT <code>Table</code> control.
+ * <p>
+ * This class is not intended to be subclassed outside the viewer framework. It
+ * is designed to be instantiated with a pre-existing SWT table control and
+ * configured with a domain-specific content provider, table label provider,
+ * element filter (optional), and element sorter (optional).
+ * </p>
+ * <p>
+ * Label providers for table viewers must implement either the
+ * <code>ITableLabelProvider</code> or the <code>ILabelProvider</code> interface
+ * (see <code>TableViewer.setLabelProvider</code> for more details).
+ * </p>
+ * <p>
+ * As of 3.1 the TableViewer now supports the SWT.VIRTUAL flag. If the
+ * underlying table is SWT.VIRTUAL, the content provider may implement {@link
+ * ILazyContentProvider} instead of {@link IStructuredContentProvider} . Note
+ * that in this case, the viewer does not support sorting or filtering. Also
+ * note that in this case, the Widget based APIs may return null if the element
+ * is not specified or not created yet.
+ * </p>
+ * <p>
+ * Users of SWT.VIRTUAL should also avoid using getItems() from the Table within
+ * the TreeViewer as this does not necessarily generate a callback for the
+ * TreeViewer to populate the items. It also has the side effect of creating all
+ * of the items thereby eliminating the performance improvements of SWT.VIRTUAL.
+ * </p>
+ * <p>
+ * Users setting up an editable table with more than 1 column <b>have</b> to pass the
+ * SWT.FULL_SELECTION style bit
+ * </p>
+ * 
+ * @see SWT#VIRTUAL
+ * @see #doFindItem(Object)
+ * @see #internalRefresh(Object, bool)
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class TableViewer : AbstractTableViewer {
+    alias AbstractTableViewer.refresh refresh;
+    alias AbstractTableViewer.setSelection setSelection;
+
+    /**
+     * This viewer's table control.
+     */
+    private Table table;
+
+    /**
+     * The cached row which is reused all over
+     */
+    private TableViewerRow cachedRow;
+
+    /**
+     * Creates a table viewer on a newly-created table control under the given
+     * parent. The table control is created using the SWT style bits
+     * <code>MULTI, H_SCROLL, V_SCROLL,</code> and <code>BORDER</code>. The
+     * viewer has no input, no content provider, a default label provider, no
+     * sorter, and no filters. The table has no columns.
+     * 
+     * @param parent
+     *      the parent control
+     */
+    public this(Composite parent) {
+        this(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
+    }
+
+    /**
+     * Creates a table viewer on a newly-created table control under the given
+     * parent. The table control is created using the given style bits. The
+     * viewer has no input, no content provider, a default label provider, no
+     * sorter, and no filters. The table has no columns.
+     * 
+     * @param parent
+     *      the parent control
+     * @param style
+     *      SWT style bits
+     */
+    public this(Composite parent, int style) {
+        this(new Table(parent, style));
+    }
+
+    /**
+     * Creates a table viewer on the given table control. The viewer has no
+     * input, no content provider, a default label provider, no sorter, and no
+     * filters.
+     * 
+     * @param table
+     *      the table control
+     */
+    public this(Table table) {
+        this.table = table;
+        hookControl(table);
+    }
+
+    public override Control getControl() {
+        return table;
+    }
+
+    /**
+     * Returns this table viewer's table control.
+     * 
+     * @return the table control
+     */
+    public Table getTable() {
+        return table;
+    }
+
+    protected override ColumnViewerEditor createViewerEditor() {
+        return new TableViewerEditor(this, null,
+                new ColumnViewerEditorActivationStrategy(this),
+                ColumnViewerEditor.DEFAULT);
+    }
+
+    /**
+     * <p>
+     * Sets a new selection for this viewer and optionally makes it visible. The
+     * TableViewer implementation of this method is inefficient for the
+     * ILazyContentProvider as lookup is done by indices rather than elements
+     * and may require population of the entire table in worse case.
+     * </p>
+     * <p>
+     * Use Table#setSelection(int[] indices) and Table#showSelection() if you
+     * wish to set selection more efficiently when using a ILazyContentProvider.
+     * </p>
+     * 
+     * @param selection
+     *      the new selection
+     * @param reveal
+     *      <code>true</code> if the selection is to be made visible, and
+     *      <code>false</code> otherwise
+     * @see Table#setSelection(int[])
+     * @see Table#showSelection()
+     */
+    public override void setSelection(ISelection selection, bool reveal) {
+        super.setSelection(selection, reveal);
+    }
+
+    protected override ViewerRow getViewerRowFromItem(Widget item) {
+        if (cachedRow is null) {
+            cachedRow = new TableViewerRow(cast(TableItem) item);
+        } else {
+            cachedRow.setItem(cast(TableItem) item);
+        }
+
+        return cachedRow;
+    }
+
+    /**
+     * Create a new row with style at index
+     * 
+     * @param style
+     * @param rowIndex
+     * @return ViewerRow
+     * @since 3.3
+     */
+    protected override ViewerRow internalCreateNewRowPart(int style, int rowIndex) {
+        TableItem item;
+
+        if (rowIndex >= 0) {
+            item = new TableItem(table, style, rowIndex);
+        } else {
+            item = new TableItem(table, style);
+        }
+
+        return getViewerRowFromItem(item);
+    }
+
+    protected override Item getItemAt(Point p) {
+        TableItem[] selection = table.getSelection();
+
+        if (selection.length is 1) {
+            int columnCount = table.getColumnCount();
+
+            for (int i = 0; i < columnCount; i++) {
+                if (selection[0].getBounds(i).contains(p)) {
+                    return selection[0];
+                }
+            }
+        }
+
+        return table.getItem(p);
+    }
+
+    // Methods to provide widget independency
+
+    protected override int doGetItemCount() {
+        return table.getItemCount();
+    }
+
+    protected override int doIndexOf(Item item) {
+        return table.indexOf(cast(TableItem) item);
+    }
+
+    protected override void doSetItemCount(int count) {
+        table.setItemCount(count);
+    }
+
+    protected override Item[] doGetItems() {
+        return table.getItems();
+    }
+
+    protected override int doGetColumnCount() {
+        return table.getColumnCount();
+    }
+
+    protected override Widget doGetColumn(int index) {
+        return table.getColumn(index);
+    }
+
+    protected override Item doGetItem(int index) {
+        return table.getItem(index);
+    }
+
+    protected override Item[] doGetSelection() {
+        return table.getSelection();
+    }
+
+    protected override int[] doGetSelectionIndices() {
+        return table.getSelectionIndices();
+    }
+
+    protected override void doClearAll() {
+        table.clearAll();
+    }
+
+    protected override void doResetItem(Item item) {
+        TableItem tableItem = cast(TableItem) item;
+        int columnCount = Math.max(1, table.getColumnCount());
+        for (int i = 0; i < columnCount; i++) {
+            tableItem.setText(i, ""); //$NON-NLS-1$
+            if (tableItem.getImage(i) !is null) {
+                tableItem.setImage(i, null);
+            }
+        }
+    }
+
+    protected override void doRemove(int start, int end) {
+        table.remove(start, end);
+    }
+
+    protected override void doRemoveAll() {
+        table.removeAll();
+    }
+
+    protected override void doRemove(int[] indices) {
+        table.remove(indices);
+    }
+
+    protected override void doShowItem(Item item) {
+        table.showItem(cast(TableItem) item);
+    }
+
+    protected override void doDeselectAll() {
+        table.deselectAll();
+    }
+
+    protected override void doSetSelection(Item[] items) {
+//         Assert.isNotNull(items, "Items-Array can not be null"); //$NON-NLS-1$
+
+        TableItem[] t = new TableItem[items.length];
+        System.arraycopy(items, 0, t, 0, t.length);
+
+        table.setSelection(t);
+    }
+
+    protected override void doShowSelection() {
+        table.showSelection();
+    }
+
+    protected override void doSetSelection(int[] indices) {
+        table.setSelection(indices);
+    }
+
+    protected override void doClear(int index) {
+        table.clear(index);
+    }
+
+    protected override void doSelect(int[] indices) {
+        table.select(indices);
+    }
+
+    /**
+     * Refreshes this viewer starting with the given element. Labels are updated
+     * as described in <code>refresh(bool updateLabels)</code>. The methods
+     * attempts to preserve the selection.
+     * <p>
+     * Unlike the <code>update</code> methods, this handles structural changes
+     * to the given element (e.g. addition or removal of children). If only the
+     * given element needs updating, it is more efficient to use the
+     * <code>update</code> methods.
+     * </p>
+     * 
+     * <p>
+     * Subclasses who can provide this feature can open this method for the
+     * public
+     * </p>
+     * 
+     * @param element
+     *      the element
+     * @param updateLabels
+     *      <code>true</code> to update labels for existing elements,
+     *      <code>false</code> to only update labels as needed, assuming that labels
+     *      for existing elements are unchanged.
+     * @param reveal
+     *      <code>true</code> to make the preserved selection visible afterwards
+     * 
+     * @since 3.3
+     */
+    public void refresh(Object element, bool updateLabels,
+            bool reveal) {
+        if (checkBusy())
+            return;
+
+        if (isCellEditorActive()) {
+            cancelEditing();
+        }
+
+        preservingSelection(new class(element,updateLabels) Runnable {
+            Object element_;
+            bool updateLabels_;
+            this(Object a,bool b){
+                element_ = a;
+                updateLabels_ = b;
+            }
+            public void run() {
+                internalRefresh(element_, updateLabels_);
+            }
+        }, reveal);
+    }
+
+    /**
+     * Refreshes this viewer with information freshly obtained from this
+     * viewer's model. If <code>updateLabels</code> is <code>true</code> then
+     * labels for otherwise unaffected elements are updated as well. Otherwise,
+     * it assumes labels for existing elements are unchanged, and labels are
+     * only obtained as needed (for example, for new elements).
+     * <p>
+     * Calling <code>refresh(true)</code> has the same effect as
+     * <code>refresh()</code>.
+     * <p>
+     * Note that the implementation may still obtain labels for existing
+     * elements even if <code>updateLabels</code> is false. The intent is simply
+     * to allow optimization where possible.
+     * 
+     * @param updateLabels
+     *      <code>true</code> to update labels for existing elements,
+     *      <code>false</code> to only update labels as needed, assuming that labels
+     *      for existing elements are unchanged.
+     * @param reveal
+     *      <code>true</code> to make the preserved selection visible afterwards
+     * 
+     * @since 3.3
+     */
+    public void refresh(bool updateLabels, bool reveal) {
+        refresh(getRoot(), updateLabels, reveal);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TableViewerColumn.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2006 Tom Schindl and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Tom Schindl - initial API and implementation
+ *     Boris Bokowski (IBM Corporation) - Javadoc improvements
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module org.eclipse.jface.viewers.TableViewerColumn;
+
+import org.eclipse.jface.viewers.ViewerColumn;
+import org.eclipse.jface.viewers.TableViewer;
+
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+
+import java.lang.all;
+
+/**
+ * ViewerColumn implementation for TableViewer to enable column-specific label
+ * providers and editing support.
+ *
+ * @since 3.3
+ */
+public final class TableViewerColumn : ViewerColumn {
+    private TableColumn column;
+
+    /**
+     * Creates a new viewer column for the given {@link TableViewer} on a new
+     * {@link TableColumn} with the given style bits. The column is added at the
+     * end of the list of columns.
+     *
+     * @param viewer
+     *            the table viewer to which this column belongs
+     * @param style
+     *            the style used to create the column, for applicable style bits
+     *            see {@link TableColumn}
+     * @see TableColumn#TableColumn(Table, int)
+     */
+    public this(TableViewer viewer, int style) {
+        this(viewer, style, -1);
+    }
+
+    /**
+     * Creates a new viewer column for the given {@link TableViewer} on a new
+     * {@link TableColumn} with the given style bits. The column is inserted at
+     * the given index into the list of columns.
+     *
+     * @param viewer
+     *            the table viewer to which this column belongs
+     * @param style
+     *            the style used to create the column, for applicable style bits
+     *            see {@link TableColumn}
+     * @param index
+     *            the index at which to place the newly created column
+     * @see TableColumn#TableColumn(Table, int, int)
+     */
+    public this(TableViewer viewer, int style, int index) {
+        this(viewer, createColumn(viewer.getTable(), style, index));
+    }
+
+    /**
+     * Creates a new viewer column for the given {@link TableViewer} on the given
+     * {@link TableColumn}.
+     *
+     * @param viewer
+     *            the table viewer to which this column belongs
+     * @param column
+     *            the underlying table column
+     */
+    public this(TableViewer viewer, TableColumn column) {
+        super(viewer, column);
+        this.column = column;
+    }
+
+    private static TableColumn createColumn(Table table, int style, int index) {
+        if (index >= 0) {
+            return new TableColumn(table, style, index);
+        }
+
+        return new TableColumn(table, style);
+    }
+
+    /**
+     * @return the underlying SWT table column
+     */
+    public TableColumn getColumn() {
+        return column;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TableViewerEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                                 fixes in bug 198665, 200731
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.TableViewerEditor;
+
+import org.eclipse.jface.viewers.ColumnViewerEditor;
+import org.eclipse.jface.viewers.SWTFocusCellManager;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.StructuredSelection;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.TableEditor;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * This is an editor-implementation for {@link Table}
+ *
+ * @since 3.3
+ *
+ */
+public final class TableViewerEditor : ColumnViewerEditor {
+    /**
+     * This viewer's table editor.
+     */
+    private TableEditor tableEditor;
+
+    private SWTFocusCellManager focusCellManager;
+
+    /**
+     * @param viewer
+     *            the viewer the editor is attached to
+     * @param focusCellManager
+     *            the cell focus manager if one used or <code>null</code>
+     * @param editorActivationStrategy
+     *            the strategy used to decide about the editor activation
+     * @param feature
+     *            the feature mask
+     */
+    this(TableViewer viewer, SWTFocusCellManager focusCellManager,
+            ColumnViewerEditorActivationStrategy editorActivationStrategy,
+            int feature) {
+        super(viewer, editorActivationStrategy, feature);
+        tableEditor = new TableEditor(viewer.getTable());
+        this.focusCellManager = focusCellManager;
+    }
+
+    /**
+     * Create a customized editor with focusable cells
+     *
+     * @param viewer
+     *            the viewer the editor is created for
+     * @param focusCellManager
+     *            the cell focus manager if one needed else <code>null</code>
+     * @param editorActivationStrategy
+     *            activation strategy to control if an editor activated
+     * @param feature
+     *            bit mask controlling the editor
+     *            <ul>
+     *            <li>{@link ColumnViewerEditor#DEFAULT}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li>
+     *            </ul>
+     * @see #create(TableViewer, ColumnViewerEditorActivationStrategy, int)
+     */
+    public static void create(TableViewer viewer,
+            SWTFocusCellManager focusCellManager,
+            ColumnViewerEditorActivationStrategy editorActivationStrategy,
+            int feature) {
+        TableViewerEditor editor = new TableViewerEditor(viewer,
+                focusCellManager, editorActivationStrategy, feature);
+        viewer.setColumnViewerEditor(editor);
+        if (focusCellManager !is null) {
+            focusCellManager.init();
+        }
+    }
+
+    /**
+     * Create a customized editor whose activation process is customized
+     *
+     * @param viewer
+     *            the viewer the editor is created for
+     * @param editorActivationStrategy
+     *            activation strategy to control if an editor activated
+     * @param feature
+     *            bit mask controlling the editor
+     *            <ul>
+     *            <li>{@link ColumnViewerEditor#DEFAULT}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li>
+     *            </ul>
+     */
+    public static void create(TableViewer viewer,
+            ColumnViewerEditorActivationStrategy editorActivationStrategy,
+            int feature) {
+        create(viewer, null, editorActivationStrategy, feature);
+    }
+
+    protected override void setEditor(Control w, Item item, int columnNumber) {
+        tableEditor.setEditor(w, cast(TableItem) item, columnNumber);
+    }
+
+    protected override void setLayoutData(LayoutData layoutData) {
+        tableEditor.grabHorizontal = layoutData.grabHorizontal;
+        tableEditor.horizontalAlignment = layoutData.horizontalAlignment;
+        tableEditor.minimumWidth = layoutData.minimumWidth;
+        tableEditor.verticalAlignment = layoutData.verticalAlignment;
+
+        if( layoutData.minimumHeight !is SWT.DEFAULT ) {
+            tableEditor.minimumHeight = layoutData.minimumHeight;
+        }
+    }
+
+    public override ViewerCell getFocusCell() {
+        if (focusCellManager !is null) {
+            return focusCellManager.getFocusCell();
+        }
+
+        return super.getFocusCell();
+    }
+
+    protected override void updateFocusCell(ViewerCell focusCell,
+            ColumnViewerEditorActivationEvent event) {
+        // Update the focus cell when we activated the editor with these 2
+        // events
+        if (event.eventType is ColumnViewerEditorActivationEvent.PROGRAMMATIC
+                || event.eventType is ColumnViewerEditorActivationEvent.TRAVERSAL) {
+
+            auto l = getViewer().getSelectionFromWidget_package();
+
+            if (focusCellManager !is null) {
+                focusCellManager.setFocusCell(focusCell);
+            }
+
+            if (!l.contains(focusCell.getElement())) {
+                getViewer().setSelection(
+                        new StructuredSelection(focusCell.getElement()),true);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TableViewerFocusCellManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                                 fix in bug: 210752
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.TableViewerFocusCellManager;
+
+import org.eclipse.jface.viewers.SWTFocusCellManager;
+import org.eclipse.jface.viewers.CellNavigationStrategy;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.FocusCellHighlighter;
+import org.eclipse.jface.viewers.ViewerCell;
+
+import org.eclipse.swt.widgets.Table;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * This class is responsible to provide the concept of cells for {@link Table}.
+ * This concept is needed to provide features like editor activation with the
+ * keyboard
+ *
+ * @since 3.3
+ *
+ */
+public class TableViewerFocusCellManager : SWTFocusCellManager {
+    private static const CellNavigationStrategy TABLE_NAVIGATE;
+    static this(){
+        TABLE_NAVIGATE = new CellNavigationStrategy();
+    }
+
+    /**
+     * Create a new manager with a default navigation strategy:
+     * <ul>
+     * <li><code>SWT.ARROW_UP</code>: navigate to cell above</li>
+     * <li><code>SWT.ARROW_DOWN</code>: navigate to cell below</li>
+     * <li><code>SWT.ARROW_RIGHT</code>: navigate to next visible cell on
+     * the right</li>
+     * <li><code>SWT.ARROW_LEFT</code>: navigate to next visible cell on the
+     * left</li>
+     * </ul>
+     *
+     * @param viewer
+     *            the viewer the manager is bound to
+     * @param focusDrawingDelegate
+     *            the delegate responsible to highlight selected cell
+     */
+    public this(TableViewer viewer,
+            FocusCellHighlighter focusDrawingDelegate) {
+        this(viewer, focusDrawingDelegate, TABLE_NAVIGATE);
+    }
+
+    /**
+     * Create a new manager
+     *
+     * @param viewer
+     *            the viewer the manager is bound to
+     * @param focusDrawingDelegate
+     *            the delegate responsible to highlight selected cell
+     * @param navigationStrategy
+     *            the strategy used to navigate the cells
+     * @since 3.4
+     */
+    public this(TableViewer viewer,
+            FocusCellHighlighter focusDrawingDelegate,
+            CellNavigationStrategy navigationStrategy) {
+        super(viewer, focusDrawingDelegate, navigationStrategy);
+    }
+
+    override ViewerCell getInitialFocusCell() {
+        Table table = cast(Table) getViewer().getControl();
+
+        if (! table.isDisposed() && table.getItemCount() > 0 && ! table.getItem(0).isDisposed()) {
+            return getViewer().getViewerRowFromItem_package(table.getItem(0))
+                    .getCell(0);
+        }
+
+        return null;
+    }
+
+    public ViewerCell getFocusCell() {
+        ViewerCell cell = super.getFocusCell();
+        Table t = cast(Table) getViewer().getControl();
+
+        // It is possible that the selection has changed under the hood
+        if (cell !is null) {
+            if (t.getSelection().length is 1
+                    && t.getSelection()[0] !is cell.getItem()) {
+                setFocusCell(getViewer().getViewerRowFromItem_package(
+                        t.getSelection()[0]).getCell(cell.getColumnIndex()));
+            }
+        }
+
+        return super.getFocusCell();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TableViewerRow.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,251 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                               - fix in bug: 174355,195908,198035,215069,227421
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.TableViewerRow;
+
+import org.eclipse.jface.viewers.ViewerRow;
+import org.eclipse.jface.viewers.TreePath;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Widget;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * TableViewerRow is the Table specific implementation of ViewerRow
+ * @since 3.3
+ *
+ */
+public class TableViewerRow : ViewerRow {
+    private TableItem item;
+
+    /**
+     * Create a new instance of the receiver from item.
+     * @param item
+     */
+    this(TableItem item) {
+        this.item = item;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#getBounds(int)
+     */
+    public override Rectangle getBounds(int columnIndex) {
+        return item.getBounds(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#getBounds()
+     */
+    public override Rectangle getBounds() {
+        return item.getBounds();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#getItem()
+     */
+    public Widget getItem() {
+        return item;
+    }
+
+    void setItem(TableItem item) {
+        this.item = item;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#getColumnCount()
+     */
+    public override int getColumnCount() {
+        return item.getParent().getColumnCount();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#getBackground(int)
+     */
+    public override Color getBackground(int columnIndex) {
+        return item.getBackground(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#getFont(int)
+     */
+    public override Font getFont(int columnIndex) {
+        return item.getFont(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#getForeground(int)
+     */
+    public override Color getForeground(int columnIndex) {
+        return item.getForeground(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#getImage(int)
+     */
+    public override Image getImage(int columnIndex) {
+        return item.getImage(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#getText(int)
+     */
+    public override String getText(int columnIndex) {
+        return item.getText(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#setBackground(int, org.eclipse.swt.graphics.Color)
+     */
+    public override void setBackground(int columnIndex, Color color) {
+        item.setBackground(columnIndex, color);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#setFont(int, org.eclipse.swt.graphics.Font)
+     */
+    public override void setFont(int columnIndex, Font font) {
+        item.setFont(columnIndex, font);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#setForeground(int, org.eclipse.swt.graphics.Color)
+     */
+    public override void setForeground(int columnIndex, Color color) {
+        item.setForeground(columnIndex, color);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#setImage(int, org.eclipse.swt.graphics.Image)
+     */
+    public override void setImage(int columnIndex, Image image) {
+        Image oldImage = item.getImage(columnIndex);
+        if (oldImage !is image) {
+            item.setImage(columnIndex,image);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#setText(int, java.lang.String)
+     */
+    public override void setText(int columnIndex, String text) {
+        item.setText(columnIndex, text is null ? "" : text); //$NON-NLS-1$
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#getControl()
+     */
+    public override Control getControl() {
+        return item.getParent();
+    }
+
+    public override ViewerRow getNeighbor(int direction, bool sameLevel) {
+        if( direction is ViewerRow.ABOVE ) {
+            return getRowAbove();
+        } else if( direction is ViewerRow.BELOW ) {
+            return getRowBelow();
+        } else {
+            throw new IllegalArgumentException("Illegal value of direction argument."); //$NON-NLS-1$
+        }
+    }
+
+
+    private ViewerRow getRowAbove() {
+        int index = item.getParent().indexOf(item) - 1;
+
+        if( index >= 0 ) {
+            return new TableViewerRow(item.getParent().getItem(index));
+        }
+
+        return null;
+    }
+
+    private ViewerRow getRowBelow() {
+        int index = item.getParent().indexOf(item) + 1;
+
+        if( index < item.getParent().getItemCount() ) {
+            TableItem tmp = item.getParent().getItem(index);
+            //TODO NULL can happen in case of VIRTUAL => How do we deal with that
+            if( tmp !is null ) {
+                return new TableViewerRow(tmp);
+            }
+        }
+
+        return null;
+    }
+
+    public override TreePath getTreePath() {
+        return new TreePath([item.getData()]);
+    }
+
+    public override Object clone() {
+        return new TableViewerRow(item);
+    }
+
+    public override Object getElement() {
+        return item.getData();
+    }
+
+    public int getVisualIndex(int creationIndex) {
+        int[] order = item.getParent().getColumnOrder();
+
+        for (int i = 0; i < order.length; i++) {
+            if (order[i] is creationIndex) {
+                return i;
+            }
+        }
+
+        return super.getVisualIndex(creationIndex);
+    }
+
+    public int getCreationIndex(int visualIndex) {
+        if( item !is null && ! item.isDisposed() && hasColumns() && isValidOrderIndex(visualIndex) ) {
+            return item.getParent().getColumnOrder()[visualIndex];
+        }
+        return super.getCreationIndex(visualIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#getTextBounds(int)
+     */
+    public Rectangle getTextBounds(int index) {
+        return item.getTextBounds(index);
+    }
+    
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#getImageBounds(int)
+     */
+    public Rectangle getImageBounds(int index) {
+        return item.getImageBounds(index);
+    }
+
+    private bool hasColumns() {
+        return this.item.getParent().getColumnCount() !is 0;
+    }
+
+    private bool isValidOrderIndex(int currentIndex) {
+        return currentIndex < this.item.getParent().getColumnOrder().length;
+    }
+    
+    int getWidth(int columnIndex) {
+        return item.getParent().getColumn(columnIndex).getWidth();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TextCellEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,496 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.TextCellEditor;
+
+import org.eclipse.jface.viewers.CellEditor;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Set;
+import tango.text.convert.Format;
+
+/**
+ * A cell editor that manages a text entry field.
+ * The cell editor's value is the text string itself.
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class TextCellEditor : CellEditor {
+
+    /**
+     * The text control; initially <code>null</code>.
+     */
+    protected Text text;
+
+    private ModifyListener modifyListener;
+
+    /**
+     * State information for updating action enablement
+     */
+    private bool isSelection = false;
+
+    private bool isDeleteable = false;
+
+    private bool isSelectable = false;
+
+    /**
+     * Default TextCellEditor style
+     * specify no borders on text widget as cell outline in table already
+     * provides the look of a border.
+     */
+    private static const int defaultStyle = SWT.SINGLE;
+
+    /**
+     * Creates a new text string cell editor with no control
+     * The cell editor value is the string itself, which is initially the empty
+     * string. Initially, the cell editor has no cell validator.
+     *
+     * @since 2.1
+     */
+    public this() {
+        setStyle(defaultStyle);
+    }
+
+    /**
+     * Creates a new text string cell editor parented under the given control.
+     * The cell editor value is the string itself, which is initially the empty string.
+     * Initially, the cell editor has no cell validator.
+     *
+     * @param parent the parent control
+     */
+    public this(Composite parent) {
+        this(parent, defaultStyle);
+    }
+
+    /**
+     * Creates a new text string cell editor parented under the given control.
+     * The cell editor value is the string itself, which is initially the empty string.
+     * Initially, the cell editor has no cell validator.
+     *
+     * @param parent the parent control
+     * @param style the style bits
+     * @since 2.1
+     */
+    public this(Composite parent, int style) {
+        super(parent, style);
+    }
+
+    /**
+     * Checks to see if the "deletable" state (can delete/
+     * nothing to delete) has changed and if so fire an
+     * enablement changed notification.
+     */
+    private void checkDeleteable() {
+        bool oldIsDeleteable = isDeleteable;
+        isDeleteable = isDeleteEnabled();
+        if (oldIsDeleteable !is isDeleteable) {
+            fireEnablementChanged(DELETE);
+        }
+    }
+
+    /**
+     * Checks to see if the "selectable" state (can select)
+     * has changed and if so fire an enablement changed notification.
+     */
+    private void checkSelectable() {
+        bool oldIsSelectable = isSelectable;
+        isSelectable = isSelectAllEnabled();
+        if (oldIsSelectable !is isSelectable) {
+            fireEnablementChanged(SELECT_ALL);
+        }
+    }
+
+    /**
+     * Checks to see if the selection state (selection /
+     * no selection) has changed and if so fire an
+     * enablement changed notification.
+     */
+    private void checkSelection() {
+        bool oldIsSelection = isSelection;
+        isSelection = text.getSelectionCount() > 0;
+        if (oldIsSelection !is isSelection) {
+            fireEnablementChanged(COPY);
+            fireEnablementChanged(CUT);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     */
+    protected override Control createControl(Composite parent) {
+        text = new Text(parent, getStyle());
+        text.addSelectionListener(new class SelectionAdapter {
+            public void widgetDefaultSelected(SelectionEvent e) {
+                handleDefaultSelection(e);
+            }
+        });
+        text.addKeyListener(new class KeyAdapter {
+            // hook key pressed - see PR 14201
+            public void keyPressed(KeyEvent e) {
+                keyReleaseOccured(e);
+
+                // as a result of processing the above call, clients may have
+                // disposed this cell editor
+                if ((getControl() is null) || getControl().isDisposed()) {
+                    return;
+                }
+                checkSelection(); // see explanation below
+                checkDeleteable();
+                checkSelectable();
+            }
+        });
+        text.addTraverseListener(new class TraverseListener {
+            public void keyTraversed(TraverseEvent e) {
+                if (e.detail is SWT.TRAVERSE_ESCAPE
+                        || e.detail is SWT.TRAVERSE_RETURN) {
+                    e.doit = false;
+                }
+            }
+        });
+        // We really want a selection listener but it is not supported so we
+        // use a key listener and a mouse listener to know when selection changes
+        // may have occurred
+        text.addMouseListener(new class MouseAdapter {
+            public void mouseUp(MouseEvent e) {
+                checkSelection();
+                checkDeleteable();
+                checkSelectable();
+            }
+        });
+        text.addFocusListener(new class FocusAdapter {
+            public void focusLost(FocusEvent e) {
+                this.outer.focusLost();
+            }
+        });
+        text.setFont(parent.getFont());
+        text.setBackground(parent.getBackground());
+        text.setText("");//$NON-NLS-1$
+        text.addModifyListener(getModifyListener());
+        return text;
+    }
+
+    /**
+     * The <code>TextCellEditor</code> implementation of
+     * this <code>CellEditor</code> framework method returns
+     * the text string.
+     *
+     * @return the text string
+     */
+    protected override Object doGetValue() {
+        return new ArrayWrapperString(text.getText());
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     */
+    protected override void doSetFocus() {
+        if (text !is null) {
+            text.selectAll();
+            text.setFocus();
+            checkSelection();
+            checkDeleteable();
+            checkSelectable();
+        }
+    }
+
+    /**
+     * The <code>TextCellEditor</code> implementation of
+     * this <code>CellEditor</code> framework method accepts
+     * a text string (type <code>String</code>).
+     *
+     * @param value a text string (type <code>String</code>)
+     */
+    protected override void doSetValue(Object value) {
+        Assert.isTrue(text !is null && ( cast(ArrayWrapperString)value ));
+        text.removeModifyListener(getModifyListener());
+        text.setText((cast(ArrayWrapperString)value).array);
+        text.addModifyListener(getModifyListener());
+    }
+
+    /**
+     * Processes a modify event that occurred in this text cell editor.
+     * This framework method performs validation and sets the error message
+     * accordingly, and then reports a change via <code>fireEditorValueChanged</code>.
+     * Subclasses should call this method at appropriate times. Subclasses
+     * may extend or reimplement.
+     *
+     * @param e the SWT modify event
+     */
+    protected void editOccured(ModifyEvent e) {
+        String value = text.getText();
+        if (value is null) {
+            value = "";//$NON-NLS-1$
+        }
+        Object typedValue = new ArrayWrapperString(value);
+        bool oldValidState = isValueValid();
+        bool newValidState = isCorrect(typedValue);
+        if (typedValue is null && newValidState) {
+            Assert.isTrue(false,
+                    "Validator isn't limiting the cell editor's type range");//$NON-NLS-1$
+        }
+        if (!newValidState) {
+            // try to insert the current value into the error message.
+            setErrorMessage(Format(getErrorMessage(), value ));
+        }
+        valueChanged(oldValidState, newValidState);
+    }
+
+    /**
+     * Since a text editor field is scrollable we don't
+     * set a minimumSize.
+     */
+    public override LayoutData getLayoutData() {
+        return new LayoutData();
+    }
+
+    /**
+     * Return the modify listener.
+     */
+    private ModifyListener getModifyListener() {
+        if (modifyListener is null) {
+            modifyListener = new class ModifyListener {
+                public void modifyText(ModifyEvent e) {
+                    editOccured(e);
+                }
+            };
+        }
+        return modifyListener;
+    }
+
+    /**
+     * Handles a default selection event from the text control by applying the editor
+     * value and deactivating this cell editor.
+     *
+     * @param event the selection event
+     *
+     * @since 3.0
+     */
+    protected void handleDefaultSelection(SelectionEvent event) {
+        // same with enter-key handling code in keyReleaseOccured(e);
+        fireApplyEditorValue();
+        deactivate();
+    }
+
+    /**
+     * The <code>TextCellEditor</code>  implementation of this
+     * <code>CellEditor</code> method returns <code>true</code> if
+     * the current selection is not empty.
+     */
+    public override bool isCopyEnabled() {
+        if (text is null || text.isDisposed()) {
+            return false;
+        }
+        return text.getSelectionCount() > 0;
+    }
+
+    /**
+     * The <code>TextCellEditor</code>  implementation of this
+     * <code>CellEditor</code> method returns <code>true</code> if
+     * the current selection is not empty.
+     */
+    public override bool isCutEnabled() {
+        if (text is null || text.isDisposed()) {
+            return false;
+        }
+        return text.getSelectionCount() > 0;
+    }
+
+    /**
+     * The <code>TextCellEditor</code>  implementation of this
+     * <code>CellEditor</code> method returns <code>true</code>
+     * if there is a selection or if the caret is not positioned
+     * at the end of the text.
+     */
+    public override bool isDeleteEnabled() {
+        if (text is null || text.isDisposed()) {
+            return false;
+        }
+        return text.getSelectionCount() > 0
+                || text.getCaretPosition() < text.getCharCount();
+    }
+
+    /**
+     * The <code>TextCellEditor</code>  implementation of this
+     * <code>CellEditor</code> method always returns <code>true</code>.
+     */
+    public override bool isPasteEnabled() {
+        if (text is null || text.isDisposed()) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Check if save all is enabled
+     * @return true if it is
+     */
+    public bool isSaveAllEnabled() {
+        if (text is null || text.isDisposed()) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns <code>true</code> if this cell editor is
+     * able to perform the select all action.
+     * <p>
+     * This default implementation always returns
+     * <code>false</code>.
+     * </p>
+     * <p>
+     * Subclasses may override
+     * </p>
+     * @return <code>true</code> if select all is possible,
+     *  <code>false</code> otherwise
+     */
+    public override bool isSelectAllEnabled() {
+        if (text is null || text.isDisposed()) {
+            return false;
+        }
+        return text.getCharCount() > 0;
+    }
+
+    /**
+     * Processes a key release event that occurred in this cell editor.
+     * <p>
+     * The <code>TextCellEditor</code> implementation of this framework method
+     * ignores when the RETURN key is pressed since this is handled in
+     * <code>handleDefaultSelection</code>.
+     * An exception is made for Ctrl+Enter for multi-line texts, since
+     * a default selection event is not sent in this case.
+     * </p>
+     *
+     * @param keyEvent the key event
+     */
+    protected override void keyReleaseOccured(KeyEvent keyEvent) {
+        if (keyEvent.character is '\r') { // Return key
+            // Enter is handled in handleDefaultSelection.
+            // Do not apply the editor value in response to an Enter key event
+            // since this can be received from the IME when the intent is -not-
+            // to apply the value.
+            // See bug 39074 [CellEditors] [DBCS] canna input mode fires bogus event from Text Control
+            //
+            // An exception is made for Ctrl+Enter for multi-line texts, since
+            // a default selection event is not sent in this case.
+            if (text !is null && !text.isDisposed()
+                    && (text.getStyle() & SWT.MULTI) !is 0) {
+                if ((keyEvent.stateMask & SWT.CTRL) !is 0) {
+                    super.keyReleaseOccured(keyEvent);
+                }
+            }
+            return;
+        }
+        super.keyReleaseOccured(keyEvent);
+    }
+
+    /**
+     * The <code>TextCellEditor</code> implementation of this
+     * <code>CellEditor</code> method copies the
+     * current selection to the clipboard.
+     */
+    public override void performCopy() {
+        text.copy();
+    }
+
+    /**
+     * The <code>TextCellEditor</code> implementation of this
+     * <code>CellEditor</code> method cuts the
+     * current selection to the clipboard.
+     */
+    public override void performCut() {
+        text.cut();
+        checkSelection();
+        checkDeleteable();
+        checkSelectable();
+    }
+
+    /**
+     * The <code>TextCellEditor</code> implementation of this
+     * <code>CellEditor</code> method deletes the
+     * current selection or, if there is no selection,
+     * the character next character from the current position.
+     */
+    public override void performDelete() {
+        if (text.getSelectionCount() > 0) {
+            // remove the contents of the current selection
+            text.insert(""); //$NON-NLS-1$
+        } else {
+            // remove the next character
+            int pos = text.getCaretPosition();
+            if (pos < text.getCharCount()) {
+                text.setSelection(pos, pos + 1);
+                text.insert(""); //$NON-NLS-1$
+            }
+        }
+        checkSelection();
+        checkDeleteable();
+        checkSelectable();
+    }
+
+    /**
+     * The <code>TextCellEditor</code> implementation of this
+     * <code>CellEditor</code> method pastes the
+     * the clipboard contents over the current selection.
+     */
+    public override void performPaste() {
+        text.paste();
+        checkSelection();
+        checkDeleteable();
+        checkSelectable();
+    }
+
+    /**
+     * The <code>TextCellEditor</code> implementation of this
+     * <code>CellEditor</code> method selects all of the
+     * current text.
+     */
+    public override void performSelectAll() {
+        text.selectAll();
+        checkSelection();
+        checkDeleteable();
+    }
+
+    /**
+     * This implementation of
+     * {@link CellEditor#dependsOnExternalFocusListener()} returns false if the
+     * current instance's class is TextCellEditor, and true otherwise.
+     * Subclasses that hook their own focus listener should override this method
+     * and return false. See also bug 58777.
+     * 
+     * @since 3.4
+     */
+    protected override bool dependsOnExternalFocusListener() {
+        return this.classinfo !is TextCellEditor.classinfo;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TreeColumnViewerLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.TreeColumnViewerLabelProvider;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.TableColumnViewerLabelProvider;
+import org.eclipse.jface.viewers.ITreePathLabelProvider;
+import org.eclipse.jface.viewers.ViewerLabel;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * TreeViewerLabelProvider is the ViewerLabelProvider that handles TreePaths.
+ *
+ * @since 3.3
+ *
+ */
+public class TreeColumnViewerLabelProvider :
+        TableColumnViewerLabelProvider {
+    private ITreePathLabelProvider treePathProvider;
+    private void init_treePathProvider(){
+        treePathProvider = new class ITreePathLabelProvider {
+            /*
+            * (non-Javadoc)
+            *
+            * @see org.eclipse.jface.viewers.ITreePathLabelProvider#updateLabel(org.eclipse.jface.viewers.ViewerLabel,
+            *      org.eclipse.jface.viewers.TreePath)
+            */
+            public void updateLabel(ViewerLabel label, TreePath elementPath) {
+                // Do nothing by default
+
+            }
+
+            /*
+            * (non-Javadoc)
+            *
+            * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
+            */
+            public void dispose() {
+                // Do nothing by default
+
+            }
+
+            /*
+            * (non-Javadoc)
+            *
+            * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
+            */
+            public void addListener(ILabelProviderListener listener) {
+                // Do nothing by default
+
+            }
+
+            /*
+            * (non-Javadoc)
+            *
+            * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
+            */
+            public void removeListener(ILabelProviderListener listener) {
+                // Do nothing by default
+
+            }
+
+            /* (non-Javadoc)
+            * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, java.lang.String)
+            */
+            public bool isLabelProperty(Object element, String property) {
+                return false;
+            }
+
+        };
+    }
+
+    /**
+     * Create a new instance of the receiver with the supplied labelProvider.
+     *
+     * @param labelProvider
+     */
+    public this(IBaseLabelProvider labelProvider) {
+        init_treePathProvider();
+        super(labelProvider);
+    }
+
+    /**
+     * Update the label for the element with TreePath.
+     *
+     * @param label
+     * @param elementPath
+     */
+    public void updateLabel(ViewerLabel label, TreePath elementPath) {
+        treePathProvider.updateLabel(label, elementPath);
+
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerLabelProvider#setProviders(java.lang.Object)
+     */
+    public override void setProviders(Object provider) {
+        super.setProviders(provider);
+        if ( auto p = cast(ITreePathLabelProvider) provider )
+            treePathProvider = p;
+    }
+
+    /**
+     * Return the ITreePathLabelProvider for the receiver.
+     *
+     * @return Returns the treePathProvider.
+     */
+    public ITreePathLabelProvider getTreePathProvider() {
+        return treePathProvider;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TreeExpansionEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.TreeExpansionEvent;
+
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+
+import java.lang.all;//.EventObject;
+import java.util.EventObject;
+
+/**
+ * Event object describing a tree node being expanded
+ * or collapsed. The source of these events is the tree viewer.
+ *
+ * @see ITreeViewerListener
+ */
+public class TreeExpansionEvent : EventObject {
+
+    /**
+     * Generated serial version UID for this class.
+     * @since 3.1
+     */
+    private static const long serialVersionUID = 3618414930227835185L;
+
+    /**
+     * The element that was expanded or collapsed.
+     */
+    private Object element;
+
+    /**
+     * Creates a new event for the given source and element.
+     *
+     * @param source the tree viewer
+     * @param element the element
+     */
+    public this(AbstractTreeViewer source, Object element) {
+        super(source);
+        this.element = element;
+    }
+
+    /**
+     * Returns the element that got expanded or collapsed.
+     *
+     * @return the element
+     */
+    public Object getElement() {
+        return element;
+    }
+
+    /**
+     * Returns the originator of the event.
+     *
+     * @return the originating tree viewer
+     */
+    public AbstractTreeViewer getTreeViewer() {
+        return cast(AbstractTreeViewer) source;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TreeNode.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.jface.viewers.TreeNode;
+
+import org.eclipse.jface.util.Util;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A simple data structure that is useful for implemented tree models. This can
+ * be returned by
+ * {@link org.eclipse.jface.viewers.IStructuredContentProvider#getElements(Object)}.
+ * It allows simple delegation of methods from
+ * {@link org.eclipse.jface.viewers.ITreeContentProvider} such as
+ * {@link org.eclipse.jface.viewers.ITreeContentProvider#getChildren(Object)},
+ * {@link org.eclipse.jface.viewers.ITreeContentProvider#getParent(Object)} and
+ * {@link org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(Object)}
+ *
+ * @since 3.2
+ */
+public class TreeNode {
+
+    /**
+     * The array of child tree nodes for this tree node. If there are no
+     * children, then this value may either by an empty array or
+     * <code>null</code>. There should be no <code>null</code> children in
+     * the array.
+     */
+    private TreeNode[] children;
+
+    /**
+     * The parent tree node for this tree node. This value may be
+     * <code>null</code> if there is no parent.
+     */
+    private TreeNode parent;
+
+    /**
+     * The value contained in this node. This value may be anything.
+     */
+    protected Object value;
+
+    /**
+     * Constructs a new instance of <code>TreeNode</code>.
+     *
+     * @param value
+     *            The value held by this node; may be anything.
+     */
+    public this(Object value) {
+        this.value = value;
+    }
+
+    public override int opEquals(Object object) {
+        if ( auto tn = cast(TreeNode)object ) {
+            return Util.opEquals(this.value, tn.value);
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the child nodes. Empty arrays are converted to <code>null</code>
+     * before being returned.
+     *
+     * @return The child nodes; may be <code>null</code>, but never empty.
+     *         There should be no <code>null</code> children in the array.
+     */
+    public TreeNode[] getChildren() {
+        if (children !is null && children.length is 0) {
+            return null;
+        }
+        return children;
+    }
+
+    /**
+     * Returns the parent node.
+     * 
+     * @return The parent node; may be <code>null</code> if there are no
+     *         parent nodes.
+     */
+    public TreeNode getParent() {
+        return parent;
+    }
+
+    /**
+     * Returns the value held by this node.
+     * 
+     * @return The value; may be anything.
+     */
+    public Object getValue() {
+        return value;
+    }
+
+    /**
+     * Returns whether the tree has any children.
+     * 
+     * @return <code>true</code> if its array of children is not
+     *         <code>null</code> and is non-empty; <code>false</code>
+     *         otherwise.
+     */
+    public bool hasChildren() {
+        return children !is null && children.length > 0;
+    }
+    
+    public override hash_t toHash() {
+        return Util.toHash(value);
+    }
+
+    /**
+     * Sets the children for this node.
+     * 
+     * @param children
+     *            The child nodes; may be <code>null</code> or empty. There
+     *            should be no <code>null</code> children in the array.
+     */
+    public void setChildren(TreeNode[] children) {
+        this.children = children;
+    }
+
+    /**
+     * Sets the parent for this node.
+     * 
+     * @param parent
+     *            The parent node; may be <code>null</code>.
+     */
+    public void setParent(TreeNode parent) {
+        this.parent = parent;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TreeNodeContentProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.jface.viewers.TreeNodeContentProvider;
+
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.TreeNode;
+
+import java.lang.all;
+
+/**
+ * <p>
+ * A content provider that expects every element to be a <code>TreeNode</code>.
+ * Most methods delegate to <code>TreeNode</code>. <code>dispose()</code>
+ * and <code>inputChanged(Viewer, Object, Object)</code> do nothing by
+ * default.
+ * </p>
+ * <p>
+ * This class and all of its methods may be overridden or extended.
+ * </p>
+ *
+ * @since 3.2
+ * @see org.eclipse.jface.viewers.TreeNode
+ */
+public class TreeNodeContentProvider : ITreeContentProvider {
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.jface.viewers.IContentProvider#dispose()
+     */
+    public void dispose() {
+        // Do nothing
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
+     */
+    public Object[] getChildren(Object parentElement) {
+        TreeNode node = cast(TreeNode) parentElement;
+        return node.getChildren();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
+     */
+    public Object[] getElements(Object inputElement) {
+        if ( auto tn = cast(ArrayWrapperT!(TreeNode)) inputElement ) {
+            return tn.array;
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
+     */
+    public Object getParent(Object element) {
+        TreeNode node = cast(TreeNode) element;
+        return node.getParent();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
+     */
+    public bool hasChildren(Object element) {
+        TreeNode node = cast(TreeNode) element;
+        return node.hasChildren();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
+     *      java.lang.Object, java.lang.Object)
+     */
+    public void inputChanged(Viewer viewer, Object oldInput,
+          Object newInput) {
+        // Do nothing
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TreePath.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,251 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.jface.viewers.TreePath;
+
+import org.eclipse.jface.viewers.IElementComparer;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+
+/**
+ * A tree path denotes a model element in a tree viewer. Tree path objects have
+ * value semantics. A model element is represented by a path of elements in the
+ * tree from the root element to the leaf element.
+ * <p>
+ * Clients may instantiate this class. Not intended to be subclassed.
+ * </p>
+ *
+ * @since 3.2
+ */
+public final class TreePath {
+
+    /**
+     * Constant for representing an empty tree path.
+     */
+    public static const TreePath EMPTY;
+
+    private Object[] segments;
+
+    private int hash;
+
+    static this(){
+        EMPTY = new TreePath(new Object[0]);
+    }
+
+    /**
+     * Constructs a path identifying a leaf node in a tree.
+     *
+     * @param segments
+     *            path of elements to a leaf node in a tree, starting with the
+     *            root element
+     */
+    public this(Object[] segments) {
+//         Assert.isNotNull(segments);
+        for (int i = 0; i < segments.length; i++) {
+            Assert.isNotNull(segments[i]);
+        }
+        this.segments = segments;
+    }
+
+    /**
+     * Returns the element at the specified index in this path.
+     *
+     * @param index
+     *            index of element to return
+     * @return element at the specified index
+     */
+    public Object getSegment(int index) {
+        return segments[index];
+    }
+
+    /**
+     * Returns the number of elements in this path.
+     *
+     * @return the number of elements in this path
+     */
+    public int getSegmentCount() {
+        return segments.length;
+    }
+
+    /**
+     * Returns the first element in this path, or <code>null</code> if this
+     * path has no segments.
+     *
+     * @return the first element in this path
+     */
+    public Object getFirstSegment() {
+        if (segments.length is 0) {
+            return null;
+        }
+        return segments[0];
+    }
+
+    /**
+     * Returns the last element in this path, or <code>null</code> if this
+     * path has no segments.
+     *
+     * @return the last element in this path
+     */
+    public Object getLastSegment() {
+        if (segments.length is 0) {
+            return null;
+        }
+        return segments[segments.length - 1];
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public override int opEquals(Object other) {
+        if (!(cast(TreePath)other )) {
+            return false;
+        }
+        return opEquals(cast(TreePath) other, null);
+    }
+
+    /**
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#hashCode()
+     */
+    public override hash_t toHash() {
+        if (hash is 0) {
+            hash = toHash(null);
+        }
+        return hash;
+    }
+
+    /**
+     * Returns a hash code computed from the hash codes of the segments, using
+     * the given comparer to compute the hash codes of the segments.
+     *
+     * @param comparer
+     *            comparer to use or <code>null</code> if the segments' hash
+     *            codes should be computed by calling their hashCode() methods.
+     * @return the computed hash code
+     */
+    public hash_t toHash(IElementComparer comparer) {
+        int result = 0;
+        for (int i = 0; i < segments.length; i++) {
+            if (comparer is null) {
+                result += segments[i].toHash();
+            } else {
+                result += comparer.toHash(segments[i]);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns whether this path is equivalent to the given path using the
+     * specified comparer to compare individual elements.
+     *
+     * @param otherPath
+     *            tree path to compare to
+     * @param comparer
+     *            comparator to use or <code>null</code> if segments should be
+     *            compared using equals()
+     * @return whether the paths are equal
+     */
+    public int opEquals(TreePath otherPath, IElementComparer comparer) {
+        if (otherPath is null) {
+            return false;
+        }
+        if (segments.length !is otherPath.segments.length) {
+            return false;
+        }
+        for (int i = 0; i < segments.length; i++) {
+            if (comparer is null) {
+                if (!segments[i].opEquals(otherPath.segments[i])) {
+                    return false;
+                }
+            } else {
+                if (!comparer.opEquals(segments[i], otherPath.segments[i])) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns whether this path starts with the same segments as the given
+     * path, using the given comparer to compare segments.
+     *
+     * @param treePath
+     *            path to compare to
+     * @param comparer
+     *            the comparer to use, or <code>null</code> if equals() should
+     *            be used to compare segments
+     * @return whether the given path is a prefix of this path, or the same as
+     *         this path
+     */
+    public bool startsWith(TreePath treePath, IElementComparer comparer) {
+        int thisSegmentCount = getSegmentCount();
+        int otherSegmentCount = treePath.getSegmentCount();
+        if (otherSegmentCount is thisSegmentCount) {
+            return opEquals(treePath, comparer) !is 0;
+        }
+        if (otherSegmentCount > thisSegmentCount) {
+            return false;
+        }
+        for (int i = 0; i < otherSegmentCount; i++) {
+            Object otherSegment = treePath.getSegment(i);
+            if (comparer is null) {
+                if (!otherSegment.opEquals(segments[i])) {
+                    return false;
+                }
+            } else {
+                if (!comparer.opEquals(otherSegment, segments[i])) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns a copy of this tree path with one segment removed from the end,
+     * or <code>null</code> if this tree path has no segments.
+     * @return a tree path
+     */
+    public TreePath getParentPath() {
+        int segmentCount = getSegmentCount();
+        if (segmentCount < 1) {
+            return null;
+        } else if (segmentCount is 1) {
+            return EMPTY;
+        }
+        Object[] parentSegments = new Object[segmentCount - 1];
+        System.arraycopy(segments, 0, parentSegments, 0, segmentCount - 1);
+        return new TreePath(parentSegments);
+    }
+
+    /**
+     * Returns a copy of this tree path with the given segment added at the end.
+     * @param newSegment
+     * @return a tree path
+     */
+    public TreePath createChildPath(Object newSegment) {
+        int segmentCount = getSegmentCount();
+        Object[] childSegments = new Object[segmentCount + 1];
+        if(segmentCount>0) {
+            System.arraycopy(segments, 0, childSegments, 0, segmentCount);
+        }
+        childSegments[segmentCount] = newSegment;
+        return new TreePath(childSegments);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TreePathViewerSorter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.viewers.TreePathViewerSorter;
+
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.Viewer;
+
+// import java.util.Arrays;
+// import java.util.Comparator;
+
+import java.lang.all;
+import java.util.Arrays;
+import tango.core.Array;
+
+/**
+ * A viewer sorter that is provided extra context in the form of the path of the
+ * parent element of the elements being sorted.
+ *
+ * @since 3.2
+ */
+public class TreePathViewerSorter : ViewerSorter {
+    alias ViewerSorter.category category;
+    alias ViewerSorter.compare compare;
+    alias ViewerSorter.isSorterProperty isSorterProperty;
+    alias ViewerSorter.sort sort;
+
+    /**
+     * Provide a category for the given element that will have the given parent
+     * path when it is added to the viewer. The provided path is
+     * relative to the viewer input. The parent path will
+     * be <code>null</code> when the elements are root elements.
+     * <p>
+     * By default, the this method calls
+     * {@link ViewerSorter#category(Object)}. Subclasses may override.
+     *
+     * @param parentPath
+     *            the parent path for the element
+     * @param element
+     *            the element
+     * @return the category of the element
+     */
+    public int category(TreePath parentPath, Object element) {
+        return category(element);
+    }
+
+    /**
+     * Compare the given elements that will have the given parent
+     * path when they are added to the viewer. The provided path is
+     * relative to the viewer input. The parent path will
+     * be <code>null</code> when the elements are root elements.
+     * <p>
+     * By default, the this method calls
+     * {@link ViewerSorter#sort(Viewer, Object[])}. Subclasses may override.
+     * @param viewer the viewer
+     * @param parentPath the parent path for the two elements
+     * @param e1 the first element
+     * @param e2 the second element
+     * @return a negative number if the first element is less  than the
+     *  second element; the value <code>0</code> if the first element is
+     *  equal to the second element; and a positive
+     */
+    public int compare(Viewer viewer, TreePath parentPath, Object e1, Object e2) {
+        return compare(viewer, e1, e2);
+    }
+
+    /**
+     * Returns whether this viewer sorter would be affected
+     * by a change to the given property of the given element.
+     * The provided path is
+     * relative to the viewer input. The parent path will
+     * be <code>null</code> when the elements are root elements.
+     * <p>
+     * The default implementation of this method calls
+     * {@link ViewerSorter#isSorterProperty(Object, String)}.
+     * Subclasses may reimplement.
+     * @param parentPath the parent path of the element
+     * @param element the element
+     * @param property the property
+     * @return <code>true</code> if the sorting would be affected,
+     *    and <code>false</code> if it would be unaffected
+     */
+    public bool isSorterProperty(TreePath parentPath, Object element, String property) {
+        return isSorterProperty(element, property);
+    }
+
+    /**
+     * Sorts the given elements in-place, modifying the given array.
+     * The provided path is
+     * relative to the viewer input. The parent path will
+     * be <code>null</code> when the elements are root elements.
+     * <p>
+     * The default implementation of this method uses the
+     * java.util.Arrays#sort algorithm on the given array,
+     * calling {@link #compare(Viewer, TreePath, Object, Object)} to compare elements.
+     * </p>
+     * <p>
+     * Subclasses may reimplement this method to provide a more optimized implementation.
+     * </p>
+     *
+     * @param viewer the viewer
+     * @param parentPath the parent path of the given elements
+     * @param elements the elements to sort
+     */
+    public void sort(Viewer viewer, TreePath parentPath, Object[] elements) {
+        tango.core.Array.sort(elements, delegate int (Object a, Object b) {
+                return compare(viewer, parentPath, a, b);
+            }
+        );
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TreeSelection.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,227 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.jface.viewers.TreeSelection;
+
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.CustomHashtable;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.IElementComparer;
+
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A concrete implementation of the <code>ITreeSelection</code> interface,
+ * suitable for instantiating.
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ *
+ * @since 3.2
+ */
+public class TreeSelection : StructuredSelection, ITreeSelection {
+
+    /* Implementation note.  This class extends StructuredSelection because many pre-existing
+     * JFace viewer clients assumed that the only implementation of IStructuredSelection
+     * was StructuredSelection.  By extending StructuredSelection rather than implementing
+     * ITreeSelection directly, we avoid this problem.
+     * For more details, see Bug 121939 [Viewers] TreeSelection should subclass StructuredSelection.
+     */
+
+    private TreePath[] paths = null;
+    private CustomHashtable element2TreePaths = null;
+
+    /**
+     * The canonical empty selection. This selection should be used instead of
+     * <code>null</code>.
+     */
+    public static const TreeSelection EMPTY;
+
+    private static const TreePath[] EMPTY_TREE_PATHS = null;
+    static this(){
+        EMPTY = new TreeSelection();
+    }
+
+    private static class InitializeData {
+        List selection;
+        TreePath[] paths;
+        CustomHashtable element2TreePaths;
+
+        private this(TreePath[] paths, IElementComparer comparer) {
+            this.paths= new TreePath[paths.length];
+            System.arraycopy(paths, 0, this.paths, 0, paths.length);
+            element2TreePaths = new CustomHashtable(comparer);
+            int size = paths.length;
+            selection = new ArrayList(size);
+            for (int i = 0; i < size; i++) {
+                Object lastSegment= paths[i].getLastSegment();
+                Object mapped= element2TreePaths.get(lastSegment);
+                if (mapped is null) {
+                    selection.add(lastSegment);
+                    element2TreePaths.put(lastSegment, paths[i]);
+                } else if ( cast(List)mapped ) {
+                    (cast(List)mapped).add( cast(Object)paths[i]);
+                } else {
+                    List newMapped= new ArrayList();
+                    newMapped.add(mapped);
+                    newMapped.add(paths[i]);
+                    element2TreePaths.put(lastSegment, cast(Object) newMapped);
+                }
+            }
+        }
+    }
+
+    /**
+     * Constructs a selection based on the elements identified by the given tree
+     * paths.
+     *
+     * @param paths
+     *            tree paths
+     */
+    public this(TreePath[] paths) {
+        this(new InitializeData(paths, null));
+    }
+
+    /**
+     * Constructs a selection based on the elements identified by the given tree
+     * paths.
+     *
+     * @param paths
+     *            tree paths
+     * @param comparer
+     *            the comparer, or <code>null</code> if default equals is to be used
+     */
+    public this(TreePath[] paths, IElementComparer comparer) {
+        this(new InitializeData(paths, comparer));
+    }
+
+    /**
+     * Constructs a selection based on the elements identified by the given tree
+     * path.
+     *
+     * @param treePath
+     *            tree path, or <code>null</code> for an empty selection
+     */
+    public this(TreePath treePath) {
+        this(treePath !is null ? [ treePath ] : EMPTY_TREE_PATHS, null);
+    }
+
+    /**
+     * Constructs a selection based on the elements identified by the given tree
+     * path.
+     *
+     * @param treePath
+     *            tree path, or <code>null</code> for an empty selection
+     * @param comparer
+     *            the comparer, or <code>null</code> if default equals is to be used
+     */
+    public this(TreePath treePath, IElementComparer comparer) {
+        this(treePath !is null ? [ treePath ] : EMPTY_TREE_PATHS, comparer);
+    }
+
+    /**
+     * Creates a new tree selection based on the initialization data.
+     *
+     * @param data the data
+     */
+    private this(InitializeData data) {
+        super(data.selection);
+        paths= data.paths;
+        element2TreePaths= data.element2TreePaths;
+    }
+
+    /**
+     * Creates a new empty selection. See also the static field
+     * <code>EMPTY</code> which contains an empty selection singleton.
+     * <p>
+     * Note that TreeSelection.EMPTY is not equals() to StructuredViewer.EMPTY.
+     * </p>
+     *
+     * @see #EMPTY
+     */
+    public this() {
+        super();
+    }
+
+    /**
+     * Returns the element comparer passed in when the tree selection
+     * has been created or <code>null</code> if no comparer has been
+     * provided.
+     *
+     * @return the element comparer or <code>null</code>
+     *
+     * @since 3.2
+     */
+    public IElementComparer getElementComparer() {
+        if (element2TreePaths is null)
+            return null;
+        return element2TreePaths.getComparer();
+    }
+
+    public override int opEquals(Object obj) {
+        if (!(cast(TreeSelection)obj)) {
+            // Fall back to super implementation, see bug 135837.
+            return super.opEquals(obj);
+        }
+        TreeSelection selection = cast(TreeSelection) obj;
+        int size = getPaths().length;
+        if (selection.getPaths().length is size) {
+            IElementComparer comparerOrNull = (getElementComparer() is selection
+                    .getElementComparer()) ? getElementComparer() : null;
+            if (size > 0) {
+                for (int i = 0; i < paths.length; i++) {
+                    if (!paths[i].opEquals(selection.paths[i], comparerOrNull)) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public override hash_t toHash() {
+        int code = this.classinfo.toHash();
+        if (paths !is null) {
+            for (int i = 0; i < paths.length; i++) {
+                code = code * 17 + paths[i].toHash(getElementComparer());
+            }
+        }
+        return code;
+    }
+
+    public TreePath[] getPaths() {
+        return paths is null ? EMPTY_TREE_PATHS : paths.dup;
+    }
+
+    public TreePath[] getPathsFor(Object element) {
+        Object value= element2TreePaths is null ? null : element2TreePaths.get(element);
+        if (value is null) {
+            return EMPTY_TREE_PATHS;
+        } else if (cast(TreePath)value ) {
+            return [ cast(TreePath)value ];
+        } else if (cast(List)value ) {
+            auto l= cast(List)value;
+            return arraycast!(TreePath)( l.toArray() );
+        } else {
+            // should not happen:
+            Assert.isTrue(false, "Unhandled case"); //$NON-NLS-1$
+            return null;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TreeViewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1167 @@
+/*******************************************************************************
+ * 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - concept of ViewerRow,
+ *                                                 refactoring (bug 153993), bug 167323, 191468, 205419
+ *     Matthew Hall - bug 221988
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.TreeViewer;
+
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.TreeViewerRow;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ColumnViewerEditor;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.jface.viewers.ViewerRow;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.TreeViewerEditor;
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
+import org.eclipse.jface.viewers.ILazyTreeContentProvider;
+import org.eclipse.jface.viewers.ILazyTreePathContentProvider;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.TreeExpansionEvent;
+import org.eclipse.jface.viewers.ViewerCell;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.TreeEvent;
+import org.eclipse.swt.events.TreeListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.jface.util.Policy;
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A concrete viewer based on an SWT <code>Tree</code> control.
+ * <p>
+ * This class is not intended to be subclassed outside the viewer framework. It
+ * is designed to be instantiated with a pre-existing SWT tree control and
+ * configured with a domain-specific content provider, label provider, element
+ * filter (optional), and element sorter (optional).
+ * </p>
+ * <p>
+ * Content providers for tree viewers must implement either the
+ * {@link ITreeContentProvider} interface, (as of 3.2) the
+ * {@link ILazyTreeContentProvider} interface, or (as of 3.3) the
+ * {@link ILazyTreePathContentProvider}. If the content provider is an
+ * <code>ILazyTreeContentProvider</code> or an
+ * <code>ILazyTreePathContentProvider</code>, the underlying Tree must be
+ * created using the {@link SWT#VIRTUAL} style bit, the tree viewer will not
+ * support sorting or filtering, and hash lookup must be enabled by calling
+ * {@link #setUseHashlookup(bool)}.
+ * </p>
+ * <p>
+ * Users setting up an editable tree with more than 1 column <b>have</b> to pass the
+ * SWT.FULL_SELECTION style bit
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class TreeViewer : AbstractTreeViewer {
+    alias AbstractTreeViewer.addTreeListener addTreeListener;
+    alias AbstractTreeViewer.getLabelProvider getLabelProvider;
+    alias AbstractTreeViewer.getSelection getSelection;
+    alias AbstractTreeViewer.preservingSelection preservingSelection;
+    alias AbstractTreeViewer.remove remove;
+    alias AbstractTreeViewer.setSelection setSelection;
+
+    private static final String VIRTUAL_DISPOSE_KEY = Policy.JFACE
+            ~ ".DISPOSE_LISTENER"; //$NON-NLS-1$
+
+    /**
+     * This viewer's control.
+     */
+    private Tree tree;
+
+    /**
+     * Flag for whether the tree has been disposed of.
+     */
+    private bool treeIsDisposed = false;
+
+    private bool contentProviderIsLazy;
+
+    private bool contentProviderIsTreeBased;
+
+    /**
+     * The row object reused
+     */
+    private TreeViewerRow cachedRow;
+
+    /**
+     * true if we are inside a preservingSelection() call
+     */
+    private bool preservingSelection_;
+
+    /**
+     * Creates a tree viewer on a newly-created tree control under the given
+     * parent. The tree control is created using the SWT style bits
+     * <code>MULTI, H_SCROLL, V_SCROLL,</code> and <code>BORDER</code>. The
+     * viewer has no input, no content provider, a default label provider, no
+     * sorter, and no filters.
+     *
+     * @param parent
+     *            the parent control
+     */
+    public this(Composite parent) {
+        this(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
+    }
+
+    /**
+     * Creates a tree viewer on a newly-created tree control under the given
+     * parent. The tree control is created using the given SWT style bits. The
+     * viewer has no input, no content provider, a default label provider, no
+     * sorter, and no filters.
+     *
+     * @param parent
+     *            the parent control
+     * @param style
+     *            the SWT style bits used to create the tree.
+     */
+    public this(Composite parent, int style) {
+        this(new Tree(parent, style));
+    }
+
+    /**
+     * Creates a tree viewer on the given tree control. The viewer has no input,
+     * no content provider, a default label provider, no sorter, and no filters.
+     *
+     * @param tree
+     *            the tree control
+     */
+    public this(Tree tree) {
+        super();
+        this.tree = tree;
+        hookControl(tree);
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override void addTreeListener(Control c, TreeListener listener) {
+        (cast(Tree) c).addTreeListener(listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ColumnViewer#getColumnViewerOwner(int)
+     */
+    protected override Widget getColumnViewerOwner(int columnIndex) {
+        if (columnIndex < 0 || ( columnIndex > 0 && columnIndex >= getTree().getColumnCount() ) ) {
+            return null;
+        }
+
+        if (getTree().getColumnCount() is 0)// Hang it off the table if it
+            return getTree();
+
+        return getTree().getColumn(columnIndex);
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override Item[] getChildren(Widget o) {
+        if (auto ti = cast(TreeItem)o ) {
+            return ti.getItems();
+        }
+        if (auto t = cast(Tree)o ) {
+            return t.getItems();
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc) Method declared in Viewer.
+     */
+    public override Control getControl() {
+        return tree;
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override bool getExpanded(Item item) {
+        return (cast(TreeItem) item).getExpanded();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ColumnViewer#getItemAt(org.eclipse.swt.graphics.Point)
+     */
+    protected override Item getItemAt(Point p) {
+        TreeItem[] selection = tree.getSelection();
+
+        if( selection.length is 1 ) {
+            int columnCount = tree.getColumnCount();
+
+            for( int i = 0; i < columnCount; i++ ) {
+                if( selection[0].getBounds(i).contains(p) ) {
+                    return selection[0];
+                }
+            }
+        }
+
+        return getTree().getItem(p);
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override int getItemCount(Control widget) {
+        return (cast(Tree) widget).getItemCount();
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override int getItemCount(Item item) {
+        return (cast(TreeItem) item).getItemCount();
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override Item[] getItems(Item item) {
+        return (cast(TreeItem) item).getItems();
+    }
+
+    /**
+     * The tree viewer implementation of this <code>Viewer</code> framework
+     * method ensures that the given label provider is an instance of either
+     * <code>ITableLabelProvider</code> or <code>ILabelProvider</code>. If
+     * it is an <code>ITableLabelProvider</code>, then it provides a separate
+     * label text and image for each column. If it is an
+     * <code>ILabelProvider</code>, then it provides only the label text and
+     * image for the first column, and any remaining columns are blank.
+     */
+    public override IBaseLabelProvider getLabelProvider() {
+        return super.getLabelProvider();
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override Item getParentItem(Item item) {
+        return (cast(TreeItem) item).getParentItem();
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override Item[] getSelection(Control widget) {
+        return (cast(Tree) widget).getSelection();
+    }
+
+    /**
+     * Returns this tree viewer's tree control.
+     *
+     * @return the tree control
+     */
+    public Tree getTree() {
+        return tree;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.AbstractTreeViewer#hookControl(org.eclipse.swt.widgets.Control)
+     */
+    protected override void hookControl(Control control) {
+        super.hookControl(control);
+        Tree treeControl = cast(Tree) control;
+
+        if ((treeControl.getStyle() & SWT.VIRTUAL) !is 0) {
+            treeControl.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent e) {
+                    treeIsDisposed = true;
+                    unmapAllElements();
+                }
+            });
+            treeControl.addListener(SWT.SetData, new class Listener {
+
+                public void handleEvent(Event event) {
+                    if (contentProviderIsLazy) {
+                        TreeItem item = cast(TreeItem) event.item;
+                        TreeItem parentItem = item.getParentItem();
+                        int index = event.index;
+                        virtualLazyUpdateWidget(
+                                parentItem is null ? cast(Widget) getTree()
+                                        : parentItem, index);
+                    }
+                }
+
+            });
+        }
+    }
+
+    protected override ColumnViewerEditor createViewerEditor() {
+        return new TreeViewerEditor(this,null,new ColumnViewerEditorActivationStrategy(this),ColumnViewerEditor.DEFAULT);
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override Item newItem(Widget parent, int flags, int ix) {
+        TreeItem item;
+
+        if ( cast(TreeItem)parent ) {
+            item = cast(TreeItem) createNewRowPart(getViewerRowFromItem(parent),
+                    flags, ix).getItem();
+        } else {
+            item = cast(TreeItem) createNewRowPart(null, flags, ix).getItem();
+        }
+
+        return item;
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override void removeAll(Control widget) {
+        (cast(Tree) widget).removeAll();
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override void setExpanded(Item node, bool expand) {
+        (cast(TreeItem) node).setExpanded(expand);
+        if (contentProviderIsLazy) {
+            // force repaints to happen
+            getControl().update();
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override void setSelection(List items) {
+
+        Item[] current = getSelection(getTree());
+
+        // Don't bother resetting the same selection
+        if (isSameSelection(items, current)) {
+            return;
+        }
+
+        getTree().setSelection( arraycast!(TreeItem)( items.toArray()));
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected override void showItem(Item item) {
+        getTree().showItem(cast(TreeItem) item);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.AbstractTreeViewer#getChild(org.eclipse.swt.widgets.Widget,
+     *      int)
+     */
+    protected override Item getChild(Widget widget, int index) {
+        if (auto ti = cast(TreeItem)widget ) {
+            return ti.getItem(index);
+        }
+        if (auto t = cast(Tree)widget ) {
+            return t.getItem(index);
+        }
+        return null;
+    }
+
+    protected override void assertContentProviderType(IContentProvider provider) {
+        if ( null !is cast(ILazyTreeContentProvider)provider
+                || null !is cast(ILazyTreePathContentProvider)provider ) {
+            return;
+        }
+        super.assertContentProviderType(provider);
+    }
+
+    protected override Object[] getRawChildren(Object parent) {
+        if (contentProviderIsLazy) {
+            return new Object[0];
+        }
+        return super.getRawChildren(parent);
+    }
+
+    override void preservingSelection(Runnable updateCode, bool reveal) {
+        if (preservingSelection_){
+            // avoid preserving the selection if called reentrantly,
+            // see bug 172640
+            updateCode.run();
+            return;
+        }
+        preservingSelection_ = true;
+        try {
+            super.preservingSelection(updateCode, reveal);
+        } finally {
+            preservingSelection_ = false;
+        }
+    }
+
+    /**
+     * For a TreeViewer with a tree with the VIRTUAL style bit set, set the
+     * number of children of the given element or tree path. To set the number
+     * of children of the invisible root of the tree, you can pass the input
+     * object or an empty tree path.
+     *
+     * @param elementOrTreePath
+     *            the element, or tree path
+     * @param count
+     *
+     * @since 3.2
+     */
+    public void setChildCount(Object elementOrTreePath, int count) {
+        if (checkBusy())
+            return;
+        preservingSelection( dgRunnable((Object elementOrTreePath_, int count_) {
+            if (internalIsInputOrEmptyPath(elementOrTreePath_)) {
+                getTree().setItemCount(count_);
+                return;
+            }
+            Widget[] items = internalFindItems(elementOrTreePath_);
+            for (int i = 0; i < items.length; i++) {
+                TreeItem treeItem = cast(TreeItem) items[i];
+                treeItem.setItemCount(count_);
+            }
+        }, elementOrTreePath,count ));
+    }
+
+    /**
+     * For a TreeViewer with a tree with the VIRTUAL style bit set, replace the
+     * given parent's child at index with the given element. If the given parent
+     * is this viewer's input or an empty tree path, this will replace the root
+     * element at the given index.
+     * <p>
+     * This method should be called by implementers of ILazyTreeContentProvider
+     * to populate this viewer.
+     * </p>
+     *
+     * @param parentElementOrTreePath
+     *            the parent of the element that should be updated, or the tree
+     *            path to that parent
+     * @param index
+     *            the index in the parent's children
+     * @param element
+     *            the new element
+     *
+     * @see #setChildCount(Object, int)
+     * @see ILazyTreeContentProvider
+     * @see ILazyTreePathContentProvider
+     *
+     * @since 3.2
+     */
+    public void replace(Object parentElementOrTreePath, int index,
+            Object element) {
+        if (checkBusy())
+            return;
+        Item[] selectedItems = getSelection(getControl());
+        TreeSelection selection = cast(TreeSelection) getSelection();
+        Widget[] itemsToDisassociate;
+        if (auto tp = cast(TreePath)parentElementOrTreePath ) {
+            TreePath elementPath = tp
+                    .createChildPath(element);
+            itemsToDisassociate = internalFindItems(elementPath);
+        } else {
+            itemsToDisassociate = internalFindItems(element);
+        }
+        if (internalIsInputOrEmptyPath(parentElementOrTreePath)) {
+            if (index < tree.getItemCount()) {
+                TreeItem item = tree.getItem(index);
+                selection = adjustSelectionForReplace(selectedItems, selection, item, element, getRoot());
+                // disassociate any different item that represents the
+                // same element under the same parent (the tree)
+                for (int i = 0; i < itemsToDisassociate.length; i++) {
+                    if (auto itemToDisassociate = cast(TreeItem)itemsToDisassociate[i]) {
+                        if (itemToDisassociate !is item
+                                && itemToDisassociate.getParentItem() is null) {
+                            int indexToDisassociate = getTree().indexOf(
+                                    itemToDisassociate);
+                            disassociate(itemToDisassociate);
+                            getTree().clear(indexToDisassociate, true);
+                        }
+                    }
+                }
+                Object oldData = item.getData();
+                updateItem(item, element);
+                if (!/+TreeViewer.this.+/opEquals(oldData, element)) {
+                    item.clearAll(true);
+                }
+            }
+        } else {
+            Widget[] parentItems = internalFindItems(parentElementOrTreePath);
+            for (int i = 0; i < parentItems.length; i++) {
+                TreeItem parentItem = cast(TreeItem) parentItems[i];
+                if (index < parentItem.getItemCount()) {
+                    TreeItem item = parentItem.getItem(index);
+                    selection = adjustSelectionForReplace(selectedItems, selection, item, element, parentItem.getData());
+                    // disassociate any different item that represents the
+                    // same element under the same parent (the tree)
+                    for (int j = 0; j < itemsToDisassociate.length; j++) {
+                        if ( auto itemToDisassociate = cast(TreeItem)itemsToDisassociate[j]  ) {
+                            if (itemToDisassociate !is item
+                                    && itemToDisassociate.getParentItem() is parentItem) {
+                                int indexToDisaccociate = parentItem
+                                        .indexOf(itemToDisassociate);
+                                disassociate(itemToDisassociate);
+                                parentItem.clear(indexToDisaccociate, true);
+                            }
+                        }
+                    }
+                    Object oldData = item.getData();
+                    updateItem(item, element);
+                    if (!/+TreeViewer.this.+/opEquals(oldData, element)) {
+                        item.clearAll(true);
+                    }
+                }
+            }
+        }
+        // Restore the selection if we are not already in a nested preservingSelection:
+        if (!preservingSelection_) {
+            setSelectionToWidget(selection, false);
+            // send out notification if old and new differ
+            ISelection newSelection = getSelection();
+            if (!(cast(Object)newSelection).opEquals(cast(Object)selection)) {
+                handleInvalidSelection(selection, newSelection);
+            }
+        }
+    }
+
+    /**
+     * Fix for bug 185673: If the currently replaced item was selected, add it
+     * to the selection that is being restored. Only do this if its getData() is
+     * currently null
+     *
+     * @param selectedItems
+     * @param selection
+     * @param item
+     * @param element
+     * @return
+     */
+    private TreeSelection adjustSelectionForReplace(Item[] selectedItems,
+            TreeSelection selection, TreeItem item, Object element, Object parentElement) {
+        if (item.getData() !is null || selectedItems.length is selection.size()
+                || parentElement is null) {
+            // Don't do anything - we are not seeing an instance of bug 185673
+            return selection;
+        }
+        for (int i = 0; i < selectedItems.length; i++) {
+            if (item is selectedItems[i]) {
+                // The current item was selected, but its data is null.
+                // The data will be replaced by the given element, so to keep
+                // it selected, we have to add it to the selection.
+                TreePath[] originalPaths = selection.getPaths();
+                int length_ = originalPaths.length;
+                TreePath[] paths = new TreePath[length_ + 1];
+                System.arraycopy(originalPaths, 0, paths, 0, length_);
+                // set the element temporarily so that we can call getTreePathFromItem
+                item.setData(element);
+                paths[length_] = getTreePathFromItem(item);
+                item.setData(null);
+                return new TreeSelection(paths, selection.getElementComparer());
+            }
+        }
+        // The item was not selected, return the given selection
+        return selection;
+    }
+
+    public override bool isExpandable(Object element) {
+        if (contentProviderIsLazy) {
+            TreeItem treeItem = cast(TreeItem) internalExpand(element, false);
+            if (treeItem is null) {
+                return false;
+            }
+            virtualMaterializeItem(treeItem);
+            return treeItem.getItemCount() > 0;
+        }
+        return super.isExpandable(element);
+    }
+
+    protected override Object getParentElement(Object element) {
+        bool oldBusy = isBusy();
+        setBusy(true);
+        try {
+            if (contentProviderIsLazy && !contentProviderIsTreeBased && !(cast(TreePath)element )) {
+                ILazyTreeContentProvider lazyTreeContentProvider = cast(ILazyTreeContentProvider) getContentProvider();
+                return lazyTreeContentProvider.getParent(element);
+            }
+            if (contentProviderIsLazy && contentProviderIsTreeBased && !(cast(TreePath)element )) {
+                ILazyTreePathContentProvider lazyTreePathContentProvider = cast(ILazyTreePathContentProvider) getContentProvider();
+                TreePath[] parents = lazyTreePathContentProvider
+                .getParents(element);
+                if (parents !is null && parents.length > 0) {
+                    return parents[0];
+                }
+            }
+            return super.getParentElement(element);
+        } finally {
+            setBusy(oldBusy);
+        }
+    }
+
+    protected override void createChildren(Widget widget) {
+        if (contentProviderIsLazy) {
+            Object element = widget.getData();
+            if (element is null && cast(TreeItem)widget ) {
+                // parent has not been materialized
+                virtualMaterializeItem(cast(TreeItem) widget);
+                // try getting the element now that updateElement was called
+                element = widget.getData();
+            }
+            if (element is  null) {
+                // give up because the parent is still not materialized
+                return;
+            }
+            Item[] children = getChildren(widget);
+            if (children.length is 1 && children[0].getData() is null) {
+                // found a dummy node
+                virtualLazyUpdateChildCount(widget, children.length);
+                children = getChildren(widget);
+            }
+            // touch all children to make sure they are materialized
+            for (int i = 0; i < children.length; i++) {
+                if (children[i].getData() is null) {
+                    virtualLazyUpdateWidget(widget, i);
+                }
+            }
+            return;
+        }
+        super.createChildren(widget);
+    }
+
+    protected override void internalAdd(Widget widget, Object parentElement,
+            Object[] childElements) {
+        if (contentProviderIsLazy) {
+            if (auto ti = cast(TreeItem)widget ) {
+                int count = ti.getItemCount() + childElements.length;
+                ti.setItemCount(count);
+                ti.clearAll(false);
+            } else {
+                Tree t = cast(Tree) widget;
+                t.setItemCount(t.getItemCount() + childElements.length);
+                t.clearAll(false);
+            }
+            return;
+        }
+        super.internalAdd(widget, parentElement, childElements);
+    }
+
+    private void virtualMaterializeItem(TreeItem treeItem) {
+        if (treeItem.getData() !is null) {
+            // already materialized
+            return;
+        }
+        if (!contentProviderIsLazy) {
+            return;
+        }
+        int index;
+        Widget parent = treeItem.getParentItem();
+        if (parent is null) {
+            parent = treeItem.getParent();
+        }
+        Object parentElement = parent.getData();
+        if (parentElement !is null) {
+            if ( auto t = cast(Tree)parent ) {
+                index = t.indexOf(treeItem);
+            } else {
+                index = (cast(TreeItem) parent).indexOf(treeItem);
+            }
+            virtualLazyUpdateWidget(parent, index);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.AbstractTreeViewer#internalRefreshStruct(org.eclipse.swt.widgets.Widget,
+     *      java.lang.Object, bool)
+     */
+    protected override void internalRefreshStruct(Widget widget, Object element,
+            bool updateLabels) {
+        if (contentProviderIsLazy) {
+            // clear all starting with the given widget
+            if (auto t = cast(Tree)widget ) {
+                t.clearAll(true);
+            } else if (cast(TreeItem)widget ) {
+                (cast(TreeItem) widget).clearAll(true);
+            }
+            int index = 0;
+            Widget parent = null;
+            if (auto treeItem = cast(TreeItem)widget ) {
+                parent = treeItem.getParentItem();
+                if (parent is null) {
+                    parent = treeItem.getParent();
+                }
+                if (cast(Tree)parent ) {
+                    index = (cast(Tree) parent).indexOf(treeItem);
+                } else {
+                    index = (cast(TreeItem) parent).indexOf(treeItem);
+                }
+            }
+            virtualRefreshExpandedItems(parent, widget, element, index);
+            return;
+        }
+        super.internalRefreshStruct(widget, element, updateLabels);
+    }
+
+    /**
+     * Traverses the visible (expanded) part of the tree and updates child
+     * counts.
+     *
+     * @param parent the parent of the widget, or <code>null</code> if the widget is the tree
+     * @param widget
+     * @param element
+     * @param index the index of the widget in the children array of its parent, or 0 if the widget is the tree
+     */
+    private void virtualRefreshExpandedItems(Widget parent, Widget widget, Object element, int index) {
+        if ( cast(Tree)widget ) {
+            if (element is null) {
+                (cast(Tree) widget).setItemCount(0);
+                return;
+            }
+            virtualLazyUpdateChildCount(widget, getChildren(widget).length);
+        } else if ((cast(TreeItem) widget).getExpanded()) {
+            // prevent SetData callback
+            (cast(TreeItem)widget).setText(" "); //$NON-NLS-1$
+            virtualLazyUpdateWidget(parent, index);
+        } else {
+            return;
+        }
+        Item[] items = getChildren(widget);
+        for (int i = 0; i < items.length; i++) {
+            Item item = items[i];
+            Object data = item.getData();
+            virtualRefreshExpandedItems(widget, item, data, i);
+        }
+    }
+
+    /*
+     * To unmap elements correctly, we need to register a dispose listener with
+     * the item if the tree is virtual.
+     */
+    protected override void mapElement(Object element, Widget item) {
+        super.mapElement(element, item);
+        // make sure to unmap elements if the tree is virtual
+        if ((getTree().getStyle() & SWT.VIRTUAL) !is 0) {
+            // only add a dispose listener if item hasn't already on assigned
+            // because it is reused
+            if (item.getData(VIRTUAL_DISPOSE_KEY) is null) {
+                item.setData(VIRTUAL_DISPOSE_KEY, Boolean.TRUE);
+                item.addDisposeListener(new class(item) DisposeListener {
+                    Widget item_;
+                    this(Widget a){
+                        item_=a;
+                    }
+                    public void widgetDisposed(DisposeEvent e) {
+                        if (!treeIsDisposed) {
+                            Object data = item_.getData();
+                            if (usingElementMap() && data !is null) {
+                                unmapElement(data, item_);
+                            }
+                        }
+                    }
+                });
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ColumnViewer#getRowPartFromItem(org.eclipse.swt.widgets.Widget)
+     */
+    protected override ViewerRow getViewerRowFromItem(Widget item) {
+        if( cachedRow is null ) {
+            cachedRow = new TreeViewerRow(cast(TreeItem) item);
+        } else {
+            cachedRow.setItem(cast(TreeItem) item);
+        }
+
+        return cachedRow;
+    }
+
+    /**
+     * Create a new ViewerRow at rowIndex
+     *
+     * @param parent
+     * @param style
+     * @param rowIndex
+     * @return ViewerRow
+     */
+    private ViewerRow createNewRowPart(ViewerRow parent, int style, int rowIndex) {
+        if (parent is null) {
+            if (rowIndex >= 0) {
+                return getViewerRowFromItem(new TreeItem(tree, style, rowIndex));
+            }
+            return getViewerRowFromItem(new TreeItem(tree, style));
+        }
+
+        if (rowIndex >= 0) {
+            return getViewerRowFromItem(new TreeItem(cast(TreeItem) parent.getItem(),
+                    SWT.NONE, rowIndex));
+        }
+
+        return getViewerRowFromItem(new TreeItem(cast(TreeItem) parent.getItem(),
+                SWT.NONE));
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.AbstractTreeViewer#internalInitializeTree(org.eclipse.swt.widgets.Control)
+     */
+    protected override void internalInitializeTree(Control widget) {
+        if (contentProviderIsLazy) {
+            if (cast(Tree)widget && widget.getData() !is null) {
+                virtualLazyUpdateChildCount(widget, 0);
+                return;
+            }
+        }
+        super.internalInitializeTree(tree);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.AbstractTreeViewer#updatePlus(org.eclipse.swt.widgets.Item,
+     *      java.lang.Object)
+     */
+    protected override void updatePlus(Item item, Object element) {
+        if (contentProviderIsLazy) {
+            Object data = item.getData();
+            int itemCount = 0;
+            if (data !is null) {
+                // item is already materialized
+                itemCount = (cast(TreeItem) item).getItemCount();
+            }
+            virtualLazyUpdateHasChildren(item, itemCount);
+        } else {
+            super.updatePlus(item, element);
+        }
+    }
+
+    /**
+     * Removes the element at the specified index of the parent.  The selection is updated if required.
+     *
+     * @param parentOrTreePath the parent element, the input element, or a tree path to the parent element
+     * @param index child index
+     * @since 3.3
+     */
+    public void remove(Object parentOrTreePath_, int index_) {
+        if (checkBusy())
+            return;
+        preservingSelection(new class((cast(TreeSelection) getSelection()).getPaths(),parentOrTreePath_,index_) Runnable {
+            List oldSelection;
+            Object parentOrTreePath;
+            int index;
+            this(TreePath[] a,Object b,int c){
+                parentOrTreePath=b;
+                index=c;
+                oldSelection = new LinkedList(Arrays.asList(a));
+            }
+            public void run() {
+                TreePath removedPath = null;
+                if (internalIsInputOrEmptyPath(parentOrTreePath)) {
+                    Tree tree = cast(Tree) getControl();
+                    if (index < tree.getItemCount()) {
+                        TreeItem item = tree.getItem(index);
+                        if (item.getData() !is null) {
+                            removedPath = getTreePathFromItem(item);
+                            disassociate(item);
+                        }
+                        item.dispose();
+                    }
+                } else {
+                    Widget[] parentItems = internalFindItems(parentOrTreePath);
+                    for (int i = 0; i < parentItems.length; i++) {
+                        TreeItem parentItem = cast(TreeItem) parentItems[i];
+                        if (parentItem.isDisposed())
+                            continue;
+                        if (index < parentItem.getItemCount()) {
+                            TreeItem item = parentItem.getItem(index);
+                            if (item.getData() !is null) {
+                                removedPath = getTreePathFromItem(item);
+                                disassociate(item);
+                            }
+                            item.dispose();
+                        }
+                    }
+                }
+                if (removedPath !is null) {
+                    bool removed = false;
+                    for (Iterator it = oldSelection.iterator(); it
+                            .hasNext();) {
+                        TreePath path = cast(TreePath) it.next();
+                        if (path.startsWith(removedPath, getComparer())) {
+                            it.remove();
+                            removed = true;
+                        }
+                    }
+                    if (removed) {
+                        setSelection(new TreeSelection(
+                                arraycast!(TreePath)( oldSelection
+                                        .toArray(new TreePath[oldSelection
+                                                .size()])), getComparer()),
+                                false);
+                    }
+                }
+            }
+        });
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractTreeViewer#handleTreeExpand(org.eclipse.swt.events.TreeEvent)
+     */
+    protected override void handleTreeExpand(TreeEvent event) {
+        if (contentProviderIsLazy) {
+            if (event.item.getData() !is null) {
+                Item[] children = getChildren(event.item);
+                if (children.length is 1 && children[0].getData() is null) {
+                    // we have a dummy child node, ask for an updated child
+                    // count
+                    virtualLazyUpdateChildCount(event.item, children.length);
+                }
+                fireTreeExpanded(new TreeExpansionEvent(this, event.item
+                        .getData()));
+            }
+            return;
+        }
+        super.handleTreeExpand(event);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.AbstractTreeViewer#setContentProvider(org.eclipse.jface.viewers.IContentProvider)
+     */
+    public override void setContentProvider(IContentProvider provider) {
+        contentProviderIsLazy = (cast(ILazyTreeContentProvider)provider )
+                || (cast(ILazyTreePathContentProvider)provider );
+        contentProviderIsTreeBased = null !is cast(ILazyTreePathContentProvider)provider ;
+        super.setContentProvider(provider);
+    }
+
+    /**
+     * For a TreeViewer with a tree with the VIRTUAL style bit set, inform the
+     * viewer about whether the given element or tree path has children. Avoid
+     * calling this method if the number of children has already been set.
+     *
+     * @param elementOrTreePath
+     *            the element, or tree path
+     * @param hasChildren
+     *
+     * @since 3.3
+     */
+    public void setHasChildren(Object elementOrTreePath_, bool hasChildren_) {
+        if (checkBusy())
+            return;
+        preservingSelection(new class(elementOrTreePath_,hasChildren_) Runnable {
+            Object elementOrTreePath;
+            bool hasChildren;
+            this(Object a,bool b){
+                elementOrTreePath=a;
+                hasChildren=b;
+            }
+            public void run() {
+                if (internalIsInputOrEmptyPath(elementOrTreePath)) {
+                    if (hasChildren) {
+                        virtualLazyUpdateChildCount(getTree(),
+                                getChildren(getTree()).length);
+                    } else {
+                        setChildCount(elementOrTreePath, 0);
+                    }
+                    return;
+                }
+                Widget[] items = internalFindItems(elementOrTreePath);
+                for (int i = 0; i < items.length; i++) {
+                    TreeItem item = cast(TreeItem) items[i];
+                    if (!hasChildren) {
+                        item.setItemCount(0);
+                    } else {
+                        if (!item.getExpanded()) {
+                            item.setItemCount(1);
+                            TreeItem child = item.getItem(0);
+                            if (child.getData() !is null) {
+                                disassociate(child);
+                            }
+                            item.clear(0, true);
+                        } else {
+                            virtualLazyUpdateChildCount(item, item.getItemCount());
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Update the widget at index.
+     * @param widget
+     * @param index
+     */
+    private void virtualLazyUpdateWidget(Widget widget, int index) {
+        bool oldBusy = isBusy();
+        setBusy(false);
+        try {
+            if (contentProviderIsTreeBased) {
+                TreePath treePath;
+                if ( auto i = cast(Item)widget ) {
+                    if (widget.getData() is null) {
+                        // we need to materialize the parent first
+                        // see bug 167668
+                        // however, that would be too risky
+                        // see bug 182782 and bug 182598
+                        // so we just ignore this call altogether
+                        // and don't do this: virtualMaterializeItem((TreeItem) widget);
+                        return;
+                    }
+                    treePath = getTreePathFromItem(i);
+                } else {
+                    treePath = TreePath.EMPTY;
+                }
+                (cast(ILazyTreePathContentProvider) getContentProvider())
+                        .updateElement(treePath, index);
+            } else {
+                (cast(ILazyTreeContentProvider) getContentProvider()).updateElement(
+                        widget.getData(), index);
+            }
+        } finally {
+            setBusy(oldBusy);
+        }
+    }
+
+    /**
+     * Update the child count
+     * @param widget
+     * @param currentChildCount
+     */
+    private void virtualLazyUpdateChildCount(Widget widget, int currentChildCount) {
+        bool oldBusy = isBusy();
+        setBusy(false);
+        try {
+            if (contentProviderIsTreeBased) {
+                TreePath treePath;
+                if (cast(Item)widget ) {
+                    treePath = getTreePathFromItem(cast(Item) widget);
+                } else {
+                    treePath = TreePath.EMPTY;
+                }
+                (cast(ILazyTreePathContentProvider) getContentProvider())
+                .updateChildCount(treePath, currentChildCount);
+            } else {
+                (cast(ILazyTreeContentProvider) getContentProvider()).updateChildCount(widget.getData(), currentChildCount);
+            }
+        } finally {
+            setBusy(oldBusy);
+        }
+    }
+
+    /**
+     * Update the item with the current child count.
+     * @param item
+     * @param currentChildCount
+     */
+    private void virtualLazyUpdateHasChildren(Item item, int currentChildCount) {
+        bool oldBusy = isBusy();
+        setBusy(false);
+        try {
+            if (contentProviderIsTreeBased) {
+                TreePath treePath;
+                treePath = getTreePathFromItem(item);
+                if (currentChildCount is 0) {
+                    // item is not expanded (but may have a plus currently)
+                    (cast(ILazyTreePathContentProvider) getContentProvider())
+                    .updateHasChildren(treePath);
+                } else {
+                    (cast(ILazyTreePathContentProvider) getContentProvider())
+                    .updateChildCount(treePath, currentChildCount);
+                }
+            } else {
+                (cast(ILazyTreeContentProvider) getContentProvider()).updateChildCount(item.getData(), currentChildCount);
+            }
+        } finally {
+            setBusy(oldBusy);
+        }
+    }
+
+    protected override void disassociate(Item item) {
+        if (contentProviderIsLazy) {
+            // avoid causing a callback:
+            item.setText(" "); //$NON-NLS-1$
+        }
+        super.disassociate(item);
+    }
+
+    protected override int doGetColumnCount() {
+        return tree.getColumnCount();
+    }
+
+    /**
+     * Sets a new selection for this viewer and optionally makes it visible.
+     * <p>
+     * <b>Currently the <code>reveal</code> parameter is not honored because
+     * {@link Tree} does not provide an API to only select an item without
+     * scrolling it into view</b>
+     * </p>
+     *
+     * @param selection
+     *            the new selection
+     * @param reveal
+     *            <code>true</code> if the selection is to be made visible,
+     *            and <code>false</code> otherwise
+     */
+    public override void setSelection(ISelection selection, bool reveal) {
+        super.setSelection(selection, reveal);
+    }
+
+    public override void editElement(Object element, int column) {
+        if( cast(TreePath)element ) {
+            try {
+                getControl().setRedraw(false);
+                setSelection(new TreeSelection(cast(TreePath) element));
+                TreeItem[] items = tree.getSelection();
+
+                if( items.length is 1 ) {
+                    ViewerRow row = getViewerRowFromItem(items[0]);
+
+                    if (row !is null) {
+                        ViewerCell cell = row.getCell(column);
+                        if (cell !is null) {
+                            triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent(cell));
+                        }
+                    }
+                }
+            } finally {
+                getControl().setRedraw(true);
+            }
+        } else {
+            super.editElement(element, column);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TreeViewerColumn.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2006 Tom Schindl and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Tom Schindl - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module org.eclipse.jface.viewers.TreeViewerColumn;
+
+import org.eclipse.jface.viewers.ViewerColumn;
+import org.eclipse.jface.viewers.TreeViewer;
+
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+
+import java.lang.all;
+
+/**
+ * ViewerColumn implementation for TreeViewer to enable column-specific label
+ * providers and editing support.
+ *
+ * @since 3.3
+ *
+ */
+public final class TreeViewerColumn : ViewerColumn {
+    private TreeColumn column;
+
+    /**
+     * Creates a new viewer column for the given {@link TreeViewer} on a new
+     * {@link TreeColumn} with the given style bits. The column is inserted at
+     * the given index into the list of columns.
+     *
+     * @param viewer
+     *            the tree viewer to which this column belongs
+     * @param style
+     *            the style bits used to create the column, for applicable style bits
+     *            see {@link TreeColumn}
+     * @see TreeColumn#TreeColumn(Tree, int)
+     */
+    public this(TreeViewer viewer, int style) {
+        this(viewer, style, -1);
+    }
+
+    /**
+     * Creates a new viewer column for the given {@link TreeViewer} on a new
+     * {@link TreeColumn} with the given style bits. The column is added at the
+     * end of the list of columns.
+     *
+     * @param viewer
+     *            the tree viewer to which this column belongs
+     * @param style
+     *            the style bits used to create the column, for applicable style bits
+     *            see {@link TreeColumn}
+     * @param index
+     *            the index at which to place the newly created column
+     * @see TreeColumn#TreeColumn(Tree, int, int)
+     */
+    public this(TreeViewer viewer, int style, int index) {
+        this(viewer, createColumn(viewer.getTree(), style, index));
+    }
+
+    /**
+     * Creates a new viewer column for the given {@link TreeViewer} on the given
+     * {@link TreeColumn}.
+     *
+     * @param viewer
+     *            the tree viewer to which this column belongs
+     * @param column
+     *            the underlying tree column
+     */
+    public this(TreeViewer viewer, TreeColumn column) {
+        super(viewer, column);
+        this.column = column;
+    }
+
+    private static TreeColumn createColumn(Tree table, int style, int index) {
+        if (index >= 0) {
+            return new TreeColumn(table, style, index);
+        }
+
+        return new TreeColumn(table, style);
+    }
+
+    /**
+     * @return the underlying SWT column
+     */
+    public TreeColumn getColumn() {
+        return column;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TreeViewerEditor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                                 fixes in bug 198665, 200731, 187963
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.TreeViewerEditor;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.ColumnViewerEditor;
+import org.eclipse.jface.viewers.SWTFocusCellManager;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
+import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.TreeSelection;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.TreeEditor;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+
+/**
+ * This is an editor implementation for {@link Tree}
+ *
+ * @since 3.3
+ */
+public class TreeViewerEditor : ColumnViewerEditor {
+    /**
+     * This viewer's tree editor.
+     */
+    private TreeEditor treeEditor;
+
+    private SWTFocusCellManager focusCellManager;
+
+    /**
+     * @param viewer
+     *            the viewer the editor is attached to
+     * @param focusCellManager
+     *            the cell focus manager if one used or <code>null</code>
+     * @param editorActivationStrategy
+     *            the strategy used to decide about the editor activation
+     * @param feature
+     *            the feature mask
+     */
+    this(TreeViewer viewer, SWTFocusCellManager focusCellManager,
+            ColumnViewerEditorActivationStrategy editorActivationStrategy,
+            int feature) {
+        super(viewer, editorActivationStrategy, feature);
+        treeEditor = new TreeEditor(viewer.getTree());
+        this.focusCellManager = focusCellManager;
+    }
+
+    /**
+     * Create a customized editor with focusable cells
+     *
+     * @param viewer
+     *            the viewer the editor is created for
+     * @param focusCellManager
+     *            the cell focus manager if one needed else <code>null</code>
+     * @param editorActivationStrategy
+     *            activation strategy to control if an editor activated
+     * @param feature
+     *            bit mask controlling the editor
+     *            <ul>
+     *            <li>{@link ColumnViewerEditor#DEFAULT}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li>
+     *            </ul>
+     * @see #create(TreeViewer, ColumnViewerEditorActivationStrategy, int)
+     */
+    public static void create(TreeViewer viewer,
+            SWTFocusCellManager focusCellManager,
+            ColumnViewerEditorActivationStrategy editorActivationStrategy,
+            int feature) {
+        TreeViewerEditor editor = new TreeViewerEditor(viewer,
+                focusCellManager, editorActivationStrategy, feature);
+        viewer.setColumnViewerEditor(editor);
+        if (focusCellManager !is null) {
+            focusCellManager.init();
+        }
+    }
+
+    /**
+     * Create a customized editor whose activation process is customized
+     *
+     * @param viewer
+     *            the viewer the editor is created for
+     * @param editorActivationStrategy
+     *            activation strategy to control if an editor activated
+     * @param feature
+     *            bit mask controlling the editor
+     *            <ul>
+     *            <li>{@link ColumnViewerEditor#DEFAULT}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li>
+     *            <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li>
+     *            </ul>
+     */
+    public static void create(TreeViewer viewer,
+            ColumnViewerEditorActivationStrategy editorActivationStrategy,
+            int feature) {
+        create(viewer, null, editorActivationStrategy, feature);
+    }
+
+    protected override void setEditor(Control w, Item item, int fColumnNumber) {
+        treeEditor.setEditor(w, cast(TreeItem) item, fColumnNumber);
+    }
+
+    protected override void setLayoutData(LayoutData layoutData) {
+        treeEditor.grabHorizontal = layoutData.grabHorizontal;
+        treeEditor.horizontalAlignment = layoutData.horizontalAlignment;
+        treeEditor.minimumWidth = layoutData.minimumWidth;
+        treeEditor.verticalAlignment = layoutData.verticalAlignment;
+        if( layoutData.minimumHeight !is SWT.DEFAULT ) {
+            treeEditor.minimumHeight = layoutData.minimumHeight;
+        }
+    }
+
+    public override ViewerCell getFocusCell() {
+        if (focusCellManager !is null) {
+            return focusCellManager.getFocusCell();
+        }
+
+        return super.getFocusCell();
+    }
+
+    protected override void updateFocusCell(ViewerCell focusCell,
+            ColumnViewerEditorActivationEvent event) {
+        // Update the focus cell when we activated the editor with these 2
+        // events
+        if (event.eventType is ColumnViewerEditorActivationEvent.PROGRAMMATIC
+                || event.eventType is ColumnViewerEditorActivationEvent.TRAVERSAL) {
+
+            auto l = getViewer().getSelectionFromWidget_package();
+
+            if (focusCellManager !is null) {
+                focusCellManager.setFocusCell(focusCell);
+            }
+
+            if (!l.contains(focusCell.getElement())) {
+                getViewer().setSelection(
+                        new TreeSelection(focusCell.getViewerRow()
+                                .getTreePath()),true);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TreeViewerFocusCellManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 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
+ *     Tom Schindl<tom.schindl@bestsolution.at> - initial API and implementation
+ *                                              - fix in bug: 195908, 210752
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.TreeViewerFocusCellManager;
+
+import org.eclipse.jface.viewers.SWTFocusCellManager;
+import org.eclipse.jface.viewers.CellNavigationStrategy;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.FocusCellHighlighter;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ColumnViewer;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * This class is responsible to provide the concept of cells for {@link Tree}.
+ * This concept is needed to provide features like editor activation with the
+ * keyboard
+ *
+ * @since 3.3
+ *
+ */
+public class TreeViewerFocusCellManager : SWTFocusCellManager {
+    private static /+final+/ CellNavigationStrategy TREE_NAVIGATE;
+    static this(){
+        TREE_NAVIGATE = new class CellNavigationStrategy {
+            public void collapse(ColumnViewer viewer, ViewerCell cellToCollapse,
+                    Event event) {
+                if (cellToCollapse !is null) {
+                    (cast(TreeItem) cellToCollapse.getItem()).setExpanded(false);
+                }
+            }
+
+            public void expand(ColumnViewer viewer, ViewerCell cellToExpand,
+                    Event event) {
+                if (cellToExpand !is null) {
+                    TreeViewer v = cast(TreeViewer) viewer;
+                    v.setExpandedState(v.getTreePathFromItem_package(cast(Item)cellToExpand
+                            .getItem()), true);
+                }
+            }
+
+            public bool isCollapseEvent(ColumnViewer viewer,
+                    ViewerCell cellToCollapse, Event event) {
+
+            if (cellToCollapse is null) {
+                return false;
+            }
+
+                return cellToCollapse !is null
+                    && (cast(TreeItem) cellToCollapse.getItem()).getExpanded()
+                    && event.keyCode is SWT.ARROW_LEFT
+                    && isFirstColumnCell(cellToCollapse);
+            }
+
+            public bool isExpandEvent(ColumnViewer viewer,
+                    ViewerCell cellToExpand, Event event) {
+
+            if (cellToExpand is null) {
+                return false;
+            }
+
+                return cellToExpand !is null
+                    && (cast(TreeItem) cellToExpand.getItem()).getItemCount() > 0
+                    && !(cast(TreeItem) cellToExpand.getItem()).getExpanded()
+                    && event.keyCode is SWT.ARROW_RIGHT
+                    && isFirstColumnCell(cellToExpand);
+        }
+
+        private bool isFirstColumnCell(ViewerCell cell) {
+            return cell.getViewerRow().getVisualIndex_package(cell.getColumnIndex()) is 0;
+        }
+        };
+    }
+
+    /**
+     * Create a new manager using a default navigation strategy:
+     * <ul>
+     * <li><code>SWT.ARROW_UP</code>: navigate to cell above</li>
+     * <li><code>SWT.ARROW_DOWN</code>: navigate to cell below</li>
+     * <li><code>SWT.ARROW_RIGHT</code>: on first column (collapses if item
+     * is expanded) else navigate to next visible cell on the right</li>
+     * <li><code>SWT.ARROW_LEFT</code>: on first column (expands if item is
+     * collapsed) else navigate to next visible cell on the left</li>
+     * </ul>
+     *
+     * @param viewer
+     *            the viewer the manager is bound to
+     * @param focusDrawingDelegate
+     *            the delegate responsible to highlight selected cell
+     */
+    public this(TreeViewer viewer,
+            FocusCellHighlighter focusDrawingDelegate) {
+        this(viewer, focusDrawingDelegate, TREE_NAVIGATE);
+    }
+
+    /**
+     * Create a new manager with a custom navigation strategy
+     *
+     * @param viewer
+     *            the viewer the manager is bound to
+     * @param focusDrawingDelegate
+     *            the delegate responsible to highlight selected cell
+     * @param navigationStrategy
+     *            the strategy used to navigate the cells
+     * @since 3.4
+     */
+    public this(TreeViewer viewer,
+            FocusCellHighlighter focusDrawingDelegate,
+            CellNavigationStrategy navigationStrategy) {
+        super(viewer, focusDrawingDelegate, navigationStrategy);
+    }
+
+    override ViewerCell getInitialFocusCell() {
+        Tree tree = cast(Tree) getViewer().getControl();
+
+        if (! tree.isDisposed() && tree.getItemCount() > 0 && ! tree.getItem(0).isDisposed()) {
+            return getViewer().getViewerRowFromItem_package(tree.getItem(0)).getCell(0);
+        }
+
+        return null;
+    }
+
+    public ViewerCell getFocusCell() {
+        ViewerCell cell = super.getFocusCell();
+        Tree t = cast(Tree) getViewer().getControl();
+
+        // It is possible that the selection has changed under the hood
+        if (cell !is null) {
+            if (t.getSelection().length is 1
+                    && t.getSelection()[0] !is cell.getItem()) {
+                setFocusCell(getViewer().getViewerRowFromItem_package(
+                        t.getSelection()[0]).getCell(cell.getColumnIndex()));
+            }
+        }
+
+        return super.getFocusCell();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/TreeViewerRow.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,405 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                               - fix in bug: 174355,171126,,195908,198035,215069,227421
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.TreeViewerRow;
+
+import org.eclipse.jface.viewers.ViewerRow;
+import org.eclipse.jface.viewers.TreePath;
+
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.LinkedList;
+import java.util.Set;
+
+/**
+ * TreeViewerRow is the Tree implementation of ViewerRow.
+ *
+ * @since 3.3
+ *
+ */
+public class TreeViewerRow : ViewerRow {
+    private TreeItem item;
+
+    /**
+     * Create a new instance of the receiver.
+     *
+     * @param item
+     */
+    this(TreeItem item) {
+        this.item = item;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerRow#getBounds(int)
+     */
+    public override Rectangle getBounds(int columnIndex) {
+        return item.getBounds(columnIndex);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerRow#getBounds()
+     */
+    public override Rectangle getBounds() {
+        return item.getBounds();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerRow#getColumnCount()
+     */
+    public override int getColumnCount() {
+        return item.getParent().getColumnCount();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerRow#getItem()
+     */
+    public override Widget getItem() {
+        return item;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerRow#getBackground(int)
+     */
+    public override Color getBackground(int columnIndex) {
+        return item.getBackground(columnIndex);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerRow#getFont(int)
+     */
+    public override Font getFont(int columnIndex) {
+        return item.getFont(columnIndex);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerRow#getForeground(int)
+     */
+    public override Color getForeground(int columnIndex) {
+        return item.getForeground(columnIndex);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerRow#getImage(int)
+     */
+    public override Image getImage(int columnIndex) {
+        return item.getImage(columnIndex);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerRow#getText(int)
+     */
+    public override String getText(int columnIndex) {
+        return item.getText(columnIndex);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerRow#setBackground(int,
+     *      org.eclipse.swt.graphics.Color)
+     */
+    public override void setBackground(int columnIndex, Color color) {
+        item.setBackground(columnIndex, color);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerRow#setFont(int,
+     *      org.eclipse.swt.graphics.Font)
+     */
+    public override void setFont(int columnIndex, Font font) {
+        item.setFont(columnIndex, font);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerRow#setForeground(int,
+     *      org.eclipse.swt.graphics.Color)
+     */
+    public override void setForeground(int columnIndex, Color color) {
+        item.setForeground(columnIndex, color);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerRow#setImage(int,
+     *      org.eclipse.swt.graphics.Image)
+     */
+    public override void setImage(int columnIndex, Image image) {
+        Image oldImage = item.getImage(columnIndex);
+        if (image !is oldImage) {
+            item.setImage(columnIndex, image);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerRow#setText(int, java.lang.String)
+     */
+    public override void setText(int columnIndex, String text) {
+        item.setText(columnIndex, text is null ? "" : text); //$NON-NLS-1$
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ViewerRow#getControl()
+     */
+    public override Control getControl() {
+        return item.getParent();
+    }
+
+
+    public override ViewerRow getNeighbor(int direction, bool sameLevel) {
+        if (direction is ViewerRow.ABOVE) {
+            return getRowAbove(sameLevel);
+        } else if (direction is ViewerRow.BELOW) {
+            return getRowBelow(sameLevel);
+        } else {
+            throw new IllegalArgumentException(
+                    "Illegal value of direction argument."); //$NON-NLS-1$
+        }
+    }
+
+    private ViewerRow getRowBelow(bool sameLevel) {
+        Tree tree = item.getParent();
+
+        // This means we have top-level item
+        if (item.getParentItem() is null) {
+            if (sameLevel || !item.getExpanded()) {
+                int index = tree.indexOf(item) + 1;
+
+                if (index < tree.getItemCount()) {
+                    return new TreeViewerRow(tree.getItem(index));
+                }
+            } else if (item.getExpanded() && item.getItemCount() > 0) {
+                return new TreeViewerRow(item.getItem(0));
+            }
+        } else {
+            if (sameLevel || !item.getExpanded()) {
+                TreeItem parentItem = item.getParentItem();
+
+                int nextIndex = parentItem.indexOf(item) + 1;
+                int totalIndex = parentItem.getItemCount();
+
+                TreeItem itemAfter;
+
+                // This would mean that it was the last item
+                if (nextIndex is totalIndex) {
+                    itemAfter = findNextItem(parentItem);
+                } else {
+                    itemAfter = parentItem.getItem(nextIndex);
+                }
+
+                if (itemAfter !is null) {
+                    return new TreeViewerRow(itemAfter);
+                }
+
+            } else if (item.getExpanded() && item.getItemCount() > 0) {
+                return new TreeViewerRow(item.getItem(0));
+            }
+        }
+
+        return null;
+    }
+
+    private ViewerRow getRowAbove(bool sameLevel) {
+        Tree tree = item.getParent();
+
+        // This means we have top-level item
+        if (item.getParentItem() is null) {
+            int index = tree.indexOf(item) - 1;
+            TreeItem nextTopItem = null;
+
+            if (index >= 0) {
+                nextTopItem = tree.getItem(index);
+            }
+
+            if (nextTopItem !is null) {
+                if (sameLevel) {
+                    return new TreeViewerRow(nextTopItem);
+                }
+
+                return new TreeViewerRow(findLastVisibleItem(nextTopItem));
+            }
+        } else {
+            TreeItem parentItem = item.getParentItem();
+            int previousIndex = parentItem.indexOf(item) - 1;
+
+            TreeItem itemBefore;
+            if (previousIndex >= 0) {
+                if (sameLevel) {
+                    itemBefore = parentItem.getItem(previousIndex);
+                } else {
+                    itemBefore = findLastVisibleItem(parentItem
+                            .getItem(previousIndex));
+                }
+            } else {
+                itemBefore = parentItem;
+            }
+
+            if (itemBefore !is null) {
+                return new TreeViewerRow(itemBefore);
+            }
+        }
+
+        return null;
+    }
+
+    private TreeItem findLastVisibleItem(TreeItem parentItem) {
+        TreeItem rv = parentItem;
+
+        while (rv.getExpanded() && rv.getItemCount() > 0) {
+            rv = rv.getItem(rv.getItemCount() - 1);
+        }
+
+        return rv;
+    }
+
+    private TreeItem findNextItem(TreeItem item) {
+        TreeItem rv = null;
+        Tree tree = item.getParent();
+        TreeItem parentItem = item.getParentItem();
+
+        int nextIndex;
+        int totalItems;
+
+        if (parentItem is null) {
+            nextIndex = tree.indexOf(item) + 1;
+            totalItems = tree.getItemCount();
+        } else {
+            nextIndex = parentItem.indexOf(item) + 1;
+            totalItems = parentItem.getItemCount();
+        }
+
+        // This is once more the last item in the tree
+        // Search on
+        if (nextIndex is totalItems) {
+            if (item.getParentItem() !is null) {
+                rv = findNextItem(item.getParentItem());
+            }
+        } else {
+            if (parentItem is null) {
+                rv = tree.getItem(nextIndex);
+            } else {
+                rv = parentItem.getItem(nextIndex);
+            }
+        }
+
+        return rv;
+    }
+
+    public override TreePath getTreePath() {
+        TreeItem tItem = item;
+        LinkedList segments = new LinkedList();
+        while (tItem !is null) {
+            Object segment = tItem.getData();
+            Assert.isNotNull(segment);
+            segments.addFirst(segment);
+            tItem = tItem.getParentItem();
+        }
+
+        return new TreePath(segments.toArray());
+    }
+
+    void setItem(TreeItem item) {
+        this.item = item;
+    }
+
+    public override Object clone() {
+        return new TreeViewerRow(item);
+    }
+
+    public override Object getElement() {
+        return item.getData();
+    }
+
+    public int getVisualIndex(int creationIndex) {
+        int[] order = item.getParent().getColumnOrder();
+
+        for (int i = 0; i < order.length; i++) {
+            if (order[i] is creationIndex) {
+                return i;
+            }
+        }
+
+        return super.getVisualIndex(creationIndex);
+    }
+
+    public int getCreationIndex(int visualIndex) {
+        if( item !is null && ! item.isDisposed() && hasColumns() && isValidOrderIndex(visualIndex) ) {
+            return item.getParent().getColumnOrder()[visualIndex];
+        }
+        return super.getCreationIndex(visualIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#getTextBounds(int)
+     */
+    public Rectangle getTextBounds(int index) {
+        return item.getTextBounds(index);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ViewerRow#getImageBounds(int)
+     */
+    public Rectangle getImageBounds(int index) {
+        return item.getImageBounds(index);
+    }
+
+    private bool hasColumns() {
+        return this.item.getParent().getColumnCount() !is 0;
+    }
+
+    private bool isValidOrderIndex(int currentIndex) {
+        return currentIndex < this.item.getParent().getColumnOrder().length;
+    }
+
+    int getWidth(int columnIndex) {
+        return item.getParent().getColumn(columnIndex).getWidth();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/Viewer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,424 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.jface.viewers.IInputSelectionProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.swt.events.HelpEvent;
+import org.eclipse.swt.events.HelpListener;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.util.SafeRunnable;
+
+import java.lang.all;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A viewer is a model-based adapter on a widget.
+ * <p>
+ * A viewer can be created as an adapter on a pre-existing control (e.g.,
+ * creating a <code>ListViewer</code> on an existing <code>List</code> control).
+ * All viewers also provide a convenience constructor for creating the control.
+ * </p>
+ * <p>
+ * Implementing a concrete viewer typically involves the following steps:
+ * <ul>
+ * <li>
+ * create SWT controls for viewer (in constructor) (optional)
+ * </li>
+ * <li>
+ * initialize SWT controls from input (inputChanged)
+ * </li>
+ * <li>
+ * define viewer-specific update methods
+ * </li>
+ * <li>
+ * support selections (<code>setSelection</code>, <code>getSelection</code>)
+ * </li>
+ * </ul>
+ * </p>
+ */
+public abstract class Viewer : IInputSelectionProvider {
+
+    /**
+     * List of selection change listeners (element type: <code>ISelectionChangedListener</code>).
+     *
+     * @see #fireSelectionChanged
+     */
+    private ListenerList selectionChangedListeners;
+
+    /**
+     * List of help request listeners (element type: <code>org.eclipse.swt.events.HelpListener</code>).
+     * Help request listeners.
+     *
+     * @see #handleHelpRequest
+     */
+    private ListenerList helpListeners;
+
+    /**
+     * The names of this viewer's properties.
+     * <code>null</code> if this viewer has no properties.
+     *
+     * @see #setData
+     */
+    private String[] keys;
+
+    /**
+     * The values of this viewer's properties.
+     * <code>null</code> if this viewer has no properties.
+     * This array parallels the value of the <code>keys</code> field.
+     *
+     * @see #setData
+     */
+    private Object[] values;
+
+    /**
+     * Remembers whether we've hooked the help listener on the control or not.
+     */
+    private bool helpHooked = false;
+
+    /**
+     * Help listener for the control, created lazily when client's first help listener is added.
+     */
+    private HelpListener helpListener = null;
+
+    /**
+     * Unique key for associating element data with widgets.
+     * @see org.eclipse.swt.widgets.Widget#setData(String, Object)
+     */
+    protected static const String WIDGET_DATA_KEY = "org.eclipse.jface.viewers.WIDGET_DATA";//$NON-NLS-1$
+
+    /**
+     * Creates a new viewer.
+     */
+    protected this() {
+        selectionChangedListeners = new ListenerList();
+        helpListeners = new ListenerList();
+    }
+
+    /**
+     * Adds a listener for help requests in this viewer.
+     * Has no effect if an identical listener is already registered.
+     *
+     * @param listener a help listener
+     */
+    public void addHelpListener(HelpListener listener) {
+        helpListeners.add(cast(Object)listener);
+        if (!helpHooked) {
+            Control control = getControl();
+            if (control !is null && !control.isDisposed()) {
+                if (this.helpListener is null) {
+                    this.helpListener = new class HelpListener {
+                        public void helpRequested(HelpEvent event) {
+                            handleHelpRequest(event);
+                        }
+                    };
+                }
+                control.addHelpListener(this.helpListener);
+                helpHooked = true;
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ISelectionProvider.
+     */
+    public void addSelectionChangedListener(ISelectionChangedListener listener) {
+        selectionChangedListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Notifies any help listeners that help has been requested.
+     * Only listeners registered at the time this method is called are notified.
+     *
+     * @param event a help event
+     *
+     * @see HelpListener#helpRequested(org.eclipse.swt.events.HelpEvent)
+     */
+    protected void fireHelpRequested(HelpEvent event) {
+        Object[] listeners = helpListeners.getListeners();
+        for (int i = 0; i < listeners.length; ++i) {
+            (cast(HelpListener) listeners[i]).helpRequested(event);
+        }
+    }
+
+    /**
+     * Notifies any selection changed listeners that the viewer's selection has changed.
+     * Only listeners registered at the time this method is called are notified.
+     *
+     * @param event a selection changed event
+     *
+     * @see ISelectionChangedListener#selectionChanged
+     */
+    protected void fireSelectionChanged(SelectionChangedEvent event) {
+        Object[] listeners = selectionChangedListeners.getListeners();
+        for (int i = 0; i < listeners.length; ++i) {
+            SafeRunnable.run(new class(event,cast(ISelectionChangedListener) listeners[i]) SafeRunnable {
+                ISelectionChangedListener l;
+                SelectionChangedEvent event_;
+                this(SelectionChangedEvent a,ISelectionChangedListener b){
+                    event_=a;
+                    l = b;
+                }
+                public void run() {
+                    l.selectionChanged(event_);
+                }
+            });
+        }
+    }
+
+    /**
+     * Returns the primary control associated with this viewer.
+     *
+     * @return the SWT control which displays this viewer's content
+     */
+    public abstract Control getControl();
+
+    /**
+     * Returns the value of the property with the given name,
+     * or <code>null</code> if the property is not found.
+     * <p>
+     * The default implementation performs a (linear) search of
+     * an internal table. Overriding this method is generally not
+     * required if the number of different keys is small. If a more
+     * efficient representation of a viewer's properties is required,
+     * override both <code>getData</code> and <code>setData</code>.
+     * </p>
+     *
+     * @param key the property name
+     * @return the property value, or <code>null</code> if
+     *    the property is not found
+     */
+    public Object getData(String key) {
+        Assert.isNotNull(key);
+        if (keys is null) {
+            return null;
+        }
+        for (int i = 0; i < keys.length; i++) {
+            if (keys[i].equals(key)) {
+                return values[i];
+            }
+        }
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * Copy-down of method declared on <code>IInputProvider</code>.
+     */
+    public abstract Object getInput();
+
+    /* (non-Javadoc)
+     * Copy-down of method declared on <code>ISelectionProvider</code>.
+     */
+    public abstract ISelection getSelection();
+
+    /**
+     * Handles a help request from the underlying SWT control.
+     * The default behavior is to fire a help request,
+     * with the event's data modified to hold this viewer.
+     * @param event the event
+     *
+     */
+    protected void handleHelpRequest(HelpEvent event) {
+        Object oldData = event.data;
+        event.data = this;
+        fireHelpRequested(event);
+        event.data = oldData;
+    }
+
+    /**
+     * Internal hook method called when the input to this viewer is
+     * initially set or subsequently changed.
+     * <p>
+     * The default implementation does nothing. Subclassers may override
+     * this method to do something when a viewer's input is set.
+     * A typical use is populate the viewer.
+     * </p>
+     *
+     * @param input the new input of this viewer, or <code>null</code> if none
+     * @param oldInput the old input element or <code>null</code> if there
+     *   was previously no input
+     */
+    protected void inputChanged(Object input, Object oldInput) {
+    }
+
+    /**
+     * Refreshes this viewer completely with information freshly obtained from this
+     * viewer's model.
+     */
+    public abstract void refresh();
+
+    /**
+     * Removes the given help listener from this viewer.
+     * Has no affect if an identical listener is not registered.
+     *
+     * @param listener a help listener
+     */
+    public void removeHelpListener(HelpListener listener) {
+        helpListeners.remove(cast(Object)listener);
+        if (helpListeners.size() is 0) {
+            Control control = getControl();
+            if (control !is null && !control.isDisposed()) {
+                control.removeHelpListener(this.helpListener);
+                helpHooked = false;
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ISelectionProvider.
+     */
+    public void removeSelectionChangedListener(
+            ISelectionChangedListener listener) {
+        selectionChangedListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Scrolls the viewer's control down by one item from the given
+     * display-relative coordinates.  Returns the newly revealed Item,
+     * or <code>null</code> if no scrolling occurred or if the viewer
+     * doesn't represent an item-based widget.
+     *
+     * @param x horizontal coordinate
+     * @param y vertical coordinate
+     * @return the item scrolled down to
+     */
+    public Item scrollDown(int x, int y) {
+        return null;
+    }
+
+    /**
+     * Scrolls the viewer's control up by one item from the given
+     * display-relative coordinates.  Returns the newly revealed Item,
+     * or <code>null</code> if no scrolling occurred or if the viewer
+     * doesn't represent an item-based widget.
+     *
+     * @param x horizontal coordinate
+     * @param y vertical coordinate
+     * @return the item scrolled up to
+     */
+    public Item scrollUp(int x, int y) {
+        return null;
+    }
+
+    /**
+     * Sets the value of the property with the given name to the
+     * given value, or to <code>null</code> if the property is to be
+     * removed. If this viewer has such a property, its value is
+     * replaced; otherwise a new property is added.
+     * <p>
+     * The default implementation records properties in an internal
+     * table which is searched linearly. Overriding this method is generally not
+     * required if the number of different keys is small. If a more
+     * efficient representation of a viewer's properties is required,
+     * override both <code>getData</code> and <code>setData</code>.
+     * </p>
+     *
+     * @param key the property name
+     * @param value the property value, or <code>null</code> if
+     *    the property is not found
+     */
+    public void setData(String key, Object value) {
+        Assert.isNotNull(key);
+        /* Remove the key/value pair */
+        if (value is null) {
+            if (keys is null) {
+                return;
+            }
+            int index = 0;
+            while (index < keys.length && !keys[index].equals(key)) {
+                index++;
+            }
+            if (index is keys.length) {
+                return;
+            }
+            if (keys.length is 1) {
+                keys = null;
+                values = null;
+            } else {
+                String[] newKeys = new String[keys.length - 1];
+                Object[] newValues = new Object[values.length - 1];
+                System.arraycopy(keys, 0, newKeys, 0, index);
+                System.arraycopy(keys, index + 1, newKeys, index,
+                        newKeys.length - index);
+                System.arraycopy(values, 0, newValues, 0, index);
+                System.arraycopy(values, index + 1, newValues, index,
+                        newValues.length - index);
+                keys = newKeys;
+                values = newValues;
+            }
+            return;
+        }
+
+        /* Add the key/value pair */
+        if (keys is null) {
+            keys = [ key ];
+            values = [ value ];
+            return;
+        }
+        for (int i = 0; i < keys.length; i++) {
+            if (keys[i].equals(key)) {
+                values[i] = value;
+                return;
+            }
+        }
+        String[] newKeys = new String[](keys.length + 1);
+        Object[] newValues = new Object[](values.length + 1);
+        System.arraycopy(keys, 0, newKeys, 0, keys.length);
+        System.arraycopy(values, 0, newValues, 0, values.length);
+        newKeys[keys.length] = key;
+        newValues[values.length] = value;
+        keys = newKeys;
+        values = newValues;
+    }
+
+    /**
+     * Sets or clears the input for this viewer.
+     *
+     * @param input the input of this viewer, or <code>null</code> if none
+     */
+    public abstract void setInput(Object input);
+
+    /**
+     * The viewer implementation of this <code>ISelectionProvider</code>
+     * method make the new selection for this viewer without making it visible.
+     * <p>
+     * This method is equivalent to <code>setSelection(selection,false)</code>.
+     * </p>
+     * <p>
+     * Note that some implementations may not be able to set the selection
+     * without also revealing it, for example (as of 3.3) TreeViewer.
+     * </p>
+     */
+    public void setSelection(ISelection selection) {
+        setSelection(selection, false);
+    }
+
+    /**
+     * Sets a new selection for this viewer and optionally makes it visible.
+     * <p>
+     * Subclasses must implement this method.
+     * </p>
+     *
+     * @param selection the new selection
+     * @param reveal <code>true</code> if the selection is to be made
+     *   visible, and <code>false</code> otherwise
+     */
+    public abstract void setSelection(ISelection selection, bool reveal);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerCell.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,428 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                               - fix in bug: 195908,198035,215069,215735,227421
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.ViewerCell;
+
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.jface.viewers.ViewerRow;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Widget;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The ViewerCell is the JFace representation of a cell entry in a ViewerRow.
+ *
+ * @since 3.3
+ *
+ */
+public class ViewerCell {
+    private int columnIndex;
+
+    private ViewerRow row;
+
+    private Object element;
+
+    /**
+     * Constant denoting the cell above current one (value is 1).
+     */
+    public static const int ABOVE = 1;
+
+    /**
+     * Constant denoting the cell below current one (value is 2).
+     */
+    public static const int BELOW = 1 << 1;
+
+    /**
+     * Constant denoting the cell to the left of the current one (value is 4).
+     */
+    public static const int LEFT = 1 << 2;
+
+    /**
+     * Constant denoting the cell to the right of the current one (value is 8).
+     */
+    public static const int RIGHT = 1 << 3;
+
+
+    /**
+     * Create a new instance of the receiver on the row.
+     *
+     * @param row
+     * @param columnIndex
+     */
+    this(ViewerRow row, int columnIndex, Object element) {
+        this.row = row;
+        this.columnIndex = columnIndex;
+        this.element = element;
+    }
+
+    /**
+     * Get the index of the cell.
+     *
+     * @return the index
+     */
+    public int getColumnIndex() {
+        return columnIndex;
+    }
+
+    /**
+     * Get the bounds of the cell.
+     *
+     * @return {@link Rectangle}
+     */
+    public Rectangle getBounds() {
+        return row.getBounds(columnIndex);
+    }
+
+    /**
+     * Get the element this row represents.
+     *
+     * @return {@link Object}
+     */
+    public Object getElement() {
+        if (element !is null) {
+            return element;
+        }
+
+        if (row !is null) {
+            return row.getElement();
+        }
+
+        return null;
+    }
+
+    /**
+     * Return the text for the cell.
+     *
+     * @return {@link String}
+     */
+    public String getText() {
+        return row.getText(columnIndex);
+    }
+
+    /**
+     * Return the Image for the cell.
+     *
+     * @return {@link Image} or <code>null</code>
+     */
+    public Image getImage() {
+        return row.getImage(columnIndex);
+    }
+
+    /**
+     * Set the background color of the cell.
+     *
+     * @param background
+     */
+    public void setBackground(Color background) {
+        row.setBackground(columnIndex, background);
+
+    }
+
+    /**
+     * Set the foreground color of the cell.
+     *
+     * @param foreground
+     */
+    public void setForeground(Color foreground) {
+        row.setForeground(columnIndex, foreground);
+
+    }
+
+    /**
+     * Set the font of the cell.
+     *
+     * @param font
+     */
+    public void setFont(Font font) {
+        row.setFont(columnIndex, font);
+
+    }
+
+    /**
+     * Set the text for the cell.
+     *
+     * @param text
+     */
+    public void setText(String text) {
+        row.setText(columnIndex, text);
+
+    }
+
+    /**
+     * Set the Image for the cell.
+     *
+     * @param image
+     */
+    public void setImage(Image image) {
+        row.setImage(columnIndex, image);
+
+    }
+
+    /**
+     * Set the style ranges to be applied on the text label
+     * Note: Requires {@link StyledCellLabelProvider} with owner draw enabled.
+     *
+     * @param styleRanges the styled ranges
+     *
+     * @since 3.4
+     */
+    public void setStyleRanges(StyleRange[] styleRanges) {
+        row.setStyleRanges(columnIndex, styleRanges);
+    }
+
+
+    /**
+     * Returns the style ranges to be applied on the text label or <code>null</code> if no
+     * style ranges have been set.
+     *
+     * @return styleRanges the styled ranges
+     *
+     * @since 3.4
+     */
+    public StyleRange[] getStyleRanges() {
+        return row.getStyleRanges(columnIndex);
+    }
+
+    /**
+     * Set the columnIndex.
+     *
+     * @param column
+     */
+    void setColumn(int column) {
+        columnIndex = column;
+
+    }
+
+    /**
+     * Set the row to rowItem and the columnIndex to column.
+     *
+     * @param rowItem
+     * @param column
+     */
+    void update(ViewerRow rowItem, int column, Object element) {
+        row = rowItem;
+        columnIndex = column;
+        this.element = element;
+    }
+
+    /**
+     * Return the item for the receiver.
+     *
+     * @return {@link Item}
+     */
+    public Widget getItem() {
+        return row.getItem();
+    }
+
+    /**
+     * Get the control for this cell.
+     *
+     * @return {@link Control}
+     */
+    public Control getControl() {
+        return row.getControl();
+    }
+
+    /**
+     * Get the current index. This can be different from the original index when
+     * columns are reordered
+     *
+     * @return the current index (as shown in the UI)
+     * @since 3.4
+     */
+    public int getVisualIndex() {
+        return row.getVisualIndex_package(getColumnIndex());
+    }
+
+    /**
+     * Returns the specified neighbor of this cell, or <code>null</code> if no
+     * neighbor exists in the given direction. Direction constants can be
+     * combined by bitwise OR; for example, this method will return the cell to
+     * the upper-left of the current cell by passing {@link #ABOVE} |
+     * {@link #LEFT}. If <code>sameLevel</code> is <code>true</code>, only
+     * cells in sibling rows (under the same parent) will be considered.
+     *
+     * @param directionMask
+     *            the direction mask used to identify the requested neighbor
+     *            cell
+     * @param sameLevel
+     *            if <code>true</code>, only consider cells from sibling rows
+     * @return the requested neighbor cell, or <code>null</code> if not found
+     */
+    public ViewerCell getNeighbor(int directionMask, bool sameLevel) {
+        ViewerRow row;
+
+        if ((directionMask & ABOVE) is ABOVE) {
+            row = this.row.getNeighbor(ViewerRow.ABOVE, sameLevel);
+        } else if ((directionMask & BELOW) is BELOW) {
+            row = this.row.getNeighbor(ViewerRow.BELOW, sameLevel);
+        } else {
+            row = this.row;
+        }
+
+        if (row !is null) {
+            int columnIndex;
+            columnIndex = getVisualIndex();
+
+            int modifier = 0;
+
+            if ((directionMask & LEFT) is LEFT) {
+                modifier = -1;
+            } else if ((directionMask & RIGHT) is RIGHT) {
+                modifier = 1;
+            }
+
+            columnIndex += modifier;
+
+            if (columnIndex >= 0 && columnIndex < row.getColumnCount()) {
+                ViewerCell cell = row.getCellAtVisualIndex(columnIndex);
+                if( cell !is null ) {
+                    while( cell !is null && columnIndex < row.getColumnCount() - 1  && columnIndex > 0 ) {
+                        if( cell.isVisible() ) {
+                            break;
+                        }
+
+                        columnIndex += modifier;
+                        cell = row.getCellAtVisualIndex(columnIndex);
+                        if( cell is null ) {
+                            break;
+                        }
+                    }
+                }
+
+                return cell;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @return the row
+     */
+    public ViewerRow getViewerRow() {
+        return row;
+    }
+
+    /**
+     * The location and bounds of the area where the text is drawn depends on
+     * various things (image displayed, control with SWT.CHECK)
+     *
+     * @return The bounds of the of the text area. May return <code>null</code>
+     *         if the underlying widget implementation doesn't provide this
+     *         information
+     * @since 3.4
+     */
+    public Rectangle getTextBounds() {
+        return row.getTextBounds(columnIndex);
+    }
+
+    /**
+     * Returns the location and bounds of the area where the image is drawn
+     *
+     * @return The bounds of the of the image area. May return <code>null</code>
+     *         if the underlying widget implementation doesn't provide this
+     *         information
+     * @since 3.4
+     */
+    public Rectangle getImageBounds() {
+        return row.getImageBounds(columnIndex);
+    }
+
+    /**
+     * Gets the foreground color of the cell.
+     *
+     * @return the foreground of the cell or <code>null</code> for the default foreground
+     *
+     * @since 3.4
+     */
+    public Color getForeground() {
+        return row.getForeground(columnIndex);
+    }
+
+    /**
+     * Gets the background color of the cell.
+     *
+     * @return the background of the cell or <code>null</code> for the default background
+     *
+     * @since 3.4
+     */
+    public Color getBackground() {
+        return row.getBackground(columnIndex);
+    }
+
+    /**
+     * Gets the font of the cell.
+     *
+     * @return the font of the cell or <code>null</code> for the default font
+     *
+     * @since 3.4
+     */
+    public Font getFont() {
+        return row.getFont(columnIndex);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#hashCode()
+     */
+    public override hash_t toHash() {
+        const int prime = 31;
+        int result = 1;
+        result = prime * result + columnIndex;
+        result = prime * result + ((row is null) ? 0 : row.toHash());
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public override int opEquals(Object obj) {
+        if (this is obj)
+            return true;
+        if (obj is null)
+            return false;
+        if (this.classinfo !is obj.classinfo )
+            return false;
+        ViewerCell other = cast(ViewerCell) obj;
+        if (columnIndex !is other.columnIndex)
+            return false;
+        if (row is null) {
+            if (other.row !is null)
+                return false;
+        } else if (!row.opEquals(other.row))
+            return false;
+        return true;
+    }
+    
+    private int getWidth() {
+        return row.getWidth(columnIndex);
+    }
+    
+    private bool isVisible() {
+        return getWidth() > 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerColumn.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,200 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *     Tom Shindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                                fix for bug 163317, 201905
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.ViewerColumn;
+
+import org.eclipse.jface.viewers.CellLabelProvider;
+import org.eclipse.jface.viewers.EditingSupport;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.LabelProviderChangedEvent;
+
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.jface.util.Policy;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Instances of this class represent a column of a {@link ColumnViewer}. Label
+ * providers and editing support can be configured for each column separately.
+ * Concrete subclasses of {@link ColumnViewer} should implement a matching
+ * concrete subclass of {@link ViewerColumn}.
+ *
+ * @since 3.3
+ *
+ */
+public abstract class ViewerColumn {
+
+    private CellLabelProvider labelProvider;
+
+    static String COLUMN_VIEWER_KEY = Policy.JFACE ~ ".columnViewer";//$NON-NLS-1$
+
+    private EditingSupport editingSupport;
+
+    private ILabelProviderListener listener;
+
+    private bool listenerRegistered = false;
+
+    private ColumnViewer viewer;
+
+    /**
+     * Create a new instance of the receiver at columnIndex.
+     *
+     * @param viewer
+     *            the viewer the column is part of
+     * @param columnOwner
+     *            the widget owning the viewer in case the widget has no columns
+     *            this could be the widget itself
+     */
+    protected this(ColumnViewer viewer, Widget columnOwner) {
+        this.viewer = viewer;
+        columnOwner.setData(ViewerColumn.COLUMN_VIEWER_KEY, this);
+        this.listener = new class(viewer) ILabelProviderListener {
+            ColumnViewer viewer_;
+            this(ColumnViewer a){
+                viewer_= a;
+            }
+            public void labelProviderChanged(LabelProviderChangedEvent event) {
+                viewer_.handleLabelProviderChanged_package(event);
+            }
+
+        };
+        columnOwner.addDisposeListener(new class(viewer) DisposeListener {
+            ColumnViewer viewer_;
+            this(ColumnViewer a){
+                viewer_= a;
+            }
+            public void widgetDisposed(DisposeEvent e) {
+                handleDispose(viewer_);
+            }
+        });
+    }
+
+    /**
+     * Return the label provider for the receiver.
+     *
+     * @return ViewerLabelProvider
+     */
+    /* package */CellLabelProvider getLabelProvider() {
+        return labelProvider;
+    }
+
+    /**
+     * Set the label provider for the column. Subclasses may extend but must
+     * call the super implementation.
+     *
+     * @param labelProvider
+     *            the new {@link CellLabelProvider}
+     */
+    public void setLabelProvider(CellLabelProvider labelProvider) {
+        setLabelProvider(labelProvider, true);
+    }
+
+    /**
+     * @param labelProvider
+     * @param registerListener
+     */
+    /* package */void setLabelProvider(CellLabelProvider labelProvider,
+            bool registerListener) {
+        if (listenerRegistered && this.labelProvider !is null) {
+            this.labelProvider.removeListener(listener);
+            listenerRegistered = false;
+            if (registerListener) {
+                this.labelProvider.dispose(viewer, this);
+            }
+        }
+
+        this.labelProvider = labelProvider;
+
+        if (registerListener) {
+            this.labelProvider.initialize_package(viewer, this);
+            this.labelProvider.addListener(listener);
+            listenerRegistered = true;
+        }
+    }
+
+    /**
+     * Return the editing support for the receiver.
+     *
+     * @return {@link EditingSupport}
+     */
+    /* package */EditingSupport getEditingSupport() {
+        return editingSupport;
+    }
+
+    /**
+     * Set the editing support. Subclasses may extend but must call the super
+     * implementation.
+     * <p>
+     * Users setting up an editable {@link TreeViewer} or {@link TableViewer} with more than 1 column <b>have</b>
+     * to pass the SWT.FULL_SELECTION style bit when creating the viewer
+     * </p>
+     * @param editingSupport
+     *            The {@link EditingSupport} to set.
+     */
+    public void setEditingSupport(EditingSupport editingSupport) {
+        this.editingSupport = editingSupport;
+    }
+
+    /**
+     * Refresh the cell for the given columnIndex. <strong>NOTE:</strong>the
+     * {@link ViewerCell} provided to this method is no longer valid after this
+     * method returns. Do not cache the cell for future use.
+     *
+     * @param cell
+     *            {@link ViewerCell}
+     */
+    /* package */void refresh(ViewerCell cell) {
+        getLabelProvider().update(cell);
+    }
+
+    /**
+     * Disposes of the label provider (if set), unregisters the listener and
+     * nulls the references to the label provider and editing support. This
+     * method is called when the underlying widget is disposed. Subclasses may
+     * extend but must call the super implementation.
+     */
+    protected void handleDispose() {
+        bool disposeLabelProvider = listenerRegistered;
+        CellLabelProvider cellLabelProvider = labelProvider;
+        setLabelProvider(null, false);
+        if (disposeLabelProvider) {
+            cellLabelProvider.dispose(viewer, this);
+        }
+        editingSupport = null;
+        listener = null;
+        viewer = null;
+    }
+
+    private void handleDispose(ColumnViewer viewer) {
+        handleDispose();
+        viewer.clearLegacyEditingSetup();
+    }
+
+    /**
+     * Returns the viewer of this viewer column.
+     *
+     * @return Returns the viewer.
+     *
+     * @since 3.4
+     */
+    public ColumnViewer getViewer() {
+        return viewer;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerComparator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,200 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.viewers.ViewerComparator;
+
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ContentViewer;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+
+// import java.util.Arrays;
+// import java.util.Comparator;
+
+import org.eclipse.jface.util.Policy;
+
+import java.lang.all;
+import java.util.Arrays;
+import tango.core.Array;
+
+/**
+ * A viewer comparator is used by a {@link StructuredViewer} to
+ * reorder the elements provided by its content provider.
+ * <p>
+ * The default <code>compare</code> method compares elements using two steps.
+ * The first step uses the values returned from <code>category</code>.
+ * By default, all elements are in the same category.
+ * The second level is based on a case insensitive compare of the strings obtained
+ * from the content viewer's label provider via <code>ILabelProvider.getText</code>.
+ * </p>
+ * <p>
+ * Subclasses may implement the <code>isSorterProperty</code> method;
+ * they may reimplement the <code>category</code> method to provide
+ * categorization; and they may override the <code>compare</code> methods
+ * to provide a totally different way of sorting elements.
+ * </p>
+ * @see IStructuredContentProvider
+ * @see StructuredViewer
+ *
+ * @since 3.2
+ */
+public class ViewerComparator {
+    /**
+     * The comparator to use to sort a viewer's contents.
+     */
+    private Comparator comparator;
+
+    /**
+     * Creates a new {@link ViewerComparator}, which uses the default comparator
+     * to sort strings.
+     *
+     */
+    public this(){
+        this(null);
+    }
+
+    /**
+     * Creates a new {@link ViewerComparator}, which uses the given comparator
+     * to sort strings.
+     *
+     * @param comparator
+     */
+    public this(Comparator comparator){
+        this.comparator = comparator;
+    }
+
+    /**
+     * Returns the comparator used to sort strings.
+     *
+     * @return the comparator used to sort strings
+     */
+    protected Comparator getComparator() {
+        if (comparator is null){
+            comparator = Policy.getComparator();
+        }
+        return comparator;
+    }
+
+    /**
+     * Returns the category of the given element. The category is a
+     * number used to allocate elements to bins; the bins are arranged
+     * in ascending numeric order. The elements within a bin are arranged
+     * via a second level sort criterion.
+     * <p>
+     * The default implementation of this framework method returns
+     * <code>0</code>. Subclasses may reimplement this method to provide
+     * non-trivial categorization.
+     * </p>
+     *
+     * @param element the element
+     * @return the category
+     */
+    public int category(Object element) {
+        return 0;
+    }
+
+    /**
+     * Returns a negative, zero, or positive number depending on whether
+     * the first element is less than, equal to, or greater than
+     * the second element.
+     * <p>
+     * The default implementation of this method is based on
+     * comparing the elements' categories as computed by the <code>category</code>
+     * framework method. Elements within the same category are further
+     * subjected to a case insensitive compare of their label strings, either
+     * as computed by the content viewer's label provider, or their
+     * <code>toString</code> values in other cases. Subclasses may override.
+     * </p>
+     *
+     * @param viewer the viewer
+     * @param e1 the first element
+     * @param e2 the second element
+     * @return a negative number if the first element is less  than the
+     *  second element; the value <code>0</code> if the first element is
+     *  equal to the second element; and a positive number if the first
+     *  element is greater than the second element
+     */
+    public int compare(Viewer viewer, Object e1, Object e2) {
+        int cat1 = category(e1);
+        int cat2 = category(e2);
+
+        if (cat1 !is cat2) {
+            return cat1 - cat2;
+        }
+
+        String name1;
+        String name2;
+
+        if (viewer is null || !(cast(ContentViewer)viewer )) {
+            name1 = e1.toString();
+            name2 = e2.toString();
+        } else {
+            IBaseLabelProvider prov = (cast(ContentViewer) viewer)
+                    .getLabelProvider();
+            if (auto lprov = cast(ILabelProvider)prov ) {
+                name1 = lprov.getText(e1);
+                name2 = lprov.getText(e2);
+            } else {
+                name1 = e1.toString();
+                name2 = e2.toString();
+            }
+        }
+        if (name1 is null) {
+            name1 = "";//$NON-NLS-1$
+        }
+        if (name2 is null) {
+            name2 = "";//$NON-NLS-1$
+        }
+
+        // use the comparator to compare the strings
+        return getComparator().compare( new ArrayWrapperString(name1), new ArrayWrapperString(name2));
+    }
+
+    /**
+     * Returns whether this viewer sorter would be affected
+     * by a change to the given property of the given element.
+     * <p>
+     * The default implementation of this method returns <code>false</code>.
+     * Subclasses may reimplement.
+     * </p>
+     *
+     * @param element the element
+     * @param property the property
+     * @return <code>true</code> if the sorting would be affected,
+     *    and <code>false</code> if it would be unaffected
+     */
+    public bool isSorterProperty(Object element, String property) {
+        return false;
+    }
+
+    /**
+     * Sorts the given elements in-place, modifying the given array.
+     * <p>
+     * The default implementation of this method uses the
+     * java.util.Arrays#sort algorithm on the given array,
+     * calling <code>compare</code> to compare elements.
+     * </p>
+     * <p>
+     * Subclasses may reimplement this method to provide a more optimized implementation.
+     * </p>
+     *
+     * @param viewer the viewer
+     * @param elements the elements to sort
+     */
+    public void sort(Viewer viewer, Object[] elements) {
+        tango.core.Array.sort(elements, delegate int(Object a, Object b) {
+                return compare(viewer, a, b) < 0;
+            }
+        );
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerDropAdapter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,496 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.ViewerDropAdapter;
+
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DropTargetAdapter;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.TransferData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.TreeItem;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * This adapter class provides generic drag-and-drop support for a viewer.
+ * <p>
+ * Subclasses must implement the following methods:
+ * <ul>
+ *   <li><code>validateDrop</code> - identifies valid drop targets in viewer</li>
+ *   <li><code>performDrop</code> - carries out a drop into a viewer</li>
+ * </ul>
+ * The <code>setFeedbackEnabled</code> method can be called to turn on and off
+ * visual insertion feedback (on by default).
+ * </p>
+ */
+public abstract class ViewerDropAdapter : DropTargetAdapter {
+
+    /**
+     * Constant describing the position of the cursor relative
+     * to the target object.  This means the mouse is positioned
+     * slightly before the target.
+     * @see #getCurrentLocation()
+     */
+    public static const int LOCATION_BEFORE = 1;
+
+    /**
+     * Constant describing the position of the cursor relative
+     * to the target object.  This means the mouse is positioned
+     * slightly after the target.
+     * @see #getCurrentLocation()
+     */
+    public static const int LOCATION_AFTER = 2;
+
+    /**
+     * Constant describing the position of the cursor relative
+     * to the target object.  This means the mouse is positioned
+     * directly on the target.
+     * @see #getCurrentLocation()
+     */
+    public static const int LOCATION_ON = 3;
+
+    /**
+     * Constant describing the position of the cursor relative
+     * to the target object.  This means the mouse is not positioned
+     * over or near any valid target.
+     * @see #getCurrentLocation()
+     */
+    public static const int LOCATION_NONE = 4;
+
+    /**
+     * The viewer to which this drop support has been added.
+     */
+    private Viewer viewer;
+
+    /**
+     * The current operation.
+     */
+    private int currentOperation = DND.DROP_NONE;
+
+    /**
+     * The last valid operation.
+     */
+    private int lastValidOperation = DND.DROP_NONE;
+
+    /**
+     * The data item currently under the mouse.
+     */
+    private Object currentTarget;
+
+    /**
+     * Information about the position of the mouse relative to the
+     * target (before, on, or after the target.  Location is one of
+     * the <code>LOCATION_* </code> constants defined in this type.
+     */
+    private int currentLocation;
+
+    /**
+     * A flag that allows adapter users to turn the insertion
+     * feedback on or off. Default is <code>true</code>.
+     */
+    private bool feedbackEnabled = true;
+
+    /**
+     * A flag that allows adapter users to turn auto scrolling
+     * on or off. Default is <code>true</code>.
+     */
+    private bool scrollEnabled = true;
+
+    /**
+     * A flag that allows adapter users to turn auto
+     * expanding on or off. Default is <code>true</code>.
+     */
+    private bool expandEnabled = true;
+    
+    /**
+     * A flag that allows adapter users to turn selection feedback
+     *  on or off. Default is <code>true</code>.
+     */
+    private bool selectFeedbackEnabled = true;
+
+    /**
+     * Creates a new drop adapter for the given viewer.
+     *
+     * @param viewer the viewer
+     */
+    protected this(Viewer viewer) {
+        this.viewer = viewer;
+    }
+
+    /**
+     * Returns the position of the given event's coordinates relative to its target.
+     * The position is determined to be before, after, or on the item, based on
+     * some threshold value.
+     *
+     * @param event the event
+     * @return one of the <code>LOCATION_* </code>constants defined in this class
+     */
+    protected int determineLocation(DropTargetEvent event) {
+        if (!( cast(Item)event.item )) {
+            return LOCATION_NONE;
+        }
+        Item item = cast(Item) event.item;
+        Point coordinates = new Point(event.x, event.y);
+        coordinates = viewer.getControl().toControl(coordinates);
+        if (item !is null) {
+            Rectangle bounds = getBounds(item);
+            if (bounds is null) {
+                return LOCATION_NONE;
+            }
+            if ((coordinates.y - bounds.y) < 5) {
+                return LOCATION_BEFORE;
+            }
+            if ((bounds.y + bounds.height - coordinates.y) < 5) {
+                return LOCATION_AFTER;
+            }
+        }
+        return LOCATION_ON;
+    }
+
+    /**
+     * Returns the target item of the given drop event.
+     *
+     * @param event the event
+     * @return The target of the drop, may be <code>null</code>.
+     */
+    protected Object determineTarget(DropTargetEvent event) {
+        return event.item is null ? null : event.item.getData();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on DropTargetAdapter.
+     * The mouse has moved over the drop target.  If the
+     * target item has changed, notify the action and check
+     * that it is still enabled.
+     */
+    private void doDropValidation(DropTargetEvent event) {
+        //update last valid operation
+        if (event.detail !is DND.DROP_NONE) {
+            lastValidOperation = event.detail;
+        }
+        //valid drop and set event detail accordingly
+        if (validateDrop(currentTarget, event.detail, event.currentDataType)) {
+            currentOperation = lastValidOperation;
+        } else {
+            currentOperation = DND.DROP_NONE;
+        }
+        event.detail = currentOperation;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on DropTargetAdapter.
+     * The drag has entered this widget's region.  See
+     * if the drop should be allowed.
+     */
+    public override void dragEnter(DropTargetEvent event) {
+        currentTarget = determineTarget(event);
+        doDropValidation(event);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on DropTargetAdapter.
+     * The drop operation has changed, see if the action
+     * should still be enabled.
+     */
+    public override void dragOperationChanged(DropTargetEvent event) {
+        currentTarget = determineTarget(event);
+        doDropValidation(event);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on DropTargetAdapter.
+     * The mouse has moved over the drop target.  If the
+     * target item has changed, notify the action and check
+     * that it is still enabled.
+     */
+    public override void dragOver(DropTargetEvent event) {
+        //use newly revealed item as target if scrolling occurs
+        Object target = determineTarget(event);
+
+        //set the location feedback
+        int oldLocation = currentLocation;
+        currentLocation = determineLocation(event);
+        setFeedback(event, currentLocation);
+
+        //see if anything has really changed before doing validation.
+        if (target !is currentTarget || currentLocation !is oldLocation) {
+            currentTarget = target;
+            doDropValidation(event);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on DropTargetAdapter.
+     * The user has dropped something on the desktop viewer.
+     */
+    public override void drop(DropTargetEvent event) {
+        currentLocation = determineLocation(event);
+
+        //perform the drop behavior
+        if (!performDrop(event.data)) {
+            event.detail = DND.DROP_NONE;
+        }
+        currentOperation = event.detail;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on DropTargetAdapter.
+     * Last chance for the action to disable itself
+     */
+    public override void dropAccept(DropTargetEvent event) {
+        if (!validateDrop(currentTarget, event.detail, event.currentDataType)) {
+            event.detail = DND.DROP_NONE;
+        }
+    }
+
+    /**
+     * Returns the bounds of the given SWT tree or table item.
+     *
+     * @param item the SWT Item
+     * @return the bounds, or <code>null</code> if it is not a known type of item
+     */
+    protected Rectangle getBounds(Item item) {
+        if ( auto i = cast(TreeItem)item ) {
+            return i.getBounds();
+        }
+        if (auto i = cast(TableItem)item  ) {
+            return i.getBounds(0);
+        }
+        return null;
+    }
+
+    /**
+     * Returns a constant describing the position of the mouse relative to the
+     * target (before, on, or after the target.
+     *
+     * @return one of the <code>LOCATION_* </code> constants defined in this type
+     */
+    protected int getCurrentLocation() {
+        return currentLocation;
+    }
+
+    /**
+     * Returns the current operation.
+     *
+     * @return a <code>DROP_*</code> constant from class <code>DND</code>
+     *
+     * @see DND#DROP_COPY
+     * @see DND#DROP_MOVE
+     * @see DND#DROP_LINK
+     * @see DND#DROP_NONE
+     */
+    protected int getCurrentOperation() {
+        return currentOperation;
+    }
+
+    /**
+     * Returns the target object currently under the mouse.
+     *
+     * @return the current target object
+     */
+    protected Object getCurrentTarget() {
+        return currentTarget;
+    }
+
+    /**
+     * Returns whether visible insertion feedback should be presented to the user.
+     * <p>
+     * Typical insertion feedback is the horizontal insertion bars that appear
+     * between adjacent items while dragging.
+     * </p>
+     *
+     * @return <code>true</code> if visual feedback is desired, and <code>false</code> if not
+     */
+    public bool getFeedbackEnabled() {
+        return feedbackEnabled;
+    }
+
+    /**
+     * Returns the object currently selected by the viewer.
+     *
+     * @return the selected object, or <code>null</code> if either no object or
+     *   multiple objects are selected
+     */
+    protected Object getSelectedObject() {
+        ISelection selection = viewer.getSelection();
+        if ( null !is cast(IStructuredSelection) selection && !selection.isEmpty()) {
+            IStructuredSelection structured = cast(IStructuredSelection) selection;
+            return structured.getFirstElement();
+        }
+        return null;
+    }
+
+    /**
+     * @return the viewer to which this drop support has been added.
+     */
+    protected Viewer getViewer() {
+        return viewer;
+    }
+
+    /**
+     * @deprecated this method should not be used. Exception handling has been
+     *  removed from DropTargetAdapter methods overridden by this class.
+     * Handles any exception that occurs during callback, including
+     * rethrowing behavior.
+     * <p>
+     * [Issue: Implementation prints stack trace and eats exception to avoid
+     *  crashing VA/J.
+     *  Consider conditionalizing the implementation to do one thing in VAJ
+     *  and something more reasonable in other operating environments.
+     * ]
+     * </p>
+     *
+     * @param exception the exception
+     * @param event the event
+     */
+    protected void handleException(Exception exception, DropTargetEvent event) {
+        // Currently we never rethrow because VA/Java crashes if an SWT
+        // callback throws anything. Generally catching Throwable is bad, but in
+        // this cases it's better than hanging the image.
+        ExceptionPrintStackTrace( exception );
+        event.detail = DND.DROP_NONE;
+    }
+
+    /**
+     * Performs any work associated with the drop.
+     * <p>
+     * Subclasses must implement this method to provide drop behavior.
+     * </p>
+     *
+     * @param data the drop data
+     * @return <code>true</code> if the drop was successful, and
+     *   <code>false</code> otherwise
+     */
+    public abstract bool performDrop(Object data);
+
+    /* (non-Javadoc)
+     * Method declared on DropTargetAdapter.
+     * The mouse has moved over the drop target.  If the
+     * target item has changed, notify the action and check
+     * that it is still enabled.
+     */
+    private void setFeedback(DropTargetEvent event, int location) {
+        if (feedbackEnabled) {
+            switch (location) {
+            case LOCATION_BEFORE:
+                event.feedback = DND.FEEDBACK_INSERT_BEFORE;
+                break;
+            case LOCATION_AFTER:
+                event.feedback = DND.FEEDBACK_INSERT_AFTER;
+                break;
+            case LOCATION_ON:
+            default:
+                event.feedback = DND.FEEDBACK_SELECT;
+                break;
+            }
+        }
+
+         // Explicitly inhibit SELECT feedback if desired
+        if (!selectFeedbackEnabled) {
+            event.feedback &= ~DND.FEEDBACK_SELECT;
+        }
+
+        if (expandEnabled) {
+            event.feedback |= DND.FEEDBACK_EXPAND;
+        }
+        if (scrollEnabled) {
+            event.feedback |= DND.FEEDBACK_SCROLL;
+        }
+    }
+
+    /**
+     * Sets whether visible insertion feedback should be presented to the user.
+     * <p>
+     * Typical insertion feedback is the horizontal insertion bars that appear
+     * between adjacent items while dragging.
+     * </p>
+     *
+     * @param value
+     *            <code>true</code> if visual feedback is desired, and
+     *            <code>false</code> if not
+     */
+    public void setFeedbackEnabled(bool value) {
+        feedbackEnabled = value;
+    }
+
+    /**
+     * Sets whether selection feedback should be provided during dragging.
+     *
+     * @param value <code>true</code> if selection feedback is desired, and
+     *   <code>false</code> if not
+     *
+     * @since 3.2
+     */
+    public void setSelectionFeedbackEnabled(bool value) {
+        selectFeedbackEnabled = value;
+    }
+
+    /**
+     * Sets whether auto scrolling and expanding should be provided during dragging.
+     *
+     * @param value <code>true</code> if scrolling and expanding is desired, and
+     *   <code>false</code> if not
+     * @since 2.0
+     */
+    public void setScrollExpandEnabled(bool value) {
+        expandEnabled = value;
+        scrollEnabled = value;
+    }
+
+    /**
+     * Sets whether auto expanding should be provided during dragging.
+     *
+     * @param value <code>true</code> if expanding is desired, and
+     *   <code>false</code> if not
+     * @since 3.4
+     */
+    public void setExpandEnabled(bool value) {
+        expandEnabled = value;
+    }
+    
+    /**
+     * Sets whether auto scrolling should be provided during dragging.
+     *
+     * @param value <code>true</code> if scrolling is desired, and
+     *   <code>false</code> if not
+     * @since 3.4
+     */
+    public void setScrollEnabled(bool value) {
+        scrollEnabled = value;
+    }
+
+    /**
+     * Validates dropping on the given object. This method is called whenever some
+     * aspect of the drop operation changes.
+     * <p>
+     * Subclasses must implement this method to define which drops make sense.
+     * </p>
+     *
+     * @param target the object that the mouse is currently hovering over, or
+     *   <code>null</code> if the mouse is hovering over empty space
+     * @param operation the current drag operation (copy, move, etc.)
+     * @param transferType the current transfer type
+     * @return <code>true</code> if the drop is valid, and <code>false</code>
+     *   otherwise
+     */
+    public abstract bool validateDrop(Object target, int operation,
+            TransferData transferType);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerFilter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ViewerFilter;
+
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.TreePath;
+
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * A viewer filter is used by a structured viewer to
+ * extract a subset of elements provided by its content provider.
+ * <p>
+ * Subclasses must implement the <code>select</code> method
+ * and may implement the <code>isFilterProperty</code> method.
+ * </p>
+ * @see IStructuredContentProvider
+ * @see StructuredViewer
+ */
+public abstract class ViewerFilter {
+    /**
+     * Creates a new viewer filter.
+     */
+    protected this() {
+    }
+
+    /**
+     * Filters the given elements for the given viewer.
+     * The input array is not modified.
+     * <p>
+     * The default implementation of this method calls
+     * <code>select</code> on each element in the array,
+     * and returns only those elements for which <code>select</code>
+     * returns <code>true</code>.
+     * </p>
+     * @param viewer the viewer
+     * @param parent the parent element
+     * @param elements the elements to filter
+     * @return the filtered elements
+     */
+    public Object[] filter(Viewer viewer, Object parent, Object[] elements) {
+        int size = elements.length;
+        ArrayList out_ = new ArrayList(size);
+        for (int i = 0; i < size; ++i) {
+            Object element = elements[i];
+            if (select(viewer, parent, element)) {
+                out_.add(element);
+            }
+        }
+        return out_.toArray();
+    }
+
+    /**
+     * Filters the given elements for the given viewer.
+     * The input array is not modified.
+     * <p>
+     * The default implementation of this method calls
+     * {@link #filter(Viewer, Object, Object[])} with the
+     * parent from the path. Subclasses may override
+     * </p>
+     * @param viewer the viewer
+     * @param parentPath the path of the parent element
+     * @param elements the elements to filter
+     * @return the filtered elements
+     * @since 3.2
+     */
+    public Object[] filter(Viewer viewer, TreePath parentPath, Object[] elements) {
+        return filter(viewer, parentPath.getLastSegment(), elements);
+    }
+
+    /**
+     * Returns whether this viewer filter would be affected
+     * by a change to the given property of the given element.
+     * <p>
+     * The default implementation of this method returns <code>false</code>.
+     * Subclasses should reimplement.
+     * </p>
+     *
+     * @param element the element
+     * @param property the property
+     * @return <code>true</code> if the filtering would be affected,
+     *    and <code>false</code> if it would be unaffected
+     */
+    public bool isFilterProperty(Object element, String property) {
+        return false;
+    }
+
+    /**
+     * Returns whether the given element makes it through this filter.
+     *
+     * @param viewer the viewer
+     * @param parentElement the parent element
+     * @param element the element
+     * @return <code>true</code> if element is included in the
+     *   filtered set, and <code>false</code> if excluded
+     */
+    public abstract bool select(Viewer viewer, Object parentElement,
+            Object element);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerLabel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,419 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ *     Tom Schindl <tom.shindl@bestsolution.at> - tooltip support
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ViewerLabel;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The ViewerLabel is the class that is passed to a viewer to handle updates of
+ * labels. It keeps track of both original and updates text.
+ *
+ * @see IViewerLabelProvider
+ * @since 3.0
+ */
+public class ViewerLabel {
+
+    // New values for the receiver. Null if nothing has been set.
+    private String newText = null;
+
+    private Image newImage = null;
+
+    private bool imageUpdated = false;
+
+    private bool textUpdated = false;
+
+    private Color background = null;
+
+    private Color foreground = null;
+
+    private Font font = null;
+
+    // The initial values for the receiver.
+    private String startText;
+
+    private Image startImage;
+
+    private bool hasPendingDecorations_;
+
+    private String tooltipText;
+
+    private Color tooltipForegroundColor;
+
+    private Color tooltipBackgroundColor;
+
+    private Point tooltipShift;
+
+    /**
+     * Create a new instance of the receiver with the supplied initial text and
+     * image.
+     *
+     * @param initialText
+     * @param initialImage
+     */
+    public this(String initialText, Image initialImage) {
+        startText = initialText;
+        startImage = initialImage;
+    }
+
+    /**
+     * Get the image for the receiver. If the new image has been set return it,
+     * otherwise return the starting image.
+     *
+     * @return Returns the image.
+     */
+    public final Image getImage() {
+        if (imageUpdated) {
+            return newImage;
+        }
+        return startImage;
+    }
+
+    /**
+     * Set the image for the receiver.
+     *
+     * @param image
+     *            The image to set.
+     */
+    public final void setImage(Image image) {
+        imageUpdated = true;
+        newImage = image;
+    }
+
+    /**
+     * Get the text for the receiver. If the new text has been set return it,
+     * otherwise return the starting text.
+     *
+     * @return String or <code>null</code> if there was no initial text and
+     *         nothing was updated.
+     */
+    public final String getText() {
+        if (textUpdated) {
+            return newText;
+        }
+        return startText;
+    }
+
+    /**
+     * Set the text for the receiver.
+     *
+     * @param text
+     *            String The label to set. This value should not be
+     *            <code>null</code>.
+     * @see #hasNewText()
+     */
+    public final void setText(String text) {
+        newText = text;
+        textUpdated = true;
+    }
+
+    /**
+     * Return whether or not the image has been set.
+     *
+     * @return bool. <code>true</code> if the image has been set to
+     *         something new.
+     *
+     * @since 3.1
+     */
+    public bool hasNewImage() {
+
+        // If we started with null any change is an update
+        if (startImage is null) {
+            return newImage !is null;
+        }
+
+        if (imageUpdated) {
+            return !(startImage.opEquals(newImage));
+        }
+        return false;
+    }
+
+    /**
+     * Return whether or not the text has been set.
+     *
+     * @return bool. <code>true</code> if the text has been set to
+     *         something new.
+     */
+    public bool hasNewText() {
+
+        // If we started with null any change is an update
+        if (startText is null) {
+            return newText !is null;
+        }
+
+        if (textUpdated) {
+            return !(startText.equals(newText));
+        }
+
+        return false;
+    }
+
+    /**
+     * Return whether or not the background color has been set.
+     *
+     * @return bool. <code>true</code> if the value has been set.
+     */
+    public bool hasNewBackground() {
+        return background !is null;
+    }
+
+    /**
+     * Return whether or not the foreground color has been set.
+     *
+     * @return bool. <code>true</code> if the value has been set.
+     *
+     * @since 3.1
+     */
+    public bool hasNewForeground() {
+        return foreground !is null;
+    }
+
+    /**
+     * Return whether or not the font has been set.
+     *
+     * @return bool. <code>true</code> if the value has been set.
+     *
+     * @since 3.1
+     */
+    public bool hasNewFont() {
+        return font !is null;
+    }
+
+    /**
+     * Get the background Color.
+     *
+     * @return Color or <code>null</code> if no new value was set.
+     *
+     * @since 3.1
+     */
+    public Color getBackground() {
+        return background;
+    }
+
+    /**
+     * Set the background Color.
+     *
+     * @param background
+     *            Color. This value should not be <code>null</code>.
+     *
+     * @since 3.1
+     */
+    public void setBackground(Color background) {
+        this.background = background;
+    }
+
+    /**
+     * Get the font.
+     *
+     * @return Font or <code>null</code> if no new value was set.
+     *
+     * @since 3.1
+     */
+    public Font getFont() {
+        return font;
+    }
+
+    /**
+     * Set the font.
+     *
+     * @param font
+     *            Font This value should not be <code>null</code>.
+     *
+     * @since 3.1
+     */
+    public void setFont(Font font) {
+        this.font = font;
+    }
+
+    /**
+     * Get the foreground Color.
+     *
+     * @return Color or <code>null</code> if no new value was set.
+     *
+     * @since 3.1
+     */
+    public Color getForeground() {
+        return foreground;
+    }
+
+    /**
+     * Set the foreground Color.
+     *
+     * @param foreground
+     *            Color This value should not be <code>null</code>.
+     *
+     * @since 3.1
+     */
+    public void setForeground(Color foreground) {
+        this.foreground = foreground;
+    }
+
+    /**
+     * Set whether or not there are any decorations pending.
+     *
+     * @param hasPendingDecorations
+     */
+    void setHasPendingDecorations(bool hasPendingDecorations_) {
+        this.hasPendingDecorations_ = hasPendingDecorations_;
+    }
+
+    /**
+     * @return <code>bool</code>. <code>true</code> if there are any
+     *         decorations pending.
+     */
+    bool hasPendingDecorations() {
+        return hasPendingDecorations_;
+    }
+
+    /**
+     * Returns the tooltipText.
+     *
+     * @return {@link String} or <code>null</code> if the tool tip text was
+     *         never set.
+     *
+     * @since 3.3
+     */
+    public String getTooltipText() {
+        return tooltipText;
+    }
+
+    /**
+     * Set the tool tip text.
+     *
+     * @param tooltipText
+     *            The tooltipText {@link String} to set. This value should not
+     *            be <code>null</code>.
+     *
+     * @since 3.3
+     */
+    public void setTooltipText(String tooltipText) {
+        this.tooltipText = tooltipText;
+    }
+
+    /**
+     * Return whether or not the tool tip text has been set.
+     *
+     * @return <code>bool</code>. <code>true</code> if the tool tip text
+     *         has been set.
+     *
+     * @since 3.3
+     */
+    public bool hasNewTooltipText() {
+        return this.tooltipText !is null;
+    }
+
+    /**
+     * Return the tool tip background color.
+     *
+     * @return {@link Color} or <code>null</code> if the tool tip background
+     *         color has not been set.
+     *
+     * @since 3.3
+     */
+    public Color getTooltipBackgroundColor() {
+        return tooltipBackgroundColor;
+    }
+
+    /**
+     * Set the background {@link Color} for tool tip.
+     *
+     * @param tooltipBackgroundColor
+     *            The {@link Color} to set. This value should not be
+     *            <code>null</code>.
+     *
+     * @since 3.3
+     */
+    public void setTooltipBackgroundColor(Color tooltipBackgroundColor) {
+        this.tooltipBackgroundColor = tooltipBackgroundColor;
+    }
+
+    /**
+     * Return whether or not the tool tip background color has been set.
+     *
+     * @return <code>bool</code>. <code>true</code> if the tool tip text
+     *         has been set.
+     *
+     * @since 3.3
+     */
+    public bool hasNewTooltipBackgroundColor() {
+        return tooltipBackgroundColor !is null;
+    }
+
+    /**
+     * Return the foreground {@link Color}.
+     *
+     * @return Returns {@link Color} or <code>null</code> if the tool tip
+     *         foreground color has not been set.
+     *
+     * @since 3.3
+     */
+    public Color getTooltipForegroundColor() {
+        return tooltipForegroundColor;
+    }
+
+    /**
+     * Set the foreground {@link Color} for tool tip.
+     *
+     * @param tooltipForegroundColor
+     *            The tooltipForegroundColor to set.
+     *
+     * @since 3.3
+     */
+    public void setTooltipForegroundColor(Color tooltipForegroundColor) {
+        this.tooltipForegroundColor = tooltipForegroundColor;
+    }
+
+    /**
+     *
+     * Return whether or not the tool tip foreground color has been set.
+     *
+     * @return <code>bool</code>. <code>true</code> if the tool tip foreground
+     *         has been set.
+     *
+     * @since 3.3
+     */
+    public bool hasNewTooltipForegroundColor() {
+        return tooltipForegroundColor !is null;
+    }
+
+    /**
+     * @return Returns the tooltipShift.
+     * @since 3.3
+     */
+    public Point getTooltipShift() {
+        return tooltipShift;
+    }
+
+    /**
+     * @param tooltipShift
+     *            The tooltipShift to set.
+     * @since 3.3
+     */
+    public void setTooltipShift(Point tooltipShift) {
+        this.tooltipShift = tooltipShift;
+    }
+
+    /**
+     * @return Return whether or not the tool tip shift has been set.
+     * @since 3.3
+     */
+    public bool hasTooltipShift() {
+        return this.tooltipShift !is null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerRow.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,391 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                               - fix in bug: 166346,167325,174355,195908,198035,215069,227421
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.viewers.ViewerRow;
+
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ViewerRow;
+import org.eclipse.jface.viewers.TreePath;
+
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.jface.util.Policy;
+
+import java.lang.all;
+import java.util.Set;
+import tango.util.Convert;
+
+/**
+ * ViewerRow is the abstract superclass of the part that represents items in a
+ * Table or Tree. Implementors of {@link ColumnViewer} have to provide a
+ * concrete implementation for the underlying widget
+ *
+ * @since 3.3
+ *
+ */
+public abstract class ViewerRow : Cloneable {
+
+    /**
+     * Constant denoting the row above the current one (value is 1).
+     *
+     * @see #getNeighbor(int, bool)
+     */
+    public static const int ABOVE = 1;
+
+    /**
+     * Constant denoting the row below the current one (value is 2).
+     *
+     * @see #getNeighbor(int, bool)
+     */
+    public static const int BELOW = 2;
+
+    private static const String KEY_TEXT_LAYOUT = Policy.JFACE ~ "styled_label_key_"; //$NON-NLS-1$
+
+    /**
+     * Get the bounds of the entry at the columnIndex,
+     *
+     * @param columnIndex
+     * @return {@link Rectangle}
+     */
+    public abstract Rectangle getBounds(int columnIndex);
+
+    /**
+     * Return the bounds for the whole item.
+     *
+     * @return {@link Rectangle}
+     */
+    public abstract Rectangle getBounds();
+
+    /**
+     * Return the item for the receiver.
+     *
+     * @return {@link Widget}
+     */
+    public abstract Widget getItem();
+
+    /**
+     * Return the number of columns for the receiver.
+     *
+     * @return the number of columns
+     */
+    public abstract int getColumnCount();
+
+    /**
+     * Return the image at the columnIndex.
+     *
+     * @param columnIndex
+     * @return {@link Image} or <code>null</code>
+     */
+    public abstract Image getImage(int columnIndex);
+
+    /**
+     * Set the image at the columnIndex
+     *
+     * @param columnIndex
+     * @param image
+     */
+    public abstract void setImage(int columnIndex, Image image);
+
+    /**
+     * Get the text at the columnIndex.
+     *
+     * @param columnIndex
+     * @return {@link String}
+     */
+    public abstract String getText(int columnIndex);
+
+    /**
+     * Set the text at the columnIndex
+     *
+     * @param columnIndex
+     * @param text
+     */
+    public abstract void setText(int columnIndex, String text);
+
+    /**
+     * Get the background at the columnIndex,
+     *
+     * @param columnIndex
+     * @return {@link Color} or <code>null</code>
+     */
+    public abstract Color getBackground(int columnIndex);
+
+    /**
+     * Set the background at the columnIndex.
+     *
+     * @param columnIndex
+     * @param color
+     */
+    public abstract void setBackground(int columnIndex, Color color);
+
+    /**
+     * Get the foreground at the columnIndex.
+     *
+     * @param columnIndex
+     * @return {@link Color} or <code>null</code>
+     */
+    public abstract Color getForeground(int columnIndex);
+
+    /**
+     * Set the foreground at the columnIndex.
+     *
+     * @param columnIndex
+     * @param color
+     */
+    public abstract void setForeground(int columnIndex, Color color);
+
+    /**
+     * Get the font at the columnIndex.
+     *
+     * @param columnIndex
+     * @return {@link Font} or <code>null</code>
+     */
+    public abstract Font getFont(int columnIndex);
+
+    /**
+     * Set the {@link Font} at the columnIndex.
+     *
+     * @param columnIndex
+     * @param font
+     */
+    public abstract void setFont(int columnIndex, Font font);
+
+    /**
+     * Get the ViewerCell at point.
+     *
+     * @param point
+     * @return {@link ViewerCell}
+     */
+    public ViewerCell getCell(Point point) {
+        int index = getColumnIndex(point);
+        return getCell(index);
+    }
+
+    /**
+     * Get the columnIndex of the point.
+     *
+     * @param point
+     * @return int or -1 if it cannot be found.
+     */
+    public int getColumnIndex(Point point) {
+        int count = getColumnCount();
+
+        // If there are no columns the column-index is 0
+        if (count is 0) {
+            return 0;
+        }
+
+        for (int i = 0; i < count; i++) {
+            if (getBounds(i).contains(point)) {
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Get a ViewerCell for the column at index.
+     *
+     * @param column
+     * @return {@link ViewerCell} or <code>null</code> if the index is
+     *         negative.
+     */
+    public ViewerCell getCell(int column) {
+        if (column >= 0)
+            return new ViewerCell(cast(ViewerRow) clone(), column, getElement());
+
+        return null;
+    }
+
+    /**
+     * Get the Control for the receiver.
+     *
+     * @return {@link Control}
+     */
+    public abstract Control getControl();
+
+    /**
+     * Returns a neighboring row, or <code>null</code> if no neighbor exists
+     * in the given direction. If <code>sameLevel</code> is <code>true</code>,
+     * only sibling rows (under the same parent) will be considered.
+     *
+     * @param direction
+     *            the direction {@link #BELOW} or {@link #ABOVE}
+     *
+     * @param sameLevel
+     *            if <code>true</code>, search only within sibling rows
+     * @return the row above/below, or <code>null</code> if not found
+     */
+    public abstract ViewerRow getNeighbor(int direction, bool sameLevel);
+
+    /**
+     * The tree path used to identify an element by the unique path
+     *
+     * @return the path
+     */
+    public abstract TreePath getTreePath();
+
+    public /+override+/ abstract Object clone();
+
+    /**
+     * @return the model element
+     */
+    public abstract Object getElement();
+
+    public override hash_t toHash() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ((getItem() is null) ? 0 : getItem().toHash());
+        return result;
+    }
+
+    public override int opEquals(Object obj) {
+        if (this is obj)
+            return true;
+        if (obj is null)
+            return false;
+        if (this.classinfo !is obj.classinfo)
+            return false;
+        ViewerRow other = cast(ViewerRow) obj;
+        if (getItem() is null) {
+            if (other.getItem() !is null)
+                return false;
+        } else if (!getItem().opEquals(other.getItem()))
+            return false;
+        return true;
+    }
+
+    /**
+     * The cell at the current index (as shown in the UI). This can be different
+     * to the original index when columns are reordered.
+     *
+     * @param visualIndex
+     *            the current index (as shown in the UI)
+     * @return the cell at the currently visible index
+     */
+    ViewerCell getCellAtVisualIndex(int visualIndex) {
+        return getCell(getCreationIndex(visualIndex));
+    }
+
+    /**
+     * Translate the original column index to the actual one.
+     * <p>
+     * <b>Because of backwards API compatibility the default implementation
+     * returns the original index. Implementators of {@link ColumnViewer} should
+     * overwrite this method if their widget supports reordered columns</b>
+     * </p>
+     *
+     * @param creationIndex
+     *            the original index
+     * @return the current index (as shown in the UI)
+     * @since 3.4
+     */
+    protected int getVisualIndex(int creationIndex) {
+        return creationIndex;
+    }
+    package int getVisualIndex_package(int creationIndex) {
+        return getVisualIndex(creationIndex);
+    }
+
+    /**
+     * Translate the current column index (as shown in the UI) to the original
+     * one.
+     * <p>
+     * <b>Because of backwards API compatibility the default implementation
+     * returns the original index. Implementators of {@link ColumnViewer} should
+     * overwrite this method if their widget supports reordered columns</b>
+     * </p>
+     *
+     * @param visualIndex
+     *            the current index (as shown in the UI)
+     * @return the original index
+     * @since 3.4
+     */
+    protected int getCreationIndex(int visualIndex) {
+        return visualIndex;
+    }
+    package int getCreationIndex_package(int visualIndex) {
+        return getCreationIndex(visualIndex);
+    }
+
+    /**
+     * The location and bounds of the area where the text is drawn depends on
+     * various things (image displayed, control with SWT.CHECK)
+     *
+     * @param index
+     *            the column index
+     * @return the bounds of the of the text area. May return <code>null</code>
+     *         if the underlying widget implementation doesn't provide this
+     *         information
+     * @since 3.4
+     */
+    public Rectangle getTextBounds(int index) {
+        return null;
+    }
+
+
+    /**
+     * Returns the location and bounds of the area where the image is drawn.
+     *
+     * @param index
+     *            the column index
+     * @return the bounds of the of the image area. May return <code>null</code>
+     *         if the underlying widget implementation doesn't provide this
+     *         information
+     * @since 3.4
+     */
+    public Rectangle getImageBounds(int index) {
+        return null;
+    }
+
+    /**
+     * Set the style ranges to be applied on the text label at the column index
+     * Note: Requires {@link StyledCellLabelProvider} with owner draw enabled.
+     *
+     * @param columnIndex the index of the column
+     * @param styleRanges the styled ranges
+     *
+     * @since 3.4
+     */
+    public void setStyleRanges(int columnIndex, StyleRange[] styleRanges) {
+        getItem().setData(KEY_TEXT_LAYOUT ~ to!(String)(columnIndex), new ArrayWrapperT!(StyleRange)(styleRanges));
+    }
+
+
+    /**
+     * Returns the style ranges to be applied on the text label at the column index or <code>null</code> if no
+     * style ranges have been set.
+     *
+     * @param columnIndex the index of the column
+     * @return styleRanges the styled ranges
+     *
+     * @since 3.4
+     */
+    public StyleRange[] getStyleRanges(int columnIndex) {
+        return (cast(ArrayWrapperT!(StyleRange)) getItem().getData(KEY_TEXT_LAYOUT ~ to!(String)(columnIndex))).array;
+    }
+    
+    int getWidth(int columnIndex) {
+        return getBounds(columnIndex).width;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/ViewerSorter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.ViewerSorter;
+
+import org.eclipse.jface.viewers.ViewerComparator;
+
+import java.lang.all;
+import java.text.Collator;
+
+/**
+ * A viewer sorter is used by a {@link StructuredViewer} to reorder the elements
+ * provided by its content provider.
+ * <p>
+ * The default <code>compare</code> method compares elements using two steps.
+ * The first step uses the values returned from <code>category</code>.
+ * By default, all elements are in the same category.
+ * The second level is based on a case insensitive compare of the strings obtained
+ * from the content viewer's label provider via <code>ILabelProvider.getText</code>.
+ * </p>
+ * <p>
+ * Subclasses may implement the <code>isSorterProperty</code> method;
+ * they may reimplement the <code>category</code> method to provide
+ * categorization; and they may override the <code>compare</code> methods
+ * to provide a totally different way of sorting elements.
+ * </p>
+ * <p>
+ * It is recommended to use <code>ViewerComparator</code> instead.
+ * </p>
+ * @see IStructuredContentProvider
+ * @see StructuredViewer
+ */
+public class ViewerSorter : ViewerComparator {
+    /**
+     * The collator used to sort strings.
+     *
+     * @deprecated as of 3.3 Use {@link ViewerComparator#getComparator()}
+     */
+    protected Collator collator;
+
+    /**
+     * Creates a new viewer sorter, which uses the default collator
+     * to sort strings.
+     */
+    public this() {
+        this(Collator.getInstance());
+    }
+
+    /**
+     * Creates a new viewer sorter, which uses the given collator
+     * to sort strings.
+     *
+     * @param collator the collator to use to sort strings
+     */
+    public this(Collator collator) {
+        super(collator);
+        this.collator = collator;
+    }
+
+    /**
+     * Returns the collator used to sort strings.
+     *
+     * @return the collator used to sort strings
+     * @deprecated as of 3.3 Use {@link ViewerComparator#getComparator()}
+     */
+    public Collator getCollator() {
+        return collator;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/WrappedViewerLabelProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.viewers.WrappedViewerLabelProvider;
+
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IColorProvider;
+import org.eclipse.jface.viewers.IFontProvider;
+import org.eclipse.jface.viewers.IViewerLabelProvider;
+import org.eclipse.jface.viewers.ITreePathLabelProvider;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ViewerLabel;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TreePath;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The WrappedViewerLabelProvider is a label provider that allows
+ * {@link ILabelProvider}, {@link IColorProvider} and {@link IFontProvider} to
+ * be mapped to a ColumnLabelProvider.
+ *
+ * @since 3.3
+ *
+ */
+class WrappedViewerLabelProvider : ColumnLabelProvider {
+
+    private static ILabelProvider defaultLabelProvider;
+
+    static this(){
+        defaultLabelProvider = new LabelProvider();
+    }
+
+    private ILabelProvider labelProvider;
+
+    private IColorProvider colorProvider;
+
+    private IFontProvider fontProvider;
+
+    private IViewerLabelProvider viewerLabelProvider;
+
+    private ITreePathLabelProvider treePathLabelProvider;
+
+    /**
+     * Create a new instance of the receiver based on labelProvider.
+     *
+     * @param labelProvider
+     */
+    public this(IBaseLabelProvider labelProvider) {
+        this.labelProvider = defaultLabelProvider;
+        super();
+        setProviders(cast(Object)labelProvider);
+    }
+
+    /**
+     * Set the any providers for the receiver that can be adapted from provider.
+     *
+     * @param provider
+     *            {@link Object}
+     */
+    public void setProviders(Object provider) {
+        if ( auto c = cast(ITreePathLabelProvider)provider )
+            treePathLabelProvider = c;
+
+        if ( auto c = cast(IViewerLabelProvider)provider )
+            viewerLabelProvider = c;
+
+        if ( auto c = cast(ILabelProvider)provider )
+            labelProvider = c;
+
+        if ( auto c = cast(IColorProvider)provider )
+            colorProvider = c;
+
+        if ( auto c = cast(IFontProvider)provider )
+            fontProvider = c;
+
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.IFontProvider#getFont(java.lang.Object)
+     */
+    public override Font getFont(Object element) {
+        if (fontProvider is null) {
+            return null;
+        }
+
+        return fontProvider.getFont(element);
+
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.IColorProvider#getBackground(java.lang.Object)
+     */
+    public override Color getBackground(Object element) {
+        if (colorProvider is null) {
+            return null;
+        }
+
+        return colorProvider.getBackground(element);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
+     */
+    public override String getText(Object element) {
+        return getLabelProvider().getText(element);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object)
+     */
+    public override Image getImage(Object element) {
+        return getLabelProvider().getImage(element);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.viewers.IColorProvider#getForeground(java.lang.Object)
+     */
+    public override Color getForeground(Object element) {
+        if (colorProvider is null) {
+            return null;
+        }
+
+        return colorProvider.getForeground(element);
+    }
+
+    /**
+     * Get the label provider
+     *
+     * @return {@link ILabelProvider}
+     */
+    ILabelProvider getLabelProvider() {
+        return labelProvider;
+    }
+
+    /**
+     * Get the color provider
+     *
+     * @return {@link IColorProvider}
+     */
+    IColorProvider getColorProvider() {
+        return colorProvider;
+    }
+
+    /**
+     * Get the font provider
+     *
+     * @return {@link IFontProvider}.
+     */
+    IFontProvider getFontProvider() {
+        return fontProvider;
+    }
+
+    public override void update(ViewerCell cell) {
+        Object element = cell.getElement();
+        if(viewerLabelProvider is null && treePathLabelProvider is null){
+            // inlined super implementation with performance optimizations
+            cell.setText(getText(element));
+            Image image = getImage(element);
+            cell.setImage(image);
+            if (colorProvider !is null) {
+                cell.setBackground(getBackground(element));
+                cell.setForeground(getForeground(element));
+            }
+            if (fontProvider !is null) {
+                cell.setFont(getFont(element));
+            }
+            return;
+        }
+
+        ViewerLabel label = new ViewerLabel(cell.getText(), cell.getImage());
+
+        if (treePathLabelProvider !is null) {
+            TreePath treePath = cell.getViewerRow().getTreePath();
+
+            Assert.isNotNull(treePath);
+            treePathLabelProvider.updateLabel(label, treePath);
+        } else if (viewerLabelProvider !is null) {
+            viewerLabelProvider.updateLabel(label, element);
+        }
+        if (!label.hasNewForeground() && colorProvider !is null)
+            label.setForeground(getForeground(element));
+
+        if (!label.hasNewBackground() && colorProvider !is null)
+            label.setBackground(getBackground(element));
+
+        if (!label.hasNewFont() && fontProvider !is null)
+            label.setFont(getFont(element));
+
+        applyViewerLabel(cell, label);
+    }
+
+    private void applyViewerLabel(ViewerCell cell, ViewerLabel label) {
+        if (label.hasNewText()) {
+            cell.setText(label.getText());
+        }
+        if (label.hasNewImage()) {
+            cell.setImage(label.getImage());
+        }
+        if (colorProvider !is null || label.hasNewBackground()) {
+            cell.setBackground(label.getBackground());
+        }
+        if (colorProvider !is null || label.hasNewForeground()) {
+            cell.setForeground(label.getForeground());
+        }
+        if (fontProvider !is null || label.hasNewFont()) {
+            cell.setFont(label.getFont());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/AbstractConcurrentModel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.viewers.deferred.AbstractConcurrentModel;
+
+import org.eclipse.jface.viewers.deferred.IConcurrentModel;
+import org.eclipse.jface.viewers.deferred.IConcurrentModelListener;
+
+import org.eclipse.core.runtime.ListenerList;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Abstract base class for all IConcurrentModel implementations. Clients should
+ * subclass this class instead of implementing IConcurrentModel directly.
+ *
+ * @since 3.1
+ */
+public abstract class AbstractConcurrentModel :
+        IConcurrentModel {
+
+    private ListenerList listeners;
+
+    public this(){
+        listeners = new ListenerList();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.deferred.IConcurrentContentProvider#addListener(org.eclipse.jface.viewers.deferred.IConcurrentContentProviderListener)
+     */
+    public void addListener(IConcurrentModelListener listener) {
+        listeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Fires an add notification to all listeners
+     *
+     * @param added objects added to the set
+     */
+    protected final void fireAdd(Object[] added) {
+        Object[] listenerArray = listeners.getListeners();
+
+        for (int i = 0; i < listenerArray.length; i++) {
+            IConcurrentModelListener next = cast(IConcurrentModelListener) listenerArray[i];
+
+            next.add(added);
+        }
+    }
+
+    /**
+     * Fires a remove notification to all listeners
+     *
+     * @param removed objects removed from the set
+     */
+    protected final void fireRemove(Object[] removed) {
+        Object[] listenerArray = listeners.getListeners();
+
+        for (int i = 0; i < listenerArray.length; i++) {
+            IConcurrentModelListener next = cast(IConcurrentModelListener) listenerArray[i];
+
+            next.remove(removed);
+        }
+    }
+
+    /**
+     * Fires an update notification to all listeners
+     *
+     * @param updated objects that have changed
+     */
+    protected final void fireUpdate(Object[] updated) {
+        Object[] listenerArray = listeners.getListeners();
+
+        for (int i = 0; i < listenerArray.length; i++) {
+            IConcurrentModelListener next = cast(IConcurrentModelListener) listenerArray[i];
+
+            next.update(updated);
+        }
+    }
+
+    /**
+     * Returns the array of listeners for this model
+     *
+     * @return the array of listeners for this model
+     */
+    protected final IConcurrentModelListener[] getListeners() {
+        Object[] l = listeners.getListeners();
+        IConcurrentModelListener[] result = new IConcurrentModelListener[l.length];
+
+        for (int i = 0; i < l.length; i++) {
+            result[i] = cast(IConcurrentModelListener)l[i];
+        }
+
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.deferred.IConcurrentContentProvider#removeListener(org.eclipse.jface.viewers.deferred.IConcurrentContentProviderListener)
+     */
+    public void removeListener(IConcurrentModelListener listener) {
+        listeners.remove(cast(Object)listener);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/AbstractVirtualTable.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.deferred.AbstractVirtualTable;
+
+import java.lang.all;
+
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * Wrapper for a virtual-table-like widget. Contains all methods needed for lazy updates.
+ * The JFace algorithms for deferred or lazy content providers should talk to this class
+ * instead of directly to a TableViewer. This will allow them to be used with other virtual
+ * viewers and widgets in the future.
+ * 
+ * <p>
+ * For example, if SWT starts to support virtual Lists in the future, it should be possible
+ * to create an adapter from <code>AbstractVirtualTable</code> to <code>ListViewer</code> in 
+ * order to reuse the existing algorithms for deferred updates. 
+ * </p>
+ * 
+ * <p>
+ * This is package visiblity by design. It would only need to be made public if there was
+ * a demand to use the deferred content provider algorithms like 
+ * <code>BackgroundContentProvider</code> with non-JFace viewers.
+ * </p>
+ * 
+ * @since 3.1
+ */
+abstract class AbstractVirtualTable {
+    /**
+     * Tells the receiver that the item at given row has changed. This may indicate
+     * that a different element is now at this row, but does not necessarily indicate
+     * that the element itself has changed. The receiver should request information for
+     * this row the next time it becomes visibile.
+     * 
+     * @param index row to clear
+     */
+    public abstract void clear(int index);
+    
+    /**
+     * Notifies the receiver that the given element is now located at the given index.
+     * 
+     * @param element object located at the row
+     * @param itemIndex row number
+     */
+    public abstract void replace(Object element, int itemIndex);
+    
+    /**
+     * Sets the item count for this table 
+     * 
+     * @param total new total number of items
+     */
+    public abstract void setItemCount(int total);
+    
+    /**
+     * Returns the index of the top item visible in the table
+     * 
+     * @return the index of the top item visible in the table
+     */
+    public abstract int getTopIndex();
+    
+    /**
+     * Returns the number of items currently visible in the table. This is
+     * the size of the currently visible window, not the total size of the table.
+     * 
+     * @return the number of items currently visible in the table
+     */
+    public abstract int getVisibleItemCount();
+    
+    /**
+     * Returns the total number of items in the table
+     * 
+     * @return the total number of items in the table
+     */
+    public abstract int getItemCount();
+    
+    /**
+     * Returns the SWT control that this API is wrappering.
+     * @return Control.
+     */
+    public abstract Control getControl();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/BackgroundContentProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,595 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.deferred.BackgroundContentProvider;
+
+import org.eclipse.jface.viewers.deferred.ConcurrentTableUpdator;
+import org.eclipse.jface.viewers.deferred.IConcurrentModel;
+import org.eclipse.jface.viewers.deferred.IConcurrentModelListener;
+import org.eclipse.jface.viewers.deferred.ChangeQueue;
+import org.eclipse.jface.viewers.deferred.FastProgressReporter;
+import org.eclipse.jface.viewers.deferred.AbstractVirtualTable;
+import org.eclipse.jface.viewers.deferred.LazySortedCollection;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.resource.JFaceResources;
+
+import org.eclipse.jface.viewers.AcceptAllFilter;
+import org.eclipse.jface.viewers.IFilter;
+
+import java.lang.all;
+import java.util.Set;
+import java.lang.JThread;
+
+/**
+ * Contains the algorithm for performing background sorting and filtering in a virtual
+ * table. This is the real implementation for <code>DeferredContentProvider</code>.
+ * However, this class will work with anything that implements <code>AbstractVirtualTable</code>
+ * rather than being tied to a <code>TableViewer</code>.
+ *
+ * <p>
+ * This is package visiblity since it currently only needs to be used in one place,
+ * but it could potentially be made public if there was a need to use the same background
+ * sorting algorithm for something other than a TableViewer.
+ * </p>
+ *
+ * <p>
+ * Information flow is like this:
+ * </p>
+ * <ol>
+ * <li>IConcurrentModel sends unordered elements to BackgroundContentProvider (in a background thread)</li>
+ * <li>BackgroundContentProvider sorts, filters, and sends element/index pairs to
+ *     ConcurrentTableUpdator (in a background thread)</li>
+ * <li>ConcurrentTableUpdator batches the updates and sends them to an AbstractVirtualTable
+ *     (in the UI thread)</li>
+ * </ol>
+ *
+ * <p>
+ * Internally, sorting is done using a <code>LazySortedCollection</code>. This data structure
+ * allows the content provider to locate and sort the visible range without fully sorting
+ * all elements in the table. It also supports fast cancellation, allowing the visible range
+ * to change in the middle of a sort without discarding partially-sorted information from
+ * the previous range.
+ * </p>
+ *
+ * @since 3.1
+ */
+/* package */ final class BackgroundContentProvider {
+
+    /**
+     * Sorting message string
+     */
+    private static const String SORTING;
+    static this(){
+        SORTING = JFaceResources.getString("Sorting"); //$NON-NLS-1$
+    }
+    /**
+     * Table limit. -1 if unlimited
+     */
+    private int limit = -1;
+
+    /**
+     * Model that is currently providing input to this content provider.
+     */
+    private IConcurrentModel model;
+
+    /**
+     * Current sort order
+     */
+    private /+volatile+/ Comparator sortOrder;
+
+    /**
+     * True iff the content provider has
+     */
+    private /+volatile+/ IFilter filter;
+
+    /**
+     * Queued changes
+     */
+    private ChangeQueue changeQueue;
+
+    /**
+     * Listener that gets callbacks from the model
+     */
+    private IConcurrentModelListener listener;
+    private void init_listener(){
+        listener = new class IConcurrentModelListener {
+            /* (non-Javadoc)
+            * @see org.eclipse.jface.viewers.deferred.IConcurrentModelListener#add(java.lang.Object[])
+            */
+            public void add(Object[] added) {
+                this.outer.add(added);
+            }
+
+            /* (non-Javadoc)
+            * @see org.eclipse.jface.viewers.deferred.IConcurrentModelListener#remove(java.lang.Object[])
+            */
+            public void remove(Object[] removed) {
+                this.outer.remove(removed);
+            }
+
+            /* (non-Javadoc)
+            * @see org.eclipse.jface.viewers.deferred.IConcurrentModelListener#setContents(java.lang.Object[])
+            */
+            public void setContents(Object[] newContents) {
+                this.outer.setContents(newContents);
+            }
+
+            /* (non-Javadoc)
+            * @see org.eclipse.jface.viewers.deferred.IConcurrentModelListener#update(java.lang.Object[])
+            */
+            public void update(Object[] changed) {
+                this.outer.update(changed);
+            }
+
+        };
+    }
+    /**
+     * Object that posts updates to the UI thread. Must synchronize on
+     * sortMutex when accessing.
+     */
+    private ConcurrentTableUpdator updator;
+
+    private IProgressMonitor sortingProgressMonitor;
+    private JThread sortThread = null;
+
+    private /+volatile+/ FastProgressReporter sortMon;
+
+    private /+volatile+/ ConcurrentTableUpdator.Range range;
+
+    /**
+     * Creates a new background content provider
+     *
+     * @param table table that will receive updates
+     * @param model data source
+     * @param sortOrder initial sort order
+     */
+    public this(AbstractVirtualTable table,
+            IConcurrentModel model, Comparator sortOrder) {
+        lock = new Object();
+        filter = AcceptAllFilter.getInstance();
+        changeQueue = new ChangeQueue();
+        init_listener();
+        sortingProgressMonitor = new NullProgressMonitor();
+        sortMon = new FastProgressReporter();
+        updator = new ConcurrentTableUpdator(table);
+        range = new ConcurrentTableUpdator.Range(0,0);
+        this.model = model;
+        this.sortOrder = sortOrder;
+        model.addListener(listener);
+    }
+
+    /**
+     * Cleans up this content provider, detaches listeners, frees up memory, etc.
+     * Must be the last public method called on this object.
+     */
+    public void dispose() {
+        cancelSortJob();
+        updator.dispose();
+        model.removeListener(listener);
+    }
+
+    /**
+     * Force a refresh. Asks the model to re-send its complete contents.
+     */
+    public void refresh() {
+        if (updator.isDisposed()) {
+            return;
+        }
+        model.requestUpdate(listener);
+    }
+
+    /**
+     * Called from sortJob. Sorts the elements defined by sortStart and sortLength.
+     * Schedules a UI update when finished.
+     *
+     * @param mon monitor where progress will be reported
+     */
+    private void doSort(IProgressMonitor mon) {
+
+        // Workaround for some weirdness in the Jobs framework: if you cancel a monitor
+        // for a job that has ended and reschedule that same job, it will start
+        // the job with a monitor that is already cancelled. We can workaround this by
+        // removing all references to the progress monitor whenever the job terminates,
+        // but this would require additional synchronize blocks (which are slow) and more
+        // complexity. Instead, we just un-cancel the monitor at the start of each job.
+        mon.setCanceled(false);
+
+        mon.beginTask(SORTING, 100);
+
+        // Create a LazySortedCollection
+        Comparator order = sortOrder;
+        IFilter f = filter;
+        LazySortedCollection collection = new LazySortedCollection(order);
+
+        // Fill it in with all existing known objects
+        Object[] knownObjects = updator.getKnownObjects();
+        for (int i = 0; i < knownObjects.length; i++) {
+            Object object = knownObjects[i];
+            if (object !is null) {
+                collection.add(object);
+            }
+        }
+
+        bool dirty = false;
+        int prevSize = knownObjects.length;
+        updator.setTotalItems(prevSize);
+
+        // Start processing changes
+        while(true) {
+            // If the sort order has changed, build a new LazySortedCollection with
+            // the new comparator
+            if (order !is sortOrder) {
+                dirty = true;
+                order = sortOrder;
+                // Copy all elements from the old collection to the new one
+                LazySortedCollection newCollection = new LazySortedCollection(order);
+
+                Object[] items = collection.getItems(false);
+                for (int j = 0; j < items.length && order is sortOrder; j++) {
+                    Object item = items[j];
+
+                    newCollection.add(item);
+                }
+
+                // If the sort order changed again, re-loop
+                if (order !is sortOrder) {
+                    continue;
+                }
+                collection = newCollection;
+                continue;
+            }
+
+            // If the filter has changed
+            if (f !is filter) {
+                dirty = true;
+                f = filter;
+
+                Object[] items = collection.getItems(false);
+
+                // Remove any items that don't pass the new filter
+                for (int j = 0; j < items.length && f is filter; j++) {
+                    Object toTest = items[j];
+
+                    if (!f.select(toTest)) {
+                        collection.remove(toTest);
+                    }
+                }
+                continue;
+            }
+
+            // If there are pending changes, process one of them
+            if (!changeQueue.isEmpty()) {
+                dirty = true;
+                ChangeQueue.Change next = changeQueue.dequeue();
+
+                switch(next.getType()) {
+                    case ChangeQueue.ADD: {
+                        filteredAdd(collection, next.getElements(), f);
+                        break;
+                    }
+                    case ChangeQueue.REMOVE: {
+                        Object[] toRemove = next.getElements();
+
+                        flush(toRemove, collection);
+                        collection.removeAll(toRemove);
+
+                        break;
+                    }
+                    case ChangeQueue.UPDATE: {
+                        Object[] items  = next.getElements();
+
+                        for (int i = 0; i < items.length; i++) {
+                            Object item = items[i];
+
+                            if (collection.contains(item)) {
+                                // TODO: write a collection.update(...) method
+                                collection.remove(item);
+                                collection.add(item);
+                                updator.clear(item);
+                            }
+                        }
+
+                        break;
+                    }
+                    case ChangeQueue.SET: {
+                        Object[] items = next.getElements();
+                        collection.clear();
+                        filteredAdd(collection, items, f);
+
+                        break;
+                    }
+                    default:
+                }
+
+                continue;
+            }
+
+            int totalElements = collection.size();
+            if (limit !is -1) {
+                if (totalElements > limit) {
+                    totalElements = limit;
+                }
+            }
+
+            if (totalElements !is prevSize) {
+                prevSize = totalElements;
+                // Send the total items to the updator ASAP -- the user may want
+                // to scroll to a different section of the table, which would
+                // cause our sort range to change and cause this job to get cancelled.
+                updator.setTotalItems(totalElements);
+                dirty = true;
+            }
+
+            // Terminate loop
+            if (!dirty) {
+                break;
+            }
+
+            try {
+                ConcurrentTableUpdator.Range updateRange = updator.getVisibleRange();
+                sortMon = new FastProgressReporter();
+                range = updateRange;
+                int sortStart = updateRange.start;
+                int sortLength = updateRange.length;
+
+                if (limit !is -1) {
+                    collection.retainFirst(limit, sortMon);
+                }
+
+                sortLength = Math.min(sortLength, totalElements - sortStart);
+                sortLength = Math.max(sortLength, 0);
+
+                Object[] objectsOfInterest = new Object[sortLength];
+
+                collection.getRange(objectsOfInterest, sortStart, true, sortMon);
+
+                // Send the new elements to the table
+                for (int i = 0; i < sortLength; i++) {
+                    Object object = objectsOfInterest[i];
+                    updator.replace(object, sortStart + i);
+                }
+
+                objectsOfInterest = new Object[collection.size()];
+
+                collection.getFirst(objectsOfInterest, true, sortMon);
+
+                // Send the new elements to the table
+                for (int i = 0; i < totalElements; i++) {
+                    Object object = objectsOfInterest[i];
+                    updator.replace(object, i);
+                }
+
+            } catch (InterruptedException e) {
+                continue;
+            }
+
+            dirty = false;
+        }
+
+        mon.done();
+    }
+
+    /**
+     * @param collection
+     * @param toAdd
+     */
+    private static void filteredAdd(LazySortedCollection collection, Object[] toAdd, IFilter filter) {
+        if (filter !is AcceptAllFilter.getInstance()) {
+            for (int i = 0; i < toAdd.length; i++) {
+                Object object = toAdd[i];
+
+                if (filter.select(object)) {
+                    collection.add(object);
+                }
+            }
+        } else {
+            collection.addAll(toAdd);
+        }
+    }
+
+    /**
+     * Sets the sort order for this content provider
+     *
+     * @param sorter sort order
+     */
+    public void setSortOrder(Comparator sorter) {
+        Assert.isNotNull(cast(Object)sorter);
+        this.sortOrder = sorter;
+        sortMon.cancel();
+        refresh();
+    }
+
+    /**
+     * Sets the filter for this content provider
+     *
+     * @param toSet filter to set
+     */
+    public void setFilter(IFilter toSet) {
+        Assert.isNotNull(cast(Object)toSet);
+        this.filter = toSet;
+        sortMon.cancel();
+        refresh();
+    }
+
+    /**
+     * Sets the maximum table size. Based on the current sort order,
+     * the table will be truncated if it grows beyond this size.
+     * Using a limit improves memory usage and performance, and is
+     * strongly recommended for large tables.
+     *
+     * @param limit maximum rows to show in the table or -1 if unbounded
+     */
+    public void setLimit(int limit) {
+        this.limit = limit;
+        refresh();
+    }
+
+    /**
+     * Returns the maximum table size or -1 if unbounded
+     *
+     * @return the maximum number of rows in the table or -1 if unbounded
+     */
+    public int getLimit() {
+        return limit;
+    }
+
+    /**
+     * Checks if currently visible range has changed, and triggers and update
+     * and resort if necessary. Must be called in the UI thread, typically
+     * within a SWT.SetData callback.
+     * @param includeIndex the index that should be included in the visible range.
+     */
+    public void checkVisibleRange(int includeIndex) {
+        updator.checkVisibleRange(includeIndex);
+        ConcurrentTableUpdator.Range newRange = updator.getVisibleRange();
+        ConcurrentTableUpdator.Range oldRange = range;
+
+        // If we're in the middle of processing an invalid range, cancel the sort
+        if (newRange.start !is oldRange.start || newRange.length !is oldRange.length) {
+            sortMon.cancel();
+        }
+    }
+
+    /**
+     * This lock protects the two bool variables sortThreadStarted and resortScheduled.
+     */
+    private Object lock;
+
+    /**
+     * true if the sort thread is running
+     */
+    private bool sortThreadStarted = false;
+
+    /**
+     * true if we need to sort
+     */
+    private bool sortScheduled = false;
+
+    private final class SortThread : JThread {
+        private this(String name) {
+            super(name);
+        }
+
+        public override void run() {
+            loop: while (true) {
+                synchronized (lock) {
+                    sortScheduled = false;
+                }
+                try {
+                    // this is the main work
+                    doSort(sortingProgressMonitor);
+                } catch (Exception ex) {
+                    // ignore
+                }
+                synchronized (lock) {
+                    if (sortScheduled) {
+                        continue loop;
+                    }
+                    sortThreadStarted = false;
+                    break loop;
+                }
+            }
+        }
+    }
+
+    /**
+     * Must be called whenever the model changes. Dirties this object and triggers a sort
+     * if necessary.
+     */
+    private void makeDirty() {
+        synchronized (lock) {
+            sortMon.cancel();
+            // request sorting
+            sortScheduled = true;
+            if (!sortThreadStarted) {
+                sortThreadStarted = true;
+                sortThread = new SortThread(SORTING);
+                sortThread.setDaemon( true );
+                sortThread.setPriority( sortThread.getPriority() - 1 );
+                sortThread.start();
+            }
+        }
+    }
+
+    /**
+     * Cancels any sort in progress. Note that we try to use the
+     * FastProgresReporter if possible since this is more responsive than
+     * cancelling the sort job. However, it is not a problem to cancel in both
+     * ways.
+     */
+    private void cancelSortJob() {
+        sortMon.cancel();
+        sortingProgressMonitor.setCanceled(true);
+    }
+
+    /**
+     * Called when new elements are added to the model.
+     *
+     * @param toAdd
+     *            newly added elements
+     */
+    private void add(Object[] toAdd) {
+        changeQueue.enqueue(ChangeQueue.ADD, toAdd);
+        makeDirty();
+    }
+
+    /**
+     * Called with the complete contents of the model
+     *
+     * @param contents new contents of the model
+     */
+    private void setContents(Object[] contents) {
+        changeQueue.enqueue(ChangeQueue.SET, contents);
+        makeDirty();
+    }
+
+    /**
+     * Called when elements are removed from the model
+     *
+     * @param toRemove elements removed from the model
+     */
+    private void remove(Object[] toRemove) {
+        changeQueue.enqueue(ChangeQueue.REMOVE, toRemove);
+        makeDirty();
+        refresh();
+    }
+
+    /**
+     * Notifies the updator that the given elements have changed
+     *
+     * @param toFlush changed elements
+     * @param collection collection of currently-known elements
+     */
+    private void flush(Object[] toFlush, LazySortedCollection collection) {
+        for (int i = 0; i < toFlush.length; i++) {
+            Object item = toFlush[i];
+
+            if (collection.contains(item)) {
+                updator.clear(item);
+            }
+        }
+    }
+
+
+    /**
+     * Called when elements in the model change
+     *
+     * @param items changed items
+     */
+    private void update(Object[] items) {
+        changeQueue.enqueue(ChangeQueue.UPDATE, items);
+        makeDirty();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/ChangeQueue.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.deferred.ChangeQueue;
+
+
+import java.lang.all;
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Holds a queue of additions, removals, updates, and SET calls for a
+ * BackgroundContentProvider
+ */
+final class ChangeQueue {
+    /**
+     * Represents the addition of an item
+     * @since 3.1
+     */
+    public static const int ADD = 0;
+    /**
+     * Represents the removal of an item
+     * @since 3.1
+     */
+    public static const int REMOVE = 1;
+    /**
+     * Represents a reset of all the items
+     * @since 3.1
+     */
+    public static const int SET = 2;
+    /**
+     * Represents an update of an item
+     * @since 3.1
+     */
+    public static const int UPDATE = 3;
+
+    /**
+     *
+     * @since 3.1
+     */
+    public static final class Change {
+        private int type;
+        private Object[] elements;
+
+        /**
+         * Create a change of the specified type that affects the given elements.
+         *
+         * @param type one of <code>ADD</code>, <code>REMOVE</code>, <code>SET</code>, or <code>UPDATE</code>.
+         * @param elements the elements affected by the change.
+         *
+         * @since 3.1
+         */
+        public this(int type, Object[] elements) {
+            this.type = type;
+            this.elements = elements;
+        }
+
+        /**
+         * Get the type of change.
+         * @return one of <code>ADD</code>, <code>REMOVE</code>, <code>SET</code>, or <code>UPDATE</code>.
+         *
+         * @since 3.1
+         */
+        public int getType() {
+            return type;
+        }
+
+        /**
+         * Return the elements associated with the change.
+         * @return the elements affected by the change.
+         *
+         * @since 3.1
+         */
+        public Object[] getElements() {
+            return elements;
+        }
+    }
+
+    private LinkedList queue;
+    private int workload = 0;
+
+    public this(){
+        queue = new LinkedList();
+    }
+
+    /**
+     * Create a change of the given type and elements and enqueue it.
+     *
+     * @param type the type of change to be created
+     * @param elements the elements affected by the change
+     */
+    public synchronized void enqueue(int type, Object[] elements) {
+        enqueue(new Change(type, elements));
+    }
+
+    /**
+     * Add the specified change to the queue
+     * @param toQueue the change to be added
+     */
+    public synchronized void enqueue(Change toQueue) {
+        // A SET event makes all previous adds, removes, and sets redundant... so remove
+        // them from the queue
+        if (toQueue.type is SET) {
+            workload = 0;
+            LinkedList newQueue = new LinkedList();
+            for (Iterator iter = queue.iterator(); iter.hasNext();) {
+                Change next = cast(Change) iter.next();
+
+                if (next.getType() is ADD || next.getType() is REMOVE || next.getType() is SET) {
+                    continue;
+                }
+
+                newQueue.add(next);
+                workload += next.elements.length;
+            }
+            queue = newQueue;
+        }
+
+        queue.add(toQueue);
+        workload += toQueue.elements.length;
+    }
+
+    /**
+     * Remove the first change from the queue.
+     * @return the first change
+     */
+    public synchronized Change dequeue() {
+        Change result = cast(Change)queue.removeFirst();
+
+
+        workload -= result.elements.length;
+        return result;
+    }
+
+    /**
+     * Return whether the queue is empty
+     * @return <code>true</code> if empty, <code>false</code> otherwise
+     */
+    public synchronized bool isEmpty() {
+        return queue.isEmpty();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/ConcurrentTableUpdator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,398 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.deferred.ConcurrentTableUpdator;
+
+import org.eclipse.jface.viewers.deferred.IntHashMap;
+import org.eclipse.jface.viewers.deferred.AbstractVirtualTable;
+
+import java.lang.all;
+import java.util.Map;
+import java.util.Set;
+
+
+
+/**
+ * Allows a table to be accessed from a background thread. Provides a table-like public
+ * interface that can accessed from a background thread. As updates arrive from the
+ * background thread, it batches and schedules updates to the real table in the UI thread.
+ * This class can be used with any widget that can be wrapped in the
+ * <code>AbstractVirtualTable</code> interface.
+ *
+ * @since 3.1
+ */
+/* package */ final class ConcurrentTableUpdator {
+    /**
+     * Wrapper for the real table. May only be accessed in the UI thread.
+     */
+    private AbstractVirtualTable table;
+
+    /**
+     * The array of objects that have been sent to the UI. Elements are null
+     * if they either haven't been sent yet or have been scheduled for clear.
+     * Maps indices onto elements.
+     */
+    private Object[] sentObjects;
+
+    /**
+     * Map of elements to object indices (inverse of the knownObjects array)
+     */
+    private IntHashMap knownIndices;
+
+    /**
+     * Contains all known objects that have been sent here from the background
+     * thread.
+     */
+    private Object[] knownObjects;
+
+    // Minimum length for the pendingFlushes stack
+    private static const int MIN_FLUSHLENGTH = 64;
+
+    /**
+     * Array of element indices. Contains elements scheduled to be
+     * cleared. Only the beginning of the array is used. The number
+     * of used elements is stored in lastClear
+     */
+    private int[] pendingClears;
+
+    /**
+     * Number of pending clears in the pendingClears array (this is normally
+     * used instead of pendingClears.length since the
+     * pendingClears array is usually larger than the actual number of pending
+     * clears)
+     */
+    private int lastClear = 0;
+
+    /**
+     * Last known visible range
+     */
+    private /+volatile+/ Range lastRange;
+
+    /**
+     * True iff a UI update has been scheduled
+     */
+    private /+volatile+/ bool updateScheduled;
+
+    /**
+     * True iff this object has been disposed
+     */
+    private /+volatile+/ bool disposed = false;
+
+    /**
+     * Object that holds a start index and length. Allows
+     * the visible range to be returned as an atomic operation.
+     */
+    public final static class Range {
+        int start = 0;
+        int length = 0;
+
+        /**
+         * @param s
+         * @param l
+         */
+        public this(int s, int l) {
+            start = s;
+            length = l;
+        }
+    }
+
+    /**
+     * Runnable that can be posted with an asyncExec to schedule
+     * an update to the real table.
+     */
+    Runnable uiRunnable;
+    private void init_uiRunnable(){
+        uiRunnable = new class Runnable {
+            public void run() {
+                updateScheduled = false;
+                if(!table.getControl().isDisposed()) {
+                    updateTable();
+                }
+            }
+        };
+    }
+
+    /**
+     * Creates a new table updator
+     *
+     * @param table real table to update
+     */
+    public this(AbstractVirtualTable table) {
+        init_uiRunnable();
+        knownIndices = new IntHashMap();
+        pendingClears = new int[MIN_FLUSHLENGTH];
+        lastRange = new Range(0,0);
+        this.table = table;
+    }
+
+    /**
+     * Cleans up the updator object (but not the table itself).
+     */
+    public void dispose() {
+        disposed = true;
+    }
+
+    /**
+     * True iff this object has been disposed.
+     *
+     * @return true iff dispose() has been called
+     */
+    public bool isDisposed() {
+        return disposed;
+    }
+
+    /**
+     * Returns the currently visible range
+     *
+     * @return the currently visible range
+     */
+    public Range getVisibleRange() {
+        return lastRange;
+    }
+
+    /**
+     * Marks the given object as dirty. Will cause it to be cleared
+     * in the table.
+     *
+     * @param toFlush
+     */
+    public void clear(Object toFlush) {
+        synchronized(this) {
+            int currentIdx = knownIndices.get(toFlush, -1);
+
+            // If we've never heard of this object, bail out.
+            if (currentIdx is -1) {
+                return;
+            }
+
+            pushClear(currentIdx);
+        }
+
+    }
+
+    /**
+     * Sets the size of the table. Called from a background thread.
+     *
+     * @param newTotal
+     */
+    public void setTotalItems(int newTotal) {
+        synchronized (this) {
+            if (newTotal !is knownObjects.length) {
+                if (newTotal < knownObjects.length) {
+                    // Flush any objects that are being removed as a result of the resize
+                    for (int i = newTotal; i < knownObjects.length; i++) {
+                        Object toFlush = knownObjects[i];
+
+                        if (toFlush !is null) {
+                            knownIndices.remove(toFlush);
+                        }
+                    }
+                }
+
+                int minSize = Math.min(knownObjects.length, newTotal);
+
+                Object[] newKnownObjects = new Object[newTotal];
+                System.arraycopy(knownObjects, 0, newKnownObjects, 0, minSize);
+                knownObjects = newKnownObjects;
+
+                scheduleUIUpdate();
+            }
+        }
+    }
+
+    /**
+     * Pushes an index onto the clear stack
+     *
+     * @param toClear row to clear
+     */
+    private void pushClear(int toClear) {
+
+        // If beyond the end of the table
+        if (toClear >= sentObjects.length) {
+            return;
+        }
+
+        // If already flushed or never sent
+        if (sentObjects[toClear] is null) {
+            return;
+        }
+
+        // Mark as flushed
+        sentObjects[toClear] = null;
+
+        if (lastClear >= pendingClears.length) {
+            int newCapacity = Math.min(MIN_FLUSHLENGTH, lastClear * 2);
+            int[] newPendingClears = new int[newCapacity];
+            System.arraycopy(pendingClears, 0, newPendingClears, 0, lastClear);
+            pendingClears = newPendingClears;
+        }
+
+        pendingClears[lastClear++] = toClear;
+    }
+
+    /**
+     * Sets the item on the given row to the given value. May be called from a background
+     * thread. Schedules a UI update if necessary
+     *
+     * @param idx row to change
+     * @param value new value for the given row
+     */
+    public void replace(Object value, int idx) {
+        // Keep the synchronized block as small as possible, since the UI may
+        // be waiting on it.
+        synchronized(this) {
+            Object oldObject = knownObjects[idx];
+
+            if (oldObject !is value) {
+                if (oldObject !is null) {
+                    knownIndices.remove(oldObject);
+                }
+
+                knownObjects[idx] = value;
+
+                if (value !is null) {
+                    int oldIndex = knownIndices.get(value, -1);
+                    if (oldIndex !is -1) {
+                        knownObjects[oldIndex] = null;
+                        pushClear(oldIndex);
+                    }
+
+                    knownIndices.put(value, idx);
+                }
+
+                pushClear(idx);
+
+                scheduleUIUpdate();
+            }
+        }
+    }
+
+    /**
+     * Schedules a UI update. Has no effect if an update has already been
+     * scheduled.
+     */
+    private void scheduleUIUpdate() {
+        synchronized(this) {
+            if (!updateScheduled) {
+                updateScheduled = true;
+                if(!table.getControl().isDisposed()) {
+                    table.getControl().getDisplay().asyncExec(uiRunnable);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Called in the UI thread by a SetData callback. Refreshes the
+     * table if necessary. Returns true iff a refresh is needed.
+     * @param includeIndex the index that should be included in the visible range.
+     */
+    public void checkVisibleRange(int includeIndex) {
+        int start = Math.min(table.getTopIndex() - 1, includeIndex);
+        int length = Math.max(table.getVisibleItemCount(), includeIndex - start);
+        Range r = lastRange;
+
+        if (start !is r.start || length !is r.length) {
+            updateTable();
+        }
+    }
+
+    /**
+     * Updates the table. Sends any unsent items in the visible range to the table,
+     * and clears any previously-visible items that have not yet been sent to the table.
+     * Must be called from the UI thread.
+     */
+    private void updateTable() {
+
+        synchronized(this) {
+
+            // Resize the table if necessary
+            if (sentObjects.length !is knownObjects.length) {
+                Object[] newSentObjects = new Object[knownObjects.length];
+                System.arraycopy(newSentObjects, 0, sentObjects, 0,
+                        Math.min(newSentObjects.length, sentObjects.length));
+                sentObjects = newSentObjects;
+                table.setItemCount(newSentObjects.length);
+            }
+
+            // Compute the currently visible range
+            int start = Math.min(table.getTopIndex(), knownObjects.length);
+            int length = Math.min(table.getVisibleItemCount(), knownObjects.length - start);
+            int itemCount = table.getItemCount();
+
+            int oldStart = lastRange.start;
+            int oldLen = lastRange.length;
+
+            // Store the visible range. Do it BEFORE sending any table.clear calls,
+            // since clearing a visible row will result in a SetData callback which
+            // cause another table update if the visible range is different from
+            // the stored values -- this could cause infinite recursion.
+            lastRange = new Range(start, length);
+
+            // Re-clear any items in the old range that were never filled in
+            for(int idx = 0; idx < oldLen; idx++) {
+                int row = idx + oldStart;
+
+                // If this item is no longer visible
+                if (row < itemCount && (row < start || row >= start + length)) {
+
+                    // Note: if we wanted to be really aggressive about clearing
+                    // items that are no longer visible, we could clear here unconditionally.
+                    // The current way of doing things won't clear a row if its contents are
+                    // up-to-date.
+                    if (sentObjects[row] is null) {
+                        table.clear(row);
+                    }
+                }
+            }
+
+            // Process any pending clears
+            if (lastClear > 0) {
+                for (int i = 0; i < lastClear; i++) {
+                    int row = pendingClears[i];
+
+                    if (row < sentObjects.length) {
+                        table.clear(row);
+                    }
+                }
+
+                if (pendingClears.length > MIN_FLUSHLENGTH) {
+                    pendingClears = new int[MIN_FLUSHLENGTH];
+                }
+                lastClear = 0;
+            }
+
+            // Send any unsent items in the visible range
+            for (int idx = 0; idx < length; idx++) {
+                int row = idx + start;
+
+                Object obj = knownObjects[row];
+                if (obj !is null && obj !is sentObjects[idx]) {
+                    table.replace(obj, row);
+                    sentObjects[idx] = obj;
+                }
+            }
+
+        }
+    }
+
+    /**
+     * Return the array of all known objects that have been sent here from the background
+     * thread.
+     * @return the array of all known objects
+     */
+    public Object[] getKnownObjects() {
+        return knownObjects;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/DeferredContentProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,235 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.deferred.DeferredContentProvider;
+
+import org.eclipse.jface.viewers.ILazyContentProvider;
+import org.eclipse.jface.viewers.deferred.AbstractVirtualTable;
+import org.eclipse.jface.viewers.deferred.BackgroundContentProvider;
+import org.eclipse.jface.viewers.deferred.IConcurrentModel;
+
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.viewers.AcceptAllFilter;
+import org.eclipse.jface.viewers.IFilter;
+import org.eclipse.jface.viewers.ILazyContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Content provider that performs sorting and filtering in a background thread.
+ * Requires a <code>TableViewer</code> created with the <code>SWT.VIRTUAL</code>
+ * flag and an <code>IConcurrentModel</code> as input.
+ * <p>
+ * The sorter and filter must be set directly on the content provider.
+ * Any sorter or filter on the TableViewer will be ignored.
+ * </p>
+ *
+ * <p>
+ * The real implementation is in <code>BackgroundContentProvider</code>. This
+ * object is a lightweight wrapper that adapts the algorithm to work with
+ * <code>TableViewer</code>.
+ * </p>
+ *
+ * @since 3.1
+ */
+public class DeferredContentProvider : ILazyContentProvider {
+
+    private int limit = -1;
+    private BackgroundContentProvider provider;
+    private Comparator sortOrder;
+    private IFilter filter;
+
+    private AbstractVirtualTable table;
+
+    private static final class TableViewerAdapter : AbstractVirtualTable {
+
+        private TableViewer viewer;
+
+        /**
+         * @param viewer
+         */
+        public this(TableViewer viewer) {
+            this.viewer = viewer;
+        }
+
+        /* (non-Javadoc)
+         * @see org.eclipse.jface.viewers.deferred.AbstractVirtualTable#flushCache(java.lang.Object)
+         */
+        public override void clear(int index) {
+            viewer.clear(index);
+        }
+
+        /* (non-Javadoc)
+         * @see org.eclipse.jface.viewers.deferred.AbstractVirtualTable#replace(java.lang.Object, int)
+         */
+        public override void replace(Object element, int itemIndex) {
+            viewer.replace(element, itemIndex);
+        }
+
+        /* (non-Javadoc)
+         * @see org.eclipse.jface.viewers.deferred.AbstractVirtualTable#setItemCount(int)
+         */
+        public override void setItemCount(int total) {
+            viewer.setItemCount(total);
+        }
+
+        /* (non-Javadoc)
+         * @see org.eclipse.jface.viewers.deferred.AbstractVirtualTable#getItemCount()
+         */
+        public override int getItemCount() {
+            return viewer.getTable().getItemCount();
+        }
+
+        /* (non-Javadoc)
+         * @see org.eclipse.jface.viewers.deferred.AbstractVirtualTable#getTopIndex()
+         */
+        public override int getTopIndex() {
+            return Math.max(viewer.getTable().getTopIndex() - 1, 0);
+        }
+
+        /* (non-Javadoc)
+         * @see org.eclipse.jface.viewers.deferred.AbstractVirtualTable#getVisibleItemCount()
+         */
+        public override int getVisibleItemCount() {
+            Table table = viewer.getTable();
+            Rectangle rect = table.getClientArea ();
+            int itemHeight = table.getItemHeight ();
+            int headerHeight = table.getHeaderHeight ();
+            return (rect.height - headerHeight + itemHeight - 1) / (itemHeight + table.getGridLineWidth());
+        }
+
+        /* (non-Javadoc)
+         * @see org.eclipse.jface.viewers.deferred.AbstractVirtualTable#getControl()
+         */
+        public override Control getControl() {
+            return viewer.getControl();
+        }
+
+    }
+
+    /**
+     * Create a DeferredContentProvider with the given sort order.
+     * @param sortOrder a comparator that sorts the content.
+     */
+    public this(Comparator sortOrder) {
+        this.filter = AcceptAllFilter.getInstance();
+        this.sortOrder = sortOrder;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.IContentProvider#dispose()
+     */
+    public void dispose() {
+        setProvider(null);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
+     */
+    public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+        if (newInput is null) {
+            setProvider(null);
+            return;
+        }
+
+        Assert.isTrue(null !is cast(IConcurrentModel)newInput );
+        Assert.isTrue(null !is cast(TableViewer)viewer );
+        IConcurrentModel model = cast(IConcurrentModel)newInput;
+
+        this.table = new TableViewerAdapter(cast(TableViewer)viewer);
+
+        BackgroundContentProvider newProvider = new BackgroundContentProvider(
+                table,
+                model, sortOrder);
+
+        setProvider(newProvider);
+
+        newProvider.setLimit(limit);
+        newProvider.setFilter(filter);
+    }
+
+    /**
+     * Sets the sort order for this content provider. This sort order takes priority
+     * over anything that was supplied to the <code>TableViewer</code>.
+     *
+     * @param sortOrder new sort order. The comparator must be able to support being
+     * used in a background thread.
+     */
+    public void setSortOrder(Comparator sortOrder) {
+        Assert.isNotNull(cast(Object)sortOrder);
+        this.sortOrder = sortOrder;
+        if (provider !is null) {
+            provider.setSortOrder(sortOrder);
+        }
+    }
+
+    /**
+     * Sets the filter for this content provider. This filter takes priority over
+     * anything that was supplied to the <code>TableViewer</code>. The filter
+     * must be capable of being used in a background thread.
+     *
+     * @param toSet filter to set
+     */
+    public void setFilter(IFilter toSet) {
+        this.filter = toSet;
+        if (provider !is null) {
+            provider.setFilter(toSet);
+        }
+    }
+
+    /**
+     * Sets the maximum number of rows in the table. If the model contains more
+     * than this number of elements, only the top elements will be shown based on
+     * the current sort order.
+     *
+     * @param limit maximum number of rows to show or -1 if unbounded
+     */
+    public void setLimit(int limit) {
+        this.limit = limit;
+        if (provider !is null) {
+            provider.setLimit(limit);
+        }
+    }
+
+    /**
+     * Returns the current maximum number of rows or -1 if unbounded
+     *
+     * @return the current maximum number of rows or -1 if unbounded
+     */
+    public int getLimit() {
+        return limit;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.ILazyContentProvider#updateElement(int)
+     */
+    public void updateElement(int element) {
+        if (provider !is null) {
+            provider.checkVisibleRange(element);
+        }
+    }
+
+    private void setProvider(BackgroundContentProvider newProvider) {
+        if (provider !is null) {
+            provider.dispose();
+        }
+
+        provider = newProvider;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/FastProgressReporter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.deferred.FastProgressReporter;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A more efficient alternative to an IProgressMonitor. In particular, the implementation
+ * is designed to make isCanceled() run as efficiently as possible. Currently package-visible
+ * because the implementation is incomplete.
+ *
+ * @since 3.1
+ */
+final class FastProgressReporter {
+    private IProgressMonitor monitor;
+    private /+volatile+/ bool canceled = false;
+    private int cancelCheck = 0;
+//    private String taskName;
+//
+//    private int taskDepth = 0;
+//    private int subTaskSize = 1;
+//    private int totalWork = 1;
+//    private int parentWork = 1;
+//    private int monitorUnitsRemaining;
+
+    private static int CANCEL_CHECK_PERIOD = 40;
+
+    /**
+     * Constructs a null FastProgressReporter
+     */
+    public this() {
+    }
+
+    /**
+     * Constructs a FastProgressReporter that wraps the given progress monitor
+     *
+     * @param monitor the monitor to wrap
+     * @param totalProgress the total progress to be reported
+     */
+    public this(IProgressMonitor monitor, int totalProgress) {
+        this.monitor = monitor;
+        //monitorUnitsRemaining = totalProgress;
+        canceled = monitor.isCanceled();
+    }
+
+//    /**
+//     * Every call to beginTask must have a corresponding call to endTask, with the
+//     * same argument.
+//     *
+//     * @param totalWork
+//     * @since 3.1
+//     */
+//    public void beginTask(int totalWork) {
+//
+//        if (monitor is null) {
+//            return;
+//        }
+//
+//        taskDepth++;
+//
+//        if (totalWork is 0) {
+//            return;
+//        }
+//
+//        this.totalWork *= totalWork;
+//    }
+//
+//    public void beginSubTask(int subTaskWork) {
+//        subTaskSize *= subTaskWork;
+//    }
+//
+//    public void endSubTask(int subTaskWork) {
+//        subTaskSize /= subTaskWork;
+//    }
+//
+//    public void worked(int amount) {
+//        amount *= subTaskSize;
+//
+//        if (amount > totalWork) {
+//            amount = totalWork;
+//        }
+//
+//        int consumed = monitorUnitsRemaining * amount / totalWork;
+//
+//        if (consumed > 0) {
+//            monitor.worked(consumed);
+//            monitorUnitsRemaining -= consumed;
+//        }
+//        totalWork -= amount;
+//    }
+//
+//    public void endTask(int totalWork) {
+//        taskDepth--;
+//
+//        if (taskDepth is 0) {
+//            if (monitor !is null && monitorUnitsRemaining > 0) {
+//                monitor.worked(monitorUnitsRemaining);
+//            }
+//        }
+//
+//        if (totalWork is 0) {
+//            return;
+//        }
+//
+//        this.totalWork /= totalWork;
+//
+//    }
+
+    /**
+     * Return whether the progress monitor has been canceled.
+     *
+     * @return <code>true</code> if the monitor has been cancelled, <code>false</code> otherwise.
+     */
+    public bool isCanceled() {
+        if (monitor is null) {
+            return canceled;
+        }
+
+        cancelCheck++;
+        if (cancelCheck > CANCEL_CHECK_PERIOD) {
+            canceled = monitor.isCanceled();
+            cancelCheck = 0;
+        }
+        return canceled;
+    }
+
+    /**
+     * Cancel the progress monitor.
+     */
+    public void cancel() {
+        canceled = true;
+
+        if (monitor is null) {
+            return;
+        }
+        monitor.setCanceled(true);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/IConcurrentModel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.deferred.IConcurrentModel;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.deferred.IConcurrentModelListener;
+
+/**
+ * Interface for a set of unordered elements that can fire change notifications.
+ * IConcurrentModel returns its contents asynchronous. Rather than implementing
+ * "get" methods, listeners can request an update and the model fires back
+ * information at its earliest convenience.
+ *
+ * <p>
+ * The model is allowed to send back notifications to its listeners in any thread,
+ * and the listeners must not assume that the notifications will arrive in the UI
+ * thread.
+ * </p>
+ *
+ * <p>
+ * Not intended to be implemented by clients. Clients should subclass
+ * <code>AbstractConcurrentModel</code> instead.
+ * </p>
+ *
+ * @since 3.1
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IConcurrentModel {
+
+    /**
+     * Requests that the receiver to call the given listener's setContents(...)
+     * method at its earliest convenience. The receiver is allowed to compute the
+     * elements asynchronously. That is, it can compute the result in a background
+     * thread and call setContents(...) once the result is ready. If the result is
+     * too large to return in one batch, it can call setContents with an empty array
+     * followed by a sequence of adds.
+     * <p>
+     * Has no effect if an update is already queued for an identical listener.
+     * </p>
+     *
+     * @param listener listener whose setContents method should be called. The
+     * listener must have been previously registered with addListener.
+     */
+    public void requestUpdate(IConcurrentModelListener listener);
+
+    /**
+     * Adds a listener to this model. The listener should be given the model's
+     * current contents (either through setContents or a sequence of adds) at the
+     * receiver's earliest convenience. The receiver will notify the listener
+     * about any changes in state until the listener is removed.
+     *
+     * <p>
+     * Has no effect if an identical listener is already registered.
+     * </p>
+     *
+     * @param listener listener to add
+     */
+    public void addListener(IConcurrentModelListener listener);
+
+    /**
+     * Removes a listener from this model. The receiver will stop sending
+     * notifications to the given listener as soon as possible (although
+     * some additional notifications may still if arrive if the receiver
+     * was in the process of sending notifications in another thread).
+     * Any pending updates for this listener will be cancelled.
+     * <p>
+     * Has no effect if the given listener is not known to this model.
+     * </p>
+     *
+     * @param listener listener to remove
+     */
+    public void removeListener(IConcurrentModelListener listener);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/IConcurrentModelListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.viewers.deferred.IConcurrentModelListener;
+
+import java.lang.all;
+
+/**
+ * Interface for objects that can listen to changes in an IConcurrentModel.
+ * Elements in an IConcurrentModel are unordered.
+ * 
+ * @since 3.1
+ */
+public interface IConcurrentModelListener {
+    
+    /**
+     * Called when elements are added to the model 
+     * 
+     * @param added elements added to the model
+     */
+    public void add(Object[] added);
+    
+    /**
+     * Called when elements are removed from the model
+     * 
+     * @param removed elements removed from the model
+     */
+    public void remove(Object[] removed);
+    
+    /**
+     * Called when elements in the model have changed
+     * 
+     * @param changed elements that have changed
+     */
+    public void update(Object[] changed);
+    
+    /**
+     * Notifies the receiver about the complete set
+     * of elements in the model. Most models will
+     * not call this method unless the listener explicitly
+     * requests it by calling 
+     * <code>IConcurrentModel.requestUpdate</code>
+     *  
+     * @param newContents contents of the model
+     */
+    public void setContents(Object[] newContents);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/IntHashMap.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.deferred.IntHashMap;
+
+import java.lang.all;
+import java.util.HashMap;
+
+
+/**
+ * Represents a map of objects onto ints. This is intended for future optimization:
+ * using int primitives would allow for an implementation that doesn't require
+ * additional object allocations for Integers. However, the current implementation
+ * simply delegates to the Java HashMap class.
+ *
+ * @since 3.1
+ */
+/* package */ class IntHashMap {
+    private int[Object] map;
+
+    /**
+     * @param size
+     * @param loadFactor
+     */
+    public this(int size, float loadFactor) {
+//         map = new HashMap(size,loadFactor);
+    }
+
+    /**
+     *
+     */
+    public this() {
+//         map = new HashMap();
+    }
+
+    /**
+     * @param key
+     */
+    public void remove(Object key) {
+        map.remove(key);
+    }
+
+    /**
+     * @param key
+     * @param value
+     */
+    public void put(Object key, int value) {
+        map[key] = value;
+    }
+
+    /**
+     * @param key
+     * @return the int value at the given key
+     */
+    public int get(Object key) {
+        return get(key, 0);
+    }
+
+    /**
+     * @param key
+     * @param defaultValue
+     * @return the int value at the given key, or the default value if this map does not contain the given key
+     */
+    public int get(Object key, int defaultValue) {
+        if( auto res = key in map ){
+            return *res;
+        }
+        return defaultValue;
+    }
+
+    /**
+     * @param key
+     * @return <code>true</code> if this map contains the given key, <code>false</code> otherwise
+     */
+    public bool containsKey(Object key) {
+        if( auto res = key in map ){
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @return the number of key/value pairs
+     */
+    public int size() {
+        return map.length;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/LazySortedCollection.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1452 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.deferred.LazySortedCollection;
+
+import org.eclipse.jface.viewers.deferred.IntHashMap;
+import org.eclipse.jface.viewers.deferred.FastProgressReporter;
+// import java.util.Collection;
+// import java.util.Comparator;
+// import java.util.Iterator;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * This object maintains a collection of elements, sorted by a comparator
+ * given in the constructor. The collection is lazily sorted, allowing
+ * more efficient runtimes for most methods. There are several methods on this
+ * object that allow objects to be queried by their position in the sorted
+ * collection.
+ *
+ * <p>
+ * This is a modified binary search tree. Each subtree has a value, a left and right subtree,
+ * a count of the number of children, and a set of unsorted children.
+ * Insertion happens lazily. When a new node N is inserted into a subtree T, it is initially
+ * added to the set of unsorted children for T without actually comparing it with the value for T.
+ * </p>
+ * <p>
+ * The unsorted children will remain in the unsorted set until some subsequent operation requires
+ * us to know the exact set of elements in one of the subtrees. At that time, we partition
+ * T by comparing all of its unsorted children with T's value and moving them into the left
+ * or right subtrees.
+ * </p>
+ *
+ * @since 3.1
+ */
+public class LazySortedCollection {
+    private const int MIN_CAPACITY = 8;
+    private Object[] contents;
+    private int[] leftSubTree;
+    private int[] rightSubTree;
+    private int[] nextUnsorted;
+    private int[] treeSize;
+    private int[] parentTree;
+    private int root = -1;
+    private int lastNode = 0;
+    private int firstUnusedNode = -1;
+
+    private static const float loadFactor = 0.75f;
+
+    private IntHashMap objectIndices;
+    private Comparator comparator;
+    private static int counter = 0;
+
+    /**
+     * Disables randomization and enables additional runtime error checking.
+     * Severely degrades performance if set to true. Intended for use in test
+     * suites only.
+     */
+    public bool enableDebug = false;
+
+    // This object is inserted as the value into any node scheduled for lazy removal
+    private Object lazyRemovalFlag;
+
+    private void init_instance(){
+        contents = new Object[MIN_CAPACITY];
+        leftSubTree = new int[MIN_CAPACITY];
+        rightSubTree = new int[MIN_CAPACITY];
+        nextUnsorted = new int[MIN_CAPACITY];
+        treeSize = new int[MIN_CAPACITY];
+        parentTree = new int[MIN_CAPACITY];
+        lazyRemovalFlag = new class Object {
+            public String toString() {
+                return "Lazy removal flag";  //$NON-NLS-1$
+            }
+        };
+    }
+
+    private const static int DIR_LEFT = 0;
+    private const static int DIR_RIGHT = 1;
+    private const static int DIR_UNSORTED = 2;
+
+    // Direction constants indicating root nodes
+    private const static int DIR_ROOT = 3;
+    private const static int DIR_UNUSED = 4;
+
+    private final class Edge {
+        private int startNode;
+        private int direction;
+
+        private this() {
+            startNode = -1;
+            direction = -1;
+        }
+
+        private this(int node, int dir) {
+            startNode = node;
+            direction = dir;
+        }
+
+        private int getStart() {
+            return startNode;
+        }
+
+        private int getTarget() {
+            if (startNode is -1) {
+                if (direction is DIR_UNSORTED) {
+                    return firstUnusedNode;
+                } else if (direction is DIR_ROOT) {
+                    return root;
+                }
+                return -1;
+            }
+
+            if (direction is DIR_LEFT) {
+                return leftSubTree[startNode];
+            }
+            if (direction is DIR_RIGHT) {
+                return rightSubTree[startNode];
+            }
+            return nextUnsorted[startNode];
+        }
+
+        private bool isNull() {
+            return getTarget() is -1;
+        }
+
+        /**
+         * Redirects this edge to a new node
+         * @param newNode
+         * @since 3.1
+         */
+        private void setTarget(int newNode) {
+            if (direction is DIR_LEFT) {
+                leftSubTree[startNode] = newNode;
+            } else if (direction is DIR_RIGHT) {
+                rightSubTree[startNode] = newNode;
+            } else if (direction is DIR_UNSORTED) {
+                nextUnsorted[startNode] = newNode;
+            } else if (direction is DIR_ROOT) {
+                root = newNode;
+            } else if (direction is DIR_UNUSED) {
+                firstUnusedNode = newNode;
+            }
+
+            if (newNode !is -1) {
+                parentTree[newNode] = startNode;
+            }
+        }
+
+        private void advance(int direction) {
+            startNode = getTarget();
+            this.direction = direction;
+        }
+    }
+
+    private void setRootNode(int node) {
+        root = node;
+        if (node !is -1) {
+            parentTree[node] = -1;
+        }
+    }
+
+    /**
+     * Creates a new sorted collection using the given comparator to determine
+     * sort order.
+     *
+     * @param c comparator that determines the sort order
+     */
+    public this(Comparator c) {
+        this.comparator = c;
+        init_instance();
+    }
+
+    /**
+     * Tests if this object's internal state is valid. Throws a runtime
+     * exception if the state is invalid, indicating a programming error
+     * in this class. This method is intended for use in test
+     * suites and should not be called by clients.
+     */
+    public void testInvariants() {
+        if (!enableDebug) {
+            return;
+        }
+
+        testInvariants(root);
+    }
+
+    private void testInvariants(int node) {
+        if (node is -1) {
+            return;
+        }
+
+        // Get the current tree size (we will later force the tree size
+        // to be recomputed from scratch -- if everything works properly, then
+        // there should be no change.
+        int treeSize = getSubtreeSize(node);
+
+        int left = leftSubTree[node];
+        int right = rightSubTree[node];
+        int unsorted = nextUnsorted[node];
+
+        if (isUnsorted(node)) {
+            Assert.isTrue(left is -1, "unsorted nodes shouldn't have a left subtree"); //$NON-NLS-1$
+            Assert.isTrue(right is -1, "unsorted nodes shouldn't have a right subtree"); //$NON-NLS-1$
+        }
+
+        if (left !is -1) {
+            testInvariants(left);
+            Assert.isTrue(parentTree[left] is node, "left node has invalid parent pointer"); //$NON-NLS-1$
+        }
+        if (right !is -1) {
+            testInvariants(right);
+            Assert.isTrue(parentTree[right] is node, "right node has invalid parent pointer");             //$NON-NLS-1$
+        }
+
+        int previous = node;
+        while (unsorted !is -1) {
+            int oldTreeSize = this.treeSize[unsorted];
+            recomputeTreeSize(unsorted);
+
+            Assert.isTrue(this.treeSize[unsorted] is oldTreeSize,
+                    "Invalid node size for unsorted node"); //$NON-NLS-1$
+            Assert.isTrue(leftSubTree[unsorted] is -1, "unsorted nodes shouldn't have left subtrees"); //$NON-NLS-1$
+            Assert.isTrue(rightSubTree[unsorted] is -1, "unsorted nodes shouldn't have right subtrees"); //$NON-NLS-1$
+            Assert.isTrue(parentTree[unsorted] is previous, "unsorted node has invalid parent pointer"); //$NON-NLS-1$
+            Assert.isTrue(contents[unsorted] !is lazyRemovalFlag, "unsorted nodes should not be lazily removed"); //$NON-NLS-1$
+            previous = unsorted;
+            unsorted = nextUnsorted[unsorted];
+        }
+
+        // Note that we've already tested that the child sizes are correct... if our size is
+        // correct, then recomputing it now should not cause any change.
+        recomputeTreeSize(node);
+
+        Assert.isTrue(treeSize is getSubtreeSize(node), "invalid tree size"); //$NON-NLS-1$
+    }
+
+    private bool isUnsorted(int node) {
+        int parent = parentTree[node];
+
+        if (parent !is -1) {
+            return nextUnsorted[parent] is node;
+        }
+
+        return false;
+    }
+
+    private final bool isLess(int element1, int element2) {
+        return comparator.compare(contents[element1], contents[element2]) < 0;
+    }
+
+    /**
+     * Adds the given element to the given subtree. Returns the new
+     * root of the subtree.
+     *
+     * @param subTree index of the subtree to insert elementToAdd into. If -1,
+     *                then a new subtree will be created for elementToAdd
+     * @param elementToAdd index of the element to add to the subtree. If -1, this method
+     *                 is a NOP.
+     * @since 3.1
+     */
+    private final int addUnsorted(int subTree, int elementToAdd) {
+        if (elementToAdd is -1) {
+            return subTree;
+        }
+
+        if (subTree is -1) {
+            nextUnsorted[elementToAdd] = -1;
+            treeSize[elementToAdd] = 1;
+            return elementToAdd;
+        }
+
+        // If the subTree is empty (ie: it only contains nodes flagged for lazy removal),
+        // chop it off.
+        if (treeSize[subTree] is 0) {
+            removeSubTree(subTree);
+            nextUnsorted[elementToAdd] = -1;
+            treeSize[elementToAdd] = 1;
+            return elementToAdd;
+        }
+
+        // If neither subtree has any children, add a pseudorandom chance of the
+        // newly added element becoming the new pivot for this node. Note: instead
+        // of a real pseudorandom generator, we simply use a counter here.
+        if (!enableDebug && leftSubTree[subTree] is -1 && rightSubTree[subTree] is -1
+                && leftSubTree[elementToAdd] is -1 && rightSubTree[elementToAdd] is -1) {
+            counter--;
+
+            if (counter % treeSize[subTree] is 0) {
+                // Make the new node into the new pivot
+                nextUnsorted[elementToAdd] = subTree;
+                parentTree[elementToAdd] = parentTree[subTree];
+                parentTree[subTree] = elementToAdd;
+                treeSize[elementToAdd] = treeSize[subTree] + 1;
+                return elementToAdd;
+            }
+        }
+
+        int oldNextUnsorted = nextUnsorted[subTree];
+        nextUnsorted[elementToAdd] = oldNextUnsorted;
+
+        if (oldNextUnsorted is -1) {
+            treeSize[elementToAdd] = 1;
+        } else {
+            treeSize[elementToAdd] = treeSize[oldNextUnsorted] + 1;
+            parentTree[oldNextUnsorted] = elementToAdd;
+        }
+
+        parentTree[elementToAdd] = subTree;
+
+        nextUnsorted[subTree] = elementToAdd;
+        treeSize[subTree]++;
+        return subTree;
+    }
+
+    /**
+     * Returns the number of elements in the collection
+     *
+     * @return the number of elements in the collection
+     */
+    public int size() {
+        int result = getSubtreeSize(root);
+
+        testInvariants();
+
+        return result;
+    }
+
+    /**
+     * Given a tree and one of its unsorted children, this sorts the child by moving
+     * it into the left or right subtrees. Returns the next unsorted child or -1 if none
+     *
+     * @param subTree parent tree
+     * @param toMove child (unsorted) subtree
+     * @since 3.1
+     */
+    private final int partition(int subTree, int toMove) {
+        int result = nextUnsorted[toMove];
+
+        if (isLess(toMove, subTree)) {
+            int nextLeft = addUnsorted(leftSubTree[subTree], toMove);
+            leftSubTree[subTree] = nextLeft;
+            parentTree[nextLeft] = subTree;
+        } else {
+            int nextRight = addUnsorted(rightSubTree[subTree], toMove);
+            rightSubTree[subTree] = nextRight;
+            parentTree[nextRight] = subTree;
+        }
+
+        return result;
+    }
+
+    /**
+     * Partitions the given subtree. Moves all unsorted elements at the given node
+     * to either the left or right subtrees. If the node itself was scheduled for
+     * lazy removal, this will force the node to be removed immediately. Returns
+     * the new subTree.
+     *
+     * @param subTree
+     * @return the replacement node (this may be different from subTree if the subtree
+     * was replaced during the removal)
+     * @since 3.1
+     */
+    private final int partition(int subTree, FastProgressReporter mon) {
+        if (subTree is -1) {
+            return -1;
+        }
+
+        if (contents[subTree] is lazyRemovalFlag) {
+            subTree = removeNode(subTree);
+            if (subTree is -1) {
+                return -1;
+            }
+        }
+
+        for (int idx = nextUnsorted[subTree]; idx !is -1;) {
+            idx = partition(subTree, idx);
+            nextUnsorted[subTree] = idx;
+            if (idx !is -1) {
+                parentTree[idx] = subTree;
+            }
+
+            if (mon.isCanceled()) {
+                throw new InterruptedException();
+            }
+        }
+
+        // At this point, there are no remaining unsorted nodes in this subtree
+        nextUnsorted[subTree] = -1;
+
+        return subTree;
+    }
+
+    private final int getSubtreeSize(int subTree) {
+        if (subTree is -1) {
+            return 0;
+        }
+        return treeSize[subTree];
+    }
+
+    /**
+     * Increases the capacity of this collection, if necessary, so that it can hold the
+     * given number of elements. This can be used prior to a sequence of additions to
+     * avoid memory reallocation. This cannot be used to reduce the amount
+     * of memory used by the collection.
+     *
+     * @param newSize capacity for this collection
+     */
+    public final void setCapacity(int newSize) {
+        if (newSize > contents.length) {
+            setArraySize(newSize);
+        }
+    }
+
+    /**
+     * Adjusts the capacity of the array.
+     *
+     * @param newCapacity
+     */
+    private final void setArraySize(int newCapacity) {
+        Object[] newContents = new Object[newCapacity];
+        System.arraycopy(contents, 0, newContents, 0, lastNode);
+        contents = newContents;
+
+        int[] newLeftSubTree = new int[newCapacity];
+        System.arraycopy(leftSubTree, 0, newLeftSubTree, 0, lastNode);
+        leftSubTree = newLeftSubTree;
+
+        int[] newRightSubTree = new int[newCapacity];
+        System.arraycopy(rightSubTree, 0, newRightSubTree, 0, lastNode);
+        rightSubTree = newRightSubTree;
+
+        int[] newNextUnsorted = new int[newCapacity];
+        System.arraycopy(nextUnsorted, 0, newNextUnsorted, 0, lastNode);
+        nextUnsorted = newNextUnsorted;
+
+        int[] newTreeSize = new int[newCapacity];
+        System.arraycopy(treeSize, 0, newTreeSize, 0, lastNode);
+        treeSize = newTreeSize;
+
+        int[] newParentTree = new int[newCapacity];
+        System.arraycopy(parentTree, 0, newParentTree, 0, lastNode);
+        parentTree = newParentTree;
+    }
+
+    /**
+     * Creates a new node with the given value. Returns the index of the newly
+     * created node.
+     *
+     * @param value
+     * @return the index of the newly created node
+     * @since 3.1
+     */
+    private final int createNode(Object value) {
+        int result = -1;
+
+        if (firstUnusedNode is -1) {
+            // If there are no unused nodes from prior removals, then
+            // we add a node at the end
+            result = lastNode;
+
+            // If this would cause the array to overflow, reallocate the array
+            if (contents.length <= lastNode) {
+                setCapacity(lastNode * 2);
+            }
+
+            lastNode++;
+        } else {
+            // Reuse a node from a prior removal
+            result = firstUnusedNode;
+            firstUnusedNode = nextUnsorted[result];
+        }
+
+        contents[result] = value;
+        treeSize[result] = 1;
+
+        // Clear pointers
+        leftSubTree[result] = -1;
+        rightSubTree[result] = -1;
+        nextUnsorted[result] = -1;
+
+        // As long as we have a hash table of values onto tree indices, incrementally
+        // update the hash table. Note: the table is only constructed as needed, and it
+        // is destroyed whenever the arrays are reallocated instead of reallocating it.
+        if (objectIndices !is null) {
+            objectIndices.put(value, result);
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns the current tree index for the given object.
+     *
+     * @param value
+     * @return the current tree index
+     * @since 3.1
+     */
+    private int getObjectIndex(Object value) {
+        // If we don't have a map of values onto tree indices, build the map now.
+        if (objectIndices is null) {
+            int result = -1;
+
+            objectIndices = new IntHashMap(cast(int)(contents.length / loadFactor) + 1, loadFactor);
+
+            for (int i = 0; i < lastNode; i++) {
+                Object element = contents[i];
+
+                if (element !is null && element !is lazyRemovalFlag) {
+                    objectIndices.put(element, i);
+
+                    if (value is element) {
+                        result = i;
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        // If we have a map of values onto tree indices, return the result by looking it up in
+        // the map
+        return objectIndices.get(value, -1);
+    }
+
+    /**
+     * Redirects any pointers from the original to the replacement. If the replacement
+     * causes a change in the number of elements in the parent tree, the changes are
+     * propogated toward the root.
+     *
+     * @param nodeToReplace
+     * @param replacementNode
+     * @since 3.1
+     */
+    private void replaceNode(int nodeToReplace, int replacementNode) {
+        int parent = parentTree[nodeToReplace];
+
+        if (parent is -1) {
+            if (root is nodeToReplace) {
+                setRootNode(replacementNode);
+            }
+        } else {
+            if (leftSubTree[parent] is nodeToReplace) {
+                leftSubTree[parent] = replacementNode;
+            } else if (rightSubTree[parent] is nodeToReplace) {
+                rightSubTree[parent] = replacementNode;
+            } else if (nextUnsorted[parent] is nodeToReplace) {
+                nextUnsorted[parent] = replacementNode;
+            }
+            if (replacementNode !is -1) {
+                parentTree[replacementNode] = parent;
+            }
+        }
+    }
+
+    private void recomputeAncestorTreeSizes(int node) {
+        while (node !is -1) {
+            int oldSize = treeSize[node];
+
+            recomputeTreeSize(node);
+
+            if (treeSize[node] is oldSize) {
+                break;
+            }
+
+            node = parentTree[node];
+        }
+    }
+
+    /**
+     * Recomputes the tree size for the given node.
+     *
+     * @param node
+     * @since 3.1
+     */
+    private void recomputeTreeSize(int node) {
+        if (node is -1) {
+            return;
+        }
+        treeSize[node] = getSubtreeSize(leftSubTree[node])
+            + getSubtreeSize(rightSubTree[node])
+            + getSubtreeSize(nextUnsorted[node])
+            + (contents[node] is lazyRemovalFlag ? 0 : 1);
+    }
+
+    /**
+     *
+     * @param toRecompute
+     * @param whereToStop
+     * @since 3.1
+     */
+    private void forceRecomputeTreeSize(int toRecompute, int whereToStop) {
+        while (toRecompute !is -1 && toRecompute !is whereToStop) {
+            recomputeTreeSize(toRecompute);
+
+            toRecompute = parentTree[toRecompute];
+        }
+    }
+
+    /**
+     * Destroy the node at the given index in the tree
+     * @param nodeToDestroy
+     * @since 3.1
+     */
+    private void destroyNode(int nodeToDestroy) {
+        // If we're maintaining a map of values onto tree indices, remove this entry from
+        // the map
+        if (objectIndices !is null) {
+            Object oldContents = contents[nodeToDestroy];
+            if (oldContents !is lazyRemovalFlag) {
+                objectIndices.remove(oldContents);
+            }
+        }
+
+        contents[nodeToDestroy] = null;
+        leftSubTree[nodeToDestroy] = -1;
+        rightSubTree[nodeToDestroy] = -1;
+
+        if (firstUnusedNode is -1) {
+            treeSize[nodeToDestroy] = 1;
+        } else {
+            treeSize[nodeToDestroy] = treeSize[firstUnusedNode] + 1;
+            parentTree[firstUnusedNode] = nodeToDestroy;
+        }
+
+        nextUnsorted[nodeToDestroy] = firstUnusedNode;
+
+        firstUnusedNode = nodeToDestroy;
+    }
+
+    /**
+     * Frees up memory by clearing the list of nodes that have been freed up through removals.
+     *
+     * @since 3.1
+     */
+    private final void pack() {
+
+        // If there are no unused nodes, then there is nothing to do
+        if (firstUnusedNode is -1) {
+            return;
+        }
+
+        int reusableNodes = getSubtreeSize(firstUnusedNode);
+        int nonPackableNodes = lastNode - reusableNodes;
+
+        // Only pack the array if we're utilizing less than 1/4 of the array (note:
+        // this check is important, or it will change the time bounds for removals)
+        if (contents.length < MIN_CAPACITY || nonPackableNodes > contents.length / 4) {
+            return;
+        }
+
+        // Rather than update the entire map, just null it out. If it is needed,
+        // it will be recreated lazily later. This will save some memory if the
+        // map isn't needed, and it takes a similar amount of time to recreate the
+        // map as to update all the indices.
+        objectIndices = null;
+
+        // Maps old index -> new index
+        int[] mapNewIdxOntoOld = new int[contents.length];
+        int[] mapOldIdxOntoNew = new int[contents.length];
+
+        int nextNewIdx = 0;
+        // Compute the mapping. Determine the new index for each element
+        for (int oldIdx = 0; oldIdx < lastNode; oldIdx++) {
+            if (contents[oldIdx] !is null) {
+                mapOldIdxOntoNew[oldIdx] = nextNewIdx;
+                mapNewIdxOntoOld[nextNewIdx] = oldIdx;
+                nextNewIdx++;
+            } else {
+                mapOldIdxOntoNew[oldIdx] = -1;
+            }
+        }
+
+        // Make the actual array size double the number of nodes to allow
+        // for expansion.
+        int newNodes = nextNewIdx;
+        int newCapacity = Math.max(newNodes * 2, MIN_CAPACITY);
+
+        // Allocate new arrays
+        Object[] newContents = new Object[newCapacity];
+        int[] newTreeSize = new int[newCapacity];
+        int[] newNextUnsorted = new int[newCapacity];
+        int[] newLeftSubTree = new int[newCapacity];
+        int[] newRightSubTree = new int[newCapacity];
+        int[] newParentTree = new int[newCapacity];
+
+        for (int newIdx = 0; newIdx < newNodes; newIdx++) {
+            int oldIdx = mapNewIdxOntoOld[newIdx];
+            newContents[newIdx] = contents[oldIdx];
+            newTreeSize[newIdx] = treeSize[oldIdx];
+
+            int left = leftSubTree[oldIdx];
+            if (left is -1) {
+                newLeftSubTree[newIdx] = -1;
+            } else {
+                newLeftSubTree[newIdx] = mapOldIdxOntoNew[left];
+            }
+
+            int right = rightSubTree[oldIdx];
+            if (right is -1) {
+                newRightSubTree[newIdx] = -1;
+            } else {
+                newRightSubTree[newIdx] = mapOldIdxOntoNew[right];
+            }
+
+            int unsorted = nextUnsorted[oldIdx];
+            if (unsorted is -1) {
+                newNextUnsorted[newIdx] = -1;
+            } else {
+                newNextUnsorted[newIdx] = mapOldIdxOntoNew[unsorted];
+            }
+
+            int parent = parentTree[oldIdx];
+            if (parent is -1) {
+                newParentTree[newIdx] = -1;
+            } else {
+                newParentTree[newIdx] = mapOldIdxOntoNew[parent];
+            }
+        }
+
+        contents = newContents;
+        nextUnsorted = newNextUnsorted;
+        treeSize = newTreeSize;
+        leftSubTree = newLeftSubTree;
+        rightSubTree = newRightSubTree;
+        parentTree = newParentTree;
+
+        if (root !is -1) {
+            root = mapOldIdxOntoNew[root];
+        }
+
+        // All unused nodes have been removed
+        firstUnusedNode = -1;
+        lastNode = newNodes;
+    }
+
+    /**
+     * Adds the given object to the collection. Runs in O(1) amortized time.
+     *
+     * @param toAdd object to add
+     */
+    public final void add(Object toAdd) {
+        Assert.isNotNull(toAdd);
+        // Create the new node
+        int newIdx = createNode(toAdd);
+
+        // Insert the new node into the root tree
+        setRootNode(addUnsorted(root, newIdx));
+
+        testInvariants();
+    }
+
+    /**
+     * Adds all items from the given collection to this collection
+     *
+     * @param toAdd objects to add
+     */
+    public final void addAll( Collection toAdd) {
+        Assert.isNotNull(cast(Object)toAdd);
+        Iterator iter = toAdd.iterator();
+        while (iter.hasNext()) {
+            add(iter.next());
+        }
+
+        testInvariants();
+    }
+
+    /**
+     * Adds all items from the given array to the collection
+     *
+     * @param toAdd objects to add
+     */
+    public final void addAll(Object[] toAdd) {
+//         Assert.isNotNull(toAdd);
+        for (int i = 0; i < toAdd.length; i++) {
+            Object object = toAdd[i];
+
+            add(object);
+        }
+
+        testInvariants();
+    }
+
+    /**
+     * Returns true iff the collection is empty
+     *
+     * @return true iff the collection contains no elements
+     */
+    public final bool isEmpty() {
+        bool result = (root is -1);
+
+        testInvariants();
+
+        return result;
+    }
+
+    /**
+     * Removes the given object from the collection. Has no effect if
+     * the element does not exist in this collection.
+     *
+     * @param toRemove element to remove
+     */
+    public final void remove(Object toRemove) {
+        internalRemove(toRemove);
+
+        pack();
+
+        testInvariants();
+    }
+
+    /**
+     * Internal implementation of remove. Removes the given element but does not
+     * pack the container after the removal.
+     *
+     * @param toRemove element to remove
+     */
+    private void internalRemove(Object toRemove) {
+        int objectIndex = getObjectIndex(toRemove);
+
+        if (objectIndex !is -1) {
+            int parent = parentTree[objectIndex];
+            lazyRemoveNode(objectIndex);
+            //Edge parentEdge = getEdgeTo(objectIndex);
+            //parentEdge.setTarget(lazyRemoveNode(objectIndex));
+            recomputeAncestorTreeSizes(parent);
+        }
+
+        //testInvariants();
+    }
+
+    /**
+     * Removes all elements in the given array from this collection.
+     *
+     * @param toRemove elements to remove
+     */
+    public final void removeAll(Object[] toRemove) {
+//         Assert.isNotNull(toRemove);
+
+        for (int i = 0; i < toRemove.length; i++) {
+            Object object = toRemove[i];
+
+            internalRemove(object);
+        }
+        pack();
+    }
+
+    /**
+     * Retains the n smallest items in the collection, removing the rest. When
+     * this method returns, the size of the collection will be n. Note that
+     * this is a no-op if n > the current size of the collection.
+     *
+     * Temporarily package visibility until the implementation of FastProgressReporter
+     * is finished.
+     *
+     * @param n number of items to retain
+     * @param mon progress monitor
+     * @throws InterruptedException if the progress monitor is cancelled in another thread
+     */
+    /* package */ final void retainFirst(int n, FastProgressReporter mon) {
+        int sz = size();
+
+        if (n >= sz) {
+            return;
+        }
+
+        removeRange(n, sz - n, mon);
+
+        testInvariants();
+    }
+
+    /**
+     * Retains the n smallest items in the collection, removing the rest. When
+     * this method returns, the size of the collection will be n. Note that
+     * this is a no-op if n > the current size of the collection.
+     *
+     * @param n number of items to retain
+     */
+    public final void retainFirst(int n) {
+        try {
+            retainFirst(n, new FastProgressReporter());
+        } catch (InterruptedException e) {
+        }
+
+        testInvariants();
+    }
+
+    /**
+     * Removes all elements in the given range from this collection.
+     * For example, removeRange(10, 3) would remove the 11th through 13th
+     * smallest items from the collection.
+     *
+     * @param first 0-based index of the smallest item to remove
+     * @param length number of items to remove
+     */
+    public final void removeRange(int first, int length) {
+        try {
+            removeRange(first, length, new FastProgressReporter());
+        } catch (InterruptedException e) {
+        }
+
+        testInvariants();
+    }
+
+    /**
+     * Removes all elements in the given range from this collection.
+     * For example, removeRange(10, 3) would remove the 11th through 13th
+     * smallest items from the collection.
+     *
+     * Temporarily package visiblity until the implementation of FastProgressReporter is
+     * finished.
+     *
+     * @param first 0-based index of the smallest item to remove
+     * @param length number of items to remove
+     * @param mon progress monitor
+     * @throws InterruptedException if the progress monitor is cancelled in another thread
+     */
+    /* package */ final void removeRange(int first, int length, FastProgressReporter mon) {
+        removeRange(root, first, length, mon);
+
+        pack();
+
+        testInvariants();
+    }
+
+    private final void removeRange(int node, int rangeStart, int rangeLength, FastProgressReporter mon) {
+        if (rangeLength is 0) {
+            return;
+        }
+
+        int size = getSubtreeSize(node);
+
+        if (size <= rangeStart) {
+            return;
+        }
+
+        // If we can chop off this entire subtree without any sorting, do so.
+        if (rangeStart is 0 && rangeLength >= size) {
+            removeSubTree(node);
+            return;
+        }
+        try {
+            // Partition any unsorted nodes
+            node = partition(node, mon);
+
+            int left = leftSubTree[node];
+            int leftSize = getSubtreeSize(left);
+
+            int toRemoveFromLeft = Math.min(leftSize - rangeStart, rangeLength);
+
+            // If we're removing anything from the left node
+            if (toRemoveFromLeft >= 0) {
+                removeRange(leftSubTree[node], rangeStart, toRemoveFromLeft, mon);
+
+                // Check if we're removing from both sides
+                int toRemoveFromRight = rangeStart + rangeLength - leftSize - 1;
+
+                if (toRemoveFromRight >= 0) {
+                    // Remove from right subtree
+                    removeRange(rightSubTree[node], 0, toRemoveFromRight, mon);
+
+                    // ... removing from both sides means we need to remove the node itself too
+                    removeNode(node);
+                    return;
+                }
+            } else {
+                // If removing from the right side only
+                removeRange(rightSubTree[node], rangeStart - leftSize - 1, rangeLength, mon);
+            }
+        } finally {
+            recomputeTreeSize(node);
+        }
+    }
+
+    /**
+     * Prunes the given subtree (and all child nodes, sorted or unsorted).
+     *
+     * @param subTree
+     * @since 3.1
+     */
+    private final void removeSubTree(int subTree) {
+        if (subTree is -1) {
+            return;
+        }
+
+        // Destroy all unsorted nodes
+        for (int next = nextUnsorted[subTree]; next !is -1;) {
+            int current = next;
+            next = nextUnsorted[next];
+
+            // Destroy this unsorted node
+            destroyNode(current);
+        }
+
+        // Destroy left subtree
+        removeSubTree(leftSubTree[subTree]);
+
+        // Destroy right subtree
+        removeSubTree(rightSubTree[subTree]);
+
+        replaceNode(subTree, -1);
+        // Destroy pivot node
+        destroyNode(subTree);
+    }
+
+    /**
+     * Schedules the node for removal. If the node can be removed in constant time,
+     * it is removed immediately.
+     *
+     * @param subTree
+     * @return the replacement node
+     * @since 3.1
+     */
+    private final int lazyRemoveNode(int subTree) {
+        int left = leftSubTree[subTree];
+        int right = rightSubTree[subTree];
+
+        // If this is a leaf node, remove it immediately
+        if (left is -1 && right is -1) {
+            int result = nextUnsorted[subTree];
+            replaceNode(subTree, result);
+            destroyNode(subTree);
+            return result;
+        }
+
+        // Otherwise, flag it for future removal
+        Object value = contents[subTree];
+        contents[subTree] = lazyRemovalFlag;
+        treeSize[subTree]--;
+        if (objectIndices !is null) {
+            objectIndices.remove(value);
+        }
+
+        return subTree;
+    }
+
+    /**
+     * Removes the given subtree, replacing it with one of its children.
+     * Returns the new root of the subtree
+     *
+     * @param subTree
+     * @return the index of the new root
+     * @since 3.1
+     */
+    private final int removeNode(int subTree) {
+        int left = leftSubTree[subTree];
+        int right = rightSubTree[subTree];
+
+        if (left is -1 || right is -1) {
+            int result = -1;
+
+            if (left is -1 && right is -1) {
+                // If this is a leaf node, replace it with its first unsorted child
+                result = nextUnsorted[subTree];
+            } else {
+                // Either the left or right child is missing -- replace with the remaining child
+                if (left is -1) {
+                    result = right;
+                } else {
+                    result = left;
+                }
+
+                try {
+                    result = partition(result, new FastProgressReporter());
+                } catch (InterruptedException e) {
+
+                }
+                if (result is -1) {
+                    result = nextUnsorted[subTree];
+                } else {
+                    int unsorted = nextUnsorted[subTree];
+                    nextUnsorted[result] = unsorted;
+                    int additionalNodes = 0;
+                    if (unsorted !is -1) {
+                        parentTree[unsorted] = result;
+                        additionalNodes = treeSize[unsorted];
+                    }
+                    treeSize[result] += additionalNodes;
+                }
+            }
+
+            replaceNode(subTree, result);
+            destroyNode(subTree);
+            return result;
+        }
+
+        // Find the edges that lead to the next-smallest and
+        // next-largest nodes
+        Edge nextSmallest = new Edge(subTree, DIR_LEFT);
+        while (!nextSmallest.isNull()) {
+            nextSmallest.advance(DIR_RIGHT);
+        }
+
+        Edge nextLargest = new Edge(subTree, DIR_RIGHT);
+        while (!nextLargest.isNull()) {
+            nextLargest.advance(DIR_LEFT);
+        }
+
+        // Index of the replacement node
+        int replacementNode = -1;
+
+        // Count of number of nodes moved to the right
+
+        int leftSize = getSubtreeSize(left);
+        int rightSize = getSubtreeSize(right);
+
+        // Swap with a child from the larger subtree
+        if (leftSize > rightSize) {
+            replacementNode = nextSmallest.getStart();
+
+            // Move any unsorted nodes that are larger than the replacement node into
+            // the left subtree of the next-largest node
+            Edge unsorted = new Edge(replacementNode, DIR_UNSORTED);
+            while (!unsorted.isNull()) {
+                int target = unsorted.getTarget();
+
+                if (!isLess(target, replacementNode)) {
+                    unsorted.setTarget(nextUnsorted[target]);
+                    nextLargest.setTarget(addUnsorted(nextLargest.getTarget(), target));
+                } else {
+                    unsorted.advance(DIR_UNSORTED);
+                }
+            }
+
+            forceRecomputeTreeSize(unsorted.getStart(), replacementNode);
+            forceRecomputeTreeSize(nextLargest.getStart(), subTree);
+        } else {
+            replacementNode = nextLargest.getStart();
+
+            // Move any unsorted nodes that are smaller than the replacement node into
+            // the right subtree of the next-smallest node
+            Edge unsorted = new Edge(replacementNode, DIR_UNSORTED);
+            while (!unsorted.isNull()) {
+                int target = unsorted.getTarget();
+
+                if (isLess(target, replacementNode)) {
+                    unsorted.setTarget(nextUnsorted[target]);
+                    nextSmallest.setTarget(addUnsorted(nextSmallest.getTarget(), target));
+                } else {
+                    unsorted.advance(DIR_UNSORTED);
+                }
+            }
+
+            forceRecomputeTreeSize(unsorted.getStart(), replacementNode);
+            forceRecomputeTreeSize(nextSmallest.getStart(), subTree);
+        }
+
+        // Now all the affected treeSize[...] elements should be updated to reflect the
+        // unsorted nodes that moved. Swap nodes.
+        Object replacementContent = contents[replacementNode];
+        contents[replacementNode] = contents[subTree];
+        contents[subTree] = replacementContent;
+
+        if (objectIndices !is null) {
+            objectIndices.put(replacementContent, subTree);
+            // Note: currently we don't bother updating the index of the replacement
+            // node since we're going to remove it immediately afterwards and there's
+            // no good reason to search for the index in a method that was given the
+            // index as a parameter...
+
+            // objectIndices.put(contents[replacementNode], replacementNode)
+        }
+
+        int replacementParent = parentTree[replacementNode];
+
+        replaceNode(replacementNode, removeNode(replacementNode));
+        //Edge parentEdge = getEdgeTo(replacementNode);
+        //parentEdge.setTarget(removeNode(replacementNode));
+
+        forceRecomputeTreeSize(replacementParent, subTree);
+        recomputeTreeSize(subTree);
+
+        //testInvariants();
+
+        return subTree;
+    }
+
+    /**
+     * Removes all elements from the collection
+     */
+    public final void clear() {
+        lastNode = 0;
+        setArraySize(MIN_CAPACITY);
+        root = -1;
+        firstUnusedNode = -1;
+        objectIndices = null;
+
+        testInvariants();
+    }
+
+    /**
+     * Returns the comparator that is determining the sort order for this collection
+     *
+     * @return comparator for this collection
+     */
+    public Comparator getComparator() {
+        return comparator;
+    }
+
+    /**
+     * Fills in an array of size n with the n smallest elements from the collection.
+     * Can compute the result in sorted or unsorted order.
+     *
+     * Currently package visible until the implementation of FastProgressReporter is finished.
+     *
+     * @param result array to be filled
+     * @param sorted if true, the result array will be sorted. If false, the result array
+     * may be unsorted. This does not affect which elements appear in the result, only their
+     * order.
+     * @param mon monitor used to report progress and check for cancellation
+     * @return the number of items inserted into the result array. This will be equal to the minimum
+     * of result.length and container.size()
+     * @throws InterruptedException if the progress monitor is cancelled
+     */
+    /* package */ final int getFirst(Object[] result, bool sorted, FastProgressReporter mon) {
+        int returnValue = getRange(result, 0, sorted, mon);
+
+        testInvariants();
+
+        return returnValue;
+    }
+
+    /**
+     * Fills in an array of size n with the n smallest elements from the collection.
+     * Can compute the result in sorted or unsorted order.
+     *
+     * @param result array to be filled
+     * @param sorted if true, the result array will be sorted. If false, the result array
+     * may be unsorted. This does not affect which elements appear in the result. It only
+     * affects their order. Computing an unsorted result is asymptotically faster.
+     * @return the number of items inserted into the result array. This will be equal to the minimum
+     * of result.length and container.size()
+     */
+    public final int getFirst(Object[] result, bool sorted) {
+        int returnValue = 0;
+
+        try {
+            returnValue = getFirst(result, sorted, new FastProgressReporter());
+        } catch (InterruptedException e) {
+        }
+
+        testInvariants();
+
+        return returnValue;
+    }
+
+    /**
+     * Given a position defined by k and an array of size n, this fills in the array with
+     * the kth smallest element through to the (k+n)th smallest element. For example,
+     * getRange(myArray, 10, false) would fill in myArray starting with the 10th smallest item
+     * in the collection. The result can be computed in sorted or unsorted order. Computing the
+     * result in unsorted order is more efficient.
+     * <p>
+     * Temporarily set to package visibility until the implementation of FastProgressReporter
+     * is finished.
+     * </p>
+     *
+     * @param result array to be filled in
+     * @param rangeStart index of the smallest element to appear in the result
+     * @param sorted true iff the result array should be sorted
+     * @param mon progress monitor used to cancel the operation
+     * @throws InterruptedException if the progress monitor was cancelled in another thread
+     */
+    /* package */ final int getRange(Object[] result, int rangeStart, bool sorted, FastProgressReporter mon) {
+        return getRange(result, 0, rangeStart, root, sorted, mon);
+    }
+
+    /**
+     * Computes the n through n+k items in this collection.
+     * Computing the result in unsorted order is more efficient. Sorting the result will
+     * not change which elements actually show up in the result. That is, even if the result is
+     * unsorted, it will still contain the same elements as would have been at that range in
+     * a fully sorted collection.
+     *
+     * @param result array containing the result
+     * @param rangeStart index of the first element to be inserted into the result array
+     * @param sorted true iff the result will be computed in sorted order
+     * @return the number of items actually inserted into the result array (will be the minimum
+     * of result.length and this.size())
+     */
+    public final int getRange(Object[] result, int rangeStart, bool sorted) {
+        int returnValue = 0;
+
+        try {
+            returnValue = getRange(result, rangeStart, sorted, new FastProgressReporter());
+        } catch (InterruptedException e) {
+        }
+
+        testInvariants();
+
+        return returnValue;
+    }
+
+    /**
+     * Returns the item at the given index. Indexes are based on sorted order.
+     *
+     * @param index index to test
+     * @return the item at the given index
+     */
+    public final Object getItem(int index) {
+        Object[] result = new Object[1];
+        try {
+            getRange(result, index, false, new FastProgressReporter());
+        } catch (InterruptedException e) {
+            // shouldn't happen
+        }
+        Object returnValue = result[0];
+
+        testInvariants();
+
+        return returnValue;
+    }
+
+    /**
+     * Returns the contents of this collection as a sorted or unsorted
+     * array. Computing an unsorted array is more efficient.
+     *
+     * @param sorted if true, the result will be in sorted order. If false,
+     * the result may be in unsorted order.
+     * @return the contents of this collection as an array.
+     */
+    public final Object[] getItems(bool sorted) {
+        Object[] result = new Object[size()];
+
+        getRange(result, 0, sorted);
+
+        return result;
+    }
+
+    private final int getRange(Object[] result, int resultIdx, int rangeStart, int node, bool sorted, FastProgressReporter mon) {
+        if (node is -1) {
+            return 0;
+        }
+
+        int availableSpace = result.length - resultIdx;
+
+        // If we're asking for all children of the current node, simply call getChildren
+        if (rangeStart is 0) {
+            if (treeSize[node] <= availableSpace) {
+                return getChildren(result, resultIdx, node, sorted, mon);
+            }
+        }
+
+        node = partition(node, mon);
+        if (node is -1) {
+            return 0;
+        }
+
+        int inserted = 0;
+
+        int numberLessThanNode = getSubtreeSize(leftSubTree[node]);
+
+        if (rangeStart < numberLessThanNode) {
+            if (inserted < availableSpace) {
+                inserted += getRange(result, resultIdx, rangeStart, leftSubTree[node], sorted, mon);
+            }
+        }
+
+        if (rangeStart <= numberLessThanNode) {
+            if (inserted < availableSpace) {
+                result[resultIdx + inserted] = contents[node];
+                inserted++;
+            }
+        }
+
+        if (inserted < availableSpace) {
+            inserted += getRange(result, resultIdx + inserted,
+                Math.max(rangeStart - numberLessThanNode - 1, 0), rightSubTree[node], sorted, mon);
+        }
+
+        return inserted;
+    }
+
+    /**
+     * Fills in the available space in the given array with all children of the given node.
+     *
+     * @param result
+     * @param resultIdx index in the result array where we will begin filling in children
+     * @param node
+     * @return the number of children added to the array
+     * @since 3.1
+     */
+    private final int getChildren(Object[] result, int resultIdx, int node, bool sorted, FastProgressReporter mon) {
+        if (node is -1) {
+            return 0;
+        }
+
+        int tempIdx = resultIdx;
+
+        if (sorted) {
+            node = partition(node, mon);
+            if (node is -1) {
+                return 0;
+            }
+        }
+
+        // Add child nodes smaller than this one
+        if (tempIdx < result.length) {
+            tempIdx += getChildren(result, tempIdx, leftSubTree[node], sorted, mon);
+        }
+
+        // Add the pivot
+        if (tempIdx < result.length) {
+            Object value = contents[node];
+            if (value !is lazyRemovalFlag) {
+                result[tempIdx++] = value;
+            }
+        }
+
+        // Add child nodes larger than this one
+        if (tempIdx < result.length) {
+            tempIdx += getChildren(result, tempIdx, rightSubTree[node], sorted, mon);
+        }
+
+        // Add unsorted children (should be empty if the sorted flag was true)
+        for (int unsortedNode = nextUnsorted[node]; unsortedNode !is -1 && tempIdx < result.length;
+            unsortedNode = nextUnsorted[unsortedNode]) {
+
+            result[tempIdx++] = contents[unsortedNode];
+        }
+
+        return tempIdx - resultIdx;
+    }
+
+    /**
+     * Returns true iff this collection contains the given item
+     *
+     * @param item item to test
+     * @return true iff this collection contains the given item
+     */
+    public bool contains(Object item) {
+        Assert.isNotNull(item);
+        bool returnValue = (getObjectIndex(item) !is -1);
+
+        testInvariants();
+
+        return returnValue;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/viewers/deferred/SetModel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.viewers.deferred.SetModel;
+
+import org.eclipse.jface.viewers.deferred.AbstractConcurrentModel;
+import org.eclipse.jface.viewers.deferred.IConcurrentModelListener;
+
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.Collection;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * Trivial implementation of an <code>IConcurrentModel</code>. Implements
+ * an unordered set of elements that fires off change notifications whenever
+ * elements are added or removed from the set. All notifications are sent
+ * synchronously.
+ *
+ * @since 3.1
+ */
+public class SetModel : AbstractConcurrentModel {
+
+    private HashSet data;
+
+    public this(){
+        data = new HashSet();
+    }
+
+    /**
+     * Return the contents of the model.
+     * @return the array of elements
+     *
+     */
+    public Object[] getElements() {
+        return data.toArray();
+    }
+
+    /**
+     * Sets the contents to the given array of elements
+     *
+     * @param newContents new contents of this set
+     */
+    public void set(Object[] newContents) {
+//         Assert.isNotNull(newContents);
+        data.clear();
+        for (int i = 0; i < newContents.length; i++) {
+            Object object = newContents[i];
+
+            data.add(object);
+        }
+
+        IConcurrentModelListener[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            IConcurrentModelListener listener = listeners[i];
+            listener.setContents(newContents);
+        }
+    }
+
+    /**
+     * Empties the set
+     */
+    public void clear() {
+        Object[] removed = data.toArray();
+        data.clear();
+        fireRemove(removed);
+    }
+
+    /**
+     * Adds the given elements to the set
+     *
+     * @param toAdd elements to add
+     */
+    public void addAll(Object[] toAdd) {
+//         Assert.isNotNull(toAdd);
+        for (int i = 0; i < toAdd.length; i++) {
+            Object object = toAdd[i];
+
+            data.add(object);
+        }
+
+        fireAdd(toAdd);
+    }
+
+    /**
+     * Adds the given elements to the set. Duplicate elements are ignored.
+     *
+     * @param toAdd elements to add
+     */
+    public void addAll(Collection toAdd) {
+        Assert.isNotNull(cast(Object)toAdd);
+        addAll(toAdd.toArray());
+    }
+
+    /**
+     * Fires a change notification for all elements in the given array
+     *
+     * @param changed array of elements that have changed
+     */
+    public void changeAll(Object[] changed) {
+//         Assert.isNotNull(changed);
+        fireUpdate(changed);
+    }
+
+    /**
+     * Removes all of the given elements from the set.
+     *
+     * @param toRemove elements to remove
+     */
+    public void removeAll(Object[] toRemove) {
+//         Assert.isNotNull(toRemove);
+        for (int i = 0; i < toRemove.length; i++) {
+            Object object = toRemove[i];
+
+            data.remove(object);
+        }
+
+        fireRemove(toRemove);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.viewers.deferred.IConcurrentModel#requestUpdate(org.eclipse.jface.viewers.deferred.IConcurrentModelListener)
+     */
+    public void requestUpdate(IConcurrentModelListener listener) {
+        Assert.isNotNull(cast(Object)listener);
+        listener.setContents(getElements());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/window/ApplicationWindow.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,872 @@
+/*******************************************************************************
+ * 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
+ *     Roman Dawydkin - bug 55116
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.window.ApplicationWindow;
+
+import org.eclipse.jface.window.Window;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.CoolBar;
+import org.eclipse.swt.widgets.Decorations;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.action.CoolBarManager;
+import org.eclipse.jface.action.ICoolBarManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.StatusLineManager;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.internal.provisional.action.ICoolBarManager2;
+import org.eclipse.jface.internal.provisional.action.IToolBarManager2;
+import org.eclipse.jface.operation.IRunnableContext;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.operation.ModalContext;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * An application window is a high-level "main window", with built-in
+ * support for an optional menu bar with standard menus, an optional toolbar,
+ * and an optional status line.
+ * <p>
+ * Creating an application window involves the following steps:
+ * <ul>
+ *   <li>creating an instance of <code>ApplicationWindow</code>
+ *   </li>
+ *   <li>assigning the window to a window manager (optional)
+ *   </li>
+ *   <li>opening the window by calling <code>open</code>
+ *   </li>
+ * </ul>
+ * Only on the last step, when the window is told to open, are
+ * the window's shell and widget tree created. When the window is
+ * closed, the shell and widget tree are disposed of and are no longer
+ * referenced, and the window is automatically removed from its window
+ * manager. Like all windows, an application window may be reopened.
+ * </p>
+ * <p>
+ * An application window is also a suitable context in which to perform
+ * long-running operations (that is, it implements <code>IRunnableContext</code>).
+ * </p>
+ */
+public class ApplicationWindow : Window, IRunnableContext {
+
+    /**
+     * Menu bar manager, or <code>null</code> if none (default).
+     *
+     * @see #addMenuBar
+     */
+    private MenuManager menuBarManager = null;
+
+    /**
+     * Tool bar manager, or <code>null</code> if none (default).
+     *
+     * @see #addToolBar
+     */
+    private IToolBarManager toolBarManager = null;
+
+    /**
+     * Status line manager, or <code>null</code> if none (default).
+     *
+     * @see #addStatusLine
+     */
+    private StatusLineManager statusLineManager = null;
+
+    /**
+     * Cool bar manager, or <code>null</code> if none (default).
+     *
+     * @see #addCoolBar
+     * @since 3.0
+     */
+    private ICoolBarManager coolBarManager = null;
+
+    /**
+     * The seperator between the menu bar and the rest of the window.
+     */
+    protected Label seperator1;
+
+    /**
+     * A flag indicating that an operation is running.
+     */
+    private bool operationInProgress = false;
+
+    /**
+     * Internal application window layout class.
+     * This vertical layout supports a tool bar area (fixed size),
+     * a separator line, the content area (variable size), and a
+     * status line (fixed size).
+     */
+    /*package*/class ApplicationWindowLayout : Layout {
+
+        static final int VGAP = 2;
+
+        static final int BAR_SIZE = 23;
+
+        protected override Point computeSize(Composite composite, int wHint, int hHint,
+                bool flushCache) {
+            if (wHint !is SWT.DEFAULT && hHint !is SWT.DEFAULT) {
+                return new Point(wHint, hHint);
+            }
+
+            Point result = new Point(0, 0);
+            Control[] ws = composite.getChildren();
+            for (int i = 0; i < ws.length; i++) {
+                Control w = ws[i];
+
+                bool hide = false;
+                if (getToolBarControl() is w) {
+                    if (!toolBarChildrenExist()) {
+                        hide = true;
+                        result.y += BAR_SIZE; // REVISIT
+                    }
+                } else if (getCoolBarControl() is w) {
+                    if (!coolBarChildrenExist()) {
+                        hide = true;
+                        result.y += BAR_SIZE;
+                    }
+                } else if (statusLineManager !is null
+                        && statusLineManager.getControl() is w) {
+                } else if (i > 0) { /* we assume this window is contents */
+                    hide = false;
+                }
+
+                if (!hide) {
+                    Point e = w.computeSize(wHint, hHint, flushCache);
+                    result.x = Math.max(result.x, e.x);
+                    result.y += e.y + VGAP;
+                }
+            }
+
+            if (wHint !is SWT.DEFAULT) {
+                result.x = wHint;
+            }
+            if (hHint !is SWT.DEFAULT) {
+                result.y = hHint;
+            }
+            return result;
+        }
+
+        protected override void layout(Composite composite, bool flushCache) {
+            Rectangle clientArea = composite.getClientArea();
+
+            Control[] ws = composite.getChildren();
+
+            // Lay out the separator, the tool bar control, the cool bar control, the status line, and the page composite.
+            // The following code assumes that the page composite is the last child, and that there are no unexpected other controls.
+
+            for (int i = 0; i < ws.length; i++) {
+                Control w = ws[i];
+
+                if (w is seperator1) { // Separator
+                    Point e = w.computeSize(SWT.DEFAULT, SWT.DEFAULT,
+                            flushCache);
+                    w.setBounds(clientArea.x, clientArea.y, clientArea.width,
+                            e.y);
+                    clientArea.y += e.y;
+                    clientArea.height -= e.y;
+                } else if (getToolBarControl() is w) {
+                    if (toolBarChildrenExist()) {
+                        Point e = w.computeSize(SWT.DEFAULT, SWT.DEFAULT,
+                                flushCache);
+                        w.setBounds(clientArea.x, clientArea.y,
+                                clientArea.width, e.y);
+                        clientArea.y += e.y + VGAP;
+                        clientArea.height -= e.y + VGAP;
+                    }
+                } else if (getCoolBarControl() is w) {
+                    if (coolBarChildrenExist()) {
+                        Point e = w.computeSize(clientArea.width, SWT.DEFAULT,
+                                flushCache);
+                        w.setBounds(clientArea.x, clientArea.y,
+                                clientArea.width, e.y);
+                        clientArea.y += e.y + VGAP;
+                        clientArea.height -= e.y + VGAP;
+                    }
+                } else if (statusLineManager !is null
+                        && statusLineManager.getControl() is w) {
+                    Point e = w.computeSize(SWT.DEFAULT, SWT.DEFAULT,
+                            flushCache);
+                    w.setBounds(clientArea.x, clientArea.y + clientArea.height
+                            - e.y, clientArea.width, e.y);
+                    clientArea.height -= e.y + VGAP;
+                } else {
+                    w.setBounds(clientArea.x, clientArea.y + VGAP,
+                            clientArea.width, clientArea.height - VGAP);
+                }
+            }
+        }
+    }
+
+    /**
+     * Return the top seperator.
+     * @return Label
+     */
+    protected Label getSeperator1() {
+        return seperator1;
+    }
+
+    /**
+     * Create an application window instance, whose shell will be created under the
+     * given parent shell.
+     * Note that the window will have no visual representation (no widgets)
+     * until it is told to open. By default, <code>open</code> does not block.
+     *
+     * @param parentShell the parent shell, or <code>null</code> to create a top-level shell
+     */
+    public this(Shell parentShell) {
+        super(parentShell);
+    }
+
+    /**
+     * Configures this window to have a menu bar.
+     * Does nothing if it already has one.
+     * This method must be called before this window's shell is created.
+     */
+    protected void addMenuBar() {
+        if ((getShell() is null) && (menuBarManager is null)) {
+            menuBarManager = createMenuManager();
+        }
+    }
+
+    /**
+     * Configures this window to have a status line.
+     * Does nothing if it already has one.
+     * This method must be called before this window's shell is created.
+     */
+    protected void addStatusLine() {
+        if ((getShell() is null) && (statusLineManager is null)) {
+            statusLineManager = createStatusLineManager();
+        }
+    }
+
+    /**
+     * Configures this window to have a tool bar.
+     * Does nothing if it already has one.
+     * This method must be called before this window's shell is created.
+     * @param style swt style bits used to create the Toolbar
+     * @see ToolBarManager#ToolBarManager(int)
+     * @see ToolBar for style bits
+     */
+    protected void addToolBar(int style) {
+        if ((getShell() is null) && (toolBarManager is null)
+                && (coolBarManager is null)) {
+            toolBarManager = createToolBarManager2(style);
+        }
+    }
+
+    /**
+     * Configures this window to have a cool bar.
+     * Does nothing if it already has one.
+     * This method must be called before this window's shell is created.
+     *
+     * @param style the cool bar style
+     * @since 3.0
+     */
+    protected void addCoolBar(int style) {
+        if ((getShell() is null) && (toolBarManager is null)
+                && (coolBarManager is null)) {
+            coolBarManager = createCoolBarManager2(style);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on Window.
+     */
+    protected override bool canHandleShellCloseEvent() {
+        return super.canHandleShellCloseEvent() && !operationInProgress;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on Window.
+     */
+    public override bool close() {
+        if (operationInProgress) {
+            return false;
+        }
+
+        if (super.close()) {
+            if (menuBarManager !is null) {
+                menuBarManager.dispose();
+                menuBarManager = null;
+            }
+            if (toolBarManager !is null) {
+                if (cast(IToolBarManager2)toolBarManager ) {
+                    (cast(IToolBarManager2) toolBarManager).dispose();
+                } else if (cast(ToolBarManager)toolBarManager ) {
+                    (cast(ToolBarManager) toolBarManager).dispose();
+                }
+                toolBarManager = null;
+            }
+            if (statusLineManager !is null) {
+                statusLineManager.dispose();
+                statusLineManager = null;
+            }
+            if (coolBarManager !is null) {
+                if (cast(ICoolBarManager2)coolBarManager ) {
+                    (cast(ICoolBarManager2) coolBarManager).dispose();
+                } else if (cast(CoolBarManager)coolBarManager ) {
+                    (cast(CoolBarManager) coolBarManager).dispose();
+                }
+                coolBarManager = null;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Extends the super implementation by creating the trim widgets using <code>createTrimWidgets</code>.
+     */
+    protected override void configureShell(Shell shell) {
+
+        super.configureShell(shell);
+
+        createTrimWidgets(shell);
+    }
+
+    /**
+     * Creates the trim widgets around the content area.
+     *
+     * @param shell the shell
+     * @since 3.0
+     */
+    protected void createTrimWidgets(Shell shell) {
+        if (menuBarManager !is null) {
+            menuBarManager.updateAll(true);
+            shell.setMenuBar(menuBarManager.createMenuBar(cast(Decorations) shell));
+        }
+
+        if (showTopSeperator()) {
+            seperator1 = new Label(shell, SWT.SEPARATOR | SWT.HORIZONTAL);
+        }
+
+        // will create either a cool bar or a tool bar
+        createToolBarControl(shell);
+        createCoolBarControl(shell);
+        createStatusLine(shell);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.window.Window#getLayout()
+     */
+    protected override Layout getLayout() {
+        return new ApplicationWindowLayout();
+    }
+
+    /**
+     * Returns whether to show a top separator line between the menu bar
+     * and the rest of the window contents.  On some platforms such as the Mac,
+     * the menu is separated from the main window already, so a separator line
+     * is not desired.
+     *
+     * @return <code>true</code> to show the top separator, <code>false</code>
+     *   to not show it
+     * @since 3.0
+     */
+    protected bool showTopSeperator() {
+        return !"carbon".equals(SWT.getPlatform()); //$NON-NLS-1$
+    }
+
+    /**
+     * Create the status line if required.
+     * @param shell
+     */
+    protected void createStatusLine(Shell shell) {
+        if (statusLineManager !is null) {
+            statusLineManager.createControl(shell, SWT.NONE);
+        }
+    }
+
+    /**
+     * Returns a new menu manager for the window.
+     * <p>
+     * Subclasses may override this method to customize the menu manager.
+     * </p>
+     * @return a menu manager
+     */
+    protected MenuManager createMenuManager() {
+        return new MenuManager();
+    }
+
+    /**
+     * Returns a new status line manager for the window.
+     * <p>
+     * Subclasses may override this method to customize the status line manager.
+     * </p>
+     * @return a status line manager
+     */
+    protected StatusLineManager createStatusLineManager() {
+        return new StatusLineManager();
+    }
+
+    /**
+     * Returns a new tool bar manager for the window.
+     * <p>
+     * Subclasses may override this method to customize the tool bar manager.
+     * </p>
+     * @param style swt style bits used to create the Toolbar
+     *
+     * @return a tool bar manager
+     * @see ToolBarManager#ToolBarManager(int)
+     * @see ToolBar for style bits
+     */
+    protected ToolBarManager createToolBarManager(int style) {
+        return new ToolBarManager(style);
+    }
+
+    /**
+     * Returns a new tool bar manager for the window.
+     * <p>
+     * By default this method calls <code>createToolBarManager</code>.  Subclasses
+     * may override this method to provide an alternative implementation for the
+     * tool bar manager.
+     * </p>
+     *
+     * @param style swt style bits used to create the Toolbar
+     *
+     * @return a tool bar manager
+     * @since 3.2
+     * @see #createToolBarManager(int)
+     */
+    protected IToolBarManager createToolBarManager2(int style) {
+        return createToolBarManager(style);
+    }
+
+    /**
+     * Returns a new cool bar manager for the window.
+     * <p>
+     * Subclasses may override this method to customize the cool bar manager.
+     * </p>
+     *
+     * @param style swt style bits used to create the Coolbar
+     *
+     * @return a cool bar manager
+     * @since 3.0
+     * @see CoolBarManager#CoolBarManager(int)
+     * @see CoolBar for style bits
+     */
+    protected CoolBarManager createCoolBarManager(int style) {
+        return new CoolBarManager(style);
+    }
+
+    /**
+     * Returns a new cool bar manager for the window.
+     * <p>
+     * By default this method calls <code>createCoolBarManager</code>.  Subclasses
+     * may override this method to provide an alternative implementation for the
+     * cool bar manager.
+     * </p>
+     *
+     * @param style swt style bits used to create the Coolbar
+     *
+     * @return a cool bar manager
+     * @since 3.2
+     * @see #createCoolBarManager(int)
+     */
+    protected ICoolBarManager createCoolBarManager2(int style) {
+        return createCoolBarManager(style);
+    }
+
+    /**
+     * Creates the control for the tool bar manager.
+     * <p>
+     * Subclasses may override this method to customize the tool bar manager.
+     * </p>
+     * @param parent the parent used for the control
+     * @return a Control
+     */
+    protected Control createToolBarControl(Composite parent) {
+        if (toolBarManager !is null) {
+            if (cast(IToolBarManager2)toolBarManager ) {
+                return (cast(IToolBarManager2) toolBarManager).createControl2(parent);
+            }
+            if (cast(ToolBarManager)toolBarManager ) {
+                return (cast(ToolBarManager) toolBarManager).createControl(parent);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Creates the control for the cool bar manager.
+     * <p>
+     * Subclasses may override this method to customize the cool bar manager.
+     * </p>
+     * @param composite the parent used for the control
+     *
+     * @return an instance of <code>CoolBar</code>
+     * @since 3.0
+     */
+    protected Control createCoolBarControl(Composite composite) {
+        if (coolBarManager !is null) {
+            if (cast(ICoolBarManager2)coolBarManager ) {
+                return (cast(ICoolBarManager2) coolBarManager).createControl2(composite);
+            }
+            if (cast(CoolBarManager)coolBarManager ) {
+                return (cast(CoolBarManager) coolBarManager).createControl(composite);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the default font used for this window.
+     * <p>
+     * The default implementation of this framework method
+     * obtains the symbolic name of the font from the
+     * <code>getSymbolicFontName</code> framework method
+     * and retrieves this font from JFace's font
+     * registry using <code>JFaceResources.getFont</code>.
+     * Subclasses may override to use a different registry,
+     * etc.
+     * </p>
+     *
+     * @return the default font, or <code>null</code> if none
+     */
+    protected Font getFont() {
+        return JFaceResources.getFont(getSymbolicFontName());
+    }
+
+    /**
+     * Returns the menu bar manager for this window (if it has one).
+     *
+     * @return the menu bar manager, or <code>null</code> if
+     *   this window does not have a menu bar
+     * @see #addMenuBar()
+     */
+    public MenuManager getMenuBarManager() {
+        return menuBarManager;
+    }
+
+    /**
+     * Returns the status line manager for this window (if it has one).
+     *
+     * @return the status line manager, or <code>null</code> if
+     *   this window does not have a status line
+     * @see #addStatusLine
+     */
+    protected StatusLineManager getStatusLineManager() {
+        return statusLineManager;
+    }
+
+    /**
+     * Returns the symbolic font name of the font to be
+     * used to display text in this window.
+     * This is not recommended and is included for backwards
+     * compatability.
+     * It is recommended to use the default font provided by
+     * SWT (that is, do not set the font).
+     *
+     * @return the symbolic font name
+     */
+    public String getSymbolicFontName() {
+        return JFaceResources.TEXT_FONT;
+    }
+
+    /**
+     * Returns the tool bar manager for this window (if it has one).
+     *
+     * @return the tool bar manager, or <code>null</code> if
+     *   this window does not have a tool bar
+     * @see #addToolBar(int)
+     */
+    public ToolBarManager getToolBarManager() {
+        if (cast(ToolBarManager)toolBarManager ) {
+            return cast(ToolBarManager)toolBarManager;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the tool bar manager for this window (if it has one).
+     *
+     * @return the tool bar manager, or <code>null</code> if
+     *   this window does not have a tool bar
+     * @see #addToolBar(int)
+     * @since 3.2
+     */
+    public IToolBarManager getToolBarManager2() {
+        return toolBarManager;
+    }
+
+    /**
+     * Returns the cool bar manager for this window.
+     *
+     * @return the cool bar manager, or <code>null</code> if
+     *   this window does not have a cool bar
+     * @see #addCoolBar(int)
+     * @since 3.0
+     */
+    public CoolBarManager getCoolBarManager() {
+        if (cast(CoolBarManager)coolBarManager ) {
+            return cast(CoolBarManager)coolBarManager;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the cool bar manager for this window.
+     *
+     * @return the cool bar manager, or <code>null</code> if
+     *   this window does not have a cool bar
+     * @see #addCoolBar(int)
+     * @since 3.2
+     */
+    public ICoolBarManager getCoolBarManager2() {
+        return coolBarManager;
+    }
+
+    /**
+     * Returns the control for the window's toolbar.
+     * <p>
+     * Subclasses may override this method to customize the tool bar manager.
+     * </p>
+     * @return a Control
+     */
+    protected Control getToolBarControl() {
+        if (toolBarManager !is null) {
+            if (cast(IToolBarManager2)toolBarManager ) {
+                return (cast(IToolBarManager2) toolBarManager).getControl2();
+            }
+            if (cast(ToolBarManager)toolBarManager ) {
+                return (cast(ToolBarManager) toolBarManager).getControl();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the control for the window's cool bar.
+     * <p>
+     * Subclasses may override this method to customize the cool bar manager.
+     * </p>
+     *
+     * @return an instance of <code>CoolBar</code>
+     * @since 3.0
+     */
+    protected Control getCoolBarControl() {
+        if (coolBarManager !is null) {
+            if (cast(ICoolBarManager2)coolBarManager ) {
+                return (cast(ICoolBarManager2) coolBarManager).getControl2();
+            }
+            if (cast(CoolBarManager)coolBarManager ) {
+                return (cast(CoolBarManager) coolBarManager).getControl();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * This implementation of IRunnableContext#run(bool, bool,
+     * IRunnableWithProgress) blocks until the runnable has been run,
+     * regardless of the value of <code>fork</code>.
+     * It is recommended that <code>fork</code> is set to
+     * true in most cases. If <code>fork</code> is set to <code>false</code>,
+     * the runnable will run in the UI thread and it is the runnable's
+     * responsibility to call <code>Display.readAndDispatch()</code>
+     * to ensure UI responsiveness.
+     */
+    public void run(bool fork, bool cancelable,
+            IRunnableWithProgress runnable) {
+        try {
+            operationInProgress = true;
+            StatusLineManager mgr = getStatusLineManager();
+            if (mgr is null) {
+                runnable.run(new NullProgressMonitor());
+                return;
+            }
+            bool cancelWasEnabled = mgr.isCancelEnabled();
+
+            Control contents = getContents();
+            Display display = contents.getDisplay();
+            Shell shell = getShell();
+            bool contentsWasEnabled = contents.getEnabled();
+            MenuManager manager = getMenuBarManager();
+            Menu menuBar = null;
+            if (manager !is null) {
+                menuBar = manager.getMenu();
+                manager = null;
+            }
+            bool menuBarWasEnabled = false;
+            if (menuBar !is null) {
+                menuBarWasEnabled = menuBar.getEnabled();
+            }
+
+            Control toolbarControl = getToolBarControl();
+            bool toolbarWasEnabled = false;
+            if (toolbarControl !is null) {
+                toolbarWasEnabled = toolbarControl.getEnabled();
+            }
+
+            Control coolbarControl = getCoolBarControl();
+            bool coolbarWasEnabled = false;
+            if (coolbarControl !is null) {
+                coolbarWasEnabled = coolbarControl.getEnabled();
+            }
+
+            // Disable the rest of the shells on the current display
+            Shell[] shells = display.getShells();
+            bool[] enabled = new bool[shells.length];
+            for (int i = 0; i < shells.length; i++) {
+                Shell current = shells[i];
+                if (current is shell) {
+                    continue;
+                }
+                if (current !is null && !current.isDisposed()) {
+                    enabled[i] = current.getEnabled();
+                    current.setEnabled(false);
+                }
+            }
+
+            Control currentFocus = display.getFocusControl();
+            try {
+                contents.setEnabled(false);
+                if (menuBar !is null) {
+                    menuBar.setEnabled(false);
+                }
+                if (toolbarControl !is null) {
+                    toolbarControl.setEnabled(false);
+                }
+                if (coolbarControl !is null) {
+                    coolbarControl.setEnabled(false);
+                }
+                mgr.setCancelEnabled(cancelable);
+                Exception[1] holder;
+                BusyIndicator.showWhile(display, new class(fork,runnable,mgr,display) Runnable {
+                    bool fork_;
+                    IRunnableWithProgress runnable_;
+                    StatusLineManager mgr_;
+                    Display display_;
+                    this(bool f,IRunnableWithProgress a,StatusLineManager b,Display c){
+                        fork_=f;
+                        runnable_=a;
+                        mgr_=b;
+                        display_=c;
+                    }
+                    public void run() {
+                        try {
+                            ModalContext.run(runnable_, fork_, mgr_
+                                    .getProgressMonitor(), display_);
+                        } catch (InvocationTargetException ite) {
+                            holder[0] = ite;
+                        } catch (InterruptedException ie) {
+                            holder[0] = ie;
+                        }
+                    }
+                });
+
+                if (holder[0] !is null) {
+                    if (cast(InvocationTargetException)holder[0] ) {
+                        throw cast(InvocationTargetException) holder[0];
+                    } else if (cast(InterruptedException)holder[0] ) {
+                        throw cast(InterruptedException) holder[0];
+                    }
+                }
+            } finally {
+                operationInProgress = false;
+                // Enable the rest of the shells on the current display
+                for (int i = 0; i < shells.length; i++) {
+                    Shell current = shells[i];
+                    if (current is shell) {
+                        continue;
+                    }
+                    if (current !is null && !current.isDisposed()) {
+                        current.setEnabled(enabled[i]);
+                    }
+                }
+                if (!contents.isDisposed()) {
+                    contents.setEnabled(contentsWasEnabled);
+                }
+                if (menuBar !is null && !menuBar.isDisposed()) {
+                    menuBar.setEnabled(menuBarWasEnabled);
+                }
+                if (toolbarControl !is null && !toolbarControl.isDisposed()) {
+                    toolbarControl.setEnabled(toolbarWasEnabled);
+                }
+                if (coolbarControl !is null && !coolbarControl.isDisposed()) {
+                    coolbarControl.setEnabled(coolbarWasEnabled);
+                }
+                mgr.setCancelEnabled(cancelWasEnabled);
+                if (currentFocus !is null && !currentFocus.isDisposed()) {
+                    // It's necessary to restore focus after reenabling the controls
+                    // because disabling them causes focus to jump elsewhere.
+                    // Use forceFocus rather than setFocus to avoid SWT's
+                    // search for children which can take focus, so focus
+                    // ends up back on the actual control that previously had it.
+                    currentFocus.forceFocus();
+                }
+            }
+        } finally {
+            operationInProgress = false;
+        }
+    }
+
+    /**
+     * Sets or clears the message displayed in this window's status
+     * line (if it has one). This method has no effect if the
+     * window does not have a status line.
+     *
+     * @param message the status message, or <code>null</code> to clear it
+     */
+    public void setStatus(String message) {
+        if (statusLineManager !is null) {
+            statusLineManager.setMessage(message);
+        }
+    }
+
+    /**
+     * Returns whether or not children exist for the Application Window's
+     * toolbar control.
+     * <p>
+     * @return bool true if children exist, false otherwise
+     */
+    protected bool toolBarChildrenExist() {
+        Control toolControl = getToolBarControl();
+        if (cast(ToolBar)toolControl ) {
+            return (cast(ToolBar) toolControl).getItemCount() > 0;
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether or not children exist for this application window's
+     * cool bar control.
+     *
+     * @return bool true if children exist, false otherwise
+     * @since 3.0
+     */
+    protected bool coolBarChildrenExist() {
+        Control coolControl = getCoolBarControl();
+        if (cast(CoolBar)coolControl ) {
+            return (cast(CoolBar) coolControl).getItemCount() > 0;
+        }
+        return false;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/window/DefaultToolTip.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,298 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.window.DefaultToolTip;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+
+import org.eclipse.jface.window.ToolTip;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Default implementation of ToolTip that provides an iconofied label with font
+ * and color controls by subclass.
+ *
+ * @since 3.3
+ */
+public class DefaultToolTip : ToolTip {
+    private String text;
+
+    private Color backgroundColor;
+
+    private Font font;
+
+    private Image backgroundImage;
+
+    private Color foregroundColor;
+
+    private Image image;
+
+    private int style = SWT.SHADOW_NONE;
+
+    /**
+     * Create new instance which add TooltipSupport to the widget
+     *
+     * @param control the control on whose action the tooltip is shown
+     */
+    public this(Control control) {
+        super(control);
+    }
+
+    /**
+     * Create new instance which add TooltipSupport to the widget
+     *
+     * @param control the control to which the tooltip is bound
+     * @param style style passed to control tooltip behaviour
+     * @param manualActivation <code>true</code> if the activation is done manually using
+     *            {@link #show(Point)}
+     * @see #RECREATE
+     * @see #NO_RECREATE
+     */
+    public this(Control control, int style, bool manualActivation) {
+        super(control, style, manualActivation);
+    }
+
+    /**
+     * Creates the content are of the the tooltip. By default this creates a
+     * CLabel to display text. To customize the text Subclasses may override the
+     * following methods
+     * <ul>
+     * <li>{@link #getStyle(Event)}</li>
+     * <li>{@link #getBackgroundColor(Event)}</li>
+     * <li>{@link #getForegroundColor(Event)}</li>
+     * <li>{@link #getFont(Event)}</li>
+     * <li>{@link #getImage(Event)}</li>
+     * <li>{@link #getText(Event)}</li>
+     * <li>{@link #getBackgroundImage(Event)}</li>
+     * </ul>
+     *
+     * @param event
+     *            the event that triggered the activation of the tooltip
+     * @param parent
+     *            the parent of the content area
+     * @return the content area created
+     */
+    protected override Composite createToolTipContentArea(Event event, Composite parent) {
+        Image image = getImage(event);
+        Image bgImage = getBackgroundImage(event);
+        String text = getText(event);
+        Color fgColor = getForegroundColor(event);
+        Color bgColor = getBackgroundColor(event);
+        Font font = getFont(event);
+
+        CLabel label = new CLabel(parent, getStyle(event));
+        if (text !is null) {
+            label.setText(text);
+        }
+
+        if (image !is null) {
+            label.setImage(image);
+        }
+
+        if (fgColor !is null) {
+            label.setForeground(fgColor);
+        }
+
+        if (bgColor !is null) {
+            label.setBackground(bgColor);
+        }
+
+        if (bgImage !is null) {
+            label.setBackgroundImage(image);
+        }
+
+        if (font !is null) {
+            label.setFont(font);
+        }
+
+        return label;
+    }
+
+    /**
+     * The style used to create the {@link CLabel} in the default implementation
+     *
+     * @param event
+     *            the event triggered the popup of the tooltip
+     * @return the style
+     */
+    protected int getStyle(Event event) {
+        return style;
+    }
+
+    /**
+     * The {@link Image} displayed in the {@link CLabel} in the default
+     * implementation implementation
+     *
+     * @param event
+     *            the event triggered the popup of the tooltip
+     * @return the {@link Image} or <code>null</code> if no image should be
+     *         displayed
+     */
+    protected Image getImage(Event event) {
+        return image;
+    }
+
+    /**
+     * The foreground {@link Color} used by {@link CLabel} in the default
+     * implementation
+     *
+     * @param event
+     *            the event triggered the popup of the tooltip
+     * @return the {@link Color} or <code>null</code> if default foreground
+     *         color should be used
+     */
+    protected Color getForegroundColor(Event event) {
+        return (foregroundColor is null) ? event.widget.getDisplay()
+                .getSystemColor(SWT.COLOR_INFO_FOREGROUND) : foregroundColor;
+    }
+
+    /**
+     * The background {@link Color} used by {@link CLabel} in the default
+     * implementation
+     *
+     * @param event
+     *            the event triggered the popup of the tooltip
+     * @return the {@link Color} or <code>null</code> if default background
+     *         color should be used
+     */
+    protected Color getBackgroundColor(Event event) {
+        return (backgroundColor is null) ? event.widget.getDisplay()
+                .getSystemColor(SWT.COLOR_INFO_BACKGROUND) : backgroundColor;
+    }
+
+    /**
+     * The background {@link Image} used by {@link CLabel} in the default
+     * implementation
+     *
+     * @param event
+     *            the event triggered the popup of the tooltip
+     * @return the {@link Image} or <code>null</code> if no image should be
+     *         displayed in the background
+     */
+    protected Image getBackgroundImage(Event event) {
+        return backgroundImage;
+    }
+
+    /**
+     * The {@link Font} used by {@link CLabel} in the default implementation
+     *
+     * @param event
+     *            the event triggered the popup of the tooltip
+     * @return the {@link Font} or <code>null</code> if the default font
+     *         should be used
+     */
+    protected Font getFont(Event event) {
+        return font;
+    }
+
+    /**
+     * The text displayed in the {@link CLabel} in the default implementation
+     *
+     * @param event
+     *            the event triggered the popup of the tooltip
+     * @return the text or <code>null</code> if no text has to be displayed
+     */
+    protected String getText(Event event) {
+        return text;
+    }
+
+    /**
+     * The background {@link Image} used by {@link CLabel} in the default
+     * implementation
+     *
+     * @param backgroundColor
+     *            the {@link Color} or <code>null</code> if default background
+     *            color ({@link SWT#COLOR_INFO_BACKGROUND}) should be used
+     */
+    public void setBackgroundColor(Color backgroundColor) {
+        this.backgroundColor = backgroundColor;
+    }
+
+    /**
+     * The background {@link Image} used by {@link CLabel} in the default
+     * implementation
+     *
+     * @param backgroundImage
+     *            the {@link Image} or <code>null</code> if no image should be
+     *            displayed in the background
+     */
+    public void setBackgroundImage(Image backgroundImage) {
+        this.backgroundImage = backgroundImage;
+    }
+
+    /**
+     * The {@link Font} used by {@link CLabel} in the default implementation
+     *
+     * @param font
+     *            the {@link Font} or <code>null</code> if the default font
+     *            should be used
+     */
+    public void setFont(Font font) {
+        this.font = font;
+    }
+
+    /**
+     * The foreground {@link Color} used by {@link CLabel} in the default
+     * implementation
+     *
+     * @param foregroundColor
+     *            the {@link Color} or <code>null</code> if default foreground
+     *            color should be used
+     */
+    public void setForegroundColor(Color foregroundColor) {
+        this.foregroundColor = foregroundColor;
+    }
+
+    /**
+     * The {@link Image} displayed in the {@link CLabel} in the default
+     * implementation implementation
+     *
+     * @param image
+     *            the {@link Image} or <code>null</code> if no image should be
+     *            displayed
+     */
+    public void setImage(Image image) {
+        this.image = image;
+    }
+
+    /**
+     * The style used to create the {@link CLabel} in the default implementation
+     *
+     * @param style
+     *            the event triggered the popup of the tooltip
+     */
+    public void setStyle(int style) {
+        this.style = style;
+    }
+
+    /**
+     * The text displayed in the {@link CLabel} in the default implementation
+     *
+     * @param text
+     *            the text or <code>null</code> if no text has to be displayed
+     */
+    public void setText(String text) {
+        this.text = text;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/window/IShellProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.window.IShellProvider;
+
+import java.lang.all;
+
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Interface for objects that can return a shell. This is normally used for 
+ * opening child windows. An object that wants to open child shells can take 
+ * an IShellProvider in its constructor, and the object that implements IShellProvider
+ * can dynamically choose where child shells should be opened.  
+ * 
+ * @since 3.1
+ */
+public interface IShellProvider {
+    /**
+     * Returns the current shell (or null if none). This return value may
+     * change over time, and should not be cached.
+     * 
+     * @return the current shell or null if none
+     */
+    Shell getShell();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/window/SameShellProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.window.SameShellProvider;
+
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.jface.window.IShellProvider;
+
+import java.lang.all;
+
+/**
+ * Standard shell provider that always returns the shell containing the given
+ * control. This will always return the correct shell for the control, even if
+ * the control is reparented.
+ *
+ * @since 3.1
+ */
+public class SameShellProvider : IShellProvider {
+
+    private Control targetControl;
+
+    /**
+     * Returns a shell provider that always returns the current
+     * shell for the given control.
+     *
+     * @param targetControl control whose shell will be tracked, or null if getShell() should always
+     * return null
+     */
+    public this(Control targetControl) {
+        this.targetControl = targetControl;
+    }
+
+    /* (non-javadoc)
+     * @see IShellProvider#getShell()
+     */
+    public Shell getShell() {
+        if ( auto res = cast(Shell)targetControl ) {
+            return res;
+        }
+
+        return targetControl is null? null :targetControl.getShell();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/window/ToolTip.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,729 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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:
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
+ *                                                 bugfix in: 195137, 198089, 225190
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.window.ToolTip;
+
+import tango.util.log.Trace;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Monitor;
+import org.eclipse.swt.widgets.Shell;
+// import org.eclipse.jface.viewers.ColumnViewer;
+// import org.eclipse.jface.viewers.ViewerCell;
+
+import java.lang.all;
+import java.util.HashMap;
+import java.util.Set;
+import java.lang.JThread;
+/**
+ * This class gives implementors to provide customized tooltips for any control.
+ *
+ * @since 3.3
+ */
+public abstract class ToolTip {
+    private Control control;
+
+    private int xShift = 3;
+
+    private int yShift = 0;
+
+    private int popupDelay = 0;
+
+    private int hideDelay = 0;
+
+    private ToolTipOwnerControlListener listener;
+
+    private HashMap data;
+
+    // Ensure that only one tooltip is active in time
+    private static Shell CURRENT_TOOLTIP;
+
+    /**
+     * Recreate the tooltip on every mouse move
+     */
+    public static const int RECREATE = 1;
+
+    /**
+     * Don't recreate the tooltip as long the mouse doesn't leave the area
+     * triggering the tooltip creation
+     */
+    public static const int NO_RECREATE = 1 << 1;
+
+    private TooltipHideListener hideListener;
+
+    private Listener shellListener;
+
+    private bool hideOnMouseDown = true;
+
+    private bool respectDisplayBounds = true;
+
+    private bool respectMonitorBounds = true;
+
+    private int style;
+
+    private Object currentArea;
+
+    private static final bool IS_OSX = SWT.getPlatform().equals("carbon"); //$NON-NLS-1$
+
+    /**
+     * Create new instance which add TooltipSupport to the widget
+     *
+     * @param control
+     *            the control on whose action the tooltip is shown
+     */
+    public this(Control control) {
+        this(control, RECREATE, false);
+    }
+
+    /**
+     * @param control
+     *            the control to which the tooltip is bound
+     * @param style
+     *            style passed to control tooltip behavior
+     *
+     * @param manualActivation
+     *            <code>true</code> if the activation is done manually using
+     *            {@link #show(Point)}
+     * @see #RECREATE
+     * @see #NO_RECREATE
+     */
+    public this(Control control, int style, bool manualActivation) {
+        this.control = control;
+        this.style = style;
+        this.hideListener = new TooltipHideListener();
+        this.control.addDisposeListener(new class DisposeListener {
+
+            public void widgetDisposed(DisposeEvent e) {
+                this.outer.data = null;
+                this.outer.deactivate();
+            }
+
+        });
+
+        this.listener = new ToolTipOwnerControlListener();
+        this.shellListener = dgListener( (Event event_, Control control_, ToolTip tooltip_) {
+            if( control_ !is null
+                    && ! control_.isDisposed() ) {
+                control_.getDisplay().asyncExec( dgRunnable( (Event event__, Control control__, ToolTip tooltip__){
+                    // Check if the new active shell is the tooltip
+                    // itself
+                    if( control__.getDisplay().getActiveShell() !is CURRENT_TOOLTIP) {
+                        tooltip__.toolTipHide(CURRENT_TOOLTIP, event__);
+
+                    }
+                }, event_, control_, tooltip_));
+            }
+        }, control, this);
+
+        if (!manualActivation) {
+            activate();
+        }
+    }
+
+    /**
+     * Restore arbitrary data under the given key
+     *
+     * @param key
+     *            the key
+     * @param value
+     *            the value
+     */
+    public void setData(String key, Object value) {
+        if (data is null) {
+            data = new HashMap();
+        }
+        data.put(stringcast(key), value);
+    }
+
+    /**
+     * Get the data restored under the key
+     *
+     * @param key
+     *            the key
+     * @return data or <code>null</code> if no entry is restored under the key
+     */
+    public Object getData(String key) {
+        if (data !is null) {
+            return data.get(stringcast(key));
+        }
+        return null;
+    }
+
+    /**
+     * Set the shift (from the mouse position triggered the event) used to
+     * display the tooltip.
+     * <p>
+     * By default the tooltip is shifted 3 pixels to the right.
+     * </p>
+     *
+     * @param p
+     *            the new shift
+     */
+    public void setShift(Point p) {
+        xShift = p.x;
+        yShift = p.y;
+    }
+
+    /**
+     * Activate tooltip support for this control
+     */
+    public void activate() {
+        deactivate();
+        control.addListener(SWT.Dispose, listener);
+        control.addListener(SWT.MouseHover, listener);
+        control.addListener(SWT.MouseMove, listener);
+        control.addListener(SWT.MouseExit, listener);
+        control.addListener(SWT.MouseDown, listener);
+        control.addListener(SWT.MouseWheel, listener);
+    }
+
+    /**
+     * Deactivate tooltip support for the underlying control
+     */
+    public void deactivate() {
+        control.removeListener(SWT.Dispose, listener);
+        control.removeListener(SWT.MouseHover, listener);
+        control.removeListener(SWT.MouseMove, listener);
+        control.removeListener(SWT.MouseExit, listener);
+        control.removeListener(SWT.MouseDown, listener);
+        control.removeListener(SWT.MouseWheel, listener);
+    }
+
+    /**
+     * Return whether the tooltip respects bounds of the display.
+     *
+     * @return <code>true</code> if the tooltip respects bounds of the display
+     */
+    public bool isRespectDisplayBounds() {
+        return respectDisplayBounds;
+    }
+
+    /**
+     * Set to <code>false</code> if display bounds should not be respected or
+     * to <code>true</code> if the tooltip is should repositioned to not
+     * overlap the display bounds.
+     * <p>
+     * Default is <code>true</code>
+     * </p>
+     *
+     * @param respectDisplayBounds
+     */
+    public void setRespectDisplayBounds(bool respectDisplayBounds) {
+        this.respectDisplayBounds = respectDisplayBounds;
+    }
+
+    /**
+     * Return whether the tooltip respects bounds of the monitor.
+     *
+     * @return <code>true</code> if tooltip respects the bounds of the monitor
+     */
+    public bool isRespectMonitorBounds() {
+        return respectMonitorBounds;
+    }
+
+    /**
+     * Set to <code>false</code> if monitor bounds should not be respected or
+     * to <code>true</code> if the tooltip is should repositioned to not
+     * overlap the monitors bounds. The monitor the tooltip belongs to is the
+     * same is control's monitor the tooltip is shown for.
+     * <p>
+     * Default is <code>true</code>
+     * </p>
+     *
+     * @param respectMonitorBounds
+     */
+    public void setRespectMonitorBounds(bool respectMonitorBounds) {
+        this.respectMonitorBounds = respectMonitorBounds;
+    }
+
+    /**
+     * Should the tooltip displayed because of the given event.
+     * <p>
+     * <b>Subclasses may overwrite this to get custom behavior</b>
+     * </p>
+     *
+     * @param event
+     *            the event
+     * @return <code>true</code> if tooltip should be displayed
+     */
+    protected bool shouldCreateToolTip(Event event) {
+        if ((style & NO_RECREATE) !is 0) {
+            Object tmp = getToolTipArea(event);
+
+            // No new area close the current tooltip
+            if (tmp is null) {
+                hide();
+                return false;
+            }
+
+            bool rv = !tmp.opEquals(currentArea);
+            return rv;
+        }
+
+        return true;
+    }
+
+    /**
+     * This method is called before the tooltip is hidden
+     *
+     * @param event
+     *            the event trying to hide the tooltip
+     * @return <code>true</code> if the tooltip should be hidden
+     */
+    private bool shouldHideToolTip(Event event) {
+        if (event !is null && event.type is SWT.MouseMove
+                && (style & NO_RECREATE) !is 0) {
+            Object tmp = getToolTipArea(event);
+
+            // No new area close the current tooltip
+            if (tmp is null) {
+                hide();
+                return false;
+            }
+
+            bool rv = !tmp.opEquals(currentArea);
+            return rv;
+        }
+
+        return true;
+    }
+
+    /**
+     * This method is called to check for which area the tooltip is
+     * created/hidden for. In case of {@link #NO_RECREATE} this is used to
+     * decide if the tooltip is hidden recreated.
+     *
+     * <code>By the default it is the widget the tooltip is created for but could be any object. To decide if
+     * the area changed the {@link Object#equals(Object)} method is used.</code>
+     *
+     * @param event
+     *            the event
+     * @return the area responsible for the tooltip creation or
+     *         <code>null</code> this could be any object describing the area
+     *         (e.g. the {@link Control} onto which the tooltip is bound to, a
+     *         part of this area e.g. for {@link ColumnViewer} this could be a
+     *         {@link ViewerCell})
+     */
+    protected Object getToolTipArea(Event event) {
+        return control;
+    }
+
+    /**
+     * Start up the tooltip programmatically
+     *
+     * @param location
+     *            the location relative to the control the tooltip is shown
+     */
+    public void show(Point location) {
+        Event event = new Event();
+        event.x = location.x;
+        event.y = location.y;
+        event.widget = control;
+        toolTipCreate(event);
+    }
+
+    private Shell toolTipCreate(Event event) {
+        if (shouldCreateToolTip(event)) {
+            Shell shell = new Shell(control.getShell(), SWT.ON_TOP | SWT.TOOL
+                    | SWT.NO_FOCUS);
+            shell.setLayout(new FillLayout());
+
+            toolTipOpen(shell, event);
+
+            return shell;
+        }
+
+        return null;
+    }
+
+    private void toolTipShow(Shell tip, Event event) {
+        if (!tip.isDisposed()) {
+            currentArea = getToolTipArea(event);
+            createToolTipContentArea(event, tip);
+            if (isHideOnMouseDown()) {
+                toolTipHookBothRecursively(tip);
+            } else {
+                toolTipHookByTypeRecursively(tip, true, SWT.MouseExit);
+            }
+
+            tip.pack();
+            Point size = tip.getSize();
+            Point location = fixupDisplayBounds(size, getLocation(size, event));
+
+            // Need to adjust a bit more if the mouse cursor.y is tip.y and
+            // the cursor.x is inside the tip
+            Point cursorLocation = tip.getDisplay().getCursorLocation();
+
+            if (cursorLocation.y is location.y && location.x < cursorLocation.x
+                    && location.x + size.x > cursorLocation.x) {
+                location.y -= 2;
+            }
+
+            tip.setLocation(location);
+            tip.setVisible(true);
+        }
+    }
+
+    private Point fixupDisplayBounds(Point tipSize, Point location) {
+        if (respectDisplayBounds || respectMonitorBounds) {
+            Rectangle bounds;
+            Point rightBounds = new Point(tipSize.x + location.x, tipSize.y
+                    + location.y);
+
+            org.eclipse.swt.widgets.Monitor.Monitor[] ms = control.getDisplay().getMonitors();
+
+            if (respectMonitorBounds && ms.length > 1) {
+                // By default present in the monitor of the control
+                bounds = control.getMonitor().getBounds();
+                Point p = new Point(location.x, location.y);
+
+                // Search on which monitor the event occurred
+                Rectangle tmp;
+                for (int i = 0; i < ms.length; i++) {
+                    tmp = ms[i].getBounds();
+                    if (tmp.contains(p)) {
+                        bounds = tmp;
+                        break;
+                    }
+                }
+
+            } else {
+                bounds = control.getDisplay().getBounds();
+            }
+
+            if (!(bounds.contains(location) && bounds.contains(rightBounds))) {
+                if (rightBounds.x > bounds.x + bounds.width) {
+                    location.x -= rightBounds.x - (bounds.x + bounds.width);
+                }
+
+                if (rightBounds.y > bounds.y + bounds.height) {
+                    location.y -= rightBounds.y - (bounds.y + bounds.height);
+                }
+
+                if (location.x < bounds.x) {
+                    location.x = bounds.x;
+                }
+
+                if (location.y < bounds.y) {
+                    location.y = bounds.y;
+                }
+            }
+        }
+
+        return location;
+    }
+
+    /**
+     * Get the display relative location where the tooltip is displayed.
+     * Subclasses may overwrite to implement custom positioning.
+     *
+     * @param tipSize
+     *            the size of the tooltip to be shown
+     * @param event
+     *            the event triggered showing the tooltip
+     * @return the absolute position on the display
+     */
+    public Point getLocation(Point tipSize, Event event) {
+        return control.toDisplay(event.x + xShift, event.y + yShift);
+    }
+
+    private void toolTipHide(Shell tip, Event event) {
+                        assert(this);
+        if (tip !is null && !tip.isDisposed() && shouldHideToolTip(event)) {
+                        assert(this);
+            control.getShell().removeListener(SWT.Deactivate, shellListener);
+            currentArea = null;
+            passOnEvent(tip, event);
+            tip.dispose();
+            CURRENT_TOOLTIP = null;
+            afterHideToolTip(event);
+        }
+    }
+
+    private void passOnEvent(Shell tip, Event event) {
+        if (control !is null && !control.isDisposed() && event !is null
+              && event.widget !is control && event.type is SWT.MouseDown) {
+            Display display = control.getDisplay();
+            Point newPt = display.map(tip, null, new Point(event.x, event.y));
+
+            Event newEvent = new Event();
+            newEvent.button = event.button;
+            newEvent.character = event.character;
+            newEvent.count = event.count;
+            newEvent.data = event.data;
+            newEvent.detail = event.detail;
+            newEvent.display = event.display;
+            newEvent.doit = event.doit;
+            newEvent.end = event.end;
+            newEvent.gc = event.gc;
+            newEvent.height = event.height;
+            newEvent.index = event.index;
+            newEvent.item = event.item;
+            newEvent.keyCode = event.keyCode;
+            newEvent.start = event.start;
+            newEvent.stateMask = event.stateMask;
+            newEvent.text = event.text;
+            newEvent.time = event.time;
+            newEvent.type = event.type;
+            newEvent.widget = event.widget;
+            newEvent.width = event.width;
+            newEvent.x = newPt.x;
+            newEvent.y = newPt.y;
+
+            tip.close();
+            display.asyncExec(dgRunnable( delegate(Display display_, Event newEvent_) {
+                if (IS_OSX) {
+                    try {
+                        JThread.sleep(300);
+                    } catch (InterruptedException e) {
+
+                    }
+
+                    display_.post(newEvent_);
+                    newEvent_.type = SWT.MouseUp;
+                    display_.post(newEvent_);
+                } else {
+                    display_.post(newEvent_);
+                }
+            }, display,newEvent));
+        }
+    }
+
+    private void toolTipOpen(Shell shell, Event event) {
+        // Ensure that only one Tooltip is shown in time
+        if (CURRENT_TOOLTIP !is null) {
+            toolTipHide(CURRENT_TOOLTIP, null);
+        }
+
+        CURRENT_TOOLTIP = shell;
+
+        control.getShell().addListener(SWT.Deactivate, shellListener);
+
+        if (popupDelay > 0) {
+            control.getDisplay().timerExec(popupDelay, dgRunnable( (Shell shell_,Event event_, ToolTip tooltip_){
+                assert(tooltip_);
+                tooltip_.toolTipShow(shell_, event_);
+            },shell,event, this));
+        } else {
+            toolTipShow(CURRENT_TOOLTIP, event);
+        }
+
+        if (hideDelay > 0) {
+            control.getDisplay().timerExec(popupDelay + hideDelay, dgRunnable( (Shell shell_, ToolTip tooltip_){
+                assert(tooltip_);
+                tooltip_.toolTipHide(shell_, null);
+            }, shell, this ));
+        }
+    }
+
+    private void toolTipHookByTypeRecursively(Control c, bool add, int type) {
+        if (add) {
+            c.addListener(type, hideListener);
+        } else {
+            c.removeListener(type, hideListener);
+        }
+
+        if ( auto c2 = cast(Composite)c ) {
+            Control[] children = c2.getChildren();
+            for (int i = 0; i < children.length; i++) {
+                toolTipHookByTypeRecursively(children[i], add, type);
+            }
+        }
+    }
+
+    private void toolTipHookBothRecursively(Control c) {
+        c.addListener(SWT.MouseDown, hideListener);
+        c.addListener(SWT.MouseExit, hideListener);
+
+        if ( auto comp = cast(Composite) c ) {
+            Control[] children = comp.getChildren();
+            for (int i = 0; i < children.length; i++) {
+                toolTipHookBothRecursively(children[i]);
+            }
+        }
+    }
+
+    /**
+     * Creates the content area of the the tooltip.
+     *
+     * @param event
+     *            the event that triggered the activation of the tooltip
+     * @param parent
+     *            the parent of the content area
+     * @return the content area created
+     */
+    protected abstract Composite createToolTipContentArea(Event event,
+            Composite parent);
+
+    /**
+     * This method is called after a tooltip is hidden.
+     * <p>
+     * <b>Subclasses may override to clean up requested system resources</b>
+     * </p>
+     *
+     * @param event
+     *            event triggered the hiding action (may be <code>null</code>
+     *            if event wasn't triggered by user actions directly)
+     */
+    protected void afterHideToolTip(Event event) {
+
+    }
+
+    /**
+     * Set the hide delay.
+     *
+     * @param hideDelay
+     *            the delay before the tooltip is hidden. If <code>0</code>
+     *            the tooltip is shown until user moves to other item
+     */
+    public void setHideDelay(int hideDelay) {
+        this.hideDelay = hideDelay;
+    }
+
+    /**
+     * Set the popup delay.
+     *
+     * @param popupDelay
+     *            the delay before the tooltip is shown to the user. If
+     *            <code>0</code> the tooltip is shown immediately
+     */
+    public void setPopupDelay(int popupDelay) {
+        this.popupDelay = popupDelay;
+    }
+
+    /**
+     * Return if hiding on mouse down is set.
+     *
+     * @return <code>true</code> if hiding on mouse down in the tool tip is on
+     */
+    public bool isHideOnMouseDown() {
+        return hideOnMouseDown;
+    }
+
+    /**
+     * If you don't want the tool tip to be hidden when the user clicks inside
+     * the tool tip set this to <code>false</code>. You maybe also need to
+     * hide the tool tip yourself depending on what you do after clicking in the
+     * tooltip (e.g. if you open a new {@link Shell})
+     *
+     * @param hideOnMouseDown
+     *            flag to indicate of tooltip is hidden automatically on mouse
+     *            down inside the tool tip
+     */
+    public void setHideOnMouseDown(bool hideOnMouseDown) {
+        // Only needed if there's currently a tooltip active
+        if (CURRENT_TOOLTIP !is null && !CURRENT_TOOLTIP.isDisposed()) {
+            // Only change if value really changed
+            if (hideOnMouseDown !is this.hideOnMouseDown) {
+                control.getDisplay().syncExec(new class(hideOnMouseDown) Runnable {
+                    bool hideOnMouseDown_;
+                    this(bool a){ hideOnMouseDown_=a; }
+                    public void run() {
+                        if (CURRENT_TOOLTIP !is null
+                                && CURRENT_TOOLTIP.isDisposed()) {
+                            toolTipHookByTypeRecursively(CURRENT_TOOLTIP,
+                                    hideOnMouseDown_, SWT.MouseDown);
+                        }
+                    }
+
+                });
+            }
+        }
+
+        this.hideOnMouseDown = hideOnMouseDown;
+    }
+
+    /**
+     * Hide the currently active tool tip
+     */
+    public void hide() {
+        toolTipHide(CURRENT_TOOLTIP, null);
+    }
+
+    private class ToolTipOwnerControlListener : Listener {
+        public void handleEvent(Event event) {
+            switch (event.type) {
+            case SWT.Dispose:
+            case SWT.KeyDown:
+            case SWT.MouseDown:
+            case SWT.MouseMove:
+            case SWT.MouseWheel:
+                toolTipHide(CURRENT_TOOLTIP, event);
+                break;
+            case SWT.MouseHover:
+                toolTipCreate(event);
+                break;
+            case SWT.MouseExit:
+                /*
+                 * Check if the mouse exit happened because we move over the
+                 * tooltip
+                 */
+                if (CURRENT_TOOLTIP !is null && !CURRENT_TOOLTIP.isDisposed()) {
+                    if (CURRENT_TOOLTIP.getBounds().contains(
+                            control.toDisplay(event.x, event.y))) {
+                        break;
+                    }
+                }
+
+                toolTipHide(CURRENT_TOOLTIP, event);
+                break;
+            default:
+            }
+        }
+    }
+
+    private class TooltipHideListener : Listener {
+        public void handleEvent(Event event) {
+            if ( auto c = cast(Control)event.widget ) {
+
+                Shell shell = c.getShell();
+
+                switch (event.type) {
+                case SWT.MouseDown:
+                    if (isHideOnMouseDown()) {
+                        toolTipHide(shell, event);
+                    }
+                    break;
+                case SWT.MouseExit:
+                    /*
+                     * Give some insets to ensure we get exit informations from
+                     * a wider area ;-)
+                     */
+                    Rectangle rect = shell.getBounds();
+                    rect.x += 5;
+                    rect.y += 5;
+                    rect.width -= 10;
+                    rect.height -= 10;
+
+                    if (!rect.contains(c.getDisplay().getCursorLocation())) {
+                        toolTipHide(shell, event);
+                    }
+
+                    break;
+                default:
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/window/Window.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1106 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.window.Window;
+
+import org.eclipse.jface.window.IShellProvider;
+import org.eclipse.jface.window.WindowManager;
+import org.eclipse.jface.window.SameShellProvider;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.events.ShellListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Monitor;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.Geometry;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Set;
+import tango.io.Stdout;
+
+/**
+ * A JFace window is an object that has no visual representation (no widgets)
+ * until it is told to open.
+ * <p>
+ * Creating a window involves the following steps:
+ * <ul>
+ * <li>creating an instance of a concrete subclass of <code>Window</code>
+ * </li>
+ * <li>creating the window's shell and widget tree by calling
+ * <code>create</code> (optional)</li>
+ * <li>assigning the window to a window manager using
+ * <code>WindowManager.add</code> (optional)</li>
+ * <li>opening the window by calling <code>open</code></li>
+ * </ul>
+ * Opening the window will create its shell and widget tree if they have not
+ * already been created. When the window is closed, the shell and widget tree
+ * are disposed of and are no longer referenced, and the window is automatically
+ * removed from its window manager. A window may be reopened.
+ * </p>
+ * <p>
+ * The JFace window framework (this package) consists of this class,
+ * <code>Window</code>, the abstract base of all windows, and one concrete
+ * window classes (<code>ApplicationWindow</code>) which may also be
+ * subclassed. Clients may define additional window subclasses as required.
+ * </p>
+ * <p>
+ * The <code>Window</code> class provides methods that subclasses may
+ * override to configure the window, including:
+ * <ul>
+ * <li><code>close</code>- extend to free other SWT resources</li>
+ * <li><code>configureShell</code>- extend or reimplement to set shell
+ * properties before window opens</li>
+ * <li><code>createContents</code>- extend or reimplement to create controls
+ * before window opens</li>
+ * <li><code>getInitialSize</code>- reimplement to give the initial size for
+ * the shell</li>
+ * <li><code>getInitialLocation</code>- reimplement to give the initial
+ * location for the shell</li>
+ * <li><code>getShellListener</code>- extend or reimplement to receive shell
+ * events</li>
+ * <li><code>handleFontChange</code>- reimplement to respond to font changes
+ * </li>
+ * <li><code>handleShellCloseEvent</code>- extend or reimplement to handle
+ * shell closings</li>
+ * </ul>
+ * </p>
+ */
+public abstract class Window : IShellProvider {
+
+    /**
+     * Standard return code constant (value 0) indicating that the window was
+     * opened.
+     *
+     * @see #open
+     */
+    public static const int OK = 0;
+
+    /**
+     * Standard return code constant (value 1) indicating that the window was
+     * canceled.
+     *
+     * @see #open
+     */
+    public static const int CANCEL = 1;
+
+    /**
+     * An array of images to be used for the window. It is expected that the
+     * array will contain the same icon rendered at different resolutions.
+     */
+    private static Image[] defaultImages;
+
+    /**
+     * This interface defines a Exception Handler which can be set as a global
+     * handler and will be called if an exception happens in the event loop.
+     */
+    public interface IExceptionHandler {
+        /**
+         * Handle the exception.
+         *
+         * @param t
+         *            The exception that occured.
+         */
+        public void handleException(Exception t);
+    }
+
+    /**
+     * Defines a default exception handler.
+     */
+    private static class DefaultExceptionHandler : IExceptionHandler {
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.jface.window.Window.IExceptionHandler#handleException(java.lang.Throwable)
+         */
+        public void handleException(Exception t) {
+            /+
+            if (t instanceof ThreadDeath) {
+                // Don't catch ThreadDeath as this is a normal occurrence when
+                // the thread dies
+                throw (ThreadDeath) t;
+            }
+            +/
+            // Try to keep running.
+            ExceptionPrintStackTrace(t);
+        }
+    }
+
+    /**
+     * The exception handler for this application.
+     */
+    private static IExceptionHandler exceptionHandler_;
+    private static IExceptionHandler exceptionHandler(){
+        if( exceptionHandler_ is null ){
+            synchronized if( exceptionHandler_ is null ){
+                exceptionHandler_ = new DefaultExceptionHandler();
+            }
+        }
+        return exceptionHandler_;
+    }
+    /**
+     * The default orientation of the window. By default
+     * it is SWT#NONE but it can also be SWT#LEFT_TO_RIGHT
+     * or SWT#RIGHT_TO_LEFT
+     */
+    private static int orientation = SWT.NONE;
+
+    /**
+     * Object used to locate the default parent for modal shells
+     */
+    private static IShellProvider defaultModalParent_;
+    private static IShellProvider defaultModalParent(){
+        if( defaultModalParent_ is null ){
+            synchronized if( defaultModalParent_ is null ){
+                defaultModalParent_ = new class IShellProvider {
+                    public Shell getShell() {
+                        Display d = Display.getCurrent();
+
+                        if (d is null) {
+                            return null;
+                        }
+
+                        Shell parent = d.getActiveShell();
+
+                        // Make sure we don't pick a parent that has a modal child (this can lock the app)
+                        if (parent is null) {
+                            // If this is a top-level window, then there must not be any open modal windows.
+                            parent = getModalChild(Display.getCurrent().getShells());
+                        } else {
+                            // If we picked a parent with a modal child, use the modal child instead
+                            Shell modalChild = getModalChild(parent.getShells());
+                            if (modalChild !is null) {
+                                parent = modalChild;
+                            }
+                        }
+
+                        return parent;
+                    }
+                };
+            }
+        }
+        return defaultModalParent_;
+    }
+
+    /**
+     * Object that returns the parent shell.
+     */
+    private IShellProvider parentShell;
+
+    /**
+     * Shell style bits.
+     *
+     * @see #setShellStyle
+     */
+    private int shellStyle = SWT.SHELL_TRIM;
+
+    /**
+     * Window manager, or <code>null</code> if none.
+     *
+     * @see #setWindowManager
+     */
+    private WindowManager windowManager;
+
+    /**
+     * Window shell, or <code>null</code> if none.
+     */
+    private Shell shell;
+
+    /**
+     * Top level SWT control, or <code>null</code> if none
+     */
+    private Control contents;
+
+    /**
+     * Window return code; initially <code>OK</code>.
+     *
+     * @see #setReturnCode
+     */
+    private int returnCode = OK;
+
+    /**
+     * <code>true</code> if the <code>open</code> method should not return
+     * until the window closes, and <code>false</code> if the
+     * <code>open</code> method should return immediately; initially
+     * <code>false</code> (non-blocking).
+     *
+     * @see #setBlockOnOpen
+     */
+    private bool block = false;
+
+    /**
+     * Internal class for informing this window when fonts change.
+     */
+    private class FontChangeListener : IPropertyChangeListener {
+        public void propertyChange(PropertyChangeEvent event) {
+            handleFontChange(event);
+        }
+    }
+
+    /**
+     * Internal font change listener.
+     */
+    private FontChangeListener fontChangeListener;
+
+    /**
+     * Internal fields to detect if shell size has been set
+     */
+    private bool resizeHasOccurred = false;
+
+    private Listener resizeListener;
+
+    /**
+     * Creates a window instance, whose shell will be created under the given
+     * parent shell. Note that the window will have no visual representation
+     * until it is told to open. By default, <code>open</code> does not block.
+     *
+     * @param parentShell
+     *            the parent shell, or <code>null</code> to create a top-level
+     *            shell. Try passing "(Shell)null" to this method instead of "null"
+     *            if your compiler complains about an ambiguity error.
+     * @see #setBlockOnOpen
+     * @see #getDefaultOrientation()
+     */
+    protected this(Shell parentShell) {
+        this(new SameShellProvider(parentShell));
+
+        if(parentShell is null) {
+            setShellStyle(getShellStyle() | getDefaultOrientation());
+        }
+    }
+
+    /**
+     * Creates a new window which will create its shell as a child of whatever
+     * the given shellProvider returns.
+     *
+     * @param shellProvider object that will return the current parent shell. Not null.
+     *
+     * @since 3.1
+     */
+    protected this(IShellProvider shellProvider) {
+        Assert.isNotNull(cast(Object)shellProvider);
+        this.parentShell = shellProvider;
+    }
+
+    /**
+     * Determines if the window should handle the close event or do nothing.
+     * <p>
+     * The default implementation of this framework method returns
+     * <code>true</code>, which will allow the
+     * <code>handleShellCloseEvent</code> method to be called. Subclasses may
+     * extend or reimplement.
+     * </p>
+     *
+     * @return whether the window should handle the close event.
+     */
+    protected bool canHandleShellCloseEvent() {
+        return true;
+    }
+
+    /**
+     * Closes this window, disposes its shell, and removes this window from its
+     * window manager (if it has one).
+     * <p>
+     * This framework method may be extended (<code>super.close</code> must
+     * be called).
+     * </p>
+     * <p>
+     *  Note that in order to prevent recursive calls to this method
+     *  it does not call <code>Shell#close()</code>. As a result <code>ShellListener</code>s
+     *  will not receive a <code>shellClosed</code> event.
+     *  </p>
+     *
+     * @return <code>true</code> if the window is (or was already) closed, and
+     *         <code>false</code> if it is still open
+     */
+    public bool close() {
+
+        // stop listening for font changes
+        if (fontChangeListener !is null) {
+            JFaceResources.getFontRegistry().removeListener(fontChangeListener);
+            fontChangeListener = null;
+        }
+
+        // remove this window from a window manager if it has one
+        if (windowManager !is null) {
+            windowManager.remove(this);
+            windowManager = null;
+        }
+
+        if (shell is null || shell.isDisposed()) {
+            return true;
+        }
+
+        // If we "close" the shell recursion will occur.
+        // Instead, we need to "dispose" the shell to remove it from the
+        // display.
+        shell.dispose();
+        shell = null;
+        contents = null;
+
+        return true;
+    }
+
+    /**
+     * Configures the given shell in preparation for opening this window in it.
+     * <p>
+     * The default implementation of this framework method sets the shell's
+     * image and gives it a grid layout. Subclasses may extend or reimplement.
+     * </p>
+     *
+     * @param newShell
+     *            the shell
+     */
+    protected void configureShell(Shell newShell) {
+
+        // The single image version of this code had a comment related to bug
+        // 46624,
+        // and some code that did nothing if the stored image was already
+        // disposed.
+        // The equivalent in the multi-image version seems to be to remove the
+        // disposed images from the array passed to the shell.
+        if (defaultImages !is null && defaultImages.length > 0) {
+            ArrayList nonDisposedImages = new ArrayList(defaultImages.length);
+            for (int i = 0; i < defaultImages.length; ++i) {
+                if (defaultImages[i] !is null && !defaultImages[i].isDisposed()) {
+                    nonDisposedImages.add(defaultImages[i]);
+                }
+            }
+
+            if (nonDisposedImages.size() <= 0) {
+                Stderr.formatln("Window.configureShell: images disposed"); //$NON-NLS-1$
+            } else {
+                newShell.setImages(arraycast!(Image)(nonDisposedImages.toArray()));
+            }
+        }
+
+        Layout layout = getLayout();
+        if (layout !is null) {
+            newShell.setLayout(layout);
+        }
+    }
+
+    /**
+     * Creates the layout for the shell. The layout created here will be
+     * attached to the composite passed into createContents. The default
+     * implementation returns a GridLayout with no margins. Subclasses that
+     * change the layout type by overriding this method should also override
+     * createContents.
+     *
+     * <p>
+     * A return value of null indicates that no layout should be attached to the
+     * composite. In this case, the layout may be attached within
+     * createContents.
+     * </p>
+     *
+     * @return a newly created Layout or null if no layout should be attached.
+     * @since 3.0
+     */
+    protected Layout getLayout() {
+        GridLayout layout = new GridLayout();
+        layout.marginHeight = 0;
+        layout.marginWidth = 0;
+        return layout;
+    }
+
+    /**
+     * Constrain the shell size to be no larger than the display bounds.
+     *
+     * @since 2.0
+     */
+    protected void constrainShellSize() {
+        // limit the shell size to the display size
+        Rectangle bounds = shell.getBounds();
+        Rectangle constrained = getConstrainedShellBounds(bounds);
+        if (!bounds.opEquals(constrained)) {
+            shell.setBounds(constrained);
+        }
+    }
+
+    /**
+     * Creates this window's widgetry in a new top-level shell.
+     * <p>
+     * The default implementation of this framework method creates this window's
+     * shell (by calling <code>createShell</code>), and its controls (by
+     * calling <code>createContents</code>), then initializes this window's
+     * shell bounds (by calling <code>initializeBounds</code>).
+     * </p>
+     */
+    public void create() {
+        shell = createShell();
+        contents = createContents(shell);
+
+        //initialize the bounds of the shell to that appropriate for the
+        // contents
+        initializeBounds();
+    }
+
+    /**
+     * Creates and returns this window's contents. Subclasses may attach any
+     * number of children to the parent. As a convenience, the return value of
+     * this method will be remembered and returned by subsequent calls to
+     * getContents(). Subclasses may modify the parent's layout if they overload
+     * getLayout() to return null.
+     *
+     * <p>
+     * It is common practise to create and return a single composite that
+     * contains the entire window contents.
+     * </p>
+     *
+     * <p>
+     * The default implementation of this framework method creates an instance
+     * of <code>Composite</code>. Subclasses may override.
+     * </p>
+     *
+     * @param parent
+     *            the parent composite for the controls in this window. The type
+     *            of layout used is determined by getLayout()
+     *
+     * @return the control that will be returned by subsequent calls to
+     *         getContents()
+     */
+    protected Control createContents(Composite parent) {
+        // by default, just create a composite
+        return new Composite(parent, SWT.NONE);
+    }
+
+    /**
+     * Creates and returns this window's shell.
+     * <p>
+     * The default implementation of this framework method creates a new shell
+     * and configures it using <code/>configureShell</code>. Rather than
+     * override this method, subclasses should instead override
+     * <code/>configureShell</code>.
+     * </p>
+     *
+     * @return the shell
+     */
+    protected final Shell createShell() {
+
+        Shell newParent = getParentShell();
+        if(newParent !is null &&  newParent.isDisposed()){
+            parentShell = new SameShellProvider(null);
+            newParent = getParentShell();//Find a better parent
+        }
+
+        //Create the shell
+        Shell newShell = new Shell(newParent, getShellStyle());
+
+        resizeListener = new class Listener {
+            public void handleEvent(Event e) {
+                resizeHasOccurred = true;
+            }
+        };
+
+        newShell.addListener(SWT.Resize, resizeListener);
+        newShell.setData(this);
+
+        //Add a listener
+        newShell.addShellListener(getShellListener());
+
+        //Set the layout
+        configureShell(newShell);
+
+        //Register for font changes
+        if (fontChangeListener is null) {
+            fontChangeListener = new FontChangeListener();
+        }
+        JFaceResources.getFontRegistry().addListener(fontChangeListener);
+
+        return newShell;
+    }
+
+    /**
+     * Returns the top level control for this window. The parent of this control
+     * is the shell.
+     *
+     * @return the top level control, or <code>null</code> if this window's
+     *         control has not been created yet
+     */
+    protected Control getContents() {
+        return contents;
+    }
+
+    /**
+     * Returns the default image. This is the image that will be used for
+     * windows that have no shell image at the time they are opened. There is no
+     * default image unless one is installed via <code>setDefaultImage</code>.
+     *
+     * @return the default image, or <code>null</code> if none
+     * @see #setDefaultImage
+     */
+    public static Image getDefaultImage() {
+        return (defaultImages is null || defaultImages.length < 1) ? null
+                : defaultImages[0];
+    }
+
+    /**
+     * Returns the array of default images to use for newly opened windows. It
+     * is expected that the array will contain the same icon rendered at
+     * different resolutions.
+     *
+     * @see org.eclipse.swt.widgets.Decorations#setImages(org.eclipse.swt.graphics.Image[])
+     *
+     * @return the array of images to be used when a new window is opened
+     * @see #setDefaultImages
+     * @since 3.0
+     */
+    public static Image[] getDefaultImages() {
+        return (defaultImages is null ? new Image[0] : defaultImages);
+    }
+
+    /**
+     * Returns the initial location to use for the shell. The default
+     * implementation centers the shell horizontally (1/2 of the difference to
+     * the left and 1/2 to the right) and vertically (1/3 above and 2/3 below)
+     * relative to the parent shell, or display bounds if there is no parent
+     * shell.
+     *
+     * @param initialSize
+     *            the initial size of the shell, as returned by
+     *            <code>getInitialSize</code>.
+     * @return the initial location of the shell
+     */
+    protected Point getInitialLocation(Point initialSize) {
+        Composite parent = shell.getParent();
+
+        org.eclipse.swt.widgets.Monitor.Monitor monitor = shell.getDisplay().getPrimaryMonitor();
+        if (parent !is null) {
+            monitor = parent.getMonitor();
+        }
+
+        Rectangle monitorBounds = monitor.getClientArea();
+        Point centerPoint;
+        if (parent !is null) {
+            centerPoint = Geometry.centerPoint(parent.getBounds());
+        } else {
+            centerPoint = Geometry.centerPoint(monitorBounds);
+        }
+
+        return new Point(centerPoint.x - (initialSize.x / 2), Math.max(
+                monitorBounds.y, Math.min(centerPoint.y
+                        - (initialSize.y * 2 / 3), monitorBounds.y
+                        + monitorBounds.height - initialSize.y)));
+    }
+
+    /**
+     * Returns the initial size to use for the shell. The default implementation
+     * returns the preferred size of the shell, using
+     * <code>Shell.computeSize(SWT.DEFAULT, SWT.DEFAULT, true)</code>.
+     *
+     * @return the initial size of the shell
+     */
+    protected Point getInitialSize() {
+        return shell.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+    }
+
+    /**
+     * Returns the most specific modal child from the given list of Shells.
+     *
+     * @param toSearch shells to search for modal children
+     * @return the most specific modal child, or null if none
+     *
+     * @since 3.1
+     */
+    private static Shell getModalChild(Shell[] toSearch) {
+        int modal = SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL | SWT.PRIMARY_MODAL;
+
+        for (int i = toSearch.length - 1; i >= 0; i--) {
+            Shell shell = toSearch[i];
+
+            // Check if this shell has a modal child
+            Shell[] children = shell.getShells();
+            Shell modalChild = getModalChild(children);
+            if (modalChild !is null) {
+                return modalChild;
+            }
+
+            // If not, check if this shell is modal itself
+            if (shell.isVisible() && (shell.getStyle() & modal) !is 0) {
+                return shell;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns parent shell, under which this window's shell is created.
+     *
+     * @return the parent shell, or <code>null</code> if there is no parent
+     *         shell
+     */
+    protected Shell getParentShell() {
+        Shell parent = parentShell.getShell();
+
+        int modal = SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL | SWT.PRIMARY_MODAL;
+
+        if ((getShellStyle() & modal) !is 0) {
+            // If this is a modal shell with no parent, pick a shell using defaultModalParent.
+            if (parent is null) {
+                parent = defaultModalParent.getShell();
+            }
+        }
+
+        return parent;
+    }
+
+    /**
+     * Returns this window's return code. A window's return codes are
+     * window-specific, although two standard return codes are predefined:
+     * <code>OK</code> and <code>CANCEL</code>.
+     *
+     * @return the return code
+     */
+    public int getReturnCode() {
+        return returnCode;
+    }
+
+    /**
+     * Returns this window's shell.
+     *
+     * @return this window's shell, or <code>null</code> if this window's
+     *         shell has not been created yet
+     */
+    public Shell getShell() {
+        return shell;
+    }
+
+    /**
+     * Returns a shell listener. This shell listener gets registered with this
+     * window's shell.
+     * <p>
+     * The default implementation of this framework method returns a new
+     * listener that makes this window the active window for its window manager
+     * (if it has one) when the shell is activated, and calls the framework
+     * method <code>handleShellCloseEvent</code> when the shell is closed.
+     * Subclasses may extend or reimplement.
+     * </p>
+     *
+     * @return a shell listener
+     */
+    protected ShellListener getShellListener() {
+        return new class ShellAdapter {
+            public void shellClosed(ShellEvent event) {
+                event.doit = false; // don't close now
+                if (canHandleShellCloseEvent()) {
+                    handleShellCloseEvent();
+                }
+            }
+        };
+    }
+
+    /**
+     * Returns the shell style bits.
+     * <p>
+     * The default value is <code>SWT.CLOSE|SWT.MIN|SWT.MAX|SWT.RESIZE</code>.
+     * Subclassers should call <code>setShellStyle</code> to change this
+     * value, rather than overriding this method.
+     * </p>
+     *
+     * @return the shell style bits
+     */
+    protected int getShellStyle() {
+        return shellStyle;
+    }
+
+    /**
+     * Returns the window manager of this window.
+     *
+     * @return the WindowManager, or <code>null</code> if none
+     */
+    public WindowManager getWindowManager() {
+        return windowManager;
+    }
+
+    /**
+     * Notifies of a font property change.
+     * <p>
+     * The default implementation of this framework method does nothing.
+     * Subclasses may reimplement.
+     * </p>
+     *
+     * @param event
+     *            the property change event detailing what changed
+     */
+    protected void handleFontChange(PropertyChangeEvent event) {
+        // do nothing
+    }
+
+    /**
+     * Notifies that the window's close button was pressed, the close menu was
+     * selected, or the ESCAPE key pressed.
+     * <p>
+     * The default implementation of this framework method sets the window's
+     * return code to <code>CANCEL</code> and closes the window using
+     * <code>close</code>. Subclasses may extend or reimplement.
+     * </p>
+     */
+    protected void handleShellCloseEvent() {
+        setReturnCode(CANCEL);
+        close();
+    }
+
+    /**
+     * Initializes the location and size of this window's SWT shell after it has
+     * been created.
+     * <p>
+     * This framework method is called by the <code>create</code> framework
+     * method. The default implementation calls <code>getInitialSize</code>
+     * and <code>getInitialLocation</code> and passes the results to
+     * <code>Shell.setBounds</code>. This is only done if the bounds of the
+     * shell have not already been modified. Subclasses may extend or
+     * reimplement.
+     * </p>
+     */
+    protected void initializeBounds() {
+        if (resizeListener !is null) {
+            shell.removeListener(SWT.Resize, resizeListener);
+        }
+        if (resizeHasOccurred) { // Check if shell size has been set already.
+            return;
+        }
+
+        Point size = getInitialSize();
+        Point location = getInitialLocation(size);
+        shell.setBounds(getConstrainedShellBounds(new Rectangle(location.x,
+                location.y, size.x, size.y)));
+    }
+
+    /**
+     * Opens this window, creating it first if it has not yet been created.
+     * <p>
+     * If this window has been configured to block on open (
+     * <code>setBlockOnOpen</code>), this method waits until the window is
+     * closed by the end user, and then it returns the window's return code;
+     * otherwise, this method returns immediately. A window's return codes are
+     * window-specific, although two standard return codes are predefined:
+     * <code>OK</code> and <code>CANCEL</code>.
+     * </p>
+     *
+     * @return the return code
+     *
+     * @see #create()
+     */
+    public int open() {
+
+        if (shell is null || shell.isDisposed()) {
+            shell = null;
+            // create the window
+            create();
+        }
+
+        // limit the shell size to the display size
+        constrainShellSize();
+
+        // open the window
+        shell.open();
+
+        // run the event loop if specified
+        if (block) {
+            runEventLoop(shell);
+        }
+
+        return returnCode;
+    }
+
+    /**
+     * Runs the event loop for the given shell.
+     *
+     * @param loopShell
+     *            the shell
+     */
+    private void runEventLoop(Shell loopShell) {
+
+        //Use the display provided by the shell if possible
+        Display display;
+        if (shell is null) {
+            display = Display.getCurrent();
+        } else {
+            display = loopShell.getDisplay();
+        }
+
+        while (loopShell !is null && !loopShell.isDisposed()) {
+            try {
+                if (!display.readAndDispatch()) {
+                    display.sleep();
+                }
+            } catch (Exception e) {
+                exceptionHandler.handleException(e);
+            }
+        }
+        display.update();
+    }
+
+    /**
+     * Sets whether the <code>open</code> method should block until the window
+     * closes.
+     *
+     * @param shouldBlock
+     *            <code>true</code> if the <code>open</code> method should
+     *            not return until the window closes, and <code>false</code>
+     *            if the <code>open</code> method should return immediately
+     */
+    public void setBlockOnOpen(bool shouldBlock) {
+        block = shouldBlock;
+    }
+
+    /**
+     * Sets the default image. This is the image that will be used for windows
+     * that have no shell image at the time they are opened. There is no default
+     * image unless one is installed via this method.
+     *
+     * @param image
+     *            the default image, or <code>null</code> if none
+     */
+    public static void setDefaultImage(Image image) {
+        defaultImages = image is null ? null : [ image ];
+    }
+
+    /**
+     * Sets the array of default images to use for newly opened windows. It is
+     * expected that the array will contain the same icon rendered at different
+     * resolutions.
+     *
+     * @see org.eclipse.swt.widgets.Decorations#setImages(org.eclipse.swt.graphics.Image[])
+     *
+     * @param images
+     *            the array of images to be used when this window is opened
+     * @since 3.0
+     */
+    public static void setDefaultImages(Image[] images) {
+        Image[] newArray = new Image[images.length];
+        System.arraycopy(images, 0, newArray, 0, newArray.length);
+        defaultImages = newArray;
+    }
+
+    /**
+     * Changes the parent shell. This is only safe to use when the shell is not
+     * yet realized (i.e., created). Once the shell is created, it must be
+     * disposed (i.e., closed) before this method can be called.
+     *
+     * @param newParentShell
+     *            The new parent shell; this value may be <code>null</code> if
+     *            there is to be no parent.
+     * @since 3.1
+     */
+    protected void setParentShell(Shell newParentShell) {
+        Assert.isTrue((shell is null), "There must not be an existing shell."); //$NON-NLS-1$
+        parentShell = new SameShellProvider(newParentShell);
+    }
+
+    /**
+     * Sets this window's return code. The return code is automatically returned
+     * by <code>open</code> if block on open is enabled. For non-blocking
+     * opens, the return code needs to be retrieved manually using
+     * <code>getReturnCode</code>.
+     *
+     * @param code
+     *            the return code
+     */
+    protected void setReturnCode(int code) {
+        returnCode = code;
+    }
+
+    /**
+     * Returns the monitor whose client area contains the given point. If no
+     * monitor contains the point, returns the monitor that is closest to the
+     * point. If this is ever made public, it should be moved into a separate
+     * utility class.
+     *
+     * @param toSearch
+     *            point to find (display coordinates)
+     * @param toFind
+     *            point to find (display coordinates)
+     * @return the montor closest to the given point
+     */
+    private static org.eclipse.swt.widgets.Monitor.Monitor getClosestMonitor(Display toSearch, Point toFind) {
+        int closest = Integer.MAX_VALUE;
+
+        org.eclipse.swt.widgets.Monitor.Monitor[] monitors = toSearch.getMonitors();
+        org.eclipse.swt.widgets.Monitor.Monitor result = monitors[0];
+
+        for (int idx = 0; idx < monitors.length; idx++) {
+            org.eclipse.swt.widgets.Monitor.Monitor current = monitors[idx];
+
+            Rectangle clientArea = current.getClientArea();
+
+            if (clientArea.contains(toFind)) {
+                return current;
+            }
+
+            int distance = Geometry.distanceSquared(Geometry
+                    .centerPoint(clientArea), toFind);
+            if (distance < closest) {
+                closest = distance;
+                result = current;
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Given the desired position of the window, this method returns an adjusted
+     * position such that the window is no larger than its monitor, and does not
+     * extend beyond the edge of the monitor. This is used for computing the
+     * initial window position, and subclasses can use this as a utility method
+     * if they want to limit the region in which the window may be moved.
+     *
+     * @param preferredSize
+     *            the preferred position of the window
+     * @return a rectangle as close as possible to preferredSize that does not
+     *         extend outside the monitor
+     *
+     * @since 3.0
+     */
+    protected Rectangle getConstrainedShellBounds(Rectangle preferredSize) {
+        Rectangle result = new Rectangle(preferredSize.x, preferredSize.y,
+                preferredSize.width, preferredSize.height);
+
+        org.eclipse.swt.widgets.Monitor.Monitor mon = getClosestMonitor(getShell().getDisplay(), Geometry
+                .centerPoint(result));
+
+        Rectangle bounds = mon.getClientArea();
+
+        if (result.height > bounds.height) {
+            result.height = bounds.height;
+        }
+
+        if (result.width > bounds.width) {
+            result.width = bounds.width;
+        }
+
+        result.x = Math.max(bounds.x, Math.min(result.x, bounds.x
+                + bounds.width - result.width));
+        result.y = Math.max(bounds.y, Math.min(result.y, bounds.y
+                + bounds.height - result.height));
+
+        return result;
+    }
+
+    /**
+     * Sets the shell style bits. This method has no effect after the shell is
+     * created.
+     * <p>
+     * The shell style bits are used by the framework method
+     * <code>createShell</code> when creating this window's shell.
+     * </p>
+     *
+     * @param newShellStyle
+     *            the new shell style bits
+     */
+    protected void setShellStyle(int newShellStyle) {
+        shellStyle = newShellStyle;
+    }
+
+    /**
+     * Sets the window manager of this window.
+     * <p>
+     * Note that this method is used by <code>WindowManager</code> to maintain
+     * a backpointer. Clients must not call the method directly.
+     * </p>
+     *
+     * @param manager
+     *            the window manager, or <code>null</code> if none
+     */
+    public void setWindowManager(WindowManager manager) {
+        windowManager = manager;
+
+        // Code to detect invalid usage
+
+        if (manager !is null) {
+            Window[] windows = manager.getWindows();
+            for (int i = 0; i < windows.length; i++) {
+                if (windows[i] is this) {
+                    return;
+                }
+            }
+            manager.add(this);
+        }
+    }
+
+    /**
+     * Sets the exception handler for this application.
+     * <p>
+     * Note that the handler may only be set once.  Subsequent calls to this method will be
+     * ignored.
+     * <p>
+     *
+     * @param handler
+     *            the exception handler for the application.
+     */
+    public static void setExceptionHandler(IExceptionHandler handler) {
+        if ( cast(DefaultExceptionHandler)exceptionHandler ) {
+            exceptionHandler_ = handler;
+        }
+    }
+
+    /**
+     * Sets the default parent for modal Windows. This will be used to locate
+     * the parent for any modal Window constructed with a null parent.
+     *
+     * @param provider shell provider that will be used to locate the parent shell
+     * whenever a Window is created with a null parent
+     * @since 3.1
+     */
+    public static void setDefaultModalParent(IShellProvider provider) {
+        defaultModalParent_ = provider;
+    }
+
+    /**
+     * Gets the default orientation for windows. If it is not
+     * set the default value will be unspecified (SWT#NONE).
+     *
+     *
+     * @return SWT#NONE, SWT.RIGHT_TO_LEFT or SWT.LEFT_TO_RIGHT
+     * @see SWT#RIGHT_TO_LEFT
+     * @see SWT#LEFT_TO_RIGHT
+     * @see SWT#NONE
+     * @since 3.1
+     */
+    public static int getDefaultOrientation() {
+        return orientation;
+
+    }
+
+    /**
+     * Sets the default orientation of windows.
+     * @param defaultOrientation one of
+     *  SWT#RIGHT_TO_LEFT, SWT#LEFT_TO_RIGHT ,SWT#NONE
+     * @see SWT#RIGHT_TO_LEFT
+     * @see SWT#LEFT_TO_RIGHT
+     * @see SWT#NONE
+     * @since 3.1
+     */
+    public static void setDefaultOrientation(int defaultOrientation) {
+        orientation = defaultOrientation;
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/window/WindowManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.window.WindowManager;
+
+import org.eclipse.jface.window.Window;
+
+
+import org.eclipse.core.runtime.Assert;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A manager for a group of windows. Window managers are an optional JFace
+ * feature used in applications which create many different windows (dialogs,
+ * wizards, etc.) in addition to a main window. A window manager can be used to
+ * remember all the windows that an application has created (independent of
+ * whether they are presently open or closed). There can be several window
+ * managers, and they can be arranged into a tree. This kind of organization
+ * makes it simple to close whole subgroupings of windows.
+ * <p>
+ * Creating a window manager is as simple as creating an instance of
+ * <code>WindowManager</code>. Associating a window with a window manager is
+ * done with <code>WindowManager.add(Window)</code>. A window is automatically
+ * removed from its window manager as a side effect of closing the window.
+ * </p>
+ *
+ * @see Window
+ */
+public class WindowManager {
+
+    /**
+     * List of windows managed by this window manager
+     * (element type: <code>Window</code>).
+     */
+    private ArrayList windows;
+
+    /**
+     * List of window managers who have this window manager
+     * as their parent (element type: <code>WindowManager</code>).
+     */
+    private List subManagers;
+
+    /**
+     * Creates an empty window manager without a parent window
+     * manager (that is, a root window manager).
+     */
+    public this() {
+        windows = new ArrayList();
+    }
+
+    /**
+     * Creates an empty window manager with the given
+     * window manager as parent.
+     *
+     * @param parent the parent window manager
+     */
+    public this(WindowManager parent) {
+        windows = new ArrayList();
+        Assert.isNotNull(parent);
+        parent.addWindowManager(this);
+    }
+
+    /**
+     * Adds the given window to the set of windows managed by
+     * this window manager. Does nothing is this window is
+     * already managed by this window manager.
+     *
+     * @param window the window
+     */
+    public void add(Window window) {
+        if (!windows.contains(window)) {
+            windows.add(window);
+            window.setWindowManager(this);
+        }
+    }
+
+    /**
+     * Adds the given window manager to the list of
+     * window managers that have this one as a parent.
+     * </p>
+     * @param wm the child window manager
+     */
+    private void addWindowManager(WindowManager wm) {
+        if (subManagers is null) {
+            subManagers = new ArrayList();
+        }
+        if (!subManagers.contains(wm)) {
+            subManagers.add(wm);
+        }
+    }
+
+    /**
+     * Attempts to close all windows managed by this window manager,
+     * as well as windows managed by any descendent window managers.
+     *
+     * @return <code>true</code> if all windows were sucessfully closed,
+     * and <code>false</code> if any window refused to close
+     */
+    public bool close() {
+        List t = cast(List) windows.clone(); // make iteration robust
+        Iterator e = t.iterator();
+        while (e.hasNext()) {
+            Window window = cast(Window) e.next();
+            bool closed = window.close();
+            if (!closed) {
+                return false;
+            }
+        }
+        if (subManagers !is null) {
+            e = subManagers.iterator();
+            while (e.hasNext()) {
+                WindowManager wm = cast(WindowManager) e.next();
+                bool closed = wm.close();
+                if (!closed) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns this window manager's number of windows
+     *
+     * @return the number of windows
+     * @since 3.0
+     */
+    public int getWindowCount() {
+        return windows.size();
+    }
+
+    /**
+     * Returns this window manager's set of windows.
+     *
+     * @return a possibly empty list of window
+     */
+    public Window[] getWindows() {
+        return arraycast!(Window)(windows.toArray());
+    }
+
+    /**
+     * Removes the given window from the set of windows managed by
+     * this window manager. Does nothing is this window is
+     * not managed by this window manager.
+     *
+     * @param window the window
+     */
+    public final void remove(Window window) {
+        if (windows.contains(window)) {
+            windows.remove(window);
+            window.setWindowManager(null);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/wizard/IWizard.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,241 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.wizard.IWizard;
+
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.jface.wizard.IWizardPage;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.jface.dialogs.IDialogSettings;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Interface for a wizard.  A wizard maintains a list of wizard pages,
+ * stacked on top of each other in card layout fashion.
+ * <p>
+ * The class <code>Wizard</code> provides an abstract implementation
+ * of this interface. However, clients are also free to implement this
+ * interface if <code>Wizard</code> does not suit their needs.
+ * </p>
+ * @see Wizard
+ */
+public interface IWizard {
+    /**
+     * Adds any last-minute pages to this wizard.
+     * <p>
+     * This method is called just before the wizard becomes visible, to give the
+     * wizard the opportunity to add any lazily created pages.
+     * </p>
+     */
+    public void addPages();
+
+    /**
+     * Returns whether this wizard could be finished without further user
+     * interaction.
+     * <p>
+     * The result of this method is typically used by the wizard container to enable
+     * or disable the Finish button.
+     * </p>
+     *
+     * @return <code>true</code> if the wizard could be finished,
+     *   and <code>false</code> otherwise
+     */
+    public bool canFinish();
+
+    /**
+     * Creates this wizard's controls in the given parent control.
+     * <p>
+     * The wizard container calls this method to create the controls
+     * for the wizard's pages before the wizard is opened. This allows
+     * the wizard to size correctly; otherwise a resize may occur when
+     * moving to a new page.
+     * </p>
+     *
+     * @param pageContainer the parent control
+     */
+    public void createPageControls(Composite pageContainer);
+
+    /**
+     * Disposes of this wizard and frees all SWT resources.
+     */
+    public void dispose();
+
+    /**
+     * Returns the container of this wizard.
+     *
+     * @return the wizard container, or <code>null</code> if this
+     *   wizard has yet to be added to a container
+     */
+    public IWizardContainer getContainer();
+
+    /**
+     * Returns the default page image for this wizard.
+     * <p>
+     * This image can be used for pages which do not
+     * supply their own image.
+     * </p>
+     *
+     * @return the default page image
+     */
+    public Image getDefaultPageImage();
+
+    /**
+     * Returns the dialog settings for this wizard.
+     * <p>
+     * The dialog store is used to record state between
+     * wizard invocations (for example, radio button selections,
+     * last directory, etc.).
+     * </p>
+     *
+     * @return the dialog settings, or <code>null</code> if none
+     */
+    public IDialogSettings getDialogSettings();
+
+    /**
+     * Returns the successor of the given page.
+     * <p>
+     * This method is typically called by a wizard page
+     * </p>
+     *
+     * @param page the page
+     * @return the next page, or <code>null</code> if none
+     */
+    public IWizardPage getNextPage(IWizardPage page);
+
+    /**
+     * Returns the wizard page with the given name belonging to this wizard.
+     *
+     * @param pageName the name of the wizard page
+     * @return the wizard page with the given name, or <code>null</code> if none
+     */
+    public IWizardPage getPage(String pageName);
+
+    /**
+     * Returns the number of pages in this wizard.
+     *
+     * @return the number of wizard pages
+     */
+    public int getPageCount();
+
+    /**
+     * Returns all the pages in this wizard.
+     *
+     * @return a list of pages
+     */
+    public IWizardPage[] getPages();
+
+    /**
+     * Returns the predecessor of the given page.
+     * <p>
+     * This method is typically called by a wizard page
+     * </p>
+     *
+     * @param page the page
+     * @return the previous page, or <code>null</code> if none
+     */
+    public IWizardPage getPreviousPage(IWizardPage page);
+
+    /**
+     * Returns the first page to be shown in this wizard.
+     *
+     * @return the first wizard page
+     */
+    public IWizardPage getStartingPage();
+
+    /**
+     * Returns the title bar color for this wizard.
+     *
+     * @return the title bar color
+     */
+    public RGB getTitleBarColor();
+
+    /**
+     * Returns the window title string for this wizard.
+     *
+     * @return the window title string, or <code>null</code> for no title
+     */
+    public String getWindowTitle();
+
+    /**
+     * Returns whether help is available for this wizard.
+     * <p>
+     * The result of this method is typically used by the container to
+     * show or hide the Help button.
+     * </p>
+     *
+     * @return <code>true</code> if help is available,
+     *   and <code>false</code> if this wizard is helpless
+     */
+    public bool isHelpAvailable();
+
+    /**
+     * Returns whether this wizard needs Previous and Next buttons.
+     * <p>
+     * The result of this method is typically used by the container.
+     * </p>
+     *
+     * @return <code>true</code> if Previous and Next buttons are required,
+     *   and <code>false</code> if none are needed
+     */
+    public bool needsPreviousAndNextButtons();
+
+    /**
+     * Returns whether this wizard needs a progress monitor.
+     * <p>
+     * The result of this method is typically used by the container.
+     * </p>
+     *
+     * @return <code>true</code> if a progress monitor is required,
+     *   and <code>false</code> if none is needed
+     */
+    public bool needsProgressMonitor();
+
+    /**
+     * Performs any actions appropriate in response to the user
+     * having pressed the Cancel button, or refuse if canceling
+     * now is not permitted.
+     *
+     * @return <code>true</code> to indicate the cancel request
+     *   was accepted, and <code>false</code> to indicate
+     *   that the cancel request was refused
+     */
+    public bool performCancel();
+
+    /**
+     * Performs any actions appropriate in response to the user
+     * having pressed the Finish button, or refuse if finishing
+     * now is not permitted.
+     *
+     * Normally this method is only called on the container's
+     * current wizard. However if the current wizard is a nested wizard
+     * this method will also be called on all wizards in its parent chain.
+     * Such parents may use this notification to save state etc. However,
+     * the value the parents return from this method is ignored.
+     *
+     * @return <code>true</code> to indicate the finish request
+     *   was accepted, and <code>false</code> to indicate
+     *   that the finish request was refused
+     */
+    public bool performFinish();
+
+    /**
+     * Sets or clears the container of this wizard.
+     *
+     * @param wizardContainer the wizard container, or <code>null</code>
+     */
+    public void setContainer(IWizardContainer wizardContainer);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/wizard/IWizardContainer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.wizard.IWizardContainer;
+
+import java.lang.all;
+
+import org.eclipse.jface.wizard.IWizardPage;
+
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.jface.operation.IRunnableContext;
+
+/**
+ * Interface for containers that can host a wizard. It displays
+ * wizard pages, at most one of which is considered
+ * the current page. <code>getCurrentPage</code> returns the
+ * current page; <code>showPage</code> programmatically changes the
+ * the current page. Note that the pages need not all belong
+ * to the same wizard.
+ * <p>
+ * The class <code>WizardDialog</code> provides a fully functional
+ * implementation of this interface which will meet the needs of
+ * most clients. However, clients are also free to implement this
+ * interface if <code>WizardDialog</code> does not suit their needs.
+ * </p>
+ * <p>
+ * Implementors are responsible for disposing of their wizards.
+ * </p>
+ *
+ * @see org.eclipse.jface.wizard.IWizardContainer2
+ */
+public interface IWizardContainer : IRunnableContext {
+    /**
+     * Returns the current wizard page for this container.
+     *
+     * @return the current wizard page, or <code>null</code> if the container
+     *   is not yet showing the wizard
+     * @see #showPage
+     */
+    public IWizardPage getCurrentPage();
+
+    /**
+     * Returns the shell for this wizard container.
+     *
+     * @return the shell, or <code>null</code> if this wizard
+     *   container does not have a shell
+     */
+    public Shell getShell();
+
+    /**
+     * Makes the given page visible.
+     * <p>
+     * This method should not be use for normal page
+     * sequencing (back, next) which is handled by the
+     * container itself. It may, however, be used to
+     * move to another page in response to some custom
+     * action such as double clicking in a list.
+     * </p>
+     *
+     * @param page the page to show
+     * @see #getCurrentPage
+     */
+    public void showPage(IWizardPage page);
+
+    /**
+     * Adjusts the enable state of the Back, Next, and Finish
+     * buttons to reflect the state of the currently active
+     * page in this container.
+     * <p>
+     * This method is called by the container itself
+     * when its wizard page changes and may be called
+     * by the page at other times to force a button state
+     * update.
+     * </p>
+     */
+    public void updateButtons();
+
+    /**
+     * Updates the message (or error message) shown in the message line to
+     * reflect the state of the currently active page in this container.
+     * <p>
+     * This method is called by the container itself
+     * when its wizard page changes and may be called
+     * by the page at other times to force a message
+     * update.
+     * </p>
+     */
+    public void updateMessage();
+
+    /**
+     * Updates the title bar (title, description, and image) to
+     * reflect the state of the currently active page in this container.
+     * <p>
+     * This method is called by the container itself
+     * when its wizard page changes and may be called
+     * by the page at other times to force a title bar
+     * update.
+     * </p>
+     */
+    public void updateTitleBar();
+
+    /**
+     * Updates the window title to reflect the state of the current wizard.
+     * <p>
+     * This method is called by the container itself
+     * when its wizard changes and may be called
+     * by the wizard at other times to force a window
+     * title change.
+     * </p>
+     */
+    public void updateWindowTitle();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/wizard/IWizardContainer2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module org.eclipse.jface.wizard.IWizardContainer2;
+
+import java.lang.all;
+
+import org.eclipse.jface.wizard.IWizardContainer;
+
+/**
+ * <p><code>IWizardContainer2</code> is a supplement to
+ * <code>IWizardContainer</code> that adds a method for updating the size of
+ * the wizard shell based on the contents of the current page.</p>
+ *
+ * <p>The class <code>WizardDialog</code> provides a fully functional
+ * implementation of this interface which will meet the needs of
+ * most clients. However, clients are also free to implement this
+ * interface if <code>WizardDialog</code> does not suit their needs.
+ * </p>
+ *
+ * @see org.eclipse.jface.wizard.IWizardContainer
+ * @since 3.0
+ */
+public interface IWizardContainer2 : IWizardContainer {
+
+    /**
+     * Updates the window size to reflect the state of the current wizard.
+     * <p>
+     * This method is called by the container itself
+     * when its wizard changes and may be called
+     * by the wizard at other times to force a window
+     * size change.
+     * </p>
+     */
+    public void updateSize();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/wizard/IWizardNode.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.wizard.IWizardNode;
+
+import org.eclipse.jface.wizard.IWizard;
+
+import org.eclipse.swt.graphics.Point;
+
+import java.lang.all;
+
+/**
+ * A wizard node acts a placeholder for a real wizard in a wizard
+ * selection page. It is done in such a way that the actual creation
+ * of a wizard can be deferred until the wizard is really needed.
+ * <p>
+ * When a wizard node comes into existence, its wizard may or may
+ * not have been created yet; <code>isContentCreated</code> can
+ * be used to determine which. A node may be asked for its wizard
+ * using <code>getWizard</code>, which will force it to be created
+ * if required. Once the client is done with a wizard node, its
+ * <code>dispose</code>method must be called to free up the wizard;
+ * once disposes, the node should no longer be used.
+ * </p>
+ * <p>
+ * This interface should be implemented by clients wishing to
+ * support this kind of wizard placeholder in a wizard selection page.
+ * </p>
+ *
+ * @see WizardSelectionPage
+ */
+public interface IWizardNode {
+    /**
+     * Disposes the wizard managed by this node. Does nothing
+     * if the wizard has not been created.
+     * <p>
+     * This is the last message that should ever be sent to this node.
+     * </p>
+     */
+    public void dispose();
+
+    /**
+     * Returns the extent of the wizard for this node.
+     * <p>
+     * If the content has not yet been created, calling this method
+     * does not trigger the creation of the wizard. This allows
+     * this node to suggest an extent in advance of actually creating
+     * the wizard.
+     * </p>
+     *
+     * @return the extent, or <code>(-1, -1)</code> extent is not known
+     */
+    public Point getExtent();
+
+    /**
+     * Returns the wizard this node stands for.
+     * <p>
+     * If the content has not been created beforehand, calling this
+     * method triggers the creation of the wizard and caches it so that
+     * the identical wizard object is returned on subsequent calls.
+     * </p>
+     *
+     * @return the wizard
+     */
+    public IWizard getWizard();
+
+    /**
+     * Returns whether a wizard has been created for this node.
+     *
+     * @return <code>true</code> if a wizard has been created,
+     *   and <code>false</code> otherwise
+     */
+    public bool isContentCreated();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/wizard/IWizardPage.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.wizard.IWizardPage;
+
+import org.eclipse.jface.wizard.IWizard;
+
+import org.eclipse.jface.dialogs.IDialogPage;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Interface for a wizard page.
+ * <p>
+ * The class <code>WizardPage</code> provides an abstract implementation
+ * of this interface. However, clients are also free to implement this
+ * interface if <code>WizardPage</code> does not suit their needs.
+ * </p>
+ */
+public interface IWizardPage : IDialogPage {
+    /**
+     * Returns whether the next page could be displayed.
+     *
+     * @return <code>true</code> if the next page could be displayed,
+     *   and <code>false</code> otherwise
+     */
+    public bool canFlipToNextPage();
+
+    /**
+     * Returns this page's name.
+     *
+     * @return the name of this page
+     */
+    public String getName();
+
+    /**
+     * Returns the wizard page that would to be shown if the user was to
+     * press the Next button.
+     *
+     * @return the next wizard page, or <code>null</code> if none
+     */
+    public IWizardPage getNextPage();
+
+    /**
+     * Returns the wizard page that would to be shown if the user was to
+     * press the Back button.
+     *
+     * @return the previous wizard page, or <code>null</code> if none
+     */
+    public IWizardPage getPreviousPage();
+
+    /**
+     * Returns the wizard that hosts this wizard page.
+     *
+     * @return the wizard, or <code>null</code> if this page has not been
+     *   added to any wizard
+     * @see #setWizard
+     */
+    public IWizard getWizard();
+
+    /**
+     * Returns whether this page is complete or not.
+     * <p>
+     * This information is typically used by the wizard to decide
+     * when it is okay to finish.
+     * </p>
+     *
+     * @return <code>true</code> if this page is complete, and
+     *  <code>false</code> otherwise
+     */
+    public bool isPageComplete();
+
+    /**
+     * Sets the wizard page that would typically be shown
+     * if the user was to press the Back button.
+     * <p>
+     * This method is called by the container.
+     * </p>
+     *
+     * @param page the previous wizard page
+     */
+    public void setPreviousPage(IWizardPage page);
+
+    /**
+     * Sets the wizard that hosts this wizard page.
+     * Once established, a page's wizard cannot be changed
+     * to a different wizard.
+     *
+     * @param newWizard the wizard
+     * @see #getWizard
+     */
+    public void setWizard(IWizard newWizard);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/wizard/ProgressMonitorPart.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,327 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.wizard.ProgressMonitorPart;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IProgressMonitorWithBlocking;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.ProgressIndicator;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A standard implementation of an IProgressMonitor. It consists
+ * of a label displaying the task and subtask name, and a
+ * progress indicator to show progress. In contrast to
+ * <code>ProgressMonitorDialog</code> this class only implements
+ * <code>IProgressMonitor</code>.
+ */
+public class ProgressMonitorPart : Composite,
+        IProgressMonitorWithBlocking {
+
+    /** the label */
+    protected Label fLabel;
+
+    /** the current task name */
+    protected String fTaskName;
+
+    /** the current sub task name */
+    protected String fSubTaskName;
+
+    /** the progress indicator */
+    protected ProgressIndicator fProgressIndicator;
+
+    /** the cancel component */
+    protected Control fCancelComponent;
+
+    /** true if canceled */
+    protected bool fIsCanceled;
+
+    /** current blocked status */
+    protected IStatus blockedStatus;
+
+    /** the cancel lister attached to the cancel component */
+    protected Listener fCancelListener;
+    private void init_fCancelListener(){
+        fCancelListener = new class Listener {
+            public void handleEvent(Event e) {
+                setCanceled(true);
+                if (fCancelComponent !is null) {
+                    fCancelComponent.setEnabled(false);
+                }
+            }
+        };
+    }
+    /**
+     * Creates a ProgressMonitorPart.
+     * @param parent The SWT parent of the part.
+     * @param layout The SWT grid bag layout used by the part. A client
+     * can supply the layout to control how the progress monitor part
+     * is layed out. If null is passed the part uses its default layout.
+     */
+    public this(Composite parent, Layout layout) {
+        this(parent, layout, SWT.DEFAULT);
+    }
+
+    /**
+     * Creates a ProgressMonitorPart.
+     * @param parent The SWT parent of the part.
+     * @param layout The SWT grid bag layout used by the part. A client
+     * can supply the layout to control how the progress monitor part
+     * is layed out. If null is passed the part uses its default layout.
+     * @param progressIndicatorHeight The height of the progress indicator in pixel.
+     */
+    public this(Composite parent, Layout layout,
+            int progressIndicatorHeight) {
+        init_fCancelListener();
+        super(parent, SWT.NONE);
+        initialize(layout, progressIndicatorHeight);
+    }
+
+    /**
+     * Attaches the progress monitor part to the given cancel
+     * component.
+     * @param cancelComponent the control whose selection will
+     * trigger a cancel
+     */
+    public void attachToCancelComponent(Control cancelComponent) {
+        Assert.isNotNull(cancelComponent);
+        fCancelComponent = cancelComponent;
+        fCancelComponent.addListener(SWT.Selection, fCancelListener);
+    }
+
+    /**
+     * Implements <code>IProgressMonitor.beginTask</code>.
+     * @see IProgressMonitor#beginTask(java.lang.String, int)
+     */
+    public void beginTask(String name, int totalWork) {
+        fTaskName = name;
+        updateLabel();
+        if (totalWork is IProgressMonitor.UNKNOWN || totalWork is 0) {
+            fProgressIndicator.beginAnimatedTask();
+        } else {
+            fProgressIndicator.beginTask(totalWork);
+        }
+    }
+
+    /**
+     * Implements <code>IProgressMonitor.done</code>.
+     * @see IProgressMonitor#done()
+     */
+    public void done() {
+        fLabel.setText("");//$NON-NLS-1$
+        fProgressIndicator.sendRemainingWork();
+        fProgressIndicator.done();
+    }
+
+    /**
+     * Escapes any occurrence of '&' in the given String so that
+     * it is not considered as a mnemonic
+     * character in SWT ToolItems, MenuItems, Button and Labels.
+     * @param in the original String
+     * @return The converted String
+     */
+    protected static String escapeMetaCharacters(String in_) {
+        if (in_ is null || in_.indexOf('&') < 0) {
+            return in_;
+        }
+        int length = in_.length;
+        StringBuffer out_ = new StringBuffer(length + 1);
+        for (int i = 0; i < length; i++) {
+            char c = in_.charAt(i);
+            if (c is '&') {
+                out_.append("&&");//$NON-NLS-1$
+            } else {
+                out_.append(c);
+            }
+        }
+        return out_.toString();
+    }
+
+    /**
+     * Creates the progress monitor's UI parts and layouts them
+     * according to the given layout. If the layout is <code>null</code>
+     * the part's default layout is used.
+     * @param layout The layout for the receiver.
+     * @param progressIndicatorHeight The suggested height of the indicator
+     */
+    protected void initialize(Layout layout, int progressIndicatorHeight) {
+        if (layout is null) {
+            GridLayout l = new GridLayout();
+            l.marginWidth = 0;
+            l.marginHeight = 0;
+            l.numColumns = 1;
+            layout = l;
+        }
+        setLayout(layout);
+
+        fLabel = new Label(this, SWT.LEFT);
+        fLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+        if (progressIndicatorHeight is SWT.DEFAULT) {
+            GC gc = new GC(fLabel);
+            FontMetrics fm = gc.getFontMetrics();
+            gc.dispose();
+            progressIndicatorHeight = fm.getHeight();
+        }
+
+        fProgressIndicator = new ProgressIndicator(this);
+        GridData gd = new GridData();
+        gd.horizontalAlignment = GridData.FILL;
+        gd.grabExcessHorizontalSpace = true;
+        gd.verticalAlignment = GridData.CENTER;
+        gd.heightHint = progressIndicatorHeight;
+        fProgressIndicator.setLayoutData(gd);
+    }
+
+    /**
+     * Implements <code>IProgressMonitor.internalWorked</code>.
+     * @see IProgressMonitor#internalWorked(double)
+     */
+    public void internalWorked(double work) {
+        fProgressIndicator.worked(work);
+    }
+
+    /**
+     * Implements <code>IProgressMonitor.isCanceled</code>.
+     * @see IProgressMonitor#isCanceled()
+     */
+    public bool isCanceled() {
+        return fIsCanceled;
+    }
+
+    /**
+     * Detach the progress monitor part from the given cancel
+     * component
+     * @param cc
+     */
+    public void removeFromCancelComponent(Control cc) {
+        Assert.isTrue(fCancelComponent is cc && fCancelComponent !is null);
+        fCancelComponent.removeListener(SWT.Selection, fCancelListener);
+        fCancelComponent = null;
+    }
+
+    /**
+     * Implements <code>IProgressMonitor.setCanceled</code>.
+     * @see IProgressMonitor#setCanceled(bool)
+     */
+    public void setCanceled(bool b) {
+        fIsCanceled = b;
+    }
+
+    /**
+     * Sets the progress monitor part's font.
+     */
+    public override void setFont(Font font) {
+        super.setFont(font);
+        fLabel.setFont(font);
+        fProgressIndicator.setFont(font);
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see org.eclipse.core.runtime.IProgressMonitor#setTaskName(java.lang.String)
+     */
+    public void setTaskName(String name) {
+        fTaskName = name;
+        updateLabel();
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see org.eclipse.core.runtime.IProgressMonitor#subTask(java.lang.String)
+     */
+    public void subTask(String name) {
+        fSubTaskName = name;
+        updateLabel();
+    }
+
+    /**
+     * Updates the label with the current task and subtask names.
+     */
+    protected void updateLabel() {
+        if (blockedStatus is null) {
+            String text = taskLabel();
+            fLabel.setText(text);
+        } else {
+            fLabel.setText(blockedStatus.getMessage());
+        }
+
+        //Force an update as we are in the UI Thread
+        fLabel.update();
+    }
+
+    /**
+     * Return the label for showing tasks
+     * @return String
+     */
+    private String taskLabel() {
+        bool hasTask= fTaskName !is null && fTaskName.length > 0;
+        bool hasSubtask= fSubTaskName !is null && fSubTaskName.length > 0;
+
+        if (hasTask) {
+            if (hasSubtask)
+                return escapeMetaCharacters(JFaceResources.format(
+                        "Set_SubTask", [ fTaskName, fSubTaskName ] ));//$NON-NLS-1$
+            return escapeMetaCharacters(fTaskName);
+
+        } else if (hasSubtask) {
+            return escapeMetaCharacters(fSubTaskName);
+
+        } else {
+            return ""; //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Implements <code>IProgressMonitor.worked</code>.
+     * @see IProgressMonitor#worked(int)
+     */
+    public void worked(int work) {
+        internalWorked(work);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#clearBlocked()
+     */
+    public void clearBlocked() {
+        blockedStatus = null;
+        updateLabel();
+
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#setBlocked(org.eclipse.core.runtime.IStatus)
+     */
+    public void setBlocked(IStatus reason) {
+        blockedStatus = reason;
+        updateLabel();
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/wizard/Wizard.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,466 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.wizard.Wizard;
+
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.IWizardContainer;
+
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * An abstract base implementation of a wizard. A typical client subclasses
+ * <code>Wizard</code> to implement a particular wizard.
+ * <p>
+ * Subclasses may call the following methods to configure the wizard:
+ * <ul>
+ * <li><code>addPage</code></li>
+ * <li><code>setHelpAvailable</code></li>
+ * <li><code>setDefaultPageImageDescriptor</code></li>
+ * <li><code>setDialogSettings</code></li>
+ * <li><code>setNeedsProgressMonitor</code></li>
+ * <li><code>setTitleBarColor</code></li>
+ * <li><code>setWindowTitle</code></li>
+ * </ul>
+ * </p>
+ * <p>
+ * Subclasses may override these methods if required:
+ * <ul>
+ * <li>reimplement <code>createPageControls</code></li>
+ * <li>reimplement <code>performCancel</code></li>
+ * <li>extend <code>addPages</code></li>
+ * <li>reimplement <code>performFinish</code></li>
+ * <li>extend <code>dispose</code></li>
+ * </ul>
+ * </p>
+ * <p>
+ * Note that clients are free to implement <code>IWizard</code> from scratch
+ * instead of subclassing <code>Wizard</code>. Correct implementations of
+ * <code>IWizard</code> will work with any correct implementation of
+ * <code>IWizardPage</code>.
+ * </p>
+ */
+public abstract class Wizard : IWizard {
+    /**
+     * Image registry key of the default image for wizard pages (value
+     * <code>"org.eclipse.jface.wizard.Wizard.pageImage"</code>).
+     */
+    public static final String DEFAULT_IMAGE = "org.eclipse.jface.wizard.Wizard.pageImage";//$NON-NLS-1$
+
+    /**
+     * The wizard container this wizard belongs to; <code>null</code> if none.
+     */
+    private IWizardContainer container = null;
+
+    /**
+     * This wizard's list of pages (element type: <code>IWizardPage</code>).
+     */
+    private List pages;
+
+    /**
+     * Indicates whether this wizard needs a progress monitor.
+     */
+    private bool needsProgressMonitor_ = false;
+
+    /**
+     * Indicates whether this wizard needs previous and next buttons even if the
+     * wizard has only one page.
+     */
+    private bool forcePreviousAndNextButtons = false;
+
+    /**
+     * Indicates whether this wizard supports help.
+     */
+    private bool isHelpAvailable_ = false;
+
+    /**
+     * The default page image for pages without one of their one;
+     * <code>null</code> if none.
+     */
+    private Image defaultImage = null;
+
+    /**
+     * The default page image descriptor, used for creating a default page image
+     * if required; <code>null</code> if none.
+     */
+    private ImageDescriptor defaultImageDescriptor;
+
+    /**
+     * The color of the wizard title bar; <code>null</code> if none.
+     */
+    private RGB titleBarColor = null;
+
+    /**
+     * The window title string for this wizard; <code>null</code> if none.
+     */
+    private String windowTitle = null;
+
+    /**
+     * The dialog settings for this wizard; <code>null</code> if none.
+     */
+    private IDialogSettings dialogSettings = null;
+
+    /**
+     * Creates a new empty wizard.
+     */
+    protected this() {
+        //super();
+        pages = new ArrayList();
+        defaultImageDescriptor = JFaceResources.getImageRegistry().getDescriptor(DEFAULT_IMAGE);
+    }
+
+    /**
+     * Adds a new page to this wizard. The page is inserted at the end of the
+     * page list.
+     *
+     * @param page
+     *            the new page
+     */
+    public void addPage(IWizardPage page) {
+        pages.add(cast(Object)page);
+        page.setWizard(this);
+    }
+
+    /**
+     * The <code>Wizard</code> implementation of this <code>IWizard</code>
+     * method does nothing. Subclasses should extend if extra pages need to be
+     * added before the wizard opens. New pages should be added by calling
+     * <code>addPage</code>.
+     */
+    public void addPages() {
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizard.
+     */
+    public bool canFinish() {
+        // Default implementation is to check if all pages are complete.
+        for (int i = 0; i < pages.size(); i++) {
+            if (!(cast(IWizardPage) pages.get(i)).isPageComplete()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * The <code>Wizard</code> implementation of this <code>IWizard</code>
+     * method creates all the pages controls using
+     * <code>IDialogPage.createControl</code>. Subclasses should reimplement
+     * this method if they want to delay creating one or more of the pages
+     * lazily. The framework ensures that the contents of a page will be created
+     * before attempting to show it.
+     */
+    public void createPageControls(Composite pageContainer) {
+        // the default behavior is to create all the pages controls
+        for (int i = 0; i < pages.size(); i++) {
+            IWizardPage page = cast(IWizardPage) pages.get(i);
+            page.createControl(pageContainer);
+            // page is responsible for ensuring the created control is
+            // accessable
+            // via getControl.
+            Assert.isNotNull(page.getControl());
+        }
+    }
+
+    /**
+     * The <code>Wizard</code> implementation of this <code>IWizard</code>
+     * method disposes all the pages controls using
+     * <code>DialogPage.dispose</code>. Subclasses should extend this method
+     * if the wizard instance maintains addition SWT resource that need to be
+     * disposed.
+     */
+    public void dispose() {
+        // notify pages
+        for (int i = 0; i < pages.size(); i++) {
+            (cast(IWizardPage) pages.get(i)).dispose();
+        }
+        // dispose of image
+        if (defaultImage !is null) {
+            JFaceResources.getResources().destroyImage(defaultImageDescriptor);
+            defaultImage = null;
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizard.
+     */
+    public IWizardContainer getContainer() {
+        return container;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizard.
+     */
+    public Image getDefaultPageImage() {
+        if (defaultImage is null) {
+            defaultImage = JFaceResources.getResources().createImageWithDefault(defaultImageDescriptor);
+        }
+        return defaultImage;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizard.
+     */
+    public IDialogSettings getDialogSettings() {
+        return dialogSettings;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizard. The default behavior is to
+     * return the page that was added to this wizard after the given page.
+     */
+    public IWizardPage getNextPage(IWizardPage page) {
+        int index = pages.indexOf( cast(Object)page );
+        if (index is pages.size() - 1 || index is -1) {
+            // last page or page not found
+            return null;
+        }
+        return cast(IWizardPage) pages.get(index + 1);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizard.
+     */
+    public IWizardPage getPage(String name) {
+        for (int i = 0; i < pages.size(); i++) {
+            IWizardPage page = cast(IWizardPage) pages.get(i);
+            String pageName = page.getName();
+            if (pageName.equals(name)) {
+                return page;
+            }
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizard.
+     */
+    public int getPageCount() {
+        return pages.size();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizard.
+     */
+    public IWizardPage[] getPages() {
+        return arraycast!(IWizardPage)( pages.toArray());
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizard. The default behavior is to
+     * return the page that was added to this wizard before the given page.
+     */
+    public IWizardPage getPreviousPage(IWizardPage page) {
+        int index = pages.indexOf(cast(Object)page);
+        if (index is 0 || index is -1) {
+            // first page or page not found
+            return null;
+        }
+        return cast(IWizardPage) pages.get(index - 1);
+    }
+
+    /**
+     * Returns the wizard's shell if the wizard is visible. Otherwise
+     * <code>null</code> is returned.
+     *
+     * @return Shell
+     */
+    public Shell getShell() {
+        if (container is null) {
+            return null;
+        }
+        return container.getShell();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizard. By default this is the first
+     * page inserted into the wizard.
+     */
+    public IWizardPage getStartingPage() {
+        if (pages.size() is 0) {
+            return null;
+        }
+        return cast(IWizardPage) pages.get(0);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizard.
+     */
+    public RGB getTitleBarColor() {
+        return titleBarColor;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizard.
+     */
+    public String getWindowTitle() {
+        return windowTitle;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizard.
+     */
+    public bool isHelpAvailable() {
+        return isHelpAvailable_;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizard.
+     */
+    public bool needsPreviousAndNextButtons() {
+        return forcePreviousAndNextButtons || pages.size() > 1;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizard.
+     */
+    public bool needsProgressMonitor() {
+        return needsProgressMonitor_;
+    }
+
+    /**
+     * The <code>Wizard</code> implementation of this <code>IWizard</code>
+     * method does nothing and returns <code>true</code>. Subclasses should
+     * reimplement this method if they need to perform any special cancel
+     * processing for their wizard.
+     */
+    public bool performCancel() {
+        return true;
+    }
+
+    /**
+     * Subclasses must implement this <code>IWizard</code> method to perform
+     * any special finish processing for their wizard.
+     */
+    public abstract bool performFinish();
+
+    /*
+     * (non-Javadoc) Method declared on IWizard.
+     */
+    public void setContainer(IWizardContainer wizardContainer) {
+        container = wizardContainer;
+    }
+
+    /**
+     * Sets the default page image descriptor for this wizard.
+     * <p>
+     * This image descriptor will be used to generate an image for a page with
+     * no image of its own; the image will be computed once and cached.
+     * </p>
+     *
+     * @param imageDescriptor
+     *            the default page image descriptor
+     */
+    public void setDefaultPageImageDescriptor(ImageDescriptor imageDescriptor) {
+        defaultImageDescriptor = imageDescriptor;
+    }
+
+    /**
+     * Sets the dialog settings for this wizard.
+     * <p>
+     * The dialog settings is used to record state between wizard invocations
+     * (for example, radio button selection, last import directory, etc.)
+     * </p>
+     *
+     * @param settings
+     *            the dialog settings, or <code>null</code> if none
+     * @see #getDialogSettings
+     *
+     */
+    public void setDialogSettings(IDialogSettings settings) {
+        dialogSettings = settings;
+    }
+
+    /**
+     * Controls whether the wizard needs Previous and Next buttons even if it
+     * currently contains only one page.
+     * <p>
+     * This flag should be set on wizards where the first wizard page adds
+     * follow-on wizard pages based on user input.
+     * </p>
+     *
+     * @param b
+     *            <code>true</code> to always show Next and Previous buttons,
+     *            and <code>false</code> to suppress Next and Previous buttons
+     *            for single page wizards
+     */
+    public void setForcePreviousAndNextButtons(bool b) {
+        forcePreviousAndNextButtons = b;
+    }
+
+    /**
+     * Sets whether help is available for this wizard.
+     * <p>
+     * The result of this method is typically used by the container to show or
+     * hide the Help button.
+     * </p>
+     *
+     * @param b
+     *            <code>true</code> if help is available, and
+     *            <code>false</code> if this wizard is helpless
+     * @see #isHelpAvailable()
+     */
+    public void setHelpAvailable(bool b) {
+        isHelpAvailable_ = b;
+    }
+
+    /**
+     * Sets whether this wizard needs a progress monitor.
+     *
+     * @param b
+     *            <code>true</code> if a progress monitor is required, and
+     *            <code>false</code> if none is needed
+     * @see #needsProgressMonitor()
+     */
+    public void setNeedsProgressMonitor(bool b) {
+        needsProgressMonitor_ = b;
+    }
+
+    /**
+     * Sets the title bar color for this wizard.
+     *
+     * @param color
+     *            the title bar color
+     */
+    public void setTitleBarColor(RGB color) {
+        titleBarColor = color;
+    }
+
+    /**
+     * Sets the window title for the container that hosts this page to the given
+     * string.
+     *
+     * @param newTitle
+     *            the window title for the container
+     */
+    public void setWindowTitle(String newTitle) {
+        windowTitle = newTitle;
+        if (container !is null) {
+            container.updateWindowTitle();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/wizard/WizardDialog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1539 @@
+/*******************************************************************************
+ * 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
+ *     Chris Gross (schtoo@schtoo.com) - patch for bug 16179
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.wizard.WizardDialog;
+
+import org.eclipse.jface.wizard.IWizardContainer2;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.ProgressMonitorPart;
+
+// import java.lang.reflect.InvocationTargetException;
+// import java.util.HashMap;
+// import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.events.HelpEvent;
+import org.eclipse.swt.events.HelpListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.dialogs.ControlEnableState;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.IPageChangeProvider;
+import org.eclipse.jface.dialogs.IPageChangedListener;
+import org.eclipse.jface.dialogs.IPageChangingListener;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.PageChangedEvent;
+import org.eclipse.jface.dialogs.PageChangingEvent;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.operation.ModalContext;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.SafeRunnable;
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+/**
+ * A dialog to show a wizard to the end user.
+ * <p>
+ * In typical usage, the client instantiates this class with a particular
+ * wizard. The dialog serves as the wizard container and orchestrates the
+ * presentation of its pages.
+ * <p>
+ * The standard layout is roughly as follows: it has an area at the top
+ * containing both the wizard's title, description, and image; the actual wizard
+ * page appears in the middle; below that is a progress indicator (which is made
+ * visible if needed); and at the bottom of the page is message line and a
+ * button bar containing Help, Next, Back, Finish, and Cancel buttons (or some
+ * subset).
+ * </p>
+ * <p>
+ * Clients may subclass <code>WizardDialog</code>, although this is rarely
+ * required.
+ * </p>
+ */
+public class WizardDialog : TitleAreaDialog, IWizardContainer2,
+        IPageChangeProvider {
+    /**
+     * Image registry key for error message image (value
+     * <code>"dialog_title_error_image"</code>).
+     */
+    public static const String WIZ_IMG_ERROR = "dialog_title_error_image"; //$NON-NLS-1$
+
+    // The wizard the dialog is currently showing.
+    private IWizard wizard;
+
+    // Wizards to dispose
+    private ArrayList createdWizards;
+
+    // Current nested wizards
+    private ArrayList nestedWizards;
+
+    // The currently displayed page.
+    private IWizardPage currentPage = null;
+
+    // The number of long running operation executed from the dialog.
+    private long activeRunningOperations = 0;
+
+    // The current page message and description
+    private String pageMessage;
+
+    private int pageMessageType = IMessageProvider.NONE;
+
+    private String pageDescription;
+
+    // The progress monitor
+    private ProgressMonitorPart progressMonitorPart;
+
+    private Cursor waitCursor;
+
+    private Cursor arrowCursor;
+
+    private MessageDialog windowClosingDialog;
+
+    // Navigation buttons
+    private Button backButton;
+
+    private Button nextButton;
+
+    private Button finishButton;
+
+    private Button cancelButton;
+
+    private Button helpButton;
+
+    private SelectionAdapter cancelListener;
+
+    private bool isMovingToPreviousPage = false;
+
+    private Composite pageContainer;
+
+    private PageContainerFillLayout pageContainerLayout;
+
+    private int pageWidth = SWT.DEFAULT;
+
+    private int pageHeight = SWT.DEFAULT;
+
+    private static const String FOCUS_CONTROL = "focusControl"; //$NON-NLS-1$
+
+    private bool lockedUI = false;
+
+    private ListenerList pageChangedListeners;
+
+    private ListenerList pageChangingListeners;
+
+    /**
+     * A layout for a container which includes several pages, like a notebook,
+     * wizard, or preference dialog. The size computed by this layout is the
+     * maximum width and height of all pages currently inserted into the
+     * container.
+     */
+    protected class PageContainerFillLayout : Layout {
+        /**
+         * The margin width; <code>5</code> pixels by default.
+         */
+        public int marginWidth = 5;
+
+        /**
+         * The margin height; <code>5</code> pixels by default.
+         */
+        public int marginHeight = 5;
+
+        /**
+         * The minimum width; <code>0</code> pixels by default.
+         */
+        public int minimumWidth = 0;
+
+        /**
+         * The minimum height; <code>0</code> pixels by default.
+         */
+        public int minimumHeight = 0;
+
+        /**
+         * Creates new layout object.
+         *
+         * @param mw
+         *            the margin width
+         * @param mh
+         *            the margin height
+         * @param minW
+         *            the minimum width
+         * @param minH
+         *            the minimum height
+         */
+        public this(int mw, int mh, int minW, int minH) {
+            marginWidth = mw;
+            marginHeight = mh;
+            minimumWidth = minW;
+            minimumHeight = minH;
+        }
+
+        /*
+         * (non-Javadoc) Method declared on Layout.
+         */
+        public override Point computeSize(Composite composite, int wHint, int hHint,
+                bool force) {
+            if (wHint !is SWT.DEFAULT && hHint !is SWT.DEFAULT) {
+                return new Point(wHint, hHint);
+            }
+            Point result = null;
+            Control[] children = composite.getChildren();
+            if (children.length > 0) {
+                result = new Point(0, 0);
+                for (int i = 0; i < children.length; i++) {
+                    Point cp = children[i].computeSize(wHint, hHint, force);
+                    result.x = Math.max(result.x, cp.x);
+                    result.y = Math.max(result.y, cp.y);
+                }
+                result.x = result.x + 2 * marginWidth;
+                result.y = result.y + 2 * marginHeight;
+            } else {
+                Rectangle rect = composite.getClientArea();
+                result = new Point(rect.width, rect.height);
+            }
+            result.x = Math.max(result.x, minimumWidth);
+            result.y = Math.max(result.y, minimumHeight);
+            if (wHint !is SWT.DEFAULT) {
+                result.x = wHint;
+            }
+            if (hHint !is SWT.DEFAULT) {
+                result.y = hHint;
+            }
+            return result;
+        }
+
+        /**
+         * Returns the client area for the given composite according to this
+         * layout.
+         *
+         * @param c
+         *            the composite
+         * @return the client area rectangle
+         */
+        public Rectangle getClientArea(Composite c) {
+            Rectangle rect = c.getClientArea();
+            rect.x = rect.x + marginWidth;
+            rect.y = rect.y + marginHeight;
+            rect.width = rect.width - 2 * marginWidth;
+            rect.height = rect.height - 2 * marginHeight;
+            return rect;
+        }
+
+        /*
+         * (non-Javadoc) Method declared on Layout.
+         */
+        public override void layout(Composite composite, bool force) {
+            Rectangle rect = getClientArea(composite);
+            Control[] children = composite.getChildren();
+            for (int i = 0; i < children.length; i++) {
+                children[i].setBounds(rect);
+            }
+        }
+
+        /**
+         * Lays outs the page according to this layout.
+         *
+         * @param w
+         *            the control
+         */
+        public void layoutPage(Control w) {
+            w.setBounds(getClientArea(w.getParent()));
+        }
+
+        /**
+         * Sets the location of the page so that its origin is in the upper left
+         * corner.
+         *
+         * @param w
+         *            the control
+         */
+        public void setPageLocation(Control w) {
+            w.setLocation(marginWidth, marginHeight);
+        }
+    }
+
+    /**
+     * Creates a new wizard dialog for the given wizard.
+     *
+     * @param parentShell
+     *            the parent shell
+     * @param newWizard
+     *            the wizard this dialog is working on
+     */
+    public this(Shell parentShell, IWizard newWizard) {
+        pageContainerLayout = new PageContainerFillLayout( 5, 5, 300, 225);
+        createdWizards = new ArrayList();
+        nestedWizards = new ArrayList();
+        pageChangedListeners = new ListenerList();
+        pageChangingListeners = new ListenerList();
+
+        super(parentShell);
+        setShellStyle(SWT.CLOSE | SWT.MAX | SWT.TITLE | SWT.BORDER
+                | SWT.APPLICATION_MODAL | SWT.RESIZE | getDefaultOrientation());
+        setWizard(newWizard);
+        // since VAJava can't initialize an instance var with an anonymous
+        // class outside a constructor we do it here:
+        cancelListener = new class SelectionAdapter {
+            public void widgetSelected(SelectionEvent e) {
+                cancelPressed();
+            }
+        };
+    }
+
+    /**
+     * About to start a long running operation triggered through the wizard.
+     * Shows the progress monitor and disables the wizard's buttons and
+     * controls.
+     *
+     * @param enableCancelButton
+     *            <code>true</code> if the Cancel button should be enabled,
+     *            and <code>false</code> if it should be disabled
+     * @return the saved UI state
+     */
+    private Object aboutToStart(bool enableCancelButton) {
+        Map savedState = null;
+        if (getShell() !is null) {
+            // Save focus control
+            Control focusControl = getShell().getDisplay().getFocusControl();
+            if (focusControl !is null && focusControl.getShell() !is getShell()) {
+                focusControl = null;
+            }
+            bool needsProgressMonitor = wizard.needsProgressMonitor();
+            cancelButton.removeSelectionListener(cancelListener);
+            // Set the busy cursor to all shells.
+            Display d = getShell().getDisplay();
+            waitCursor = new Cursor(d, SWT.CURSOR_WAIT);
+            setDisplayCursor(waitCursor);
+            // Set the arrow cursor to the cancel component.
+            arrowCursor = new Cursor(d, SWT.CURSOR_ARROW);
+            cancelButton.setCursor(arrowCursor);
+            // Deactivate shell
+            savedState = saveUIState(needsProgressMonitor && enableCancelButton);
+            if (focusControl !is null) {
+                savedState.put(stringcast(FOCUS_CONTROL), focusControl);
+            }
+            // Attach the progress monitor part to the cancel button
+            if (needsProgressMonitor) {
+                progressMonitorPart.attachToCancelComponent(cancelButton);
+                progressMonitorPart.setVisible(true);
+            }
+        }
+        return cast(Object) savedState;
+    }
+
+    /**
+     * The Back button has been pressed.
+     */
+    protected void backPressed() {
+        IWizardPage page = currentPage.getPreviousPage();
+        if (page is null) {
+            // should never happen since we have already visited the page
+            return;
+        }
+
+        // set flag to indicate that we are moving back
+        isMovingToPreviousPage = true;
+        // show the page
+        showPage(page);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Dialog.
+     */
+    protected override void buttonPressed(int buttonId) {
+        switch (buttonId) {
+        case IDialogConstants.HELP_ID: {
+            helpPressed();
+            break;
+        }
+        case IDialogConstants.BACK_ID: {
+            backPressed();
+            break;
+        }
+        case IDialogConstants.NEXT_ID: {
+            nextPressed();
+            break;
+        }
+        case IDialogConstants.FINISH_ID: {
+            finishPressed();
+            break;
+        }
+            // The Cancel button has a listener which calls cancelPressed
+            // directly
+        default:
+        }
+    }
+
+    /**
+     * Calculates the difference in size between the given page and the page
+     * container. A larger page results in a positive delta.
+     *
+     * @param page
+     *            the page
+     * @return the size difference encoded as a
+     *         <code>new Point(deltaWidth,deltaHeight)</code>
+     */
+    private Point calculatePageSizeDelta(IWizardPage page) {
+        Control pageControl = page.getControl();
+        if (pageControl is null) {
+            // control not created yet
+            return new Point(0, 0);
+        }
+        Point contentSize = pageControl.computeSize(SWT.DEFAULT, SWT.DEFAULT,
+                true);
+        Rectangle rect = pageContainerLayout.getClientArea(pageContainer);
+        Point containerSize = new Point(rect.width, rect.height);
+        return new Point(Math.max(0, contentSize.x - containerSize.x), Math
+                .max(0, contentSize.y - containerSize.y));
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Dialog.
+     */
+    protected override void cancelPressed() {
+        if (activeRunningOperations <= 0) {
+            // Close the dialog. The check whether the dialog can be
+            // closed or not is done in <code>okToClose</code>.
+            // This ensures that the check is also evaluated when the user
+            // presses the window's close button.
+            setReturnCode(CANCEL);
+            close();
+        } else {
+            cancelButton.setEnabled(false);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.window.Window#close()
+     */
+    public override bool close() {
+        if (okToClose()) {
+            return hardClose();
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Window.
+     */
+    protected override void configureShell(Shell newShell) {
+        super.configureShell(newShell);
+        // Register help listener on the shell
+        newShell.addHelpListener(new class HelpListener {
+            public void helpRequested(HelpEvent event) {
+                // call perform help on the current page
+                if (currentPage !is null) {
+                    currentPage.performHelp();
+                }
+            }
+        });
+    }
+
+    /**
+     * Creates the buttons for this dialog's button bar.
+     * <p>
+     * The <code>WizardDialog</code> implementation of this framework method
+     * prevents the parent composite's columns from being made equal width in
+     * order to remove the margin between the Back and Next buttons.
+     * </p>
+     *
+     * @param parent
+     *            the parent composite to contain the buttons
+     */
+    protected override void createButtonsForButtonBar(Composite parent) {
+        (cast(GridLayout) parent.getLayout()).makeColumnsEqualWidth = false;
+        if (wizard.isHelpAvailable()) {
+            helpButton = createButton(parent, IDialogConstants.HELP_ID,
+                    IDialogConstants.HELP_LABEL, false);
+        }
+        if (wizard.needsPreviousAndNextButtons()) {
+            createPreviousAndNextButtons(parent);
+        }
+        finishButton = createButton(parent, IDialogConstants.FINISH_ID,
+                IDialogConstants.FINISH_LABEL, true);
+        cancelButton = createCancelButton(parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.Dialog#setButtonLayoutData(org.eclipse.swt.widgets.Button)
+     */
+    protected override void setButtonLayoutData(Button button) {
+        GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+        int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
+
+        // On large fonts this can make this dialog huge
+        widthHint = Math.min(widthHint,
+                button.getDisplay().getBounds().width / 5);
+        Point minSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+        data.widthHint = Math.max(widthHint, minSize.x);
+
+        button.setLayoutData(data);
+    }
+
+    /**
+     * Creates the Cancel button for this wizard dialog. Creates a standard (<code>SWT.PUSH</code>)
+     * button and registers for its selection events. Note that the number of
+     * columns in the button bar composite is incremented. The Cancel button is
+     * created specially to give it a removeable listener.
+     *
+     * @param parent
+     *            the parent button bar
+     * @return the new Cancel button
+     */
+    private Button createCancelButton(Composite parent) {
+        // increment the number of columns in the button bar
+        (cast(GridLayout) parent.getLayout()).numColumns++;
+        Button button = new Button(parent, SWT.PUSH);
+        button.setText(IDialogConstants.CANCEL_LABEL);
+        setButtonLayoutData(button);
+        button.setFont(parent.getFont());
+        button.setData(new Integer(IDialogConstants.CANCEL_ID));
+        button.addSelectionListener(cancelListener);
+        return button;
+    }
+
+    /**
+     * Return the cancel button if the id is a the cancel id.
+     *
+     * @param id
+     *            the button id
+     * @return the button corresponding to the button id
+     */
+    protected override Button getButton(int id) {
+        if (id is IDialogConstants.CANCEL_ID) {
+            return cancelButton;
+        }
+        return super.getButton(id);
+    }
+
+    /**
+     * The <code>WizardDialog</code> implementation of this
+     * <code>Window</code> method calls call <code>IWizard.addPages</code>
+     * to allow the current wizard to add extra pages, then
+     * <code>super.createContents</code> to create the controls. It then calls
+     * <code>IWizard.createPageControls</code> to allow the wizard to
+     * pre-create their page controls prior to opening, so that the wizard opens
+     * to the correct size. And finally it shows the first page.
+     */
+    protected override Control createContents(Composite parent) {
+        // Allow the wizard to add pages to itself
+        // Need to call this now so page count is correct
+        // for determining if next/previous buttons are needed
+        wizard.addPages();
+        Control contents = super.createContents(parent);
+        // Allow the wizard pages to precreate their page controls
+        createPageControls();
+        // Show the first page
+        showStartingPage();
+        return contents;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Dialog.
+     */
+    protected override Control createDialogArea(Composite parent) {
+        Composite composite = cast(Composite) super.createDialogArea(parent);
+        // Build the Page container
+        pageContainer = createPageContainer(composite);
+        GridData gd = new GridData(GridData.FILL_BOTH);
+        gd.widthHint = pageWidth;
+        gd.heightHint = pageHeight;
+        pageContainer.setLayoutData(gd);
+        pageContainer.setFont(parent.getFont());
+        // Insert a progress monitor
+        GridLayout pmlayout = new GridLayout();
+        pmlayout.numColumns = 1;
+        progressMonitorPart = createProgressMonitorPart(composite, pmlayout);
+        GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
+        progressMonitorPart.setLayoutData(gridData);
+        progressMonitorPart.setVisible(false);
+        // Build the separator line
+        Label separator = new Label(composite, SWT.HORIZONTAL | SWT.SEPARATOR);
+        separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+        applyDialogFont(progressMonitorPart);
+        return composite;
+    }
+
+    /**
+     * Create the progress monitor part in the receiver.
+     *
+     * @param composite
+     * @param pmlayout
+     * @return ProgressMonitorPart
+     */
+    protected ProgressMonitorPart createProgressMonitorPart(
+            Composite composite, GridLayout pmlayout) {
+        return new class(composite, pmlayout, SWT.DEFAULT) ProgressMonitorPart {
+
+            String currentTask = null;
+
+            this(Composite c, Layout l, int s ){
+                super(c,l,s);
+            }
+
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.jface.wizard.ProgressMonitorPart#setBlocked(org.eclipse.core.runtime.IStatus)
+             */
+            public void setBlocked(IStatus reason) {
+                super.setBlocked(reason);
+                if (!lockedUI) {
+                    getBlockedHandler().showBlocked(getShell(), this, reason,
+                            currentTask);
+                }
+            }
+
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.jface.wizard.ProgressMonitorPart#clearBlocked()
+             */
+            public void clearBlocked() {
+                super.clearBlocked();
+                if (!lockedUI) {
+                    getBlockedHandler().clearBlocked();
+                }
+            }
+
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.jface.wizard.ProgressMonitorPart#beginTask(java.lang.String,
+             *      int)
+             */
+            public void beginTask(String name, int totalWork) {
+                super.beginTask(name, totalWork);
+                currentTask = name;
+            }
+
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.jface.wizard.ProgressMonitorPart#setTaskName(java.lang.String)
+             */
+            public void setTaskName(String name) {
+                super.setTaskName(name);
+                currentTask = name;
+            }
+
+            /*
+             * (non-Javadoc)
+             *
+             * @see org.eclipse.jface.wizard.ProgressMonitorPart#subTask(java.lang.String)
+             */
+            public void subTask(String name) {
+                super.subTask(name);
+                // If we haven't got anything yet use this value for more
+                // context
+                if (currentTask is null) {
+                    currentTask = name;
+                }
+            }
+        };
+    }
+
+    /**
+     * Creates the container that holds all pages.
+     *
+     * @param parent
+     * @return Composite
+     */
+    private Composite createPageContainer(Composite parent) {
+        Composite result = new Composite(parent, SWT.NULL);
+        result.setLayout(pageContainerLayout);
+        return result;
+    }
+
+    /**
+     * Allow the wizard's pages to pre-create their page controls. This allows
+     * the wizard dialog to open to the correct size.
+     */
+    private void createPageControls() {
+        // Allow the wizard pages to precreate their page controls
+        // This allows the wizard to open to the correct size
+        wizard.createPageControls(pageContainer);
+        // Ensure that all of the created pages are initially not visible
+        IWizardPage[] pages = wizard.getPages();
+        for (int i = 0; i < pages.length; i++) {
+            IWizardPage page = pages[i];
+            if (page.getControl() !is null) {
+                page.getControl().setVisible(false);
+            }
+        }
+    }
+
+    /**
+     * Creates the Previous and Next buttons for this wizard dialog. Creates
+     * standard (<code>SWT.PUSH</code>) buttons and registers for their
+     * selection events. Note that the number of columns in the button bar
+     * composite is incremented. These buttons are created specially to prevent
+     * any space between them.
+     *
+     * @param parent
+     *            the parent button bar
+     * @return a composite containing the new buttons
+     */
+    private Composite createPreviousAndNextButtons(Composite parent) {
+        // increment the number of columns in the button bar
+        (cast(GridLayout) parent.getLayout()).numColumns++;
+        Composite composite = new Composite(parent, SWT.NONE);
+        // create a layout with spacing and margins appropriate for the font
+        // size.
+        GridLayout layout = new GridLayout();
+        layout.numColumns = 0; // will be incremented by createButton
+        layout.marginWidth = 0;
+        layout.marginHeight = 0;
+        layout.horizontalSpacing = 0;
+        layout.verticalSpacing = 0;
+        composite.setLayout(layout);
+        GridData data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER
+                | GridData.VERTICAL_ALIGN_CENTER);
+        composite.setLayoutData(data);
+        composite.setFont(parent.getFont());
+        backButton = createButton(composite, IDialogConstants.BACK_ID,
+                IDialogConstants.BACK_LABEL, false);
+        nextButton = createButton(composite, IDialogConstants.NEXT_ID,
+                IDialogConstants.NEXT_LABEL, false);
+        return composite;
+    }
+
+    /**
+     * Creates and return a new wizard closing dialog without openiong it.
+     *
+     * @return MessageDalog
+     */
+    private MessageDialog createWizardClosingDialog() {
+        MessageDialog result = new MessageDialog(getShell(),
+                JFaceResources.getString("WizardClosingDialog.title"), //$NON-NLS-1$
+                null,
+                JFaceResources.getString("WizardClosingDialog.message"), //$NON-NLS-1$
+                MessageDialog.QUESTION,
+                [ IDialogConstants.OK_LABEL ], 0);
+        return result;
+    }
+
+    /**
+     * The Finish button has been pressed.
+     */
+    protected void finishPressed() {
+        // Wizards are added to the nested wizards list in setWizard.
+        // This means that the current wizard is always the last wizard in the
+        // list.
+        // Note that we first call the current wizard directly (to give it a
+        // chance to
+        // abort, do work, and save state) then call the remaining n-1 wizards
+        // in the
+        // list (to save state).
+        if (wizard.performFinish()) {
+            // Call perform finish on outer wizards in the nested chain
+            // (to allow them to save state for example)
+            for (int i = 0; i < nestedWizards.size() - 1; i++) {
+                (cast(IWizard) nestedWizards.get(i)).performFinish();
+            }
+            // Hard close the dialog.
+            setReturnCode(OK);
+            hardClose();
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizardContainer.
+     */
+    public IWizardPage getCurrentPage() {
+        return currentPage;
+    }
+
+    /**
+     * Returns the progress monitor for this wizard dialog (if it has one).
+     *
+     * @return the progress monitor, or <code>null</code> if this wizard
+     *         dialog does not have one
+     */
+    protected IProgressMonitor getProgressMonitor() {
+        return progressMonitorPart;
+    }
+
+    /**
+     * Returns the wizard this dialog is currently displaying.
+     *
+     * @return the current wizard
+     */
+    protected IWizard getWizard() {
+        return wizard;
+    }
+
+    /**
+     * Closes this window.
+     *
+     * @return <code>true</code> if the window is (or was already) closed, and
+     *         <code>false</code> if it is still open
+     */
+    private bool hardClose() {
+        // inform wizards
+        for (int i = 0; i < createdWizards.size(); i++) {
+            IWizard createdWizard = cast(IWizard) createdWizards.get(i);
+            createdWizard.dispose();
+            // Remove this dialog as a parent from the managed wizard.
+            // Note that we do this after calling dispose as the wizard or
+            // its pages may need access to the container during
+            // dispose code
+            createdWizard.setContainer(null);
+        }
+        return super.close();
+    }
+
+    /**
+     * The Help button has been pressed.
+     */
+    protected void helpPressed() {
+        if (currentPage !is null) {
+            currentPage.performHelp();
+        }
+    }
+
+    /**
+     * The Next button has been pressed.
+     */
+    protected void nextPressed() {
+        IWizardPage page = currentPage.getNextPage();
+        if (page is null) {
+            // something must have happend getting the next page
+            return;
+        }
+
+        // show the next page
+        showPage(page);
+    }
+
+    /**
+     * Notifies page changing listeners and returns result of page changing
+     * processing to the sender.
+     *
+     * @param eventType
+     * @return <code>true</code> if page changing listener completes
+     *         successfully, <code>false</code> otherwise
+     */
+    private bool doPageChanging(IWizardPage targetPage) {
+        PageChangingEvent e = new PageChangingEvent(this, cast(Object)getCurrentPage(),
+                cast(Object)targetPage);
+        firePageChanging(e);
+        // Prevent navigation if necessary
+        return e.doit;
+    }
+
+    /**
+     * Checks whether it is alright to close this wizard dialog and performed
+     * standard cancel processing. If there is a long running operation in
+     * progress, this method posts an alert message saying that the wizard
+     * cannot be closed.
+     *
+     * @return <code>true</code> if it is alright to close this dialog, and
+     *         <code>false</code> if it is not
+     */
+    private bool okToClose() {
+        if (activeRunningOperations > 0) {
+            synchronized (this) {
+                windowClosingDialog = createWizardClosingDialog();
+            }
+            windowClosingDialog.open();
+            synchronized (this) {
+                windowClosingDialog = null;
+            }
+            return false;
+        }
+        return wizard.performCancel();
+    }
+
+    /**
+     * Restores the enabled/disabled state of the given control.
+     *
+     * @param w
+     *            the control
+     * @param h
+     *            the map (key type: <code>String</code>, element type:
+     *            <code>Boolean</code>)
+     * @param key
+     *            the key
+     * @see #saveEnableStateAndSet
+     */
+    private void restoreEnableState(Control w, Map h, String key) {
+        if (w !is null) {
+            Boolean b = cast(Boolean) h.get(stringcast(key));
+            if (b !is null) {
+                w.setEnabled(b.booleanValue());
+            }
+        }
+    }
+
+    /**
+     * Restores the enabled/disabled state of the wizard dialog's buttons and
+     * the tree of controls for the currently showing page.
+     *
+     * @param state
+     *            a map containing the saved state as returned by
+     *            <code>saveUIState</code>
+     * @see #saveUIState
+     */
+    private void restoreUIState(Map state) {
+        restoreEnableState(backButton, state, "back"); //$NON-NLS-1$
+        restoreEnableState(nextButton, state, "next"); //$NON-NLS-1$
+        restoreEnableState(finishButton, state, "finish"); //$NON-NLS-1$
+        restoreEnableState(cancelButton, state, "cancel"); //$NON-NLS-1$
+        restoreEnableState(helpButton, state, "help"); //$NON-NLS-1$
+        Object pageValue = state.get(stringcast("page")); //$NON-NLS-1$
+        if (pageValue !is null) {
+            (cast(ControlEnableState) pageValue).restore();
+        }
+    }
+
+    /**
+     * This implementation of IRunnableContext#run(bool, bool,
+     * IRunnableWithProgress) blocks until the runnable has been run, regardless
+     * of the value of <code>fork</code>. It is recommended that
+     * <code>fork</code> is set to true in most cases. If <code>fork</code>
+     * is set to <code>false</code>, the runnable will run in the UI thread
+     * and it is the runnable's responsibility to call
+     * <code>Display.readAndDispatch()</code> to ensure UI responsiveness.
+     *
+     * UI state is saved prior to executing the long-running operation and is
+     * restored after the long-running operation completes executing. Any
+     * attempt to change the UI state of the wizard in the long-running
+     * operation will be nullified when original UI state is restored.
+     *
+     */
+    public void run(bool fork, bool cancelable,
+            IRunnableWithProgress runnable) {
+        // The operation can only be canceled if it is executed in a separate
+        // thread.
+        // Otherwise the UI is blocked anyway.
+        Object state = null;
+        if (activeRunningOperations is 0) {
+            state = aboutToStart(fork && cancelable);
+        }
+        activeRunningOperations++;
+        try {
+            if (!fork) {
+                lockedUI = true;
+            }
+            ModalContext.run(runnable, fork, getProgressMonitor(), getShell()
+                    .getDisplay());
+            lockedUI = false;
+        } finally {
+            activeRunningOperations--;
+            // Stop if this is the last one
+            if (state !is null) {
+                stopped(state);
+            }
+        }
+    }
+
+    /**
+     * Saves the enabled/disabled state of the given control in the given map,
+     * which must be modifiable.
+     *
+     * @param w
+     *            the control, or <code>null</code> if none
+     * @param h
+     *            the map (key type: <code>String</code>, element type:
+     *            <code>Boolean</code>)
+     * @param key
+     *            the key
+     * @param enabled
+     *            <code>true</code> to enable the control, and
+     *            <code>false</code> to disable it
+     * @see #restoreEnableState(Control, Map, String)
+     */
+    private void saveEnableStateAndSet(Control w, Map h, String key,
+            bool enabled) {
+        if (w !is null) {
+            h.put(stringcast(key), w.getEnabled() ? Boolean.TRUE : Boolean.FALSE);
+            w.setEnabled(enabled);
+        }
+    }
+
+    /**
+     * Captures and returns the enabled/disabled state of the wizard dialog's
+     * buttons and the tree of controls for the currently showing page. All
+     * these controls are disabled in the process, with the possible exception
+     * of the Cancel button.
+     *
+     * @param keepCancelEnabled
+     *            <code>true</code> if the Cancel button should remain
+     *            enabled, and <code>false</code> if it should be disabled
+     * @return a map containing the saved state suitable for restoring later
+     *         with <code>restoreUIState</code>
+     * @see #restoreUIState
+     */
+    private Map saveUIState(bool keepCancelEnabled) {
+        Map savedState = new HashMap(10);
+        saveEnableStateAndSet(backButton, savedState, "back", false); //$NON-NLS-1$
+        saveEnableStateAndSet(nextButton, savedState, "next", false); //$NON-NLS-1$
+        saveEnableStateAndSet(finishButton, savedState, "finish", false); //$NON-NLS-1$
+        saveEnableStateAndSet(cancelButton, savedState,
+                "cancel", keepCancelEnabled); //$NON-NLS-1$
+        saveEnableStateAndSet(helpButton, savedState, "help", false); //$NON-NLS-1$
+        if (currentPage !is null) {
+            savedState.put(stringcast("page"), ControlEnableState.disable(currentPage.getControl())); //$NON-NLS-1$
+        }
+        return savedState;
+    }
+
+    /**
+     * Sets the given cursor for all shells currently active for this window's
+     * display.
+     *
+     * @param c
+     *            the cursor
+     */
+    private void setDisplayCursor(Cursor c) {
+        Shell[] shells = getShell().getDisplay().getShells();
+        for (int i = 0; i < shells.length; i++) {
+            shells[i].setCursor(c);
+        }
+    }
+
+    /**
+     * Sets the minimum page size used for the pages.
+     *
+     * @param minWidth
+     *            the minimum page width
+     * @param minHeight
+     *            the minimum page height
+     * @see #setMinimumPageSize(Point)
+     */
+    public void setMinimumPageSize(int minWidth, int minHeight) {
+        Assert.isTrue(minWidth >= 0 && minHeight >= 0);
+        pageContainerLayout.minimumWidth = minWidth;
+        pageContainerLayout.minimumHeight = minHeight;
+    }
+
+    /**
+     * Sets the minimum page size used for the pages.
+     *
+     * @param size
+     *            the page size encoded as <code>new Point(width,height)</code>
+     * @see #setMinimumPageSize(int,int)
+     */
+    public void setMinimumPageSize(Point size) {
+        setMinimumPageSize(size.x, size.y);
+    }
+
+    /**
+     * Sets the size of all pages. The given size takes precedence over computed
+     * sizes.
+     *
+     * @param width
+     *            the page width
+     * @param height
+     *            the page height
+     * @see #setPageSize(Point)
+     */
+    public void setPageSize(int width, int height) {
+        pageWidth = width;
+        pageHeight = height;
+    }
+
+    /**
+     * Sets the size of all pages. The given size takes precedence over computed
+     * sizes.
+     *
+     * @param size
+     *            the page size encoded as <code>new Point(width,height)</code>
+     * @see #setPageSize(int,int)
+     */
+    public void setPageSize(Point size) {
+        setPageSize(size.x, size.y);
+    }
+
+    /**
+     * Sets the wizard this dialog is currently displaying.
+     *
+     * @param newWizard
+     *            the wizard
+     */
+    protected void setWizard(IWizard newWizard) {
+        wizard = newWizard;
+        wizard.setContainer(this);
+        if (!createdWizards.contains(cast(Object)wizard)) {
+            createdWizards.add(cast(Object)wizard);
+            // New wizard so just add it to the end of our nested list
+            nestedWizards.add(cast(Object)wizard);
+            if (pageContainer !is null) {
+                // Dialog is already open
+                // Allow the wizard pages to precreate their page controls
+                // This allows the wizard to open to the correct size
+                createPageControls();
+                // Ensure the dialog is large enough for the wizard
+                updateSizeForWizard(wizard);
+                pageContainer.layout(true);
+            }
+        } else {
+            // We have already seen this wizard, if it is the previous wizard
+            // on the nested list then we assume we have gone back and remove
+            // the last wizard from the list
+            int size = nestedWizards.size();
+            if (size >= 2 && nestedWizards.get(size - 2) is cast(Object)wizard) {
+                nestedWizards.remove(size - 1);
+            } else {
+                // Assume we are going forward to revisit a wizard
+                nestedWizards.add(cast(Object)wizard);
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizardContainer.
+     */
+    public void showPage(IWizardPage page) {
+        if (page is null || page is currentPage) {
+            return;
+        }
+
+        if (!isMovingToPreviousPage) {
+            // remember my previous page.
+            page.setPreviousPage(currentPage);
+        } else {
+            isMovingToPreviousPage = false;
+        }
+
+        // If page changing evaluation unsuccessful, do not change the page
+        if (!doPageChanging(page))
+            return;
+
+        // Update for the new page in a busy cursor if possible
+        if (getContents() is null) {
+            updateForPage(page);
+        } else {
+            BusyIndicator.showWhile(getContents().getDisplay(), dgRunnable( (IWizardPage finalPage) {
+                updateForPage(finalPage);
+            }, page ));
+        }
+    }
+
+    /**
+     * Update the receiver for the new page.
+     *
+     * @param page
+     */
+    private void updateForPage(IWizardPage page) {
+        // ensure this page belongs to the current wizard
+        if (wizard !is page.getWizard()) {
+            setWizard(page.getWizard());
+        }
+        // ensure that page control has been created
+        // (this allows lazy page control creation)
+        if (page.getControl() is null) {
+            page.createControl(pageContainer);
+            // the page is responsible for ensuring the created control is
+            // accessable
+            // via getControl.
+            Assert.isNotNull(page.getControl(), JFaceResources.format(
+                    JFaceResources.getString("WizardDialog.missingSetControl"), //$NON-NLS-1$
+                    [ page.getName() ]));
+            // ensure the dialog is large enough for this page
+            updateSize(page);
+        }
+        // make the new page visible
+        IWizardPage oldPage = currentPage;
+        currentPage = page;
+
+        currentPage.setVisible(true);
+        if (oldPage !is null) {
+            oldPage.setVisible(false);
+        }
+        // update the dialog controls
+        update();
+    }
+
+    /**
+     * Shows the starting page of the wizard.
+     */
+    private void showStartingPage() {
+        currentPage = wizard.getStartingPage();
+        if (currentPage is null) {
+            // something must have happend getting the page
+            return;
+        }
+        // ensure the page control has been created
+        if (currentPage.getControl() is null) {
+            currentPage.createControl(pageContainer);
+            // the page is responsible for ensuring the created control is
+            // accessable
+            // via getControl.
+            Assert.isNotNull(currentPage.getControl());
+            // we do not need to update the size since the call
+            // to initialize bounds has not been made yet.
+        }
+        // make the new page visible
+        currentPage.setVisible(true);
+        // update the dialog controls
+        update();
+    }
+
+    /**
+     * A long running operation triggered through the wizard was stopped either
+     * by user input or by normal end. Hides the progress monitor and restores
+     * the enable state wizard's buttons and controls.
+     *
+     * @param savedState
+     *            the saved UI state as returned by <code>aboutToStart</code>
+     * @see #aboutToStart
+     */
+    private void stopped(Object savedState) {
+        if (getShell() !is null && !getShell().isDisposed()) {
+            if (wizard.needsProgressMonitor()) {
+                progressMonitorPart.setVisible(false);
+                progressMonitorPart.removeFromCancelComponent(cancelButton);
+            }
+            Map state = cast(Map) savedState;
+            restoreUIState(state);
+            cancelButton.addSelectionListener(cancelListener);
+            setDisplayCursor(null);
+            cancelButton.setCursor(null);
+            waitCursor.dispose();
+            waitCursor = null;
+            arrowCursor.dispose();
+            arrowCursor = null;
+            Control focusControl = cast(Control) state.get(stringcast(FOCUS_CONTROL));
+            if (focusControl !is null && !focusControl.isDisposed()) {
+                focusControl.setFocus();
+            }
+        }
+    }
+
+    /**
+     * Updates this dialog's controls to reflect the current page.
+     */
+    protected void update() {
+        // Update the window title
+        updateWindowTitle();
+        // Update the title bar
+        updateTitleBar();
+        // Update the buttons
+        updateButtons();
+
+        // Fires the page change event
+        firePageChanged(new PageChangedEvent(this, cast(Object)getCurrentPage()));
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizardContainer.
+     */
+    public void updateButtons() {
+        bool canFlipToNextPage = false;
+        bool canFinish = wizard.canFinish();
+        if (backButton !is null) {
+            backButton.setEnabled(currentPage.getPreviousPage() !is null);
+        }
+        if (nextButton !is null) {
+            canFlipToNextPage = currentPage.canFlipToNextPage();
+            nextButton.setEnabled(canFlipToNextPage);
+        }
+        finishButton.setEnabled(canFinish);
+        // finish is default unless it is diabled and next is enabled
+        if (canFlipToNextPage && !canFinish) {
+            getShell().setDefaultButton(nextButton);
+        } else {
+            getShell().setDefaultButton(finishButton);
+        }
+    }
+
+    /**
+     * Update the message line with the page's description.
+     * <p>
+     * A discription is shown only if there is no message or error message.
+     * </p>
+     */
+    private void updateDescriptionMessage() {
+        pageDescription = currentPage.getDescription();
+        setMessage(pageDescription);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizardContainer.
+     */
+    public void updateMessage() {
+
+        if (currentPage is null) {
+            return;
+        }
+
+        pageMessage = currentPage.getMessage();
+        if (pageMessage !is null && cast(IMessageProvider)currentPage ) {
+            pageMessageType = (cast(IMessageProvider) currentPage).getMessageType();
+        } else {
+            pageMessageType = IMessageProvider.NONE;
+        }
+        if (pageMessage is null) {
+            setMessage(pageDescription);
+        } else {
+            setMessage(pageMessage, pageMessageType);
+        }
+        setErrorMessage(currentPage.getErrorMessage());
+    }
+
+    /**
+     * Changes the shell size to the given size, ensuring that it is no larger
+     * than the display bounds.
+     *
+     * @param width
+     *            the shell width
+     * @param height
+     *            the shell height
+     */
+    private void setShellSize(int width, int height) {
+        Rectangle size = getShell().getBounds();
+        size.height = height;
+        size.width = width;
+        getShell().setBounds(getConstrainedShellBounds(size));
+    }
+
+    /**
+     * Computes the correct dialog size for the current page and resizes its
+     * shell if nessessary. Also causes the container to refresh its layout.
+     *
+     * @param page
+     *            the wizard page to use to resize the dialog
+     * @since 2.0
+     */
+    protected void updateSize(IWizardPage page) {
+        if (page is null || page.getControl() is null) {
+            return;
+        }
+        updateSizeForPage(page);
+        pageContainerLayout.layoutPage(page.getControl());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.wizard.IWizardContainer2#updateSize()
+     */
+    public void updateSize() {
+        updateSize(currentPage);
+    }
+
+    /**
+     * Computes the correct dialog size for the given page and resizes its shell
+     * if nessessary.
+     *
+     * @param page
+     *            the wizard page
+     */
+    private void updateSizeForPage(IWizardPage page) {
+        // ensure the page container is large enough
+        Point delta = calculatePageSizeDelta(page);
+        if (delta.x > 0 || delta.y > 0) {
+            // increase the size of the shell
+            Shell shell = getShell();
+            Point shellSize = shell.getSize();
+            setShellSize(shellSize.x + delta.x, shellSize.y + delta.y);
+            constrainShellSize();
+        }
+    }
+
+    /**
+     * Computes the correct dialog size for the given wizard and resizes its
+     * shell if nessessary.
+     *
+     * @param sizingWizard
+     *            the wizard
+     */
+    private void updateSizeForWizard(IWizard sizingWizard) {
+        Point delta = new Point(0, 0);
+        IWizardPage[] pages = sizingWizard.getPages();
+        for (int i = 0; i < pages.length; i++) {
+            // ensure the page container is large enough
+            Point pageDelta = calculatePageSizeDelta(pages[i]);
+            delta.x = Math.max(delta.x, pageDelta.x);
+            delta.y = Math.max(delta.y, pageDelta.y);
+        }
+        if (delta.x > 0 || delta.y > 0) {
+            // increase the size of the shell
+            Shell shell = getShell();
+            Point shellSize = shell.getSize();
+            setShellSize(shellSize.x + delta.x, shellSize.y + delta.y);
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizardContainer.
+     */
+    public void updateTitleBar() {
+        String s = null;
+        if (currentPage !is null) {
+            s = currentPage.getTitle();
+        }
+        if (s is null) {
+            s = ""; //$NON-NLS-1$
+        }
+        setTitle(s);
+        if (currentPage !is null) {
+            setTitleImage(currentPage.getImage());
+            updateDescriptionMessage();
+        }
+        updateMessage();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IWizardContainer.
+     */
+    public void updateWindowTitle() {
+        if (getShell() is null) {
+            // Not created yet
+            return;
+        }
+        String title = wizard.getWindowTitle();
+        if (title is null) {
+            title = ""; //$NON-NLS-1$
+        }
+        getShell().setText(title);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.IPageChangeProvider#getSelectedPage()
+     */
+    public Object getSelectedPage() {
+        return cast(Object)getCurrentPage();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialog.IPageChangeProvider#addPageChangedListener()
+     */
+    public void addPageChangedListener(IPageChangedListener listener) {
+        pageChangedListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialog.IPageChangeProvider#removePageChangedListener()
+     */
+    public void removePageChangedListener(IPageChangedListener listener) {
+        pageChangedListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Notifies any selection changed listeners that the selected page has
+     * changed. Only listeners registered at the time this method is called are
+     * notified.
+     *
+     * @param event
+     *            a selection changed event
+     *
+     * @see IPageChangedListener#pageChanged
+     *
+     * @since 3.1
+     */
+    protected void firePageChanged(PageChangedEvent event) {
+        Object[] listeners = pageChangedListeners.getListeners();
+        for (int i = 0; i < listeners.length; ++i) {
+            SafeRunnable.run(new class(cast(IPageChangedListener) listeners[i],event) SafeRunnable {
+                PageChangedEvent event_;
+                IPageChangedListener l;
+                this(IPageChangedListener a, PageChangedEvent b){
+                    l = a;
+                    event_=b;
+                }
+                public void run() {
+                    l.pageChanged(event_);
+                }
+            });
+        }
+    }
+
+    /**
+     * Adds a listener for page changes to the list of page changing listeners
+     * registered for this dialog. Has no effect if an identical listener is
+     * already registered.
+     *
+     * @param listener
+     *            a page changing listener
+     * @since 3.3
+     */
+    public void addPageChangingListener(IPageChangingListener listener) {
+        pageChangingListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes the provided page changing listener from the list of page
+     * changing listeners registered for the dialog.
+     *
+     * @param listener
+     *            a page changing listener
+     * @since 3.3
+     */
+    public void removePageChangingListener(IPageChangingListener listener) {
+        pageChangingListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Notifies any page changing listeners that the currently selected dialog
+     * page is changing. Only listeners registered at the time this method is
+     * called are notified.
+     *
+     * @param event
+     *            a selection changing event
+     *
+     * @see IPageChangingListener#handlePageChanging(PageChangingEvent)
+     * @since 3.3
+     */
+    protected void firePageChanging(PageChangingEvent event) {
+        Object[] listeners = pageChangingListeners.getListeners();
+        for (int i = 0; i < listeners.length; ++i) {
+            SafeRunnable.run(new class(cast(IPageChangingListener) listeners[i],event) SafeRunnable {
+                PageChangingEvent event_;
+                IPageChangingListener l;
+                this(IPageChangingListener a, PageChangingEvent b){
+                    l = a;
+                    event_=b;
+                }
+                public void run() {
+                    l.handlePageChanging(event_);
+                }
+            });
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/wizard/WizardPage.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,346 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.wizard.WizardPage;
+
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.IWizardContainer;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.dialogs.DialogPage;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * An abstract base implementation of a wizard page.
+ * <p>
+ * Subclasses must implement the <code>createControl</code> method
+ * to create the specific controls for the wizard page.
+ * </p>
+ * <p>
+ * Subclasses may call the following methods to configure the wizard page:
+ * <ul>
+ *  <li><code>setDescription</code></li>
+ *  <li><code>setErrorMessage</code></li>
+ *  <li><code>setImageDescriptor</code></li>
+ *  <li><code>setMessage</code></li>
+ *  <li><code>setPageComplete</code></li>
+ *  <li><code>setPreviousPage</code></li>
+ *  <li><code>setTitle</code></li>
+ * </ul>
+ * </p>
+ * <p>
+ * Subclasses may override these methods if required:
+ * <ul>
+ *  <li><code>performHelp</code> - may be reimplemented to display help for the page</li>
+ * <li><code>canFlipToNextPage</code> - may be extended or reimplemented</li>
+ *  <li><code>isPageComplete</code> - may be extended </li>
+ *  <li><code>setDescription</code> - may be extended </li>
+ *  <li><code>setTitle</code> - may be extended </li>
+ *  <li><code>dispose</code> - may be extended to dispose additional allocated SWT resources</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Note that clients are free to implement <code>IWizardPage</code> from scratch
+ * instead of subclassing <code>WizardPage</code>. Correct implementations of
+ * <code>IWizardPage</code> will work with any correct implementation of
+ * <code>IWizard</code>.
+ * </p>
+ */
+public abstract class WizardPage : DialogPage, IWizardPage {
+    alias DialogPage.setMessage setMessage;
+
+    /**
+     * This page's name.
+     */
+    private String name;
+
+    /**
+     * The wizard to which this page belongs; <code>null</code>
+     * if this page has yet to be added to a wizard.
+     */
+    private IWizard wizard = null;
+
+    /**
+     * Indicates whether this page is complete.
+     */
+    private bool isPageComplete_ = true;
+
+    /**
+     * The page that was shown right before this page became visible;
+     * <code>null</code> if none.
+     */
+    private IWizardPage previousPage = null;
+
+    /**
+     * Creates a new wizard page with the given name, and
+     * with no title or image.
+     *
+     * @param pageName the name of the page
+     */
+    protected this(String pageName) {
+        this(pageName, null, cast(ImageDescriptor) null);
+    }
+
+    /**
+     * Creates a new wizard page with the given name, title, and image.
+     *
+     * @param pageName the name of the page
+     * @param title the title for this wizard page,
+     *   or <code>null</code> if none
+     * @param titleImage the image descriptor for the title of this wizard page,
+     *   or <code>null</code> if none
+     */
+    protected this(String pageName, String title,
+            ImageDescriptor titleImage) {
+        super(title, titleImage);
+        Assert.isNotNull(pageName); // page name must not be null
+        name = pageName;
+    }
+
+    /**
+     * The <code>WizardPage</code> implementation of this <code>IWizardPage</code>
+     * method returns <code>true</code> if this page is complete (<code>isPageComplete</code>)
+     * and there is a next page to flip to. Subclasses may override (extend or reimplement).
+     *
+     * @see #getNextPage
+     * @see #isPageComplete()
+     */
+    public bool canFlipToNextPage() {
+        return isPageComplete() && getNextPage() !is null;
+    }
+
+    /**
+     * Returns the wizard container for this wizard page.
+     *
+     * @return the wizard container, or <code>null</code> if this
+     *   wizard page has yet to be added to a wizard, or the
+     *   wizard has yet to be added to a container
+     */
+    protected IWizardContainer getContainer() {
+        if (wizard is null) {
+            return null;
+        }
+        return wizard.getContainer();
+    }
+
+    /**
+     * Returns the dialog settings for this wizard page.
+     *
+     * @return the dialog settings, or <code>null</code> if none
+     */
+    protected IDialogSettings getDialogSettings() {
+        if (wizard is null) {
+            return null;
+        }
+        return wizard.getDialogSettings();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialogPage.
+     */
+    public override Image getImage() {
+        Image result = super.getImage();
+
+        if (result is null && wizard !is null) {
+            return wizard.getDefaultPageImage();
+        }
+
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IWizardPage.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IWizardPage.
+     * The default behavior is to ask the wizard for the next page.
+     */
+    public IWizardPage getNextPage() {
+        if (wizard is null) {
+            return null;
+        }
+        return wizard.getNextPage(this);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IWizardPage.
+     * The default behavior is return the cached previous back or,
+     * lacking that, to ask the wizard for the previous page.
+     */
+    public IWizardPage getPreviousPage() {
+        if (previousPage !is null) {
+            return previousPage;
+        }
+
+        if (wizard is null) {
+            return null;
+        }
+
+        return wizard.getPreviousPage(this);
+    }
+
+    /**
+     * The <code>WizardPage</code> implementation of this method declared on
+     * <code>DialogPage</code> returns the shell of the container.
+     * The advantage of this implementation is that the shell is accessable
+     * once the container is created even though this page's control may not
+     * yet be created.
+     */
+    public override Shell getShell() {
+
+        IWizardContainer container = getContainer();
+        if (container is null) {
+            return null;
+        }
+
+        // Ask the wizard since our contents may not have been created.
+        return container.getShell();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IWizardPage.
+     */
+    public IWizard getWizard() {
+        return wizard;
+    }
+
+    /**
+     * Returns whether this page is the current one in the wizard's container.
+     *
+     * @return <code>true</code> if the page is active,
+     *  and <code>false</code> otherwise
+     */
+    protected bool isCurrentPage() {
+        return (getContainer() !is null && this is getContainer()
+                .getCurrentPage());
+    }
+
+    /**
+     * The <code>WizardPage</code> implementation of this <code>IWizard</code> method
+     * returns the value of an internal state variable set by
+     * <code>setPageComplete</code>. Subclasses may extend.
+     */
+    public bool isPageComplete() {
+        return isPageComplete_;
+    }
+
+    /**
+     * The <code>WizardPage</code> implementation of this <code>IDialogPage</code>
+     * method extends the <code>DialogPage</code> implementation to update
+     * the wizard container title bar. Subclasses may extend.
+     */
+    public override void setDescription(String description) {
+        super.setDescription(description);
+        if (isCurrentPage()) {
+            getContainer().updateTitleBar();
+        }
+    }
+
+    /**
+     * The <code>WizardPage</code> implementation of this method
+     * declared on <code>DialogPage</code> updates the container
+     * if this is the current page.
+     */
+    public override void setErrorMessage(String newMessage) {
+        super.setErrorMessage(newMessage);
+        if (isCurrentPage()) {
+            getContainer().updateMessage();
+        }
+    }
+
+    /**
+     * The <code>WizardPage</code> implementation of this method
+     * declared on <code>DialogPage</code> updates the container
+     * if this page is the current page.
+     */
+    public override void setImageDescriptor(ImageDescriptor image) {
+        super.setImageDescriptor(image);
+        if (isCurrentPage()) {
+            getContainer().updateTitleBar();
+        }
+    }
+
+    /**
+     * The <code>WizardPage</code> implementation of this method
+     * declared on <code>DialogPage</code> updates the container
+     * if this is the current page.
+     */
+    public override void setMessage(String newMessage, int newType) {
+        super.setMessage(newMessage, newType);
+        if (isCurrentPage()) {
+            getContainer().updateMessage();
+        }
+    }
+
+    /**
+     * Sets whether this page is complete.
+     * <p>
+     * This information is typically used by the wizard to decide
+     * when it is okay to move on to the next page or finish up.
+     * </p>
+     *
+     * @param complete <code>true</code> if this page is complete, and
+     *   and <code>false</code> otherwise
+     * @see #isPageComplete()
+     */
+    public void setPageComplete(bool complete) {
+        isPageComplete_ = complete;
+        if (isCurrentPage()) {
+            getContainer().updateButtons();
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IWizardPage.
+     */
+    public void setPreviousPage(IWizardPage page) {
+        previousPage = page;
+    }
+
+    /**
+     * The <code>WizardPage</code> implementation of this <code>IDialogPage</code>
+     * method extends the <code>DialogPage</code> implementation to update
+     * the wizard container title bar. Subclasses may extend.
+     */
+    public override void setTitle(String title) {
+        super.setTitle(title);
+        if (isCurrentPage()) {
+            getContainer().updateTitleBar();
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IWizardPage.
+     */
+    public void setWizard(IWizard newWizard) {
+        wizard = newWizard;
+    }
+
+    /**
+     * Returns a printable representation of this wizard page suitable
+     * only for debug purposes.
+     */
+    public override String toString() {
+        return name;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface/src/org/eclipse/jface/wizard/WizardSelectionPage.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.wizard.WizardSelectionPage;
+
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.jface.wizard.IWizardNode;
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * An abstract implementation of a wizard page that manages a
+ * set of embedded wizards.
+ * <p>
+ * A wizard selection page should present a list of wizard nodes
+ * corresponding to other wizards. When the end user selects one of
+ * them from the list, the first page of the selected wizard becomes
+ * the next page. The only new methods introduced by this class are
+ * <code>getSelectedNode</code> and <code>setSelectedNode</code>.
+ * Otherwise, the subclass contract is the same as <code>WizardPage</code>.
+ * </p>
+ */
+public abstract class WizardSelectionPage : WizardPage {
+
+    /**
+     * The selected node; <code>null</code> if none.
+     */
+    private IWizardNode selectedNode = null;
+
+    /**
+     * List of wizard nodes that have cropped up in the past
+     * (element type: <code>IWizardNode</code>).
+     */
+    private List selectedWizardNodes;
+
+    /**
+     * Creates a new wizard selection page with the given name, and
+     * with no title or image.
+     *
+     * @param pageName the name of the page
+     */
+    protected this(String pageName) {
+        super(pageName);
+        selectedWizardNodes = new ArrayList();
+        // Cannot finish from this page
+        setPageComplete(false);
+    }
+
+    /**
+     * Adds the given wizard node to the list of selected nodes if
+     * it is not already in the list.
+     *
+     * @param node the wizard node, or <code>null</code>
+     */
+    private void addSelectedNode(IWizardNode node) {
+        if (node is null) {
+            return;
+        }
+
+        if (selectedWizardNodes.contains(cast(Object)node)) {
+            return;
+        }
+
+        selectedWizardNodes.add(cast(Object)node);
+    }
+
+    /**
+     * The <code>WizardSelectionPage</code> implementation of
+     * this <code>IWizardPage</code> method returns <code>true</code>
+     * if there is a selected node.
+     */
+    public override bool canFlipToNextPage() {
+        return selectedNode !is null;
+    }
+
+    /**
+     * The <code>WizardSelectionPage</code> implementation of an <code>IDialogPage</code>
+     * method disposes of all nested wizards. Subclasses may extend.
+     */
+    public override void dispose() {
+        super.dispose();
+        // notify nested wizards
+        for (int i = 0; i < selectedWizardNodes.size(); i++) {
+            (cast(IWizardNode) selectedWizardNodes.get(i)).dispose();
+        }
+    }
+
+    /**
+     * The <code>WizardSelectionPage</code> implementation of
+     * this <code>IWizardPage</code> method returns the first page
+     * of the currently selected wizard if there is one.
+     */
+    public override IWizardPage getNextPage() {
+        if (selectedNode is null) {
+            return null;
+        }
+
+        bool isCreated = selectedNode.isContentCreated();
+
+        IWizard wizard = selectedNode.getWizard();
+
+        if (wizard is null) {
+            setSelectedNode(null);
+            return null;
+        }
+
+        if (!isCreated) {
+            // Allow the wizard to create its pages
+            wizard.addPages();
+        }
+
+        return wizard.getStartingPage();
+    }
+
+    /**
+     * Returns the currently selected wizard node within this page.
+     *
+     * @return the wizard node, or <code>null</code> if no node is selected
+     */
+    public IWizardNode getSelectedNode() {
+        return selectedNode;
+    }
+
+    /**
+     * Sets or clears the currently selected wizard node within this page.
+     *
+     * @param node the wizard node, or <code>null</code> to clear
+     */
+    protected void setSelectedNode(IWizardNode node) {
+        addSelectedNode(node);
+        selectedNode = node;
+        if (isCurrentPage()) {
+            getContainer().updateButtons();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.osgi/src/org/osgi/framework/Bundle.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1122 @@
+/*
+ * $Header: /cvshome/build/org.osgi.framework/src/org/osgi/framework/Bundle.java,v 1.54 2007/02/21 16:49:05 hargrave Exp $
+ *
+ * Copyright (c) OSGi Alliance (2000, 2007). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+module org.eclipse.osgi.framework.Bundle;
+
+import java.lang.all;
+
+// import java.io.IOException;
+// import java.io.InputStream;
+// import java.net.URL;
+// import java.util.Dictionary;
+// import java.util.Enumeration;
+
+/**
+ * An installed bundle in the Framework.
+ *
+ * <p>
+ * A <code>Bundle</code> object is the access point to define the lifecycle of
+ * an installed bundle. Each bundle installed in the OSGi environment must have
+ * an associated <code>Bundle</code> object.
+ *
+ * <p>
+ * A bundle must have a unique identity, a <code>long</code>, chosen by the
+ * Framework. This identity must not change during the lifecycle of a bundle,
+ * even when the bundle is updated. Uninstalling and then reinstalling the
+ * bundle must create a new unique identity.
+ *
+ * <p>
+ * A bundle can be in one of six states:
+ * <ul>
+ * <li>{@link #UNINSTALLED}
+ * <li>{@link #INSTALLED}
+ * <li>{@link #RESOLVED}
+ * <li>{@link #STARTING}
+ * <li>{@link #STOPPING}
+ * <li>{@link #ACTIVE}
+ * </ul>
+ * <p>
+ * Values assigned to these states have no specified ordering; they represent
+ * bit values that may be ORed together to determine if a bundle is in one of
+ * the valid states.
+ *
+ * <p>
+ * A bundle should only execute code when its state is one of
+ * <code>STARTING</code>,<code>ACTIVE</code>, or <code>STOPPING</code>.
+ * An <code>UNINSTALLED</code> bundle can not be set to another state; it is a
+ * zombie and can only be reached because references are kept somewhere.
+ *
+ * <p>
+ * The Framework is the only entity that is allowed to create
+ * <code>Bundle</code> objects, and these objects are only valid within the
+ * Framework that created them.
+ *
+ * @ThreadSafe
+ * @version $Revision: 1.54 $
+ */
+public interface Bundle {
+    /**
+     * The bundle is uninstalled and may not be used.
+     *
+     * <p>
+     * The <code>UNINSTALLED</code> state is only visible after a bundle is
+     * uninstalled; the bundle is in an unusable state but references to the
+     * <code>Bundle</code> object may still be available and used for
+     * introspection.
+     * <p>
+     * The value of <code>UNINSTALLED</code> is 0x00000001.
+     */
+    public static const int UNINSTALLED             = 0x00000001;
+
+    /**
+     * The bundle is installed but not yet resolved.
+     *
+     * <p>
+     * A bundle is in the <code>INSTALLED</code> state when it has been
+     * installed in the Framework but is not or cannot be resolved.
+     * <p>
+     * This state is visible if the bundle's code dependencies are not resolved.
+     * The Framework may attempt to resolve an <code>INSTALLED</code> bundle's
+     * code dependencies and move the bundle to the <code>RESOLVED</code>
+     * state.
+     * <p>
+     * The value of <code>INSTALLED</code> is 0x00000002.
+     */
+    public static const int INSTALLED               = 0x00000002;
+
+    /**
+     * The bundle is resolved and is able to be started.
+     *
+     * <p>
+     * A bundle is in the <code>RESOLVED</code> state when the Framework has
+     * successfully resolved the bundle's code dependencies. These dependencies
+     * include:
+     * <ul>
+     * <li>The bundle's class path from its {@link Constants#BUNDLE_CLASSPATH}
+     * Manifest header.
+     * <li>The bundle's package dependencies from its
+     * {@link Constants#EXPORT_PACKAGE} and {@link Constants#IMPORT_PACKAGE}
+     * Manifest headers.
+     * <li>The bundle's required bundle dependencies from its
+     * {@link Constants#REQUIRE_BUNDLE} Manifest header.
+     * <li>A fragment bundle's host dependency from its
+     * {@link Constants#FRAGMENT_HOST} Manifest header.
+     * </ul>
+     * <p>
+     * Note that the bundle is not active yet. A bundle must be put in the
+     * <code>RESOLVED</code> state before it can be started. The Framework may
+     * attempt to resolve a bundle at any time.
+     * <p>
+     * The value of <code>RESOLVED</code> is 0x00000004.
+     */
+    public static const int RESOLVED                = 0x00000004;
+
+    /**
+     * The bundle is in the process of starting.
+     *
+     * <p>
+     * A bundle is in the <code>STARTING</code> state when its
+     * {@link #start(int) start} method is active. A bundle must be in this
+     * state when the bundle's {@link BundleActivator#start} is called. If the
+     * <code>BundleActivator.start</code> method completes without exception,
+     * then the bundle has successfully started and must move to the
+     * <code>ACTIVE</code> state.
+     * <p>
+     * If the bundle has a
+     * {@link Constants#ACTIVATION_LAZY lazy activation policy}, then the
+     * bundle may remain in this state for some time until the activation is
+     * triggered.
+     * <p>
+     * The value of <code>STARTING</code> is 0x00000008.
+     */
+    public static const int STARTING                = 0x00000008;
+
+    /**
+     * The bundle is in the process of stopping.
+     *
+     * <p>
+     * A bundle is in the <code>STOPPING</code> state when its
+     * {@link #stop(int) stop} method is active. A bundle must be in this state
+     * when the bundle's {@link BundleActivator#stop} method is called. When the
+     * <code>BundleActivator.stop</code> method completes the bundle is
+     * stopped and must move to the <code>RESOLVED</code> state.
+     * <p>
+     * The value of <code>STOPPING</code> is 0x00000010.
+     */
+    public static const int STOPPING                = 0x00000010;
+
+    /**
+     * The bundle is now running.
+     *
+     * <p>
+     * A bundle is in the <code>ACTIVE</code> state when it has been
+     * successfully started and activated.
+     * <p>
+     * The value of <code>ACTIVE</code> is 0x00000020.
+     */
+    public static const int ACTIVE                  = 0x00000020;
+
+    /**
+     * The bundle start operation is transient and the persistent autostart
+     * setting of the bundle is not modified.
+     *
+     * <p>
+     * This bit may be set when calling {@link #start(int)} to notify the
+     * framework that the autostart setting of the bundle must not be modified.
+     * If this bit is not set, then the autostart setting of the bundle is
+     * modified.
+     *
+     * @since 1.4
+     * @see #start(int)
+     */
+    public static const int START_TRANSIENT         = 0x00000001;
+
+    /**
+     * The bundle start operation must activate the bundle according to the
+     * bundle's declared
+     * {@link Constants#BUNDLE_ACTIVATIONPOLICY activation policy}.
+     *
+     * <p>
+     * This bit may be set when calling {@link #start(int)} to notify the
+     * framework that the bundle must be activated using the bundle's declared
+     * activation policy.
+     *
+     * @since 1.4
+     * @see Constants#BUNDLE_ACTIVATIONPOLICY
+     * @see #start(int)
+     */
+    public static const int START_ACTIVATION_POLICY = 0x00000002;
+
+    /**
+     * The bundle stop is transient and the persistent autostart setting of the
+     * bundle is not modified.
+     *
+     * <p>
+     * This bit may be set when calling {@link #stop(int)} to notify the
+     * framework that the autostart setting of the bundle must not be modified.
+     * If this bit is not set, then the autostart setting of the bundle is
+     * modified.
+     *
+     * @since 1.4
+     * @see #stop(int)
+     */
+    public static final int STOP_TRANSIENT          = 0x00000001;
+
+    /**
+     * Returns this bundle's current state.
+     *
+     * <p>
+     * A bundle can be in only one state at any time.
+     *
+     * @return An element of <code>UNINSTALLED</code>,<code>INSTALLED</code>,
+     *         <code>RESOLVED</code>,<code>STARTING</code>,
+     *         <code>STOPPING</code>,<code>ACTIVE</code>.
+     */
+    public int getState();
+
+    /**
+     * Starts this bundle.
+     *
+     * <p>
+     * If this bundle's state is <code>UNINSTALLED</code> then an
+     * <code>IllegalStateException</code> is thrown.
+     * <p>
+     * If the Framework implements the optional Start Level service and the
+     * current start level is less than this bundle's start level:
+     * <ul>
+     * <li>If the {@link #START_TRANSIENT} option is set, then a
+     * <code>BundleException</code> is thrown indicating this bundle cannot be
+     * started due to the Framework's current start level.
+     *
+     * <li>Otherwise, the Framework must set this bundle's persistent autostart
+     * setting to <em>Started with declared activation</em> if the
+     * {@link #START_ACTIVATION_POLICY} option is set or
+     * <em>Started with eager activation</em> if not set.
+     * </ul>
+     * <p>
+     * When the Framework's current start level becomes equal to or more than
+     * this bundle's start level, this bundle will be started.
+     * <p>
+     * Otherwise, the following steps are required to start this bundle:
+     * <ol>
+     * <li>If this bundle is in the process of being activated or deactivated
+     * then this method must wait for activation or deactivation to complete
+     * before continuing. If this does not occur in a reasonable time, a
+     * <code>BundleException</code> is thrown to indicate this bundle was
+     * unable to be started.
+     *
+     * <li>If this bundle's state is <code>ACTIVE</code> then this method
+     * returns immediately.
+     *
+     * <li>If the {@link #START_TRANSIENT} option is not set then set this
+     * bundle's autostart setting to <em>Started with declared activation</em>
+     * if the {@link #START_ACTIVATION_POLICY} option is set or
+     * <em>Started with eager activation</em> if not set. When the Framework
+     * is restarted and this bundle's autostart setting is not <em>Stopped</em>,
+     * this bundle must be automatically started.
+     *
+     * <li>If this bundle's state is not <code>RESOLVED</code>, an attempt
+     * is made to resolve this bundle. If the Framework cannot resolve this
+     * bundle, a <code>BundleException</code> is thrown.
+     *
+     * <li>If the {@link #START_ACTIVATION_POLICY} option is set and this
+     * bundle's declared activation policy is
+     * {@link Constants#ACTIVATION_LAZY lazy} then:
+     * <ul>
+     * <li>If this bundle's state is <code>STARTING</code> then this method
+     * returns immediately.
+     * <li>This bundle's state is set to <code>STARTING</code>.
+     * <li>A bundle event of type {@link BundleEvent#LAZY_ACTIVATION} is fired.
+     * <li>This method returns immediately and the remaining steps will be
+     * followed when this bundle's activation is later triggered.
+     * </ul>
+     * <i></i>
+     * <li>This bundle's state is set to <code>STARTING</code>.
+     *
+     * <li>A bundle event of type {@link BundleEvent#STARTING} is fired.
+     *
+     * <li>The {@link BundleActivator#start} method of this bundle's
+     * <code>BundleActivator</code>, if one is specified, is called. If the
+     * <code>BundleActivator</code> is invalid or throws an exception then:
+     * <ul>
+     * <li>This bundle's state is set to <code>STOPPING</code>.
+     * <li>A bundle event of type {@link BundleEvent#STOPPING} is fired.
+     * <li>Any services registered by this bundle must be unregistered.
+     * <li>Any services used by this bundle must be released.
+     * <li>Any listeners registered by this bundle must be removed.
+     * <li>This bundle's state is set to <code>RESOLVED</code>.
+     * <li>A bundle event of type {@link BundleEvent#STOPPED} is fired.
+     * <li>A <code>BundleException</code> is then thrown.
+     * </ul>
+     * <i></i>
+     * <li>If this bundle's state is <code>UNINSTALLED</code>, because this
+     * bundle was uninstalled while the <code>BundleActivator.start</code>
+     * method was running, a <code>BundleException</code> is thrown.
+     *
+     * <li>This bundle's state is set to <code>ACTIVE</code>.
+     *
+     * <li>A bundle event of type {@link BundleEvent#STARTED} is fired.
+     * </ol>
+     *
+     * <b>Preconditions </b>
+     * <ul>
+     * <li><code>getState()</code> in {<code>INSTALLED</code>,
+     * <code>RESOLVED</code>} or {<code>INSTALLED</code>,
+     * <code>RESOLVED</code>, <code>STARTING</code>} if this bundle has a
+     * lazy activation policy.
+     * </ul>
+     * <b>Postconditions, no exceptions thrown </b>
+     * <ul>
+     * <li>Bundle autostart setting is modified unless the
+     * {@link #START_TRANSIENT} option was set.
+     * <li><code>getState()</code> in {<code>ACTIVE</code>} unless the
+     * lazy activation policy was used.
+     * <li><code>BundleActivator.start()</code> has been called and did not
+     * throw an exception unless the lazy activation policy was used.
+     * </ul>
+     * <b>Postconditions, when an exception is thrown </b>
+     * <ul>
+     * <li>Depending on when the exception occurred, bundle autostart setting
+     * is modified unless the {@link #START_TRANSIENT} option was set.
+     * <li><code>getState()</code> not in {<code>STARTING</code>,
+     * <code>ACTIVE</code>}.
+     * </ul>
+     *
+     * @param options The options for starting this bundle. See
+     *        {@link #START_TRANSIENT} and {@link #START_ACTIVATION_POLICY}.
+     *        The Framework must ignore unrecognized options.
+     * @throws BundleException If this bundle could not be started. This could
+     *         be because a code dependency could not be resolved or the
+     *         specified <code>BundleActivator</code> could not be loaded or
+     *         threw an exception or this bundle is a fragment.
+     * @throws java.lang.IllegalStateException If this bundle has been
+     *         uninstalled or this bundle tries to change its own state.
+     * @throws java.lang.SecurityException If the caller does not have the
+     *         appropriate <code>AdminPermission[this,EXECUTE]</code>, and
+     *         the Java Runtime Environment supports permissions.
+     * @since 1.4
+     */
+    public void start(int options);
+
+    /**
+     * Starts this bundle with no options.
+     *
+     * <p>
+     * This method calls <code>start(0)</code>.
+     *
+     * @throws BundleException If this bundle could not be started. This could
+     *         be because a code dependency could not be resolved or the
+     *         specified <code>BundleActivator</code> could not be loaded or
+     *         threw an exception or this bundle is a fragment.
+     * @throws java.lang.IllegalStateException If this bundle has been
+     *         uninstalled or this bundle tries to change its own state.
+     * @throws java.lang.SecurityException If the caller does not have the
+     *         appropriate <code>AdminPermission[this,EXECUTE]</code>, and
+     *         the Java Runtime Environment supports permissions.
+     * @see #start(int)
+     */
+    public void start();
+
+    /**
+     * Stops this bundle.
+     *
+     * <p>
+     * The following steps are required to stop a bundle:
+     * <ol>
+     * <li>If this bundle's state is <code>UNINSTALLED</code> then an
+     * <code>IllegalStateException</code> is thrown.
+     *
+     * <li>If this bundle is in the process of being activated or deactivated
+     * then this method must wait for activation or deactivation to complete
+     * before continuing. If this does not occur in a reasonable time, a
+     * <code>BundleException</code> is thrown to indicate this bundle was
+     * unable to be stopped.
+     * <li>If the {@link #STOP_TRANSIENT} option is not set then then set this
+     * bundle's persistent autostart setting to to <em>Stopped</em>. When the
+     * Framework is restarted and this bundle's autostart setting is
+     * <em>Stopped</em>, this bundle must not be automatically started.
+     *
+     * <li>If this bundle's state is not <code>ACTIVE</code> then this method
+     * returns immediately.
+     *
+     * <li>This bundle's state is set to <code>STOPPING</code>.
+     *
+     * <li>A bundle event of type {@link BundleEvent#STOPPING} is fired.
+     *
+     * <li>The {@link BundleActivator#stop} method of this bundle's
+     * <code>BundleActivator</code>, if one is specified, is called. If that
+     * method throws an exception, this method must continue to stop this
+     * bundle. A <code>BundleException</code> must be thrown after completion
+     * of the remaining steps.
+     *
+     * <li>Any services registered by this bundle must be unregistered.
+     * <li>Any services used by this bundle must be released.
+     * <li>Any listeners registered by this bundle must be removed.
+     *
+     * <li>If this bundle's state is <code>UNINSTALLED</code>, because this
+     * bundle was uninstalled while the <code>BundleActivator.stop</code>
+     * method was running, a <code>BundleException</code> must be thrown.
+     *
+     * <li>This bundle's state is set to <code>RESOLVED</code>.
+     *
+     * <li>A bundle event of type {@link BundleEvent#STOPPED} is fired.
+     * </ol>
+     *
+     * <b>Preconditions </b>
+     * <ul>
+     * <li><code>getState()</code> in {<code>ACTIVE</code>}.
+     * </ul>
+     * <b>Postconditions, no exceptions thrown </b>
+     * <ul>
+     * <li>Bundle autostart setting is modified unless the
+     * {@link #STOP_TRANSIENT} option was set.
+     * <li><code>getState()</code> not in {<code>ACTIVE</code>,
+     * <code>STOPPING</code>}.
+     * <li><code>BundleActivator.stop</code> has been called and did not
+     * throw an exception.
+     * </ul>
+     * <b>Postconditions, when an exception is thrown </b>
+     * <ul>
+     * <li>Bundle autostart setting is modified unless the
+     * {@link #STOP_TRANSIENT} option was set.
+     * </ul>
+     *
+     * @param options The options for stoping this bundle. See
+     *        {@link #STOP_TRANSIENT}. The Framework must ignore unrecognized
+     *        options.
+     * @throws BundleException If this bundle's <code>BundleActivator</code>
+     *         threw an exception or this bundle is a fragment.
+     * @throws java.lang.IllegalStateException If this bundle has been
+     *         uninstalled or this bundle tries to change its own state.
+     * @throws java.lang.SecurityException If the caller does not have the
+     *         appropriate <code>AdminPermission[this,EXECUTE]</code>, and
+     *         the Java Runtime Environment supports permissions.
+     * @since 1.4
+     */
+    public void stop(int options);
+
+    /**
+     * Stops this bundle with no options.
+     *
+     * <p>
+     * This method calls <code>stop(0)</code>.
+     *
+     * @throws BundleException If this bundle's <code>BundleActivator</code>
+     *         threw an exception or this bundle is a fragment.
+     * @throws java.lang.IllegalStateException If this bundle has been
+     *         uninstalled or this bundle tries to change its own state.
+     * @throws java.lang.SecurityException If the caller does not have the
+     *         appropriate <code>AdminPermission[this,EXECUTE]</code>, and
+     *         the Java Runtime Environment supports permissions.
+     * @see #start(int)
+     */
+    public void stop();
+
+    /**
+     * Updates this bundle.
+     *
+     * <p>
+     * If this bundle's state is <code>ACTIVE</code>, it must be stopped
+     * before the update and started after the update successfully completes.
+     *
+     * <p>
+     * If this bundle has exported any packages, these packages must not be
+     * updated. Instead, the previous package version must remain exported until
+     * the <code>PackageAdmin.refreshPackages</code> method has been has been
+     * called or the Framework is relaunched.
+     *
+     * <p>
+     * The following steps are required to update a bundle:
+     * <ol>
+     * <li>If this bundle's state is <code>UNINSTALLED</code> then an
+     * <code>IllegalStateException</code> is thrown.
+     *
+     * <li>If this bundle's state is <code>ACTIVE</code>,
+     * <code>STARTING</code> or <code>STOPPING</code>, this bundle is
+     * stopped as described in the <code>Bundle.stop</code> method. If
+     * <code>Bundle.stop</code> throws an exception, the exception is rethrown
+     * terminating the update.
+     *
+     * <li>The download location of the new version of this bundle is
+     * determined from either this bundle's
+     * {@link Constants#BUNDLE_UPDATELOCATION} Manifest header (if available) or
+     * this bundle's original location.
+     *
+     * <li>The location is interpreted in an implementation dependent manner,
+     * typically as a URL, and the new version of this bundle is obtained from
+     * this location.
+     *
+     * <li>The new version of this bundle is installed. If the Framework is
+     * unable to install the new version of this bundle, the original version of
+     * this bundle must be restored and a <code>BundleException</code> must be
+     * thrown after completion of the remaining steps.
+     *
+     * <li>If this bundle has declared an Bundle-RequiredExecutionEnvironment
+     * header, then the listed execution environments must be verified against
+     * the installed execution environments. If they do not all match, the
+     * original version of this bundle must be restored and a
+     * <code>BundleException</code> must be thrown after completion of the
+     * remaining steps.
+     *
+     * <li>This bundle's state is set to <code>INSTALLED</code>.
+     *
+     * <li>If the new version of this bundle was successfully installed, a
+     * bundle event of type {@link BundleEvent#UPDATED} is fired.
+     *
+     * <li>If this bundle's state was originally <code>ACTIVE</code>, the
+     * updated bundle is started as described in the <code>Bundle.start</code>
+     * method. If <code>Bundle.start</code> throws an exception, a Framework
+     * event of type {@link FrameworkEvent#ERROR} is fired containing the
+     * exception.
+     * </ol>
+     *
+     * <b>Preconditions </b>
+     * <ul>
+     * <li><code>getState()</code> not in {<code>UNINSTALLED</code>}.
+     * </ul>
+     * <b>Postconditions, no exceptions thrown </b>
+     * <ul>
+     * <li><code>getState()</code> in {<code>INSTALLED</code>,
+     * <code>RESOLVED</code>,<code>ACTIVE</code>}.
+     * <li>This bundle has been updated.
+     * </ul>
+     * <b>Postconditions, when an exception is thrown </b>
+     * <ul>
+     * <li><code>getState()</code> in {<code>INSTALLED</code>,
+     * <code>RESOLVED</code>,<code>ACTIVE</code>}.
+     * <li>Original bundle is still used; no update occurred.
+     * </ul>
+     *
+     * @throws BundleException If the update fails.
+     * @throws java.lang.IllegalStateException If this bundle has been
+     *         uninstalled or this bundle tries to change its own state.
+     * @throws java.lang.SecurityException If the caller does not have the
+     *         appropriate <code>AdminPermission[this,LIFECYCLE]</code> for
+     *         both the current bundle and the updated bundle, and the Java
+     *         Runtime Environment supports permissions.
+     * @see #stop()
+     * @see #start()
+     */
+    public void update();
+
+//     /**
+//      * Updates this bundle from an <code>InputStream</code>.
+//      *
+//      * <p>
+//      * This method performs all the steps listed in <code>Bundle.update()</code>,
+//      * except the new version of this bundle must be read from the supplied
+//      * <code>InputStream</code>, rather than a <code>URL</code>.
+//      * <p>
+//      * This method must always close the <code>InputStream</code> when it is
+//      * done, even if an exception is thrown.
+//      *
+//      * @param in The <code>InputStream</code> from which to read the new
+//      *        bundle.
+//      * @throws BundleException If the provided stream cannot be read or the
+//      *         update fails.
+//      * @throws java.lang.IllegalStateException If this bundle has been
+//      *         uninstalled or this bundle tries to change its own state.
+//      * @throws java.lang.SecurityException If the caller does not have the
+//      *         appropriate <code>AdminPermission[this,LIFECYCLE]</code> for
+//      *         both the current bundle and the updated bundle, and the Java
+//      *         Runtime Environment supports permissions.
+//      * @see #update()
+//      */
+//     public void update(InputStream in_);
+//
+//     /**
+//      * Uninstalls this bundle.
+//      *
+//      * <p>
+//      * This method causes the Framework to notify other bundles that this bundle
+//      * is being uninstalled, and then puts this bundle into the
+//      * <code>UNINSTALLED</code> state. The Framework must remove any resources
+//      * related to this bundle that it is able to remove.
+//      *
+//      * <p>
+//      * If this bundle has exported any packages, the Framework must continue to
+//      * make these packages available to their importing bundles until the
+//      * <code>PackageAdmin.refreshPackages</code> method has been called or the
+//      * Framework is relaunched.
+//      *
+//      * <p>
+//      * The following steps are required to uninstall a bundle:
+//      * <ol>
+//      * <li>If this bundle's state is <code>UNINSTALLED</code> then an
+//      * <code>IllegalStateException</code> is thrown.
+//      *
+//      * <li>If this bundle's state is <code>ACTIVE</code>,
+//      * <code>STARTING</code> or <code>STOPPING</code>, this bundle is
+//      * stopped as described in the <code>Bundle.stop</code> method. If
+//      * <code>Bundle.stop</code> throws an exception, a Framework event of type
+//      * {@link FrameworkEvent#ERROR} is fired containing the exception.
+//      *
+//      * <li>This bundle's state is set to <code>UNINSTALLED</code>.
+//      *
+//      * <li>A bundle event of type {@link BundleEvent#UNINSTALLED} is fired.
+//      *
+//      * <li>This bundle and any persistent storage area provided for this bundle
+//      * by the Framework are removed.
+//      * </ol>
+//      *
+//      * <b>Preconditions </b>
+//      * <ul>
+//      * <li><code>getState()</code> not in {<code>UNINSTALLED</code>}.
+//      * </ul>
+//      * <b>Postconditions, no exceptions thrown </b>
+//      * <ul>
+//      * <li><code>getState()</code> in {<code>UNINSTALLED</code>}.
+//      * <li>This bundle has been uninstalled.
+//      * </ul>
+//      * <b>Postconditions, when an exception is thrown </b>
+//      * <ul>
+//      * <li><code>getState()</code> not in {<code>UNINSTALLED</code>}.
+//      * <li>This Bundle has not been uninstalled.
+//      * </ul>
+//      *
+//      * @throws BundleException If the uninstall failed. This can occur if
+//      *         another thread is attempting to change this bundle's state and
+//      *         does not complete in a timely manner.
+//      * @throws java.lang.IllegalStateException If this bundle has been
+//      *         uninstalled or this bundle tries to change its own state.
+//      * @throws java.lang.SecurityException If the caller does not have the
+//      *         appropriate <code>AdminPermission[this,LIFECYCLE]</code>, and
+//      *         the Java Runtime Environment supports permissions.
+//      * @see #stop()
+//      */
+//     public void uninstall();
+//
+//     /**
+//      * Returns this bundle's Manifest headers and values. This method returns
+//      * all the Manifest headers and values from the main section of this
+//      * bundle's Manifest file; that is, all lines prior to the first blank line.
+//      *
+//      * <p>
+//      * Manifest header names are case-insensitive. The methods of the returned
+//      * <code>Dictionary</code> object must operate on header names in a
+//      * case-insensitive manner.
+//      *
+//      * If a Manifest header value starts with &quot;%&quot;, it must be
+//      * localized according to the default locale.
+//      *
+//      * <p>
+//      * For example, the following Manifest headers and values are included if
+//      * they are present in the Manifest file:
+//      *
+//      * <pre>
+//      *     Bundle-Name
+//      *     Bundle-Vendor
+//      *     Bundle-Version
+//      *     Bundle-Description
+//      *     Bundle-DocURL
+//      *     Bundle-ContactAddress
+//      * </pre>
+//      *
+//      * <p>
+//      * This method must continue to return Manifest header information while
+//      * this bundle is in the <code>UNINSTALLED</code> state.
+//      *
+//      * @return A <code>Dictionary</code> object containing this bundle's
+//      *         Manifest headers and values.
+//      *
+//      * @throws java.lang.SecurityException If the caller does not have the
+//      *         appropriate <code>AdminPermission[this,METADATA]</code>, and
+//      *         the Java Runtime Environment supports permissions.
+//      *
+//      * @see Constants#BUNDLE_LOCALIZATION
+//      */
+//     public Dictionary getHeaders();
+//
+//     /**
+//      * Returns this bundle's unique identifier. This bundle is assigned a unique
+//      * identifier by the Framework when it was installed in the OSGi
+//      * environment.
+//      *
+//      * <p>
+//      * A bundle's unique identifier has the following attributes:
+//      * <ul>
+//      * <li>Is unique and persistent.
+//      * <li>Is a <code>long</code>.
+//      * <li>Its value is not reused for another bundle, even after a bundle is
+//      * uninstalled.
+//      * <li>Does not change while a bundle remains installed.
+//      * <li>Does not change when a bundle is updated.
+//      * </ul>
+//      *
+//      * <p>
+//      * This method must continue to return this bundle's unique identifier while
+//      * this bundle is in the <code>UNINSTALLED</code> state.
+//      *
+//      * @return The unique identifier of this bundle.
+//      */
+//     public long getBundleId();
+//
+//     /**
+//      * Returns this bundle's location identifier.
+//      *
+//      * <p>
+//      * The location identifier is the location passed to
+//      * <code>BundleContext.installBundle</code> when a bundle is installed.
+//      * The location identifier does not change while this bundle remains
+//      * installed, even if this bundle is updated.
+//      *
+//      * <p>
+//      * This method must continue to return this bundle's location identifier
+//      * while this bundle is in the <code>UNINSTALLED</code> state.
+//      *
+//      * @return The string representation of this bundle's location identifier.
+//      * @throws java.lang.SecurityException If the caller does not have the
+//      *         appropriate <code>AdminPermission[this,METADATA]</code>, and
+//      *         the Java Runtime Environment supports permissions.
+//      */
+//     public String getLocation();
+//
+//     /**
+//      * Returns this bundle's <code>ServiceReference</code> list for all
+//      * services it has registered or <code>null</code> if this bundle has no
+//      * registered services.
+//      *
+//      * <p>
+//      * If the Java runtime supports permissions, a <code>ServiceReference</code>
+//      * object to a service is included in the returned list only if the caller
+//      * has the <code>ServicePermission</code> to get the service using at
+//      * least one of the named classes the service was registered under.
+//      *
+//      * <p>
+//      * The list is valid at the time of the call to this method, however, as the
+//      * Framework is a very dynamic environment, services can be modified or
+//      * unregistered at anytime.
+//      *
+//      * @return An array of <code>ServiceReference</code> objects or
+//      *         <code>null</code>.
+//      * @throws java.lang.IllegalStateException If this bundle has been
+//      *         uninstalled.
+//      * @see ServiceRegistration
+//      * @see ServiceReference
+//      * @see ServicePermission
+//      */
+//     public ServiceReference[] getRegisteredServices();
+//
+//     /**
+//      * Returns this bundle's <code>ServiceReference</code> list for all
+//      * services it is using or returns <code>null</code> if this bundle is not
+//      * using any services. A bundle is considered to be using a service if its
+//      * use count for that service is greater than zero.
+//      *
+//      * <p>
+//      * If the Java Runtime Environment supports permissions, a
+//      * <code>ServiceReference</code> object to a service is included in the
+//      * returned list only if the caller has the <code>ServicePermission</code>
+//      * to get the service using at least one of the named classes the service
+//      * was registered under.
+//      * <p>
+//      * The list is valid at the time of the call to this method, however, as the
+//      * Framework is a very dynamic environment, services can be modified or
+//      * unregistered at anytime.
+//      *
+//      * @return An array of <code>ServiceReference</code> objects or
+//      *         <code>null</code>.
+//      * @throws java.lang.IllegalStateException If this bundle has been
+//      *         uninstalled.
+//      * @see ServiceReference
+//      * @see ServicePermission
+//      */
+//     public ServiceReference[] getServicesInUse();
+//
+//     /**
+//      * Determines if this bundle has the specified permissions.
+//      *
+//      * <p>
+//      * If the Java Runtime Environment does not support permissions, this method
+//      * always returns <code>true</code>.
+//      * <p>
+//      * <code>permission</code> is of type <code>Object</code> to avoid
+//      * referencing the <code>java.security.Permission</code> class directly.
+//      * This is to allow the Framework to be implemented in Java environments
+//      * which do not support permissions.
+//      *
+//      * <p>
+//      * If the Java Runtime Environment does support permissions, this bundle and
+//      * all its resources including embedded JAR files, belong to the same
+//      * <code>java.security.ProtectionDomain</code>; that is, they must share
+//      * the same set of permissions.
+//      *
+//      * @param permission The permission to verify.
+//      *
+//      * @return <code>true</code> if this bundle has the specified permission
+//      *         or the permissions possessed by this bundle imply the specified
+//      *         permission; <code>false</code> if this bundle does not have the
+//      *         specified permission or <code>permission</code> is not an
+//      *         <code>instanceof</code> <code>java.security.Permission</code>.
+//      *
+//      * @throws java.lang.IllegalStateException If this bundle has been
+//      *         uninstalled.
+//      */
+//     public bool hasPermission(Object permission);
+//
+//     /**
+//      * Find the specified resource from this bundle.
+//      *
+//      * This bundle's class loader is called to search for the specified
+//      * resource. If this bundle's state is <code>INSTALLED</code>, this
+//      * method must attempt to resolve this bundle before attempting to get the
+//      * specified resource. If this bundle cannot be resolved, then only this
+//      * bundle must be searched for the specified resource. Imported packages
+//      * cannot be searched when this bundle has not been resolved. If this bundle
+//      * is a fragment bundle then <code>null</code> is returned.
+//      *
+//      * @param name The name of the resource. See
+//      *        <code>java.lang.ClassLoader.getResource</code> for a description
+//      *        of the format of a resource name.
+//      * @return A URL to the named resource, or <code>null</code> if the
+//      *         resource could not be found or if this bundle is a fragment
+//      *         bundle or if the caller does not have the appropriate
+//      *         <code>AdminPermission[this,RESOURCE]</code>, and the Java
+//      *         Runtime Environment supports permissions.
+//      *
+//      * @since 1.1
+//      * @throws java.lang.IllegalStateException If this bundle has been
+//      *         uninstalled.
+//      * @see #getEntry
+//      * @see #findEntries
+//      */
+//     public URL getResource(String name);
+//
+//     /**
+//      * Returns this bundle's Manifest headers and values localized to the
+//      * specified locale.
+//      *
+//      * <p>
+//      * This method performs the same function as
+//      * <code>Bundle.getHeaders()</code> except the manifest header values are
+//      * localized to the specified locale.
+//      *
+//      * <p>
+//      * If a Manifest header value starts with &quot;%&quot;, it must be
+//      * localized according to the specified locale. If a locale is specified and
+//      * cannot be found, then the header values must be returned using the
+//      * default locale. Localizations are searched for in the following order:
+//      *
+//      * <pre>
+//      *   bn + "_" + Ls + "_" + Cs + "_" + Vs
+//      *   bn + "_" + Ls + "_" + Cs
+//      *   bn + "_" + Ls
+//      *   bn + "_" + Ld + "_" + Cd + "_" + Vd
+//      *   bn + "_" + Ld + "_" + Cd
+//      *   bn + "_" + Ld
+//      *     bn
+//      * </pre>
+//      *
+//      * Where <code>bn</code> is this bundle's localization basename,
+//      * <code>Ls</code>, <code>Cs</code> and <code>Vs</code> are the
+//      * specified locale (language, country, variant) and <code>Ld</code>,
+//      * <code>Cd</code> and <code>Vd</code> are the default locale (language,
+//      * country, variant).
+//      *
+//      * If <code>null</code> is specified as the locale string, the header
+//      * values must be localized using the default locale. If the empty string
+//      * (&quot;&quot;) is specified as the locale string, the header values must
+//      * not be localized and the raw (unlocalized) header values, including any
+//      * leading &quot;%&quot;, must be returned.
+//      *
+//      * <p>
+//      * This method must continue to return Manifest header information while
+//      * this bundle is in the <code>UNINSTALLED</code> state, however the
+//      * header values must only be available in the raw and default locale
+//      * values.
+//      *
+//      * @param locale The locale name into which the header values are to be
+//      *        localized. If the specified locale is <code>null</code> then the
+//      *        locale returned by <code>java.util.Locale.getDefault</code> is
+//      *        used. If the specified locale is the empty string, this method
+//      *        will return the raw (unlocalized) manifest headers including any
+//      *        leading &quot;%&quot;.
+//      * @return A <code>Dictionary</code> object containing this bundle's
+//      *         Manifest headers and values.
+//      *
+//      * @throws java.lang.SecurityException If the caller does not have the
+//      *         appropriate <code>AdminPermission[this,METADATA]</code>, and
+//      *         the Java Runtime Environment supports permissions.
+//      *
+//      * @see #getHeaders()
+//      * @see Constants#BUNDLE_LOCALIZATION
+//      * @since 1.3
+//      */
+//     public Dictionary getHeaders(String locale);
+//
+//     /**
+//      * Returns the symbolic name of this bundle as specified by its
+//      * <code>Bundle-SymbolicName</code> manifest header. The name must be
+//      * unique, it is recommended to use a reverse domain name naming convention
+//      * like that used for java packages. If this bundle does not have a
+//      * specified symbolic name then <code>null</code> is returned.
+//      *
+//      * <p>
+//      * This method must continue to return this bundle's symbolic name while
+//      * this bundle is in the <code>UNINSTALLED</code> state.
+//      *
+//      * @return The symbolic name of this bundle.
+//      * @since 1.3
+//      */
+//     public String getSymbolicName();
+//
+//     /**
+//      * Loads the specified class using this bundle's classloader.
+//      *
+//      * <p>
+//      * If this bundle is a fragment bundle then this method must throw a
+//      * <code>ClassNotFoundException</code>.
+//      *
+//      * <p>
+//      * If this bundle's state is <code>INSTALLED</code>, this method must
+//      * attempt to resolve this bundle before attempting to load the class.
+//      *
+//      * <p>
+//      * If this bundle cannot be resolved, a Framework event of type
+//      * {@link FrameworkEvent#ERROR} is fired containing a
+//      * <code>BundleException</code> with details of the reason this bundle
+//      * could not be resolved. This method must then throw a
+//      * <code>ClassNotFoundException</code>.
+//      *
+//      * <p>
+//      * If this bundle's state is <code>UNINSTALLED</code>, then an
+//      * <code>IllegalStateException</code> is thrown.
+//      *
+//      * @param name The name of the class to load.
+//      * @return The Class object for the requested class.
+//      * @throws java.lang.ClassNotFoundException If no such class can be found or
+//      *         if this bundle is a fragment bundle or if the caller does not
+//      *         have the appropriate <code>AdminPermission[this,CLASS]</code>,
+//      *         and the Java Runtime Environment supports permissions.
+//      * @throws java.lang.IllegalStateException If this bundle has been
+//      *         uninstalled.
+//      * @since 1.3
+//      */
+//     public Class loadClass(String name) throws ClassNotFoundException;
+//
+//     /**
+//      * Find the specified resources from this bundle.
+//      *
+//      * This bundle's class loader is called to search for the specified
+//      * resources. If this bundle's state is <code>INSTALLED</code>, this
+//      * method must attempt to resolve this bundle before attempting to get the
+//      * specified resources. If this bundle cannot be resolved, then only this
+//      * bundle must be searched for the specified resources. Imported packages
+//      * cannot be searched when a bundle has not been resolved. If this bundle is
+//      * a fragment bundle then <code>null</code> is returned.
+//      *
+//      * @param name The name of the resource. See
+//      *        <code>java.lang.ClassLoader.getResources</code> for a
+//      *        description of the format of a resource name.
+//      * @return An enumeration of URLs to the named resources, or
+//      *         <code>null</code> if the resource could not be found or if this
+//      *         bundle is a fragment bundle or if the caller does not have the
+//      *         appropriate <code>AdminPermission[this,RESOURCE]</code>, and
+//      *         the Java Runtime Environment supports permissions.
+//      *
+//      * @since 1.3
+//      * @throws java.lang.IllegalStateException If this bundle has been
+//      *         uninstalled.
+//      * @throws java.io.IOException If there is an I/O error.
+//      */
+//     public Enumeration getResources(String name) throws IOException;
+//
+//     /**
+//      * Returns an Enumeration of all the paths (<code>String</code> objects)
+//      * to entries within this bundle whose longest sub-path matches the
+//      * specified path. This bundle's classloader is not used to search for
+//      * entries. Only the contents of this bundle are searched.
+//      * <p>
+//      * The specified path is always relative to the root of this bundle and may
+//      * begin with a &quot;/&quot;. A path value of &quot;/&quot; indicates the
+//      * root of this bundle.
+//      * <p>
+//      * Returned paths indicating subdirectory paths end with a &quot;/&quot;.
+//      * The returned paths are all relative to the root of this bundle and must
+//      * not begin with &quot;/&quot;.
+//      *
+//      * @param path The path name for which to return entry paths.
+//      * @return An Enumeration of the entry paths (<code>String</code>
+//      *         objects) or <code>null</code> if no entry could be found or if
+//      *         the caller does not have the appropriate
+//      *         <code>AdminPermission[this,RESOURCE]</code> and the Java
+//      *         Runtime Environment supports permissions.
+//      * @throws java.lang.IllegalStateException If this bundle has been
+//      *         uninstalled.
+//      * @since 1.3
+//      */
+//     public Enumeration getEntryPaths(String path);
+//
+//     /**
+//      * Returns a URL to the entry at the specified path in this bundle. This
+//      * bundle's classloader is not used to search for the entry. Only the
+//      * contents of this bundle are searched for the entry.
+//      * <p>
+//      * The specified path is always relative to the root of this bundle and may
+//      * begin with &quot;/&quot;. A path value of &quot;/&quot; indicates the
+//      * root of this bundle.
+//      *
+//      * @param path The path name of the entry.
+//      * @return A URL to the entry, or <code>null</code> if no entry could be
+//      *         found or if the caller does not have the appropriate
+//      *         <code>AdminPermission[this,RESOURCE]</code> and the Java
+//      *         Runtime Environment supports permissions.
+//      *
+//      * @throws java.lang.IllegalStateException If this bundle has been
+//      *         uninstalled.
+//      * @since 1.3
+//      */
+//     public URL getEntry(String path);
+//
+//     /**
+//      * Returns the time when this bundle was last modified. A bundle is
+//      * considered to be modified when it is installed, updated or uninstalled.
+//      *
+//      * <p>
+//      * The time value is the number of milliseconds since January 1, 1970,
+//      * 00:00:00 GMT.
+//      *
+//      * @return The time when this bundle was last modified.
+//      * @since 1.3
+//      */
+//     public long getLastModified();
+//
+//     /**
+//      * Returns entries in this bundle and its attached fragments. This bundle's
+//      * classloader is not used to search for entries. Only the contents of this
+//      * bundle and its attached fragments are searched for the specified entries.
+//      *
+//      * If this bundle's state is <code>INSTALLED</code>, this method must
+//      * attempt to resolve this bundle before attempting to find entries.
+//      *
+//      * <p>
+//      * This method is intended to be used to obtain configuration, setup,
+//      * localization and other information from this bundle. This method takes
+//      * into account that the &quot;contents&quot; of this bundle can be extended
+//      * with fragments. This &quot;bundle space&quot; is not a namespace with
+//      * unique members; the same entry name can be present multiple times. This
+//      * method therefore returns an enumeration of URL objects. These URLs can
+//      * come from different JARs but have the same path name. This method can
+//      * either return only entries in the specified path or recurse into
+//      * subdirectories returning entries in the directory tree beginning at the
+//      * specified path. Fragments can be attached after this bundle is resolved,
+//      * possibly changing the set of URLs returned by this method. If this bundle
+//      * is not resolved, only the entries in the JAR file of this bundle are
+//      * returned.
+//      * <p>
+//      * Examples:
+//      *
+//      * <pre>
+//      * // List all XML files in the OSGI-INF directory and below
+//      * Enumeration e = b.findEntries(&quot;OSGI-INF&quot;, &quot;*.xml&quot;, true);
+//      *
+//      * // Find a specific localization file
+//      * Enumeration e = b.findEntries(&quot;OSGI-INF/l10n&quot;,
+//      *                               &quot;bundle_nl_DU.properties&quot;,
+//      *                               false);
+//      * if (e.hasMoreElements())
+//      *  return (URL) e.nextElement();
+//      * </pre>
+//      *
+//      * @param path The path name in which to look. The path is always relative
+//      *        to the root of this bundle and may begin with &quot;/&quot;. A
+//      *        path value of &quot;/&quot; indicates the root of this bundle.
+//      * @param filePattern The file name pattern for selecting entries in the
+//      *        specified path. The pattern is only matched against the last
+//      *        element of the entry path and it supports substring matching, as
+//      *        specified in the Filter specification, using the wildcard
+//      *        character (&quot;*&quot;). If null is specified, this is
+//      *        equivalent to &quot;*&quot; and matches all files.
+//      * @param recurse If <code>true</code>, recurse into subdirectories.
+//      *        Otherwise only return entries from the specified path.
+//      * @return An enumeration of URL objects for each matching entry, or
+//      *         <code>null</code> if an entry could not be found or if the
+//      *         caller does not have the appropriate
+//      *         <code>AdminPermission[this,RESOURCE]</code>, and the Java
+//      *         Runtime Environment supports permissions. The URLs are sorted
+//      *         such that entries from this bundle are returned first followed by
+//      *         the entries from attached fragments in ascending bundle id order.
+//      *         If this bundle is a fragment, then only matching entries in this
+//      *         fragment are returned.
+//      * @since 1.3
+//      */
+//     public Enumeration findEntries(String path, String filePattern,
+//             bool recurse);
+//
+//     /**
+//      * Returns this bundle's {@link BundleContext}. The returned
+//      * <code>BundleContext</code> can be used by the caller to act on behalf
+//      * of this bundle.
+//      *
+//      * <p>
+//      * If this bundle is not in the {@link #STARTING}, {@link #ACTIVE}, or
+//      * {@link #STOPPING} states or this bundle is a fragment bundle, then this
+//      * bundle has no valid <code>BundleContext</code>. This method will
+//      * return <code>null</code> if this bundle has no valid
+//      * <code>BundleContext</code>.
+//      *
+//      * @return A <code>BundleContext</code> for this bundle or
+//      *         <code>null</code> if this bundle has no valid
+//      *         <code>BundleContext</code>.
+//      * @throws java.lang.SecurityException If the caller does not have the
+//      *         appropriate <code>AdminPermission[this,CONTEXT]</code>, and
+//      *         the Java Runtime Environment supports permissions.
+//      * @since 1.4
+//      */
+//     public BundleContext getBundleContext();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.osgi/supplement/src/org/osgi/util/NLS.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,446 @@
+/*******************************************************************************
+ * 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.osgi.util.NLS;
+
+import java.lang.all;
+
+// import java.io.IOException;
+// import java.io.InputStream;
+// import java.lang.reflect.Field;
+// import java.lang.reflect.Modifier;
+// import java.security.AccessController;
+// import java.security.PrivilegedAction;
+// import java.util.ArrayList;
+// import java.util.HashMap;
+// import java.util.Locale;
+// import java.util.Map;
+// import java.util.Properties;
+//
+// import org.eclipse.osgi.framework.debug.Debug;
+// import org.eclipse.osgi.framework.log.FrameworkLog;
+// import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+
+/**
+ * Common superclass for all message bundle classes.  Provides convenience
+ * methods for manipulating messages.
+ * <p>
+ * The <code>#bind</code> methods perform string substitution and should be considered a
+ * convenience and <em>not</em> a full substitute replacement for <code>MessageFormat#format</code>
+ * method calls.
+ * </p>
+ * <p>
+ * Text appearing within curly braces in the given message, will be interpreted
+ * as a numeric index to the corresponding substitution object in the given array. Calling
+ * the <code>#bind</code> methods with text that does not map to an integer will result in an
+ * {@link IllegalArgumentException}.
+ * </p>
+ * <p>
+ * Text appearing within single quotes is treated as a literal. A single quote is escaped by
+ * a preceeding single quote.
+ * </p>
+ * <p>
+ * Clients who wish to use the full substitution power of the <code>MessageFormat</code> class should
+ * call that class directly and not use these <code>#bind</code> methods.
+ * </p>
+ * <p>
+ * Clients may subclass this type.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class NLS {
+
+//     private static final Object[] EMPTY_ARGS = new Object[0];
+//     private static final String EXTENSION = ".properties"; //$NON-NLS-1$
+//     private static String[] nlSuffixes;
+//     /*
+//      * NOTE do not change the name of this field; it is set by the Framework using reflection
+//      */
+//     private static FrameworkLog frameworkLog;
+//
+//     static final int SEVERITY_ERROR = 0x04;
+//     static final int SEVERITY_WARNING = 0x02;
+//     /*
+//      * This object is assigned to the value of a field map to indicate
+//      * that a translated message has already been assigned to that field.
+//      */
+//     static final Object ASSIGNED = new Object();
+//
+//     /**
+//      * Creates a new NLS instance.
+//      */
+//     protected NLS() {
+//         super();
+//     }
+
+    /**
+     * Bind the given message's substitution locations with the given string value.
+     *
+     * @param message the message to be manipulated
+     * @param binding the object to be inserted into the message
+     * @return the manipulated String
+     * @throws IllegalArgumentException if the text appearing within curly braces in the given message does not map to an integer
+     */
+    public static String bind(String message, Object binding) {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+//         return internalBind(message, null, String.valueOf(binding), null);
+    }
+    public static String bind(String message, String binding) {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+//         return internalBind(message, null, String.valueOf(binding), null);
+    }
+
+    /**
+     * Bind the given message's substitution locations with the given string values.
+     *
+     * @param message the message to be manipulated
+     * @param binding1 An object to be inserted into the message
+     * @param binding2 A second object to be inserted into the message
+     * @return the manipulated String
+     * @throws IllegalArgumentException if the text appearing within curly braces in the given message does not map to an integer
+     */
+    public static String bind(String message, Object binding1, Object binding2) {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+//         return internalBind(message, null, String.valueOf(binding1), String.valueOf(binding2));
+    }
+    public static String bind(String message, String binding1, String binding2) {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+//         return internalBind(message, null, String.valueOf(binding1), String.valueOf(binding2));
+    }
+
+    /**
+     * Bind the given message's substitution locations with the given string values.
+     *
+     * @param message the message to be manipulated
+     * @param bindings An array of objects to be inserted into the message
+     * @return the manipulated String
+     * @throws IllegalArgumentException if the text appearing within curly braces in the given message does not map to an integer
+     */
+    public static String bind(String message, Object[] bindings) {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+//         return internalBind(message, bindings, null, null);
+    }
+    public static String bind(String message, String[] bindings) {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+//         return internalBind(message, bindings, null, null);
+    }
+
+//     /**
+//      * Initialize the given class with the values from the specified message bundle.
+//      *
+//      * @param bundleName fully qualified path of the class name
+//      * @param clazz the class where the constants will exist
+//      */
+//     public static void initializeMessages(final String bundleName, final Class clazz) {
+//         if (System.getSecurityManager() is null) {
+//             load(bundleName, clazz);
+//             return;
+//         }
+//         AccessController.doPrivileged(new PrivilegedAction() {
+//             public Object run() {
+//                 load(bundleName, clazz);
+//                 return null;
+//             }
+//         });
+//     }
+//
+//     /*
+//      * Perform the string substitution on the given message with the specified args.
+//      * See the class comment for exact details.
+//      */
+//     private static String internalBind(String message, Object[] args, String argZero, String argOne) {
+//         if (message is null)
+//             return "No message available."; //$NON-NLS-1$
+//         if (args is null || args.length is 0)
+//             args = EMPTY_ARGS;
+//
+//         int length = message.length();
+//         //estimate correct size of string buffer to avoid growth
+//         int bufLen = length + (args.length * 5);
+//         if (argZero !is null)
+//             bufLen += argZero.length() - 3;
+//         if (argOne !is null)
+//             bufLen += argOne.length() - 3;
+//         StringBuffer buffer = new StringBuffer(bufLen < 0 ? 0 : bufLen);
+//         for (int i = 0; i < length; i++) {
+//             char c = message.charAt(i);
+//             switch (c) {
+//                 case '{' :
+//                     int index = message.indexOf('}', i);
+//                     // if we don't have a matching closing brace then...
+//                     if (index is -1) {
+//                         buffer.append(c);
+//                         break;
+//                     }
+//                     i++;
+//                     if (i >= length) {
+//                         buffer.append(c);
+//                         break;
+//                     }
+//                     // look for a substitution
+//                     int number = -1;
+//                     try {
+//                         number = Integer.parseInt(message.substring(i, index));
+//                     } catch (NumberFormatException e) {
+//                         throw new IllegalArgumentException();
+//                     }
+//                     if (number is 0 && argZero !is null)
+//                         buffer.append(argZero);
+//                     else if (number is 1 && argOne !is null)
+//                         buffer.append(argOne);
+//                     else {
+//                         if (number >= args.length || number < 0) {
+//                             buffer.append("<missing argument>"); //$NON-NLS-1$
+//                             i = index;
+//                             break;
+//                         }
+//                         buffer.append(args[number]);
+//                     }
+//                     i = index;
+//                     break;
+//                 case '\'' :
+//                     // if a single quote is the last char on the line then skip it
+//                     int nextIndex = i + 1;
+//                     if (nextIndex >= length) {
+//                         buffer.append(c);
+//                         break;
+//                     }
+//                     char next = message.charAt(nextIndex);
+//                     // if the next char is another single quote then write out one
+//                     if (next is '\'') {
+//                         i++;
+//                         buffer.append(c);
+//                         break;
+//                     }
+//                     // otherwise we want to read until we get to the next single quote
+//                     index = message.indexOf('\'', nextIndex);
+//                     // if there are no more in the string, then skip it
+//                     if (index is -1) {
+//                         buffer.append(c);
+//                         break;
+//                     }
+//                     // otherwise write out the chars inside the quotes
+//                     buffer.append(message.substring(nextIndex, index));
+//                     i = index;
+//                     break;
+//                 default :
+//                     buffer.append(c);
+//             }
+//         }
+//         return buffer.toString();
+//     }
+//
+//     /*
+//      * Build an array of property files to search.  The returned array contains
+//      * the property fields in order from most specific to most generic.
+//      * So, in the FR_fr locale, it will return file_fr_FR.properties, then
+//      * file_fr.properties, and finally file.properties.
+//      */
+//     private static String[] buildVariants(String root) {
+//         if (nlSuffixes is null) {
+//             //build list of suffixes for loading resource bundles
+//             String nl = Locale.getDefault().toString();
+//             ArrayList result = new ArrayList(4);
+//             int lastSeparator;
+//             while (true) {
+//                 result.add('_' + nl + EXTENSION);
+//                 lastSeparator = nl.lastIndexOf('_');
+//                 if (lastSeparator is -1)
+//                     break;
+//                 nl = nl.substring(0, lastSeparator);
+//             }
+//             //add the empty suffix last (most general)
+//             result.add(EXTENSION);
+//             nlSuffixes = (String[]) result.toArray(new String[result.size()]);
+//         }
+//         root = root.replace('.', '/');
+//         String[] variants = new String[nlSuffixes.length];
+//         for (int i = 0; i < variants.length; i++)
+//             variants[i] = root + nlSuffixes[i];
+//         return variants;
+//     }
+//
+//     private static void computeMissingMessages(String bundleName, Class clazz, Map fieldMap, Field[] fieldArray, bool isAccessible) {
+//         // iterate over the fields in the class to make sure that there aren't any empty ones
+//         final int MOD_EXPECTED = Modifier.PUBLIC | Modifier.STATIC;
+//         final int MOD_MASK = MOD_EXPECTED | Modifier.FINAL;
+//         final int numFields = fieldArray.length;
+//         for (int i = 0; i < numFields; i++) {
+//             Field field = fieldArray[i];
+//             if ((field.getModifiers() & MOD_MASK) !is MOD_EXPECTED)
+//                 continue;
+//             //if the field has a a value assigned, there is nothing to do
+//             if (fieldMap.get(field.getName()) is ASSIGNED)
+//                 continue;
+//             try {
+//                 // Set a value for this empty field. We should never get an exception here because
+//                 // we know we have a public static non-final field. If we do get an exception, silently
+//                 // log it and continue. This means that the field will (most likely) be un-initialized and
+//                 // will fail later in the code and if so then we will see both the NPE and this error.
+//                 String value = "NLS missing message: " + field.getName() + " in: " + bundleName; //$NON-NLS-1$ //$NON-NLS-2$
+//                 if (Debug.DEBUG_MESSAGE_BUNDLES)
+//                     System.out.println(value);
+//                 log(SEVERITY_WARNING, value, null);
+//                 if (!isAccessible)
+//                     field.setAccessible(true);
+//                 field.set(null, value);
+//             } catch (Exception e) {
+//                 log(SEVERITY_ERROR, "Error setting the missing message value for: " + field.getName(), e); //$NON-NLS-1$
+//             }
+//         }
+//     }
+//
+//     /*
+//      * Load the given resource bundle using the specified class loader.
+//      */
+//     static void load(final String bundleName, Class clazz) {
+//         long start = System.currentTimeMillis();
+//         final Field[] fieldArray = clazz.getDeclaredFields();
+//         ClassLoader loader = clazz.getClassLoader();
+//
+//         bool isAccessible = (clazz.getModifiers() & Modifier.PUBLIC) !is 0;
+//
+//         //build a map of field names to Field objects
+//         final int len = fieldArray.length;
+//         Map fields = new HashMap(len * 2);
+//         for (int i = 0; i < len; i++)
+//             fields.put(fieldArray[i].getName(), fieldArray[i]);
+//
+//         // search the variants from most specific to most general, since
+//         // the MessagesProperties.put method will mark assigned fields
+//         // to prevent them from being assigned twice
+//         final String[] variants = buildVariants(bundleName);
+//         for (int i = 0; i < variants.length; i++) {
+//             // loader is null if we're launched off the Java boot classpath
+//             final InputStream input = loader is null ? ClassLoader.getSystemResourceAsStream(variants[i]) : loader.getResourceAsStream(variants[i]);
+//             if (input is null)
+//                 continue;
+//             try {
+//                 final MessagesProperties properties = new MessagesProperties(fields, bundleName, isAccessible);
+//                 properties.load(input);
+//             } catch (IOException e) {
+//                 log(SEVERITY_ERROR, "Error loading " + variants[i], e); //$NON-NLS-1$
+//             } finally {
+//                 if (input !is null)
+//                     try {
+//                         input.close();
+//                     } catch (IOException e) {
+//                         // ignore
+//                     }
+//             }
+//         }
+//         computeMissingMessages(bundleName, clazz, fields, fieldArray, isAccessible);
+//         if (Debug.DEBUG_MESSAGE_BUNDLES)
+//             System.out.println("Time to load message bundle: " + bundleName + " was " + (System.currentTimeMillis() - start) + "ms."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+//     }
+//
+//     /*
+//      * The method adds a log entry based on the error message and exception.
+//      * The output is written to the System.err.
+//      *
+//      * This method is only expected to be called if there is a problem in
+//      * the NLS mechanism. As a result, translation facility is not available
+//      * here and messages coming out of this log are generally not translated.
+//      *
+//      * @param severity - severity of the message (SEVERITY_ERROR or SEVERITY_WARNING)
+//      * @param message - message to log
+//      * @param e - exception to log
+//      */
+//     static void log(int severity, String message, Exception e) {
+//         if (frameworkLog !is null) {
+//             frameworkLog.log(new FrameworkLogEntry("org.eclipse.osgi", severity, 1, message, 0, e, null)); //$NON-NLS-1$
+//             return;
+//         }
+//         String statusMsg;
+//         switch (severity) {
+//             case SEVERITY_ERROR :
+//                 statusMsg = "Error: "; //$NON-NLS-1$
+//                 break;
+//             case SEVERITY_WARNING :
+//                 // intentionally fall through:
+//             default :
+//                 statusMsg = "Warning: "; //$NON-NLS-1$
+//         }
+//         if (message !is null)
+//             statusMsg += message;
+//         if (e !is null)
+//             statusMsg += ": " + e.getMessage(); //$NON-NLS-1$
+//         System.err.println(statusMsg);
+//         if (e !is null)
+//             e.printStackTrace();
+//     }
+//
+//     /*
+//      * Class which sub-classes java.util.Properties and uses the #put method
+//      * to set field values rather than storing the values in the table.
+//      */
+//     private static class MessagesProperties extends Properties {
+//
+//         private static final int MOD_EXPECTED = Modifier.PUBLIC | Modifier.STATIC;
+//         private static final int MOD_MASK = MOD_EXPECTED | Modifier.FINAL;
+//         private static final long serialVersionUID = 1L;
+//
+//         private final String bundleName;
+//         private final Map fields;
+//         private final bool isAccessible;
+//
+//         public MessagesProperties(Map fieldMap, String bundleName, bool isAccessible) {
+//             super();
+//             this.fields = fieldMap;
+//             this.bundleName = bundleName;
+//             this.isAccessible = isAccessible;
+//         }
+//
+//         /* (non-Javadoc)
+//          * @see java.util.Hashtable#put(java.lang.Object, java.lang.Object)
+//          */
+//         public synchronized Object put(Object key, Object value) {
+//             Object fieldObject = fields.put(key, ASSIGNED);
+//             // if already assigned, there is nothing to do
+//             if (fieldObject is ASSIGNED)
+//                 return null;
+//             if (fieldObject is null) {
+//                 final String msg = "NLS unused message: " + key + " in: " + bundleName;//$NON-NLS-1$ //$NON-NLS-2$
+//                 if (Debug.DEBUG_MESSAGE_BUNDLES)
+//                     System.out.println(msg);
+//                 log(SEVERITY_WARNING, msg, null);
+//                 return null;
+//             }
+//             final Field field = (Field) fieldObject;
+//             //can only set value of public static non-final fields
+//             if ((field.getModifiers() & MOD_MASK) !is MOD_EXPECTED)
+//                 return null;
+//             try {
+//                 // Check to see if we are allowed to modify the field. If we aren't (for instance
+//                 // if the class is not public) then change the accessible attribute of the field
+//                 // before trying to set the value.
+//                 if (!isAccessible)
+//                     field.setAccessible(true);
+//                 // Set the value into the field. We should never get an exception here because
+//                 // we know we have a public static non-final field. If we do get an exception, silently
+//                 // log it and continue. This means that the field will (most likely) be un-initialized and
+//                 // will fail later in the code and if so then we will see both the NPE and this error.
+//                 field.set(null, value);
+//             } catch (Exception e) {
+//                 log(SEVERITY_ERROR, "Exception setting field value.", e); //$NON-NLS-1$
+//             }
+//             return null;
+//         }
+//     }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/AbstractDocument.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1933 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.AbstractDocument;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import org.eclipse.dwtxhelper.regex;
+import tango.text.convert.Format;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+
+
+/**
+ * Abstract default implementation of <code>IDocument</code> and its extension
+ * interfaces {@link org.eclipse.jface.text.IDocumentExtension},
+ * {@link org.eclipse.jface.text.IDocumentExtension2},
+ * {@link org.eclipse.jface.text.IDocumentExtension3},
+ * {@link org.eclipse.jface.text.IDocumentExtension4}, as well as
+ * {@link org.eclipse.jface.text.IRepairableDocument}.
+ * <p>
+ *
+ * An <code>AbstractDocument</code> supports the following implementation
+ * plug-ins:
+ * <ul>
+ * <li>a text store implementing {@link org.eclipse.jface.text.ITextStore} for
+ *     storing and managing the document's content,</li>
+ * <li>a line tracker implementing {@link org.eclipse.jface.text.ILineTracker}
+ *     to map character positions to line numbers and vice versa</li>
+ * </ul>
+ * The document can dynamically change the text store when switching between
+ * sequential rewrite mode and normal mode.
+ * <p>
+ *
+ * This class must be subclassed. Subclasses must configure which implementation
+ * plug-ins the document instance should use. Subclasses are not intended to
+ * overwrite existing methods.
+ *
+ * @see org.eclipse.jface.text.ITextStore
+ * @see org.eclipse.jface.text.ILineTracker
+ */
+public abstract class AbstractDocument : IDocument, IDocumentExtension, IDocumentExtension2, IDocumentExtension3, IDocumentExtension4, IRepairableDocument, IRepairableDocumentExtension {
+
+    /**
+     * Tells whether this class is in debug mode.
+     * @since 3.1
+     */
+    private static const bool DEBUG= false;
+
+
+    /**
+     * Inner class to bundle a registered post notification replace operation together with its
+     * owner.
+     *
+     * @since 2.0
+     */
+    static private class RegisteredReplace {
+        /** The owner of this replace operation. */
+        IDocumentListener fOwner;
+        /** The replace operation */
+        IDocumentExtension.IReplace fReplace;
+
+        /**
+         * Creates a new bundle object.
+         * @param owner the document listener owning the replace operation
+         * @param replace the replace operation
+         */
+        this(IDocumentListener owner, IDocumentExtension.IReplace replace) {
+            fOwner= owner;
+            fReplace= replace;
+        }
+    }
+
+
+    /** The document's text store */
+    private ITextStore   fStore;
+    /** The document's line tracker */
+    private ILineTracker fTracker;
+    /** The registered document listeners */
+    private ListenerList fDocumentListeners;
+    /** The registered pre-notified document listeners */
+    private ListenerList fPrenotifiedDocumentListeners;
+    /** The registered document partitioning listeners */
+    private ListenerList fDocumentPartitioningListeners;
+    /** All positions managed by the document ordered by their start positions. */
+    private Map fPositions;
+    /**
+     * All positions managed by the document ordered by there end positions.
+     * @since 3.4
+     */
+    private Map fEndPositions;
+    /** All registered document position updaters */
+    private List fPositionUpdaters;
+    /**
+     * The list of post notification changes
+     * @since 2.0
+     */
+    private List fPostNotificationChanges;
+    /**
+     * The reentrance count for post notification changes.
+     * @since 2.0
+     */
+    private int fReentranceCount= 0;
+    /**
+     * Indicates whether post notification change processing has been stopped.
+     * @since 2.0
+     */
+    private int fStoppedCount= 0;
+    /**
+     * Indicates whether the registration of post notification changes should be ignored.
+     * @since 2.1
+     */
+    private bool fAcceptPostNotificationReplaces= true;
+    /**
+     * Indicates whether the notification of listeners has been stopped.
+     * @since 2.1
+     */
+    private int fStoppedListenerNotification= 0;
+    /**
+     * The document event to be sent after listener notification has been resumed.
+     * @since 2.1
+     */
+    private DocumentEvent fDeferredDocumentEvent;
+    /**
+     * The registered document partitioners.
+     * @since 3.0
+     */
+    private Map fDocumentPartitioners;
+    /**
+     * The partitioning changed event.
+     * @since 3.0
+     */
+    private DocumentPartitioningChangedEvent fDocumentPartitioningChangedEvent;
+    /**
+     * The find/replace document adapter.
+     * @since 3.0
+     */
+    private FindReplaceDocumentAdapter fFindReplaceDocumentAdapter;
+    /**
+     * The active document rewrite session.
+     * @since 3.1
+     */
+    private DocumentRewriteSession fDocumentRewriteSession;
+    /**
+     * The registered document rewrite session listeners.
+     * @since 3.1
+     */
+    private List fDocumentRewriteSessionListeners;
+    /**
+     * The current modification stamp.
+     * @since 3.1
+     */
+    private long fModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+    /**
+     * Keeps track of next modification stamp.
+     * @since 3.1.1
+     */
+    private long fNextModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+    /**
+     * This document's default line delimiter.
+     * @since 3.1
+     */
+    private String fInitialLineDelimiter;
+
+
+    /**
+     * The default constructor does not perform any configuration
+     * but leaves it to the clients who must first initialize the
+     * implementation plug-ins and then call <code>completeInitialization</code>.
+     * Results in the construction of an empty document.
+     */
+    protected this() {
+        fModificationStamp= getNextModificationStamp();
+    }
+
+
+    /**
+     * Returns the document's text store. Assumes that the
+     * document has been initialized with a text store.
+     *
+     * @return the document's text store
+     */
+    protected ITextStore getStore() {
+        Assert.isNotNull(cast(Object)fStore);
+        return fStore;
+    }
+
+    /**
+     * Returns the document's line tracker. Assumes that the
+     * document has been initialized with a line tracker.
+     *
+     * @return the document's line tracker
+     */
+    protected ILineTracker getTracker() {
+        Assert.isNotNull(cast(Object)fTracker);
+        return fTracker;
+    }
+
+    /**
+     * Returns the document's document listeners.
+     *
+     * @return the document's document listeners
+     */
+    protected List getDocumentListeners() {
+        return Arrays.asList(fDocumentListeners.getListeners());
+    }
+
+    /**
+     * Returns the document's partitioning listeners.
+     *
+     * @return the document's partitioning listeners
+     */
+    protected List getDocumentPartitioningListeners() {
+        return Arrays.asList(fDocumentPartitioningListeners.getListeners());
+    }
+
+    /**
+     * Returns all positions managed by the document grouped by category.
+     *
+     * @return the document's positions
+     */
+    protected Map getDocumentManagedPositions() {
+        return fPositions;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getDocumentPartitioner()
+     */
+    public IDocumentPartitioner getDocumentPartitioner() {
+        return getDocumentPartitioner(DEFAULT_PARTITIONING);
+    }
+
+
+
+    //--- implementation configuration interface ------------
+
+    /**
+     * Sets the document's text store.
+     * Must be called at the beginning of the constructor.
+     *
+     * @param store the document's text store
+     */
+    protected void setTextStore(ITextStore store) {
+        fStore= store;
+    }
+
+    /**
+     * Sets the document's line tracker.
+     * Must be called at the beginning of the constructor.
+     *
+     * @param tracker the document's line tracker
+     */
+    protected void setLineTracker(ILineTracker tracker) {
+        fTracker= tracker;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#setDocumentPartitioner(org.eclipse.jface.text.IDocumentPartitioner)
+     */
+    public void setDocumentPartitioner(IDocumentPartitioner partitioner) {
+        setDocumentPartitioner(DEFAULT_PARTITIONING, partitioner);
+    }
+
+    /**
+     * Initializes document listeners, positions, and position updaters.
+     * Must be called inside the constructor after the implementation plug-ins
+     * have been set.
+     */
+    protected void completeInitialization() {
+
+        fPositions= new HashMap();
+        fEndPositions= new HashMap();
+        fPositionUpdaters= new ArrayList();
+        fDocumentListeners= new ListenerList(ListenerList.IDENTITY);
+        fPrenotifiedDocumentListeners= new ListenerList(ListenerList.IDENTITY);
+        fDocumentPartitioningListeners= new ListenerList(ListenerList.IDENTITY);
+        fDocumentRewriteSessionListeners= new ArrayList();
+
+        addPositionCategory(DEFAULT_CATEGORY);
+        addPositionUpdater(new DefaultPositionUpdater(DEFAULT_CATEGORY));
+    }
+
+
+    //-------------------------------------------------------
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#addDocumentListener(org.eclipse.jface.text.IDocumentListener)
+     */
+    public void addDocumentListener(IDocumentListener listener) {
+        Assert.isNotNull(cast(Object)listener);
+        fDocumentListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#removeDocumentListener(org.eclipse.jface.text.IDocumentListener)
+     */
+    public void removeDocumentListener(IDocumentListener listener) {
+        Assert.isNotNull(cast(Object)listener);
+        fDocumentListeners.remove(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#addPrenotifiedDocumentListener(org.eclipse.jface.text.IDocumentListener)
+     */
+    public void addPrenotifiedDocumentListener(IDocumentListener listener) {
+        Assert.isNotNull(cast(Object)listener);
+        fPrenotifiedDocumentListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#removePrenotifiedDocumentListener(org.eclipse.jface.text.IDocumentListener)
+     */
+    public void removePrenotifiedDocumentListener(IDocumentListener listener) {
+        Assert.isNotNull(cast(Object)listener);
+        fPrenotifiedDocumentListeners.remove(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#addDocumentPartitioningListener(org.eclipse.jface.text.IDocumentPartitioningListener)
+     */
+    public void addDocumentPartitioningListener(IDocumentPartitioningListener listener) {
+        Assert.isNotNull(cast(Object)listener);
+        fDocumentPartitioningListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#removeDocumentPartitioningListener(org.eclipse.jface.text.IDocumentPartitioningListener)
+     */
+    public void removeDocumentPartitioningListener(IDocumentPartitioningListener listener) {
+        Assert.isNotNull(cast(Object)listener);
+        fDocumentPartitioningListeners.remove(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#addPosition(java.lang.String, org.eclipse.jface.text.Position)
+     */
+    public void addPosition(String category, Position position)  {
+
+        if ((0 > position.offset) || (0 > position.length) || (position.offset + position.length > getLength()))
+            throw new BadLocationException();
+
+        if (category is null)
+            throw new BadPositionCategoryException();
+
+        List list= cast(List) fPositions.get(category);
+        if (list is null)
+            throw new BadPositionCategoryException();
+        list.add(computeIndexInPositionList(list, position.offset), position);
+
+        List endPositions= cast(List) fEndPositions.get(category);
+        if (endPositions is null)
+            throw new BadPositionCategoryException();
+        endPositions.add(computeIndexInPositionList(endPositions, position.offset + position.length - 1, false), position);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#addPosition(org.eclipse.jface.text.Position)
+     */
+    public void addPosition(Position position)  {
+        try {
+            addPosition(DEFAULT_CATEGORY, position);
+        } catch (BadPositionCategoryException e) {
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#addPositionCategory(java.lang.String)
+     */
+    public void addPositionCategory(String category) {
+
+        if (category is null)
+            return;
+
+        if (!containsPositionCategory(category)) {
+            fPositions.put(category, new ArrayList());
+            fEndPositions.put(category, new ArrayList());
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#addPositionUpdater(org.eclipse.jface.text.IPositionUpdater)
+     */
+    public void addPositionUpdater(IPositionUpdater updater) {
+        insertPositionUpdater(updater, fPositionUpdaters.size());
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#containsPosition(java.lang.String, int, int)
+     */
+    public bool containsPosition(String category, int offset, int length) {
+
+        if (category is null)
+            return false;
+
+        List list= cast(List) fPositions.get(category);
+        if (list is null)
+            return false;
+
+        int size= list.size();
+        if (size is 0)
+            return false;
+
+        int index= computeIndexInPositionList(list, offset);
+        if (index < size) {
+            Position p= cast(Position) list.get(index);
+            while (p !is null && p.offset is offset) {
+                if (p.length is length)
+                    return true;
+                ++ index;
+                p= (index < size) ? cast(Position) list.get(index) : null;
+            }
+        }
+
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#containsPositionCategory(java.lang.String)
+     */
+    public bool containsPositionCategory(String category) {
+        if (category !is null)
+            return fPositions.containsKey(category);
+        return false;
+    }
+
+
+    /**
+     * Computes the index in the list of positions at which a position with the given
+     * offset would be inserted. The position is supposed to become the first in this list
+     * of all positions with the same offset.
+     *
+     * @param positions the list in which the index is computed
+     * @param offset the offset for which the index is computed
+     * @return the computed index
+     *
+     * @see IDocument#computeIndexInCategory(String, int)
+     * @deprecated As of 3.4, replaced by {@link #computeIndexInPositionList(List, int, bool)}
+     */
+    protected int computeIndexInPositionList(List positions, int offset) {
+        return computeIndexInPositionList(positions, offset, true);
+    }
+
+    /**
+     * Computes the index in the list of positions at which a position with the given
+     * position would be inserted. The position to insert is supposed to become the first
+     * in this list of all positions with the same position.
+     *
+     * @param positions the list in which the index is computed
+     * @param offset the offset for which the index is computed
+     * @param orderedByOffset <code>true</code> if ordered by offset, false if ordered by end position
+     * @return the computed index
+     * @since 3.4
+     */
+    protected int computeIndexInPositionList(List positions, int offset, bool orderedByOffset) {
+        if (positions.size() is 0)
+            return 0;
+
+        int left= 0;
+        int right= positions.size() -1;
+        int mid= 0;
+        Position p= null;
+
+        while (left < right) {
+
+            mid= (left + right) / 2;
+
+            p= cast(Position) positions.get(mid);
+            int pOffset= getOffset(orderedByOffset, p);
+            if (offset < pOffset) {
+                if (left is mid)
+                    right= left;
+                else
+                    right= mid -1;
+            } else if (offset > pOffset) {
+                if (right is mid)
+                    left= right;
+                else
+                    left= mid  +1;
+            } else if (offset is pOffset) {
+                left= right= mid;
+            }
+
+        }
+
+        int pos= left;
+        p= cast(Position) positions.get(pos);
+        int pPosition= getOffset(orderedByOffset, p);
+        if (offset > pPosition) {
+            // append to the end
+            pos++;
+        } else {
+            // entry will become the first of all entries with the same offset
+            do {
+                --pos;
+                if (pos < 0)
+                    break;
+                p= cast(Position) positions.get(pos);
+                pPosition= getOffset(orderedByOffset, p);
+            } while (offset is pPosition);
+            ++pos;
+        }
+
+        Assert.isTrue(0 <= pos && pos <= positions.size());
+
+        return pos;
+    }
+
+    /*
+     * @since 3.4
+     */
+    private int getOffset(bool orderedByOffset, Position position) {
+        if (orderedByOffset || position.getLength() is 0)
+            return position.getOffset();
+        return position.getOffset() + position.getLength() - 1;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#computeIndexInCategory(java.lang.String, int)
+     */
+    public int computeIndexInCategory(String category, int offset)  {
+
+        if (0 > offset || offset > getLength())
+            throw new BadLocationException();
+
+        List c= cast(List) fPositions.get(category);
+        if (c is null)
+            throw new BadPositionCategoryException();
+
+        return computeIndexInPositionList(c, offset);
+    }
+
+    /**
+     * Fires the document partitioning changed notification to all registered
+     * document partitioning listeners. Uses a robust iterator.
+     *
+     * @deprecated as of 2.0. Use <code>fireDocumentPartitioningChanged(IRegion)</code> instead.
+     */
+    protected void fireDocumentPartitioningChanged() {
+        if (fDocumentPartitioningListeners is null)
+            return;
+
+        Object[] listeners= fDocumentPartitioningListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++)
+            (cast(IDocumentPartitioningListener)listeners[i]).documentPartitioningChanged(this);
+    }
+
+    /**
+     * Fires the document partitioning changed notification to all registered
+     * document partitioning listeners. Uses a robust iterator.
+     *
+     * @param region the region in which partitioning has changed
+     *
+     * @see IDocumentPartitioningListenerExtension
+     * @since 2.0
+     * @deprecated as of 3.0. Use
+     *             <code>fireDocumentPartitioningChanged(DocumentPartitioningChangedEvent)</code>
+     *             instead.
+     */
+    protected void fireDocumentPartitioningChanged(IRegion region) {
+        if (fDocumentPartitioningListeners is null)
+            return;
+
+        Object[] listeners= fDocumentPartitioningListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++) {
+            IDocumentPartitioningListener l= cast(IDocumentPartitioningListener)listeners[i];
+            if ( cast(IDocumentPartitioningListenerExtension)l )
+                (cast(IDocumentPartitioningListenerExtension) l).documentPartitioningChanged(this, region);
+            else
+                l.documentPartitioningChanged(this);
+        }
+    }
+
+    /**
+     * Fires the document partitioning changed notification to all registered
+     * document partitioning listeners. Uses a robust iterator.
+     *
+     * @param event the document partitioning changed event
+     *
+     * @see IDocumentPartitioningListenerExtension2
+     * @since 3.0
+     */
+    protected void fireDocumentPartitioningChanged(DocumentPartitioningChangedEvent event) {
+        if (fDocumentPartitioningListeners is null)
+            return;
+
+        Object[] listeners= fDocumentPartitioningListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++) {
+            IDocumentPartitioningListener l= cast(IDocumentPartitioningListener)listeners[i];
+            if ( cast(IDocumentPartitioningListenerExtension2)l ) {
+                IDocumentPartitioningListenerExtension2 extension2= cast(IDocumentPartitioningListenerExtension2) l;
+                extension2.documentPartitioningChanged(event);
+            } else if ( cast(IDocumentPartitioningListenerExtension)l ) {
+                IDocumentPartitioningListenerExtension extension= cast(IDocumentPartitioningListenerExtension) l;
+                extension.documentPartitioningChanged(this, event.getCoverage());
+            } else {
+                l.documentPartitioningChanged(this);
+            }
+        }
+    }
+
+    /**
+     * Fires the given document event to all registers document listeners informing them
+     * about the forthcoming document manipulation. Uses a robust iterator.
+     *
+     * @param event the event to be sent out
+     */
+    protected void fireDocumentAboutToBeChanged(DocumentEvent event) {
+
+        // IDocumentExtension
+        if (fReentranceCount is 0)
+            flushPostNotificationChanges();
+
+        if (fDocumentPartitioners !is null) {
+            Iterator e= fDocumentPartitioners.values().iterator();
+            while (e.hasNext()) {
+                IDocumentPartitioner p= cast(IDocumentPartitioner) e.next();
+                if ( cast(IDocumentPartitionerExtension3)p ) {
+                    IDocumentPartitionerExtension3 extension= cast(IDocumentPartitionerExtension3) p;
+                    if (extension.getActiveRewriteSession() !is null)
+                        continue;
+                }
+                p.documentAboutToBeChanged(event);
+            }
+        }
+
+        Object[] listeners= fPrenotifiedDocumentListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++)
+            (cast(IDocumentListener)listeners[i]).documentAboutToBeChanged(event);
+
+        listeners= fDocumentListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++)
+            (cast(IDocumentListener)listeners[i]).documentAboutToBeChanged(event);
+    }
+
+
+    /**
+     * Updates document partitioning and document positions according to the
+     * specification given by the document event.
+     *
+     * @param event the document event describing the change to which structures must be adapted
+     */
+    protected void updateDocumentStructures(DocumentEvent event) {
+
+        if (fDocumentPartitioners !is null) {
+            fDocumentPartitioningChangedEvent= new DocumentPartitioningChangedEvent(this);
+            Iterator e= fDocumentPartitioners.keySet().iterator();
+            while (e.hasNext()) {
+                String partitioning= stringcast( e.next() );
+                IDocumentPartitioner partitioner= cast(IDocumentPartitioner) fDocumentPartitioners.get(partitioning);
+
+                if ( cast(IDocumentPartitionerExtension3)partitioner ) {
+                    IDocumentPartitionerExtension3 extension= cast(IDocumentPartitionerExtension3) partitioner;
+                    if (extension.getActiveRewriteSession() !is null)
+                        continue;
+                }
+
+                if ( cast(IDocumentPartitionerExtension)partitioner ) {
+                    IDocumentPartitionerExtension extension= cast(IDocumentPartitionerExtension) partitioner;
+                    IRegion r= extension.documentChanged2(event);
+                    if (r !is null)
+                        fDocumentPartitioningChangedEvent.setPartitionChange(partitioning, r.getOffset(), r.getLength());
+                } else {
+                    if (partitioner.documentChanged(event))
+                        fDocumentPartitioningChangedEvent.setPartitionChange(partitioning, 0, event.getDocument().getLength());
+                }
+            }
+        }
+
+        if (fPositions.size() > 0)
+            updatePositions(event);
+    }
+
+    /**
+     * Notifies all listeners about the given document change. Uses a robust
+     * iterator.
+     * <p>
+     * Executes all registered post notification replace operation.
+     *
+     * @param event the event to be sent out.
+     */
+    protected void doFireDocumentChanged(DocumentEvent event) {
+        bool changed= fDocumentPartitioningChangedEvent !is null && !fDocumentPartitioningChangedEvent.isEmpty();
+        IRegion change= changed ? fDocumentPartitioningChangedEvent.getCoverage() : null;
+        doFireDocumentChanged(event, changed, change);
+    }
+
+    /**
+     * Notifies all listeners about the given document change.
+     * Uses a robust iterator. <p>
+     * Executes all registered post notification replace operation.
+     *
+     * @param event the event to be sent out
+     * @param firePartitionChange <code>true</code> if a partition change notification should be sent
+     * @param partitionChange the region whose partitioning changed
+     * @since 2.0
+     * @deprecated as of 3.0. Use <code>doFireDocumentChanged2(DocumentEvent)</code> instead; this method will be removed.
+     */
+    protected void doFireDocumentChanged(DocumentEvent event, bool firePartitionChange, IRegion partitionChange) {
+        doFireDocumentChanged2(event);
+    }
+
+    /**
+     * Notifies all listeners about the given document change. Uses a robust
+     * iterator.
+     * <p>
+     * Executes all registered post notification replace operation.
+     * <p>
+     * This method will be renamed to <code>doFireDocumentChanged</code>.
+     *
+     * @param event the event to be sent out
+     * @since 3.0
+     */
+    protected void doFireDocumentChanged2(DocumentEvent event) {
+
+        DocumentPartitioningChangedEvent p= fDocumentPartitioningChangedEvent;
+        fDocumentPartitioningChangedEvent= null;
+        if (p !is null && !p.isEmpty())
+            fireDocumentPartitioningChanged(p);
+
+        Object[] listeners= fPrenotifiedDocumentListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++)
+            (cast(IDocumentListener)listeners[i]).documentChanged(event);
+
+        listeners= fDocumentListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++)
+            (cast(IDocumentListener)listeners[i]).documentChanged(event);
+
+        // IDocumentExtension
+        ++ fReentranceCount;
+        try {
+            if (fReentranceCount is 1)
+                executePostNotificationChanges();
+        } finally {
+            -- fReentranceCount;
+        }
+    }
+
+    /**
+     * Updates the internal document structures and informs all document listeners
+     * if listener notification has been enabled. Otherwise it remembers the event
+     * to be sent to the listeners on resume.
+     *
+     * @param event the document event to be sent out
+     */
+    protected void fireDocumentChanged(DocumentEvent event) {
+        updateDocumentStructures(event);
+
+        if (fStoppedListenerNotification is 0)
+            doFireDocumentChanged(event);
+        else
+            fDeferredDocumentEvent= event;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getChar(int)
+     */
+    public char getChar(int pos)  {
+        if ((0 > pos) || (pos >= getLength()))
+            throw new BadLocationException();
+        return getStore().get(pos);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getContentType(int)
+     */
+    public String getContentType(int offset)  {
+        String contentType= null;
+        try {
+            contentType= getContentType(DEFAULT_PARTITIONING, offset, false);
+            //Assert.isNotNull(contentType);
+        } catch (BadPartitioningException e) {
+            Assert.isTrue(false);
+        }
+        return contentType;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getLegalContentTypes()
+     */
+    public String[] getLegalContentTypes() {
+        String[] contentTypes= null;
+        try {
+            contentTypes= getLegalContentTypes(DEFAULT_PARTITIONING);
+            //Assert.isNotNull(contentTypes);
+        } catch (BadPartitioningException e) {
+            Assert.isTrue(false);
+        }
+        return contentTypes;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getLength()
+     */
+    public int getLength() {
+        return getStore().getLength();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getLineDelimiter(int)
+     */
+    public String getLineDelimiter(int line)  {
+        return getTracker().getLineDelimiter(line);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getLegalLineDelimiters()
+     */
+    public String[] getLegalLineDelimiters() {
+        return getTracker().getLegalLineDelimiters();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension4#getDefaultLineDelimiter()
+     * @since 3.1
+     */
+    public String getDefaultLineDelimiter() {
+
+        String lineDelimiter= null;
+
+        try {
+            lineDelimiter= getLineDelimiter(0);
+        } catch (BadLocationException x) {
+        }
+
+        if (lineDelimiter !is null)
+            return lineDelimiter;
+
+        if (fInitialLineDelimiter !is null)
+            return fInitialLineDelimiter;
+
+        String sysLineDelimiter= System.getProperty("line.separator"); //$NON-NLS-1$
+        String[] delimiters= getLegalLineDelimiters();
+        Assert.isTrue(delimiters.length > 0);
+        for (int i= 0; i < delimiters.length; i++) {
+            if (delimiters[i].equals(sysLineDelimiter)) {
+                lineDelimiter= sysLineDelimiter;
+                break;
+            }
+        }
+
+        if (lineDelimiter is null)
+            lineDelimiter= delimiters[0];
+
+        return lineDelimiter;
+
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension4#setInitialLineDelimiter(java.lang.String)
+     * @since 3.1
+     */
+    public void setInitialLineDelimiter(String lineDelimiter) {
+        Assert.isNotNull(lineDelimiter);
+        fInitialLineDelimiter= lineDelimiter;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getLineLength(int)
+     */
+    public int getLineLength(int line)  {
+        return getTracker().getLineLength(line);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getLineOfOffset(int)
+     */
+    public int getLineOfOffset(int pos)  {
+        return getTracker().getLineNumberOfOffset(pos);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getLineOffset(int)
+     */
+    public int getLineOffset(int line)  {
+        return getTracker().getLineOffset(line);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getLineInformation(int)
+     */
+    public IRegion getLineInformation(int line)  {
+        return getTracker().getLineInformation(line);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getLineInformationOfOffset(int)
+     */
+    public IRegion getLineInformationOfOffset(int offset)  {
+        return getTracker().getLineInformationOfOffset(offset);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getNumberOfLines()
+     */
+    public int getNumberOfLines() {
+        return getTracker().getNumberOfLines();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getNumberOfLines(int, int)
+     */
+    public int getNumberOfLines(int offset, int length)  {
+        return getTracker().getNumberOfLines(offset, length);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#computeNumberOfLines(java.lang.String)
+     */
+    public int computeNumberOfLines(String text) {
+        return getTracker().computeNumberOfLines(text);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getPartition(int)
+     */
+    public ITypedRegion getPartition(int offset)  {
+        ITypedRegion partition= null;
+        try {
+            partition= getPartition(DEFAULT_PARTITIONING, offset, false);
+//             Assert.isNotNull(cast(Object)partition);
+        } catch (BadPartitioningException e) {
+            Assert.isTrue(false);
+        }
+        return  partition;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#computePartitioning(int, int)
+     */
+    public ITypedRegion[] computePartitioning(int offset, int length)  {
+        ITypedRegion[] partitioning= null;
+        try {
+            partitioning= computePartitioning(DEFAULT_PARTITIONING, offset, length, false);
+//             Assert.isNotNull(cast(Object)partitioning);
+        } catch (BadPartitioningException e) {
+            Assert.isTrue(false);
+        }
+        return partitioning;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getPositions(java.lang.String)
+     */
+    public Position[] getPositions(String category)  {
+
+        if (category is null)
+            throw new BadPositionCategoryException();
+
+        List c= cast(List) fPositions.get(category);
+        if (c is null)
+            throw new BadPositionCategoryException();
+
+        Position[] positions= new Position[c.size()];
+        c.toArray(positions);
+        return positions;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getPositionCategories()
+     */
+    public String[] getPositionCategories() {
+        String[] categories= new String[fPositions.size()];
+        Iterator keys= fPositions.keySet().iterator();
+        for (int i= 0; i < categories.length; i++)
+            categories[i]= stringcast( keys.next());
+        return categories;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#getPositionUpdaters()
+     */
+    public IPositionUpdater[] getPositionUpdaters() {
+        return arraycast!(IPositionUpdater)(fPositionUpdaters.toArray());
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#get()
+     */
+    public String get() {
+        return getStore().get(0, getLength());
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#get(int, int)
+     */
+    public String get(int pos, int length)  {
+        int myLength= getLength();
+        if ((0 > pos) || (0 > length) || (pos + length > myLength))
+            throw new BadLocationException();
+        return getStore().get(pos, length);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#insertPositionUpdater(org.eclipse.jface.text.IPositionUpdater, int)
+     */
+    public void insertPositionUpdater(IPositionUpdater updater, int index) {
+
+        for (int i= fPositionUpdaters.size() - 1; i >= 0; i--) {
+            if (fPositionUpdaters.get(i) is cast(Object)updater)
+                return;
+        }
+
+        if (index is fPositionUpdaters.size())
+            fPositionUpdaters.add(cast(Object)updater);
+        else
+            fPositionUpdaters.add(index, cast(Object)updater);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#removePosition(java.lang.String, org.eclipse.jface.text.Position)
+     */
+    public void removePosition(String category, Position position)  {
+
+        if (position is null)
+            return;
+
+        if (category is null)
+            throw new BadPositionCategoryException();
+
+        List c= cast(List) fPositions.get(category);
+        if (c is null)
+            throw new BadPositionCategoryException();
+        removeFromPositionsList(c, position, true);
+
+        List endPositions= cast(List) fEndPositions.get(category);
+        if (endPositions is null)
+            throw new BadPositionCategoryException();
+        removeFromPositionsList(endPositions, position, false);
+    }
+
+    /**
+     * Remove the given position form the given list of positions based on identity not equality.
+     *
+     * @param positions a list of positions
+     * @param position the position to remove
+     * @param orderedByOffset true if <code>positions</code> is ordered by offset, false if ordered by end position
+     * @since 3.4
+     */
+    private void removeFromPositionsList(List positions, Position position, bool orderedByOffset) {
+        int size= positions.size();
+
+        //Assume position is somewhere near it was before
+        int index= computeIndexInPositionList(positions, orderedByOffset ? position.offset : position.offset + position.length - 1, orderedByOffset);
+        if (index < size && positions.get(index) is position) {
+            positions.remove(index);
+            return;
+        }
+
+        int back= index - 1;
+        int forth= index + 1;
+        while (back >= 0 || forth < size) {
+            if (back >= 0) {
+                if (position is positions.get(back)) {
+                    positions.remove(back);
+                    return;
+                }
+                back--;
+            }
+
+            if (forth < size) {
+                if (position is positions.get(forth)) {
+                    positions.remove(forth);
+                    return;
+                }
+                forth++;
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#removePosition(org.eclipse.jface.text.Position)
+     */
+    public void removePosition(Position position) {
+        try {
+            removePosition(DEFAULT_CATEGORY, position);
+        } catch (BadPositionCategoryException e) {
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#removePositionCategory(java.lang.String)
+     */
+    public void removePositionCategory(String category)  {
+
+        if (category is null)
+            return;
+
+        if ( !containsPositionCategory(category))
+            throw new BadPositionCategoryException();
+
+        fPositions.remove(category);
+        fEndPositions.remove(category);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#removePositionUpdater(org.eclipse.jface.text.IPositionUpdater)
+     */
+    public void removePositionUpdater(IPositionUpdater updater) {
+        for (int i= fPositionUpdaters.size() - 1; i >= 0; i--) {
+            if (fPositionUpdaters.get(i) is cast(Object)updater) {
+                fPositionUpdaters.remove(i);
+                return;
+            }
+        }
+    }
+
+    private long getNextModificationStamp() {
+        if (fNextModificationStamp is Long.MAX_VALUE || fNextModificationStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP)
+            fNextModificationStamp= 0;
+        else
+            fNextModificationStamp= fNextModificationStamp + 1;
+
+        return fNextModificationStamp;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension4#getModificationStamp()
+     * @since 3.1
+     */
+    public long getModificationStamp() {
+        return fModificationStamp;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#replace(int, int, java.lang.String)
+     * @since 3.1
+     */
+    public void replace(int pos, int length, String text, long modificationStamp)  {
+        if ((0 > pos) || (0 > length) || (pos + length > getLength()))
+            throw new BadLocationException();
+
+        DocumentEvent e= new DocumentEvent(this, pos, length, text);
+        fireDocumentAboutToBeChanged(e);
+
+        getStore().replace(pos, length, text);
+        getTracker().replace(pos, length, text);
+
+        fModificationStamp= modificationStamp;
+        fNextModificationStamp= Math.max(fModificationStamp, fNextModificationStamp);
+        e.fModificationStamp= fModificationStamp;
+
+        fireDocumentChanged(e);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 3.4
+     */
+    public bool isLineInformationRepairNeeded(int offset, int length, String text)  {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#replace(int, int, java.lang.String)
+     */
+    public void replace(int pos, int length, String text)  {
+        if (length is 0 && (text is null || text.length is 0))
+            replace(pos, length, text, getModificationStamp());
+        else
+            replace(pos, length, text, getNextModificationStamp());
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#set(java.lang.String)
+     */
+    public void set(String text) {
+        set(text, getNextModificationStamp());
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension4#set(java.lang.String, long)
+     * @since 3.1
+     */
+    public void set(String text, long modificationStamp) {
+        int length= getStore().getLength();
+
+        DocumentEvent e= new DocumentEvent(this, 0, length, text);
+        fireDocumentAboutToBeChanged(e);
+
+        getStore().set(text);
+        getTracker().set(text);
+
+        fModificationStamp= modificationStamp;
+        fNextModificationStamp= Math.max(fModificationStamp, fNextModificationStamp);
+        e.fModificationStamp= fModificationStamp;
+
+        fireDocumentChanged(e);
+    }
+
+    /**
+     * Updates all positions of all categories to the change described by the
+     * document event. All registered document updaters are called in the
+     * sequence they have been arranged. Uses a robust iterator.
+     *
+     * @param event the document event describing the change to which to adapt
+     *            the positions
+     */
+    protected void updatePositions(DocumentEvent event) {
+        List list= new ArrayList(fPositionUpdaters);
+        Iterator e= list.iterator();
+        while (e.hasNext()) {
+            IPositionUpdater u= cast(IPositionUpdater) e.next();
+            u.update(event);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#search(int, java.lang.String, bool, bool, bool)
+     */
+    public int search(int startPosition, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord)  {
+        try {
+            IRegion region= getFindReplaceDocumentAdapter().find(startPosition, findString, forwardSearch, caseSensitive, wholeWord, false);
+            return region is null ?  -1 : region.getOffset();
+        } catch (IllegalStateException ex) {
+            return -1;
+        } catch (PatternSyntaxException ex) {
+            return -1;
+        }
+    }
+
+    /**
+     * Returns the find/replace adapter for this document.
+     *
+     * @return this document's find/replace document adapter
+     * @since 3.0
+     */
+    private FindReplaceDocumentAdapter getFindReplaceDocumentAdapter() {
+        if (fFindReplaceDocumentAdapter is null)
+            fFindReplaceDocumentAdapter= new FindReplaceDocumentAdapter(this);
+
+        return fFindReplaceDocumentAdapter;
+    }
+
+    /**
+     * Flushes all registered post notification changes.
+     *
+     * @since 2.0
+     */
+    private void flushPostNotificationChanges() {
+        if (fPostNotificationChanges !is null)
+            fPostNotificationChanges.clear();
+    }
+
+    /**
+     * Executes all registered post notification changes. The process is
+     * repeated until no new post notification changes are added.
+     *
+     * @since 2.0
+     */
+    private void executePostNotificationChanges() {
+
+        if (fStoppedCount > 0)
+            return;
+
+        while (fPostNotificationChanges !is null) {
+            List changes= fPostNotificationChanges;
+            fPostNotificationChanges= null;
+
+            Iterator e= changes.iterator();
+            while (e.hasNext()) {
+                RegisteredReplace replace= cast(RegisteredReplace) e.next();
+                replace.fReplace.perform(this, replace.fOwner);
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension2#acceptPostNotificationReplaces()
+     * @since 2.1
+     */
+    public void acceptPostNotificationReplaces() {
+        fAcceptPostNotificationReplaces= true;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension2#ignorePostNotificationReplaces()
+     * @since 2.1
+     */
+    public void ignorePostNotificationReplaces() {
+        fAcceptPostNotificationReplaces= false;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension#registerPostNotificationReplace(org.eclipse.jface.text.IDocumentListener, org.eclipse.jface.text.IDocumentExtension.IReplace)
+     * @since 2.0
+     */
+    public void registerPostNotificationReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) {
+        if (fAcceptPostNotificationReplaces) {
+            if (fPostNotificationChanges is null)
+                fPostNotificationChanges= new ArrayList(1);
+            fPostNotificationChanges.add(new RegisteredReplace(owner, replace));
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension#stopPostNotificationProcessing()
+     * @since 2.0
+     */
+    public void stopPostNotificationProcessing() {
+        ++ fStoppedCount;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension#resumePostNotificationProcessing()
+     * @since 2.0
+     */
+    public void resumePostNotificationProcessing() {
+        -- fStoppedCount;
+        if (fStoppedCount is 0 && fReentranceCount is 0)
+            executePostNotificationChanges();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension#startSequentialRewrite(bool)
+     * @since 2.0
+     */
+    public void startSequentialRewrite(bool normalized) {
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension#stopSequentialRewrite()
+     * @since 2.0
+     */
+    public void stopSequentialRewrite() {
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension2#resumeListenerNotification()
+     * @since 2.1
+     */
+    public void resumeListenerNotification() {
+        -- fStoppedListenerNotification;
+        if (fStoppedListenerNotification is 0) {
+            resumeDocumentListenerNotification();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension2#stopListenerNotification()
+     * @since 2.1
+     */
+    public void stopListenerNotification() {
+        ++ fStoppedListenerNotification;
+    }
+
+    /**
+     * Resumes the document listener notification by sending out the remembered
+     * partition changed and document event.
+     *
+     * @since 2.1
+     */
+    private void resumeDocumentListenerNotification() {
+        if (fDeferredDocumentEvent !is null) {
+            DocumentEvent event= fDeferredDocumentEvent;
+            fDeferredDocumentEvent= null;
+            doFireDocumentChanged(event);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension3#computeZeroLengthPartitioning(java.lang.String, int, int)
+     * @since 3.0
+     */
+    public ITypedRegion[] computePartitioning(String partitioning, int offset, int length, bool includeZeroLengthPartitions)  {
+        if ((0 > offset) || (0 > length) || (offset + length > getLength()))
+            throw new BadLocationException();
+
+        IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);
+
+        if ( cast(IDocumentPartitionerExtension2)partitioner ) {
+            checkStateOfPartitioner(partitioner, partitioning);
+            return (cast(IDocumentPartitionerExtension2) partitioner).computePartitioning(offset, length, includeZeroLengthPartitions);
+        } else if (partitioner !is null) {
+            checkStateOfPartitioner(partitioner, partitioning);
+            return partitioner.computePartitioning(offset, length);
+        } else if (DEFAULT_PARTITIONING.equals(partitioning))
+            return [ new TypedRegion(offset, length, DEFAULT_CONTENT_TYPE) ];
+        else
+            throw new BadPartitioningException();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension3#getZeroLengthContentType(java.lang.String, int)
+     * @since 3.0
+     */
+    public String getContentType(String partitioning, int offset, bool preferOpenPartitions)  {
+        if ((0 > offset) || (offset > getLength()))
+            throw new BadLocationException();
+
+        IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);
+
+        if ( cast(IDocumentPartitionerExtension2)partitioner ) {
+            checkStateOfPartitioner(partitioner, partitioning);
+            return (cast(IDocumentPartitionerExtension2) partitioner).getContentType(offset, preferOpenPartitions);
+        } else if (partitioner !is null) {
+            checkStateOfPartitioner(partitioner, partitioning);
+            return partitioner.getContentType(offset);
+        } else if (DEFAULT_PARTITIONING.equals(partitioning))
+            return DEFAULT_CONTENT_TYPE;
+        else
+            throw new BadPartitioningException();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension3#getDocumentPartitioner(java.lang.String)
+     * @since 3.0
+     */
+    public IDocumentPartitioner getDocumentPartitioner(String partitioning)  {
+        return fDocumentPartitioners !is null ? cast(IDocumentPartitioner) fDocumentPartitioners.get(partitioning) : null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension3#getLegalContentTypes(java.lang.String)
+     * @since 3.0
+     */
+    public String[] getLegalContentTypes(String partitioning)  {
+        IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);
+        if (partitioner !is null)
+            return partitioner.getLegalContentTypes();
+        if (DEFAULT_PARTITIONING.equals(partitioning))
+            return [ DEFAULT_CONTENT_TYPE ];
+        throw new BadPartitioningException();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension3#getZeroLengthPartition(java.lang.String, int)
+     * @since 3.0
+     */
+    public ITypedRegion getPartition(String partitioning, int offset, bool preferOpenPartitions)  {
+        if ((0 > offset) || (offset > getLength()))
+            throw new BadLocationException();
+
+        IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);
+
+        if ( cast(IDocumentPartitionerExtension2)partitioner ) {
+            checkStateOfPartitioner(partitioner, partitioning);
+            return (cast(IDocumentPartitionerExtension2) partitioner).getPartition(offset, preferOpenPartitions);
+        } else if (partitioner !is null) {
+            checkStateOfPartitioner(partitioner, partitioning);
+            return partitioner.getPartition(offset);
+        } else if (DEFAULT_PARTITIONING.equals(partitioning))
+            return new TypedRegion(0, getLength(), DEFAULT_CONTENT_TYPE);
+        else
+            throw new BadPartitioningException();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension3#getPartitionings()
+     * @since 3.0
+     */
+    public String[] getPartitionings() {
+        if (fDocumentPartitioners is null)
+            return new String[0];
+        return stringcast(fDocumentPartitioners.keySet().toArray());
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension3#setDocumentPartitioner(java.lang.String, org.eclipse.jface.text.IDocumentPartitioner)
+     * @since 3.0
+     */
+    public void setDocumentPartitioner(String partitioning, IDocumentPartitioner partitioner) {
+        if (partitioner is null) {
+            if (fDocumentPartitioners !is null) {
+                fDocumentPartitioners.remove(partitioning);
+                if (fDocumentPartitioners.size() is 0)
+                    fDocumentPartitioners= null;
+            }
+        } else {
+            if (fDocumentPartitioners is null)
+                fDocumentPartitioners= new HashMap();
+            fDocumentPartitioners.put(partitioning, cast(Object)partitioner);
+        }
+        DocumentPartitioningChangedEvent event= new DocumentPartitioningChangedEvent(this);
+        event.setPartitionChange(partitioning, 0, getLength());
+        fireDocumentPartitioningChanged(event);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IRepairableDocument#repairLineInformation()
+     * @since 3.0
+     */
+    public void repairLineInformation() {
+        getTracker().set(get());
+    }
+
+    /**
+     * Fires the given event to all registered rewrite session listeners. Uses robust iterators.
+     *
+     * @param event the event to be fired
+     * @since 3.1
+     */
+    protected void fireRewriteSessionChanged(DocumentRewriteSessionEvent event) {
+        if (fDocumentRewriteSessionListeners.size() > 0) {
+            List list= new ArrayList(fDocumentRewriteSessionListeners);
+            Iterator e= list.iterator();
+            while (e.hasNext()) {
+                IDocumentRewriteSessionListener l= cast(IDocumentRewriteSessionListener) e.next();
+                l.documentRewriteSessionChanged(event);
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension4#getActiveRewriteSession()
+     */
+    public final DocumentRewriteSession getActiveRewriteSession() {
+        return fDocumentRewriteSession;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension4#startRewriteSession(org.eclipse.jface.text.DocumentRewriteSessionType)
+     * @since 3.1
+     */
+    public DocumentRewriteSession startRewriteSession(DocumentRewriteSessionType sessionType) {
+
+        if (getActiveRewriteSession() !is null)
+            throw new IllegalStateException();
+
+
+        fDocumentRewriteSession= new DocumentRewriteSession(sessionType);
+        if (DEBUG)
+            System.out_.println(Format("AbstractDocument: Starting rewrite session: {}", fDocumentRewriteSession)); //$NON-NLS-1$
+
+        fireRewriteSessionChanged(new DocumentRewriteSessionEvent(this, fDocumentRewriteSession, DocumentRewriteSessionEvent.SESSION_START));
+
+        startRewriteSessionOnPartitioners(fDocumentRewriteSession);
+
+        ILineTracker tracker= getTracker();
+        if ( cast(ILineTrackerExtension)tracker ) {
+            ILineTrackerExtension extension= cast(ILineTrackerExtension) tracker;
+            extension.startRewriteSession(fDocumentRewriteSession);
+        }
+
+        if (DocumentRewriteSessionType.SEQUENTIAL is sessionType)
+            startSequentialRewrite(false);
+        else if (DocumentRewriteSessionType.STRICTLY_SEQUENTIAL is sessionType)
+            startSequentialRewrite(true);
+
+        return fDocumentRewriteSession;
+    }
+
+    /**
+     * Starts the given rewrite session.
+     *
+     * @param session the rewrite session
+     * @since 3.1
+     */
+    protected final void startRewriteSessionOnPartitioners(DocumentRewriteSession session) {
+        if (fDocumentPartitioners !is null) {
+            Iterator e= fDocumentPartitioners.values().iterator();
+            while (e.hasNext()) {
+                Object partitioner= e.next();
+                if ( cast(IDocumentPartitionerExtension3)partitioner ) {
+                    IDocumentPartitionerExtension3 extension= cast(IDocumentPartitionerExtension3) partitioner;
+                    extension.startRewriteSession(session);
+                }
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension4#stopRewriteSession(org.eclipse.jface.text.DocumentRewriteSession)
+     * @since 3.1
+     */
+    public void stopRewriteSession(DocumentRewriteSession session) {
+        if (fDocumentRewriteSession is session) {
+
+            if (DEBUG)
+                System.out_.println(Format("AbstractDocument: Stopping rewrite session: {}", session)); //$NON-NLS-1$
+
+            DocumentRewriteSessionType sessionType= session.getSessionType();
+            if (DocumentRewriteSessionType.SEQUENTIAL is sessionType || DocumentRewriteSessionType.STRICTLY_SEQUENTIAL is sessionType)
+                stopSequentialRewrite();
+
+            ILineTracker tracker= getTracker();
+            if ( cast(ILineTrackerExtension)tracker ) {
+                ILineTrackerExtension extension= cast(ILineTrackerExtension) tracker;
+                extension.stopRewriteSession(session, get());
+            }
+
+            stopRewriteSessionOnPartitioners(fDocumentRewriteSession);
+
+            fDocumentRewriteSession= null;
+            fireRewriteSessionChanged(new DocumentRewriteSessionEvent(this, session, DocumentRewriteSessionEvent.SESSION_STOP));
+        }
+    }
+
+    /**
+     * Stops the given rewrite session.
+     *
+     * @param session the rewrite session
+     * @since 3.1
+     */
+    protected final void stopRewriteSessionOnPartitioners(DocumentRewriteSession session) {
+        if (fDocumentPartitioners !is null) {
+            DocumentPartitioningChangedEvent event= new DocumentPartitioningChangedEvent(this);
+            Iterator e= fDocumentPartitioners.keySet().iterator();
+            while (e.hasNext()) {
+                String partitioning= stringcast( e.next());
+                IDocumentPartitioner partitioner= cast(IDocumentPartitioner) fDocumentPartitioners.get(partitioning);
+                if ( cast(IDocumentPartitionerExtension3)partitioner ) {
+                    IDocumentPartitionerExtension3 extension= cast(IDocumentPartitionerExtension3) partitioner;
+                    extension.stopRewriteSession(session);
+                    event.setPartitionChange(partitioning, 0, getLength());
+                }
+            }
+            if (!event.isEmpty())
+                fireDocumentPartitioningChanged(event);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension4#addDocumentRewriteSessionListener(org.eclipse.jface.text.IDocumentRewriteSessionListener)
+     * @since 3.1
+     */
+    public void addDocumentRewriteSessionListener(IDocumentRewriteSessionListener listener) {
+        Assert.isNotNull(cast(Object)listener);
+        if (! fDocumentRewriteSessionListeners.contains(cast(Object)listener))
+            fDocumentRewriteSessionListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension4#removeDocumentRewriteSessionListener(org.eclipse.jface.text.IDocumentRewriteSessionListener)
+     * @since 3.1
+     */
+    public void removeDocumentRewriteSessionListener(IDocumentRewriteSessionListener listener) {
+        Assert.isNotNull(cast(Object)listener);
+        fDocumentRewriteSessionListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Checks the state for the given partitioner and stops the
+     * active rewrite session.
+     *
+     * @param partitioner the document partitioner to be checked
+     * @param partitioning the document partitioning the partitioner is registered for
+     * @since 3.1
+     */
+    protected final void checkStateOfPartitioner(IDocumentPartitioner partitioner, String partitioning) {
+        if (!( cast(IDocumentPartitionerExtension3)partitioner ))
+            return;
+
+        IDocumentPartitionerExtension3 extension= cast(IDocumentPartitionerExtension3) partitioner;
+        DocumentRewriteSession session= extension.getActiveRewriteSession();
+        if (session !is null) {
+            extension.stopRewriteSession(session);
+
+            if (DEBUG)
+                System.out_.println(Format("AbstractDocument: Flushing rewrite session for partition type: {}", partitioning)); //$NON-NLS-1$
+
+            DocumentPartitioningChangedEvent event= new DocumentPartitioningChangedEvent(this);
+            event.setPartitionChange(partitioning, 0, getLength());
+            fireDocumentPartitioningChanged(event);
+        }
+    }
+
+    /**
+     * Returns all positions of the given category that are inside the given region.
+     *
+     * @param category the position category
+     * @param offset the start position of the region, must be >= 0
+     * @param length the length of the region, must be >= 0
+     * @param canStartBefore if <code>true</code> then positions are included
+     *            which start before the region if they end at or after the regions start
+     * @param canEndAfter if <code>true</code> then positions are included
+     *            which end after the region if they start at or before the regions end
+     * @return all positions inside the region of the given category
+     * @throws BadPositionCategoryException if category is undefined in this document
+     * @since 3.4
+     */
+    public Position[] getPositions(String category, int offset, int length, bool canStartBefore, bool canEndAfter)  {
+        if (canStartBefore && canEndAfter || (!canStartBefore && !canEndAfter)) {
+            List documentPositions;
+            if (canStartBefore && canEndAfter) {
+                if (offset < getLength() / 2) {
+                    documentPositions= getStartingPositions(category, 0, offset + length);
+                } else {
+                    documentPositions= getEndingPositions(category, offset, getLength() - offset + 1);
+                }
+            } else {
+                documentPositions= getStartingPositions(category, offset, length);
+            }
+
+            ArrayList list= new ArrayList(documentPositions.size());
+
+            Position region= new Position(offset, length);
+
+            for (Iterator iterator= documentPositions.iterator(); iterator.hasNext();) {
+                Position position= cast(Position) iterator.next();
+                if (isWithinRegion(region, position, canStartBefore, canEndAfter)) {
+                    list.add(position);
+                }
+            }
+
+            Position[] positions= new Position[list.size()];
+            list.toArray(positions);
+            return positions;
+        } else if (canStartBefore) {
+            List list= getEndingPositions(category, offset, length);
+            Position[] positions= new Position[list.size()];
+            list.toArray(positions);
+            return positions;
+        } else {
+            Assert.isLegal(canEndAfter && !canStartBefore);
+
+            List list= getStartingPositions(category, offset, length);
+            Position[] positions= new Position[list.size()];
+            list.toArray(positions);
+            return positions;
+        }
+    }
+
+    /*
+     * @since 3.4
+     */
+    private bool isWithinRegion(Position region, Position position, bool canStartBefore, bool canEndAfter) {
+        if (canStartBefore && canEndAfter) {
+            return region.overlapsWith(position.getOffset(), position.getLength());
+        } else if (canStartBefore) {
+            return region.includes(position.getOffset() + position.getLength() - 1);
+        } else if (canEndAfter) {
+            return region.includes(position.getOffset());
+        } else {
+            int start= position.getOffset();
+            return region.includes(start) && region.includes(start + position.getLength() - 1);
+        }
+    }
+
+    /**
+     * A list of positions in the given category with an offset inside the given
+     * region. The order of the positions is arbitrary.
+     *
+     * @param category the position category
+     * @param offset the offset of the region
+     * @param length the length of the region
+     * @return a list of the positions in the region
+     * @throws BadPositionCategoryException if category is undefined in this document
+     * @since 3.4
+     */
+    private List getStartingPositions(String category, int offset, int length)  {
+        List positions= cast(List) fPositions.get(category);
+        if (positions is null)
+            throw new BadPositionCategoryException();
+
+        int indexStart= computeIndexInPositionList(positions, offset, true);
+        int indexEnd= computeIndexInPositionList(positions, offset + length, true);
+
+        return positions.subList(indexStart, indexEnd);
+    }
+
+    /**
+     * A list of positions in the given category with an end position inside
+     * the given region. The order of the positions is arbitrary.
+     *
+     * @param category the position category
+     * @param offset the offset of the region
+     * @param length the length of the region
+     * @return a list of the positions in the region
+     * @throws BadPositionCategoryException if category is undefined in this document
+     * @since 3.4
+     */
+    private List getEndingPositions(String category, int offset, int length)  {
+        List positions= cast(List) fEndPositions.get(category);
+        if (positions is null)
+            throw new BadPositionCategoryException();
+
+        int indexStart= computeIndexInPositionList(positions, offset, false);
+        int indexEnd= computeIndexInPositionList(positions, offset + length, false);
+
+        return positions.subList(indexStart, indexEnd);
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/AbstractLineTracker.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,474 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.AbstractLineTracker;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import tango.text.convert.Format;
+
+
+
+    /**
+     * Combines the information of the occurrence of a line delimiter. <code>delimiterIndex</code>
+     * is the index where a line delimiter starts, whereas <code>delimiterLength</code>,
+     * indicates the length of the delimiter.
+     */
+    protected class DelimiterInfo {
+        public int delimiterIndex;
+        public int delimiterLength;
+        public String delimiter;
+    }
+    alias DelimiterInfo AbstractLineTracker_DelimiterInfo;
+
+/**
+ * Abstract implementation of <code>ILineTracker</code>. It lets the definition of line
+ * delimiters to subclasses. Assuming that '\n' is the only line delimiter, this abstract
+ * implementation defines the following line scheme:
+ * <ul>
+ * <li> "" -> [0,0]
+ * <li> "a" -> [0,1]
+ * <li> "\n" -> [0,1], [1,0]
+ * <li> "a\n" -> [0,2], [2,0]
+ * <li> "a\nb" -> [0,2], [2,1]
+ * <li> "a\nbc\n" -> [0,2], [2,3], [5,0]
+ * </ul>
+ * <p>
+ * This class must be subclassed.
+ * </p>
+ */
+public abstract class AbstractLineTracker : ILineTracker, ILineTrackerExtension {
+
+    /**
+     * Tells whether this class is in debug mode.
+     *
+     * @since 3.1
+     */
+    private static const bool DEBUG= false;
+
+    /**
+     * Representation of replace and set requests.
+     *
+     * @since 3.1
+     */
+    protected static class Request {
+        public const int offset;
+        public const int length;
+        public const String text;
+
+        public this(int offset, int length, String text) {
+            this.offset= offset;
+            this.length= length;
+            this.text= text;
+        }
+
+        public this(String text) {
+            this.offset= -1;
+            this.length= -1;
+            this.text= text;
+        }
+
+        public bool isReplaceRequest() {
+            return this.offset > -1 && this.length > -1;
+        }
+    }
+
+    /**
+     * The active rewrite session.
+     *
+     * @since 3.1
+     */
+    private DocumentRewriteSession fActiveRewriteSession;
+    /**
+     * The list of pending requests.
+     *
+     * @since 3.1
+     */
+    private List fPendingRequests;
+    /**
+     * The implementation that this tracker delegates to.
+     *
+     * @since 3.2
+     */
+    private ILineTracker fDelegate;
+    private void fDelegate_init() {
+        fDelegate = new class() ListLineTracker {
+            public String[] getLegalLineDelimiters() {
+                return this.outer.getLegalLineDelimiters();
+            }
+
+            protected DelimiterInfo nextDelimiterInfo(String text, int offset) {
+                return this.outer.nextDelimiterInfo(text, offset);
+            }
+        };
+    }
+    /**
+     * Whether the delegate needs conversion when the line structure is modified.
+     */
+    private bool fNeedsConversion= true;
+
+    /**
+     * Creates a new line tracker.
+     */
+    protected this() {
+        fDelegate_init();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#computeNumberOfLines(java.lang.String)
+     */
+    public int computeNumberOfLines(String text) {
+        return fDelegate.computeNumberOfLines(text);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineDelimiter(int)
+     */
+    public String getLineDelimiter(int line)  {
+        checkRewriteSession();
+        return fDelegate.getLineDelimiter(line);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineInformation(int)
+     */
+    public IRegion getLineInformation(int line)  {
+        checkRewriteSession();
+        return fDelegate.getLineInformation(line);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineInformationOfOffset(int)
+     */
+    public IRegion getLineInformationOfOffset(int offset)  {
+        checkRewriteSession();
+        return fDelegate.getLineInformationOfOffset(offset);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineLength(int)
+     */
+    public int getLineLength(int line)  {
+        checkRewriteSession();
+        return fDelegate.getLineLength(line);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineNumberOfOffset(int)
+     */
+    public int getLineNumberOfOffset(int offset)  {
+        checkRewriteSession();
+        return fDelegate.getLineNumberOfOffset(offset);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineOffset(int)
+     */
+    public int getLineOffset(int line)  {
+        checkRewriteSession();
+        return fDelegate.getLineOffset(line);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getNumberOfLines()
+     */
+    public int getNumberOfLines() {
+        try {
+            checkRewriteSession();
+        } catch (BadLocationException x) {
+            // TODO there is currently no way to communicate that exception back to the document
+        }
+        return fDelegate.getNumberOfLines();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getNumberOfLines(int, int)
+     */
+    public int getNumberOfLines(int offset, int length)  {
+        checkRewriteSession();
+        return fDelegate.getNumberOfLines(offset, length);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#set(java.lang.String)
+     */
+    public void set(String text) {
+        if (hasActiveRewriteSession()) {
+            fPendingRequests.clear();
+            fPendingRequests.add(new Request(text));
+            return;
+        }
+
+        fDelegate.set(text);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#replace(int, int, java.lang.String)
+     */
+    public void replace(int offset, int length, String text)  {
+        if (hasActiveRewriteSession()) {
+            fPendingRequests.add(new Request(offset, length, text));
+            return;
+        }
+
+        checkImplementation();
+
+        fDelegate.replace(offset, length, text);
+    }
+
+    /**
+     * Converts the implementation to be a {@link TreeLineTracker} if it isn't yet.
+     *
+     * @since 3.2
+     */
+    private void checkImplementation() {
+        if (fNeedsConversion) {
+            fNeedsConversion= false;
+            fDelegate= new class(cast(ListLineTracker) fDelegate)  TreeLineTracker {
+                this(ListLineTracker arg){
+                    super(arg);
+                }
+                protected DelimiterInfo nextDelimiterInfo(String text, int offset) {
+                    return this.outer.nextDelimiterInfo(text, offset);
+                }
+
+                public String[] getLegalLineDelimiters() {
+                    return this.outer.getLegalLineDelimiters();
+                }
+            };
+        }
+    }
+
+    /**
+     * Returns the information about the first delimiter found in the given text starting at the
+     * given offset.
+     *
+     * @param text the text to be searched
+     * @param offset the offset in the given text
+     * @return the information of the first found delimiter or <code>null</code>
+     */
+    protected abstract DelimiterInfo nextDelimiterInfo(String text, int offset);
+
+    /*
+     * @see org.eclipse.jface.text.ILineTrackerExtension#startRewriteSession(org.eclipse.jface.text.DocumentRewriteSession)
+     * @since 3.1
+     */
+    public final void startRewriteSession(DocumentRewriteSession session) {
+        if (fActiveRewriteSession !is null)
+            throw new IllegalStateException();
+        fActiveRewriteSession= session;
+        fPendingRequests= new ArrayList(20);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTrackerExtension#stopRewriteSession(org.eclipse.jface.text.DocumentRewriteSession, java.lang.String)
+     * @since 3.1
+     */
+    public final void stopRewriteSession(DocumentRewriteSession session, String text) {
+        if (fActiveRewriteSession is session) {
+            fActiveRewriteSession= null;
+            fPendingRequests= null;
+            set(text);
+        }
+    }
+
+    /**
+     * Tells whether there's an active rewrite session.
+     *
+     * @return <code>true</code> if there is an active rewrite session, <code>false</code>
+     *         otherwise
+     * @since 3.1
+     */
+    protected final bool hasActiveRewriteSession() {
+        return fActiveRewriteSession !is null;
+    }
+
+    /**
+     * Flushes the active rewrite session.
+     *
+     * @throws BadLocationException in case the recorded requests cannot be processed correctly
+     * @since 3.1
+     */
+    protected final void flushRewriteSession()  {
+        if (DEBUG)
+            System.out_.println(Format("AbstractLineTracker: Flushing rewrite session: {}", fActiveRewriteSession)); //$NON-NLS-1$
+
+        Iterator e= fPendingRequests.iterator();
+
+        fPendingRequests= null;
+        fActiveRewriteSession= null;
+
+        while (e.hasNext()) {
+            Request request= cast(Request) e.next();
+            if (request.isReplaceRequest())
+                replace(request.offset, request.length, request.text);
+            else
+                set(request.text);
+        }
+    }
+
+    /**
+     * Checks the presence of a rewrite session and flushes it.
+     *
+     * @throws BadLocationException in case flushing does not succeed
+     * @since 3.1
+     */
+    protected final void checkRewriteSession()  {
+        if (hasActiveRewriteSession())
+            flushRewriteSession();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/Assert.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,341 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.Assert;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * <code>Assert</code> is useful for for embedding runtime sanity checks
+ * in code. The static predicate methods all test a condition and throw some
+ * type of unchecked exception if the condition does not hold.
+ * <p>
+ * Assertion failure exceptions, like most runtime exceptions, are
+ * thrown when something is misbehaving. Assertion failures are invariably
+ * unspecified behavior; consequently, clients should never rely on
+ * these being thrown (or not thrown). <b>If you find yourself in the
+ * position where you need to catch an assertion failure, you have most
+ * certainly written your program incorrectly.</b>
+ * </p>
+ * <p>
+ * Note that an <code>assert</code> statement is slated to be added to the
+ * Java language in JDK 1.4, rending this class obsolete.
+ * </p>
+ *
+ * @deprecated As of 3.3, replaced by {@link org.eclipse.core.runtime.Assert}
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public final class Assert {
+
+        /**
+         * <code>AssertionFailedException</code> is a runtime exception thrown
+         * by some of the methods in <code>Assert</code>.
+         * <p>
+         * This class is not declared public to prevent some misuses; programs that catch
+         * or otherwise depend on assertion failures are susceptible to unexpected
+         * breakage when assertions in the code are added or removed.
+         * </p>
+         * <p>
+         * This class is not intended to be serialized.
+         * </p>
+         */
+        private static class AssertionFailedException : RuntimeException {
+
+            /**
+             * Serial version UID for this class.
+             * <p>
+             * Note: This class is not intended to be serialized.
+             * </p>
+             * @since 3.1
+             */
+            private static const long serialVersionUID= 3689918374733886002L;
+
+            /**
+             * Constructs a new exception.
+             */
+            public this() {
+            }
+
+            /**
+             * Constructs a new exception with the given message.
+             *
+             * @param detail the detailed message
+             */
+            public this(String detail) {
+                super(detail);
+            }
+        }
+
+    /* This class is not intended to be instantiated. */
+    private this() {
+    }
+
+    /**
+     * Asserts that an argument is legal. If the given bool is
+     * not <code>true</code>, an <code>IllegalArgumentException</code>
+     * is thrown.
+     *
+     * @param expression the outcome of the check
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     * @exception IllegalArgumentException if the legality test failed
+     */
+    public static bool isLegal(bool expression) {
+        // succeed as quickly as possible
+        if (expression) {
+            return true;
+        }
+        return isLegal(expression, "");//$NON-NLS-1$
+    }
+
+    /**
+     * Asserts that an argument is legal. If the given bool is
+     * not <code>true</code>, an <code>IllegalArgumentException</code>
+     * is thrown.
+     * The given message is included in that exception, to aid debugging.
+     *
+     * @param expression the outcome of the check
+     * @param message the message to include in the exception
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     * @exception IllegalArgumentException if the legality test failed
+     */
+    public static bool isLegal(bool expression, String message) {
+        if (!expression)
+            throw new IllegalArgumentException("assertion failed; " ~ message); //$NON-NLS-1$
+        return expression;
+    }
+
+    /**
+     * Asserts that the given object is not <code>null</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     * <p>
+     * As a general rule, parameters passed to API methods must not be
+     * <code>null</code> unless <b>explicitly</b> allowed in the method's
+     * specification. Similarly, results returned from API methods are never
+     * <code>null</code> unless <b>explicitly</b> allowed in the method's
+     * specification. Implementations are encouraged to make regular use of
+     * <code>Assert.isNotNull</code> to ensure that <code>null</code>
+     * parameters are detected as early as possible.
+     * </p>
+     *
+     * @param object the value to test
+     * @exception RuntimeException an unspecified unchecked exception if the object
+     *   is <code>null</code>
+     */
+    public static void isNotNull(Object object) {
+        // succeed as quickly as possible
+        if (object !is null) {
+            return;
+        }
+        isNotNull(object, "");//$NON-NLS-1$
+    }
+
+    /**
+     * Asserts that the given object is not <code>null</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     * The given message is included in that exception, to aid debugging.
+     * <p>
+     * As a general rule, parameters passed to API methods must not be
+     * <code>null</code> unless <b>explicitly</b> allowed in the method's
+     * specification. Similarly, results returned from API methods are never
+     * <code>null</code> unless <b>explicitly</b> allowed in the method's
+     * specification. Implementations are encouraged to make regular use of
+     * <code>Assert.isNotNull</code> to ensure that <code>null</code>
+     * parameters are detected as early as possible.
+     * </p>
+     *
+     * @param object the value to test
+     * @param message the message to include in the exception
+     * @exception RuntimeException an unspecified unchecked exception if the object
+     *   is <code>null</code>
+     */
+    public static void isNotNull(Object object, String message) {
+        if (object is null)
+            throw new AssertionFailedException("null argument;" ~ message);//$NON-NLS-1$
+    }
+
+    /**
+     * Asserts that the given bool is <code>true</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     *
+     * @param expression the outcome of the check
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     */
+    public static bool isTrue(bool expression) {
+        // succeed as quickly as possible
+        if (expression) {
+            return true;
+        }
+        return isTrue(expression, "");//$NON-NLS-1$
+    }
+
+    /**
+     * Asserts that the given bool is <code>true</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     * The given message is included in that exception, to aid debugging.
+     *
+     * @param expression the outcome of the check
+     * @param message the message to include in the exception
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     */
+    public static bool isTrue(bool expression, String message) {
+        if (!expression)
+            throw new AssertionFailedException("Assertion failed: "~message);//$NON-NLS-1$
+        return expression;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/BadLocationException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.BadLocationException;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+
+/**
+ * Indicates the attempt to access a non-existing position. The attempt has been
+ * performed on a text store such as a document or string.
+ * <p>
+ * This class is not intended to be serialized.
+ * </p>
+ */
+public class BadLocationException : Exception {
+
+    /**
+     * Serial version UID for this class.
+     * <p>
+     * Note: This class is not intended to be serialized.
+     * </p>
+     * @since 3.1
+     */
+    private static const long serialVersionUID= 3257281452776370224L;
+
+    /**
+     * Creates a new bad location exception.
+     */
+    public this() {
+        super(null);
+    }
+
+    /**
+     * Creates a new bad location exception.
+     *
+     * @param message the exception message
+     */
+    public this(String message) {
+        super(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/BadPartitioningException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.BadPartitioningException;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Represents the attempt to refer to a non-existing document partitioning.
+ * <p>
+ * This class is not intended to be serialized.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.IDocument
+ * @see org.eclipse.jface.text.IDocumentExtension3
+ * @since 3.0
+ */
+public class BadPartitioningException : Exception {
+
+    /**
+     * Serial version UID for this class.
+     * <p>
+     * Note: This class is not intended to be serialized.
+     * </p>
+     * @since 3.1
+     */
+    private static const long serialVersionUID= 3256439205327876408L;
+
+    /**
+     * Creates a new bad partitioning exception.
+     */
+    public this() {
+        super("");
+    }
+
+    /**
+     * Creates a new bad partitioning exception.
+     *
+     * @param message message describing the exception
+     */
+    public this(String message) {
+        super(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/BadPositionCategoryException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,196 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.BadPositionCategoryException;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+
+/**
+ * Indicates the attempt to access a non-existing position
+ * category in a document.
+ * <p>
+ * This class is not intended to be serialized.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.IDocument
+ */
+public class BadPositionCategoryException : Exception {
+
+    /**
+     * Serial version UID for this class.
+     * <p>
+     * Note: This class is not intended to be serialized.
+     * </p>
+     * @since 3.1
+     */
+    private static const long serialVersionUID= 3761405300745713206L;
+
+    /**
+     * Creates a new bad position category exception.
+     */
+    public this() {
+        super( null );
+    }
+
+    /**
+     * Creates a new bad position category exception.
+     *
+     * @param message the exception's message
+     */
+    public this(String message) {
+        super(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ConfigurableLineTracker.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,224 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ConfigurableLineTracker;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * Standard implementation of a generic
+ * {@link org.eclipse.jface.text.ILineTracker}.
+ * <p>
+ * The line tracker can be configured with the set of legal line delimiters.
+ * Line delimiters are unconstrained. The line delimiters are used to compute
+ * the tracker's line structure. In the case of overlapping line delimiters, the
+ * longest line delimiter is given precedence of the shorter ones.
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ConfigurableLineTracker : AbstractLineTracker {
+
+
+    /** The strings which are considered being the line delimiter */
+    private String[] fDelimiters;
+    /** A predefined delimiter information which is always reused as return value */
+    private AbstractLineTracker_DelimiterInfo fDelimiterInfo;
+
+
+    /**
+     * Creates a standard line tracker for the given line delimiters.
+     *
+     * @param legalLineDelimiters the tracker's legal line delimiters,
+     *      may not be <code>null</code> and must be longer than 0
+     */
+    public this(String[] legalLineDelimiters) {
+        Assert.isTrue(legalLineDelimiters.length > 0);
+        fDelimiterInfo= new AbstractLineTracker_DelimiterInfo();
+        fDelimiters= TextUtilities.copy(legalLineDelimiters);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLegalLineDelimiters()
+     */
+    public String[] getLegalLineDelimiters() {
+        return TextUtilities.copy(fDelimiters);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractLineTracker#nextDelimiterInfo(java.lang.String, int)
+     */
+    protected AbstractLineTracker_DelimiterInfo nextDelimiterInfo(String text, int offset) {
+        if (fDelimiters.length > 1) {
+            int[] info= TextUtilities.indexOf(fDelimiters, text, offset);
+            if (info[0] is -1)
+                return null;
+            fDelimiterInfo.delimiterIndex= info[0];
+            fDelimiterInfo.delimiter= fDelimiters[info[1]];
+        } else {
+            int index= text.indexOf(fDelimiters[0], offset);
+            if (index is -1)
+                return null;
+            fDelimiterInfo.delimiterIndex= index;
+            fDelimiterInfo.delimiter= fDelimiters[0];
+        }
+
+        fDelimiterInfo.delimiterLength= fDelimiterInfo.delimiter.length();
+        return fDelimiterInfo;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/CopyOnWriteTextStore.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,303 @@
+/*******************************************************************************
+ * 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:
+ *     Anton Leherbauer (anton.leherbauer@windriver.com) - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.CopyOnWriteTextStore;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * Copy-on-write <code>ITextStore</code> wrapper.
+ * <p>
+ * This implementation uses an unmodifiable text store for the initial content.
+ * Upon first modification attempt, the unmodifiable store is replaced with
+ * a modifiable instance which must be supplied in the constructor.</p>
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ *
+ * @since 3.2
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class CopyOnWriteTextStore : ITextStore {
+
+    /**
+     * An unmodifiable String based text store. It is not possible to modify the content
+     * other than using {@link #set}. Trying to {@link #replace} a text range will
+     * throw an <code>UnsupportedOperationException</code>.
+     */
+    private static class StringTextStore : ITextStore {
+
+        /** Represents the content of this text store. */
+        private String fText= ""; //$NON-NLS-1$
+
+        /**
+         * Create an empty text store.
+         */
+        private this() {
+//             super();
+        }
+
+        /**
+         * Create a text store with initial content.
+         * @param text  the initial content
+         */
+        private this(String text) {
+//             super();
+            set(text);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextStore#get(int)
+         */
+        public char get(int offset) {
+            return fText.charAt(offset);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextStore#get(int, int)
+         */
+        public String get(int offset, int length) {
+            return fText.substring(offset, offset + length);
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextStore#getLength()
+         */
+        public int getLength() {
+            return fText.length();
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextStore#replace(int, int, java.lang.String)
+         */
+        public void replace(int offset, int length, String text) {
+            // modification not supported
+            throw new UnsupportedOperationException();
+        }
+
+        /*
+         * @see org.eclipse.jface.text.ITextStore#set(java.lang.String)
+         */
+        public void set(String text) {
+            fText= text !is null ? text : ""; //$NON-NLS-1$
+        }
+
+    }
+
+    /** The underlying "real" text store */
+    protected ITextStore fTextStore;
+
+    /** A modifiable <code>ITextStore</code> instance */
+    private const ITextStore fModifiableTextStore;
+
+    /**
+     * Creates an empty text store. The given text store will be used upon first
+     * modification attempt.
+     *
+     * @param modifiableTextStore
+     *            a modifiable <code>ITextStore</code> instance, may not be
+     *            <code>null</code>
+     */
+    public this(ITextStore modifiableTextStore) {
+        Assert.isNotNull(cast(Object)modifiableTextStore);
+        fTextStore= new StringTextStore();
+        fTextStore= new StringTextStore();
+        fModifiableTextStore= modifiableTextStore;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#get(int)
+     */
+    public char get(int offset) {
+        return fTextStore.get(offset);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#get(int, int)
+     */
+    public String get(int offset, int length) {
+        return fTextStore.get(offset, length);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#getLength()
+     */
+    public int getLength() {
+        return fTextStore.getLength();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#replace(int, int, java.lang.String)
+     */
+    public void replace(int offset, int length, String text) {
+        if (fTextStore !is fModifiableTextStore) {
+            String content= fTextStore.get(0, fTextStore.getLength());
+            fTextStore= fModifiableTextStore;
+            fTextStore.set(content);
+        }
+        fTextStore.replace(offset, length, text);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#set(java.lang.String)
+     */
+    public void set(String text) {
+        fTextStore= new StringTextStore(text);
+        fModifiableTextStore.set(""); //$NON-NLS-1$
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/DefaultLineTracker.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,230 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.DefaultLineTracker;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Standard implementation of {@link org.eclipse.jface.text.ILineTracker}.
+ * <p>
+ * The line tracker considers the three common line delimiters which are '\n',
+ * '\r', '\r\n'.
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class DefaultLineTracker : AbstractLineTracker {
+
+    /** The predefined delimiters of this tracker */
+    public const static String[] DELIMITERS= [ "\r", "\n", "\r\n" ]; //$NON-NLS-3$ //$NON-NLS-1$ //$NON-NLS-2$
+    /** A predefined delimiter information which is always reused as return value */
+    private AbstractLineTracker_DelimiterInfo fDelimiterInfo;
+
+
+    /**
+     * Creates a standard line tracker.
+     */
+    public this() {
+        fDelimiterInfo= new AbstractLineTracker_DelimiterInfo();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLegalLineDelimiters()
+     */
+    public String[] getLegalLineDelimiters() {
+        return TextUtilities.copy(DELIMITERS);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractLineTracker#nextDelimiterInfo(java.lang.String, int)
+     */
+    protected AbstractLineTracker_DelimiterInfo nextDelimiterInfo(String text, int offset) {
+
+        char ch;
+        int length= text.length();
+        for (int i= offset; i < length; i++) {
+
+            ch= text.charAt(i);
+            if (ch is '\r') {
+
+                if (i + 1 < length) {
+                    if (text.charAt(i + 1) is '\n') {
+                        fDelimiterInfo.delimiter= DELIMITERS[2];
+                        fDelimiterInfo.delimiterIndex= i;
+                        fDelimiterInfo.delimiterLength= 2;
+                        return fDelimiterInfo;
+                    }
+                }
+
+                fDelimiterInfo.delimiter= DELIMITERS[0];
+                fDelimiterInfo.delimiterIndex= i;
+                fDelimiterInfo.delimiterLength= 1;
+                return fDelimiterInfo;
+
+            } else if (ch is '\n') {
+
+                fDelimiterInfo.delimiter= DELIMITERS[1];
+                fDelimiterInfo.delimiterIndex= i;
+                fDelimiterInfo.delimiterLength= 1;
+                return fDelimiterInfo;
+            }
+        }
+
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/DefaultPositionUpdater.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,394 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.DefaultPositionUpdater;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Default implementation of {@link org.eclipse.jface.text.IPositionUpdater}.
+ * <p>
+ * A default position updater must be configured with the position category whose positions it will
+ * update. Other position categories are not affected by this updater.
+ * </p>
+ * <p>
+ * This implementation follows the specification below:
+ * </p>
+ * <ul>
+ * <li>Inserting or deleting text before the position shifts the position accordingly.</li>
+ * <li>Inserting text at the position offset shifts the position accordingly.</li>
+ * <li>Inserting or deleting text strictly contained by the position shrinks or stretches the
+ * position.</li>
+ * <li>Inserting or deleting text after a position does not affect the position.</li>
+ * <li>Deleting text which strictly contains the position deletes the position. Note that the
+ * position is not deleted if its only shrunken to length zero. To delete a position, the
+ * modification must delete from <i>strictly before</i> to <i>strictly after</i> the position.</li>
+ * <li>Replacing text overlapping with the position is considered as a sequence of first deleting
+ * the replaced text and afterwards inserting the new text. Thus, a position might first be shifted
+ * and shrunken and then be stretched.</li>
+ * </ul>
+ * This class can be used as is or be adapted by subclasses. Fields are protected to allow
+ * subclasses direct access. Because of the frequency with which position updaters are used this is
+ * a performance decision.
+ */
+public class DefaultPositionUpdater : IPositionUpdater {
+
+    /** The position category the updater draws responsible for */
+    private String fCategory;
+
+    /** Caches the currently investigated position */
+    protected Position fPosition;
+    /**
+     * Remembers the original state of the investigated position
+     * @since 2.1
+     */
+    protected Position fOriginalPosition;
+    /** Caches the offset of the replaced text */
+    protected int fOffset;
+    /** Caches the length of the replaced text */
+    protected int fLength;
+    /** Caches the length of the newly inserted text */
+    protected int fReplaceLength;
+    /** Catches the document */
+    protected IDocument fDocument;
+
+
+    /**
+     * Creates a new default position updater for the given category.
+     *
+     * @param category the category the updater is responsible for
+     */
+    public this(String category) {
+        fOriginalPosition= new Position(0, 0);
+        fCategory= category;
+    }
+
+    /**
+     * Returns the category this updater is responsible for.
+     *
+     * @return the category this updater is responsible for
+     */
+    protected String getCategory() {
+        return fCategory;
+    }
+
+    /**
+     * Returns whether the current event describes a well formed replace
+     * by which the current position is directly affected.
+     *
+     * @return <code>true</code> the current position is directly affected
+     * @since 3.0
+     */
+    protected bool isAffectingReplace() {
+        return fLength > 0 && fReplaceLength > 0 && fPosition.length < fOriginalPosition.length;
+    }
+
+    /**
+     * Adapts the currently investigated position to an insertion.
+     */
+    protected void adaptToInsert() {
+
+        int myStart= fPosition.offset;
+        int myEnd=   fPosition.offset + fPosition.length - 1;
+        myEnd= Math.max(myStart, myEnd);
+
+        int yoursStart= fOffset;
+        int yoursEnd=   fOffset + fReplaceLength -1;
+        yoursEnd= Math.max(yoursStart, yoursEnd);
+
+        if (myEnd < yoursStart)
+            return;
+
+        if (fLength <= 0) {
+
+            if (myStart < yoursStart)
+                fPosition.length += fReplaceLength;
+            else
+                fPosition.offset += fReplaceLength;
+
+        } else {
+
+            if (myStart <= yoursStart && fOriginalPosition.offset <= yoursStart)
+                fPosition.length += fReplaceLength;
+            else
+                fPosition.offset += fReplaceLength;
+        }
+    }
+
+    /**
+     * Adapts the currently investigated position to a deletion.
+     */
+    protected void adaptToRemove() {
+
+        int myStart= fPosition.offset;
+        int myEnd=   fPosition.offset + fPosition.length -1;
+        myEnd= Math.max(myStart, myEnd);
+
+        int yoursStart= fOffset;
+        int yoursEnd=   fOffset + fLength -1;
+        yoursEnd= Math.max(yoursStart, yoursEnd);
+
+        if (myEnd < yoursStart)
+            return;
+
+        if (myStart <= yoursStart) {
+
+            if (yoursEnd <= myEnd)
+                fPosition.length -= fLength;
+            else
+                fPosition.length -= (myEnd - yoursStart +1);
+
+        } else if (yoursStart < myStart) {
+
+            if (yoursEnd < myStart)
+                fPosition.offset -= fLength;
+            else {
+                fPosition.offset -= (myStart - yoursStart);
+                fPosition.length -= (yoursEnd - myStart +1);
+            }
+
+        }
+
+        // validate position to allowed values
+        if (fPosition.offset < 0)
+            fPosition.offset= 0;
+
+        if (fPosition.length < 0)
+            fPosition.length= 0;
+    }
+
+    /**
+     * Adapts the currently investigated position to the replace operation.
+     * First it checks whether the change replaces the whole range of the position.
+     * If not, it performs first the deletion of the previous text and afterwards
+     * the insertion of the new text.
+     */
+    protected void adaptToReplace() {
+
+        if (fPosition.offset is fOffset && fPosition.length is fLength && fPosition.length > 0) {
+
+            // replace the whole range of the position
+            fPosition.length += (fReplaceLength - fLength);
+            if (fPosition.length < 0) {
+                fPosition.offset += fPosition.length;
+                fPosition.length= 0;
+            }
+
+        } else {
+
+            if (fLength >  0)
+                adaptToRemove();
+
+            if (fReplaceLength > 0)
+                adaptToInsert();
+        }
+    }
+
+    /**
+     * Determines whether the currently investigated position has been deleted by
+     * the replace operation specified in the current event. If so, it deletes
+     * the position and removes it from the document's position category.
+     *
+     * @return <code>true</code> if position has not been deleted
+     */
+    protected bool notDeleted() {
+
+        if (fOffset < fPosition.offset && (fPosition.offset + fPosition.length < fOffset + fLength)) {
+
+            fPosition.delete_();
+
+            try {
+                fDocument.removePosition(fCategory, fPosition);
+            } catch (BadPositionCategoryException x) {
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent)
+     */
+    public void update(DocumentEvent event) {
+
+        try {
+
+
+            fOffset= event.getOffset();
+            fLength= event.getLength();
+            fReplaceLength= (event.getText() is null ? 0 : event.getText().length());
+            fDocument= event.getDocument();
+
+            Position[] category= fDocument.getPositions(fCategory);
+            for (int i= 0; i < category.length; i++) {
+
+                fPosition= category[i];
+                fOriginalPosition.offset= fPosition.offset;
+                fOriginalPosition.length= fPosition.length;
+
+                if (notDeleted())
+                    adaptToReplace();
+            }
+
+        } catch (BadPositionCategoryException x) {
+            // do nothing
+        } finally {
+            fDocument= null;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/Document.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,268 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.Document;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Default document implementation. Uses a {@link org.eclipse.jface.text.GapTextStore} wrapped
+ * inside a {@link org.eclipse.jface.text.CopyOnWriteTextStore} as text store.
+ * <p>
+ * The used line tracker considers the following strings as line delimiters: "\n", "\r", "\r\n".
+ * </p>
+ * <p>
+ * The document is ready to use. It has a default position category for which a default position
+ * updater is installed.
+ * </p>
+ * <p>
+ * <strong>Performance:</strong> The implementation should perform reasonably well for typical
+ * source code documents. It is not designed for very large documents of a size of several
+ * megabytes. Space-saving implementations are initially used for both the text store and the line
+ * tracker; the first modification after a {@link #set(String) set} incurs the cost to transform the
+ * document structures to efficiently handle updates.
+ * </p>
+ * <p>
+ * See {@link GapTextStore} and <code>TreeLineTracker</code> for algorithmic behavior of the used
+ * document structures.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.GapTextStore
+ * @see org.eclipse.jface.text.CopyOnWriteTextStore
+ */
+public class Document : AbstractDocument {
+    /**
+     * Creates a new empty document.
+     */
+    public this() {
+        super();
+        setTextStore(new CopyOnWriteTextStore(new GapTextStore()));
+        setLineTracker(new DefaultLineTracker());
+        completeInitialization();
+    }
+
+    /**
+     * Creates a new document with the given initial content.
+     *
+     * @param initialContent the document's initial content
+     */
+    public this(String initialContent) {
+        super();
+        setTextStore(new CopyOnWriteTextStore(new GapTextStore()));
+        setLineTracker(new DefaultLineTracker());
+        getStore().set(initialContent);
+        getTracker().set(initialContent);
+        completeInitialization();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IRepairableDocumentExtension#isLineInformationRepairNeeded(int, int, java.lang.String)
+     * @since 3.4
+     */
+    public bool isLineInformationRepairNeeded(int offset, int length, String text)  {
+        if ((0 > offset) || (0 > length) || (offset + length > getLength()))
+            throw new BadLocationException();
+
+        return isLineInformationRepairNeeded(text) || isLineInformationRepairNeeded(get(offset, length));
+    }
+
+    /**
+     * Checks whether the line information needs to be repaired.
+     *
+     * @param text the text to check
+     * @return <code>true</code> if the line information must be repaired
+     * @since 3.4
+     */
+    private bool isLineInformationRepairNeeded(String text) {
+        if (text is null)
+            return false;
+
+        int length= text.length();
+        if (length is 0)
+            return false;
+
+        int rIndex= text.indexOf('\r');
+        int nIndex= text.indexOf('\n');
+        if (rIndex is -1 && nIndex is -1)
+            return false;
+
+        if (rIndex > 0 && rIndex < length-1 && nIndex > 1 && rIndex < length-2)
+            return false;
+
+        String defaultLD= null;
+        try {
+            defaultLD= getLineDelimiter(0);
+        } catch (BadLocationException x) {
+            return true;
+        }
+
+        if (defaultLD is null)
+            return false;
+
+        defaultLD= getDefaultLineDelimiter();
+
+        if (defaultLD.length is 1) {
+            if (rIndex !is -1 && !"\r".equals(defaultLD)) //$NON-NLS-1$
+                return true;
+            if (nIndex !is -1 && !"\n".equals(defaultLD)) //$NON-NLS-1$
+                return true;
+        } else if (defaultLD.length is 2)
+            return rIndex is -1 || nIndex - rIndex !is 1;
+
+        return false;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/DocumentEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,304 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.DocumentEvent;
+
+// import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+// import org.eclipse.jface.text.DefaultTextHover; // packageimport
+// import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+// import org.eclipse.jface.text.TextUtilities; // packageimport
+// import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+// import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+// import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+// import org.eclipse.jface.text.ITextSelection; // packageimport
+// import org.eclipse.jface.text.Document; // packageimport
+// import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+// import org.eclipse.jface.text.ITextListener; // packageimport
+// import org.eclipse.jface.text.BadPartitioningException; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+// import org.eclipse.jface.text.IUndoManager; // packageimport
+// import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+// import org.eclipse.jface.text.IRepairableDocument; // packageimport
+// import org.eclipse.jface.text.IRewriteTarget; // packageimport
+// import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+// import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+// import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+// import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+// import org.eclipse.jface.text.TextViewer; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+// import org.eclipse.jface.text.RegExMessages; // packageimport
+// import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+// import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+// import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+// import org.eclipse.jface.text.IViewportListener; // packageimport
+// import org.eclipse.jface.text.GapTextStore; // packageimport
+// import org.eclipse.jface.text.MarkSelection; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+// import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+// import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+// import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+// import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+// import org.eclipse.jface.text.TypedRegion; // packageimport
+// import org.eclipse.jface.text.ISynchronizable; // packageimport
+// import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+// import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+// import org.eclipse.jface.text.IRegion; // packageimport
+// import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+// import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+// import org.eclipse.jface.text.Assert; // packageimport
+// import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+// import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+// import org.eclipse.jface.text.DocumentClone; // packageimport
+// import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+// import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+// import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+// import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+// import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+// import org.eclipse.jface.text.TextSelection; // packageimport
+// import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+// import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+// import org.eclipse.jface.text.IPainter; // packageimport
+// import org.eclipse.jface.text.IInformationControl; // packageimport
+// import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+// import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+// import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+// import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+// import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+// import org.eclipse.jface.text.ITextHover; // packageimport
+// import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+// import org.eclipse.jface.text.ILineTracker; // packageimport
+// import org.eclipse.jface.text.Line; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+// import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+// import org.eclipse.jface.text.TextEvent; // packageimport
+// import org.eclipse.jface.text.BadLocationException; // packageimport
+// import org.eclipse.jface.text.AbstractDocument; // packageimport
+// import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+// import org.eclipse.jface.text.TreeLineTracker; // packageimport
+// import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+// import org.eclipse.jface.text.Region; // packageimport
+// import org.eclipse.jface.text.ITextViewer; // packageimport
+// import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+// import org.eclipse.jface.text.MarginPainter; // packageimport
+// import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+// import org.eclipse.jface.text.TextPresentation; // packageimport
+// import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+// import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+// import org.eclipse.jface.text.ISelectionValidator; // packageimport
+// import org.eclipse.jface.text.IDocumentExtension; // packageimport
+// import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+// import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+// import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+// import org.eclipse.jface.text.IDocumentListener; // packageimport
+// import org.eclipse.jface.text.PaintManager; // packageimport
+// import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+// import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+// import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+// import org.eclipse.jface.text.Position; // packageimport
+// import org.eclipse.jface.text.TextMessages; // packageimport
+// import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+// import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+// import org.eclipse.jface.text.IPositionUpdater; // packageimport
+// import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+// import org.eclipse.jface.text.ListLineTracker; // packageimport
+// import org.eclipse.jface.text.ITextInputListener; // packageimport
+// import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+// import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+// import org.eclipse.jface.text.IInputChangedListener; // packageimport
+// import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+// import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+// import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+// import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+// import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+// import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+// import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+// import org.eclipse.jface.text.CursorLinePainter; // packageimport
+// import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+// import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+// import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+// import org.eclipse.jface.text.DocumentCommand; // packageimport
+// import org.eclipse.jface.text.TypedPosition; // packageimport
+// import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+// import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+// import org.eclipse.jface.text.IEditingSupport; // packageimport
+// import org.eclipse.jface.text.IMarkSelection; // packageimport
+// import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+// import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+// import org.eclipse.jface.text.ITextStore; // packageimport
+// import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+// import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+// import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+// import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+// import org.eclipse.jface.text.TextAttribute; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+// import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * Specification of changes applied to documents. All changes are represented as
+ * replace commands, i.e. specifying a document range whose text gets replaced
+ * with different text. In addition to this information, the event also contains
+ * the changed document.
+ *
+ * @see org.eclipse.jface.text.IDocument
+ */
+public class DocumentEvent {
+
+    /**
+     * Debug option for asserting that text is not null.
+     * If the <code>org.eclipse.text/debug/DocumentEvent/assertTextNotNull</code>
+     * system property is <code>true</code>
+     *
+     * @since 3.3
+     */
+    private static bool ASSERT_TEXT_NOT_NULL_init = false;
+    private static bool ASSERT_TEXT_NOT_NULL_;
+    private static bool ASSERT_TEXT_NOT_NULL(){
+        if( !ASSERT_TEXT_NOT_NULL_init ){
+            ASSERT_TEXT_NOT_NULL_init = true;
+            ASSERT_TEXT_NOT_NULL_= Boolean.getBoolean("org.eclipse.text/debug/DocumentEvent/assertTextNotNull"); //$NON-NLS-1$
+        }
+        return ASSERT_TEXT_NOT_NULL_;
+    }
+
+    /** The changed document */
+    public IDocument fDocument;
+    /** The document offset */
+    public int fOffset;
+    /** Length of the replaced document text */
+    public int fLength;
+    /** Text inserted into the document */
+    public String fText= ""; //$NON-NLS-1$
+    /**
+     * The modification stamp of the document when firing this event.
+     * @since 3.1 and public since 3.3
+     */
+    public long fModificationStamp;
+
+    /**
+     * Creates a new document event.
+     *
+     * @param doc the changed document
+     * @param offset the offset of the replaced text
+     * @param length the length of the replaced text
+     * @param text the substitution text
+     */
+    public this(IDocument doc, int offset, int length, String text) {
+
+        Assert.isNotNull(cast(Object)doc);
+        Assert.isTrue(offset >= 0);
+        Assert.isTrue(length >= 0);
+
+        if (ASSERT_TEXT_NOT_NULL)
+            Assert.isNotNull(text);
+
+        fDocument= doc;
+        fOffset= offset;
+        fLength= length;
+        fText= text;
+
+        if ( cast(IDocumentExtension4)fDocument )
+            fModificationStamp= (cast(IDocumentExtension4)fDocument).getModificationStamp();
+        else
+            fModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+    }
+
+    /**
+     * Creates a new, not initialized document event.
+     */
+    public this() {
+    }
+
+    /**
+     * Returns the changed document.
+     *
+     * @return the changed document
+     */
+    public IDocument getDocument() {
+        return fDocument;
+    }
+
+    /**
+     * Returns the offset of the change.
+     *
+     * @return the offset of the change
+     */
+    public int getOffset() {
+        return fOffset;
+    }
+
+    /**
+     * Returns the length of the replaced text.
+     *
+     * @return the length of the replaced text
+     */
+    public int getLength() {
+        return fLength;
+    }
+
+    /**
+     * Returns the text that has been inserted.
+     *
+     * @return the text that has been inserted
+     */
+    public String getText() {
+        return fText;
+    }
+
+    /**
+     * Returns the document's modification stamp at the
+     * time when this event was sent.
+     *
+     * @return the modification stamp or {@link IDocumentExtension4#UNKNOWN_MODIFICATION_STAMP}.
+     * @see IDocumentExtension4#getModificationStamp()
+     * @since 3.1
+     */
+    public long getModificationStamp() {
+        return fModificationStamp;
+    }
+
+    /*
+     * @see java.lang.Object#toString()
+     * @since 3.4
+     */
+    public override String toString() {
+        StringBuffer buffer= new StringBuffer();
+        buffer.append("offset: " ); //$NON-NLS-1$
+        buffer.append(fOffset);
+        buffer.append(", length: " ); //$NON-NLS-1$
+        buffer.append(fLength);
+        buffer.append(", timestamp: " ); //$NON-NLS-1$
+        buffer.append(fModificationStamp);
+        buffer.append("\ntext:>" ); //$NON-NLS-1$
+        buffer.append(fText);
+        buffer.append("<\n" ); //$NON-NLS-1$
+        return buffer.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/DocumentPartitioningChangedEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,270 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.DocumentPartitioningChangedEvent;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * Event describing the change of document partitionings.
+ *
+ * @see org.eclipse.jface.text.IDocumentExtension3
+ * @since 3.0
+ */
+public class DocumentPartitioningChangedEvent {
+
+    /** The document whose partitionings changed */
+    private const IDocument fDocument;
+    /** The map of partitionings to changed regions. */
+    private const Map fMap;
+
+
+    /**
+     * Creates a new document partitioning changed event for the given document.
+     * Initially this event is empty, i.e. does not describe any change.
+     *
+     * @param document the changed document
+     */
+    public this(IDocument document) {
+        fMap= new HashMap();
+        fDocument= document;
+    }
+
+    /**
+     * Returns the changed document.
+     *
+     * @return the changed document
+     */
+    public IDocument getDocument() {
+        return fDocument;
+    }
+
+    /**
+     * Returns the changed region of the given partitioning or <code>null</code>
+     * if the given partitioning did not change.
+     *
+     * @param partitioning the partitioning
+     * @return the changed region of the given partitioning or <code>null</code>
+     */
+    public IRegion getChangedRegion(String partitioning) {
+        return cast(IRegion) fMap.get(partitioning);
+    }
+
+    /**
+     * Returns the set of changed partitionings.
+     *
+     * @return the set of changed partitionings
+     */
+    public String[] getChangedPartitionings() {
+        return stringcast(fMap.keySet().toArray());
+    }
+
+    /**
+     * Sets the specified range as changed region for the given partitioning.
+     *
+     * @param partitioning the partitioning
+     * @param offset the region offset
+     * @param length the region length
+     */
+    public void setPartitionChange(String partitioning, int offset, int length) {
+        //Assert.isNotNull(partitioning);
+        fMap.put(partitioning, new Region(offset, length));
+    }
+
+    /**
+     * Returns <code>true</code> if the set of changed partitionings is empty,
+     * <code>false</code> otherwise.
+     *
+     * @return <code>true</code> if the set of changed partitionings is empty
+     */
+    public bool isEmpty() {
+        return fMap.isEmpty();
+    }
+
+    /**
+     * Returns the coverage of this event. This is the minimal region that
+     * contains all changed regions of all changed partitionings.
+     *
+     * @return the coverage of this event
+     */
+    public IRegion getCoverage() {
+        if (fMap.isEmpty())
+            return new Region(0, 0);
+
+        int offset= -1;
+        int endOffset= -1;
+        Iterator e= fMap.values().iterator();
+        while (e.hasNext()) {
+            IRegion r= cast(IRegion) e.next();
+
+            if (offset < 0 || r.getOffset() < offset)
+                offset= r.getOffset();
+
+            int end= r.getOffset() + r.getLength();
+            if (end > endOffset)
+                endOffset= end;
+        }
+
+        return new Region(offset, endOffset - offset);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/DocumentRewriteSession.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,197 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.DocumentRewriteSession;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * A document rewrite session.
+ *
+ * @see org.eclipse.jface.text.IDocument
+ * @see org.eclipse.jface.text.IDocumentExtension4
+ * @see org.eclipse.jface.text.IDocumentRewriteSessionListener
+ * @since 3.1
+ */
+public class DocumentRewriteSession {
+
+    private DocumentRewriteSessionType fSessionType;
+
+    /**
+     * Prohibit package external object creation.
+     *
+     * @param sessionType the type of this session
+     */
+    /+protected+/ this(DocumentRewriteSessionType sessionType) {
+        fSessionType= sessionType;
+    }
+
+    /**
+     * Returns the type of this session.
+     *
+     * @return the type of this session
+     */
+    public DocumentRewriteSessionType getSessionType() {
+        return fSessionType;
+    }
+
+    /*
+     * @see java.lang.Object#toString()
+     */
+    public override String toString() {
+        static assert( hash_t.sizeof is 4 );
+        return (new StringBuffer()).append(Integer.toString(toHash())).toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/DocumentRewriteSessionEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,247 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.DocumentRewriteSessionEvent;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * Description of the state of document rewrite sessions.
+ *
+ * @see org.eclipse.jface.text.IDocument
+ * @see org.eclipse.jface.text.IDocumentExtension4
+ * @see org.eclipse.jface.text.IDocumentRewriteSessionListener
+ * @since 3.1
+ */
+public class DocumentRewriteSessionEvent {
+
+    private static Object SESSION_START_;
+    public static Object SESSION_START(){
+        if( SESSION_START_ is null ){
+            synchronized(DocumentRewriteSessionEvent.classinfo){
+                if( SESSION_START_ is null ){
+                    SESSION_START_ = new Object();
+                }
+            }
+        }
+        return SESSION_START_;
+    }
+    private static Object SESSION_STOP_;
+    public static Object SESSION_STOP(){
+        if( SESSION_STOP_ is null ){
+            synchronized(DocumentRewriteSessionEvent.classinfo){
+                if( SESSION_STOP_ is null ){
+                    SESSION_STOP_ = new Object();
+                }
+            }
+        }
+        return SESSION_STOP_;
+    }
+
+    /** The changed document */
+    public IDocument fDocument;
+    /** The session */
+    public DocumentRewriteSession fSession;
+    /** The change type */
+    public Object fChangeType;
+
+    /**
+     * Creates a new document event.
+     *
+     * @param doc the changed document
+     * @param session the session
+     * @param changeType the change type. This is either
+     *            {@link DocumentRewriteSessionEvent#SESSION_START} or
+     *            {@link DocumentRewriteSessionEvent#SESSION_STOP}.
+     */
+    public this(IDocument doc, DocumentRewriteSession session, Object changeType) {
+        Assert.isNotNull(cast(Object)doc);
+        Assert.isNotNull(session);
+
+        fDocument= doc;
+        fSession= session;
+        fChangeType= changeType;
+    }
+
+    /**
+     * Returns the changed document.
+     *
+     * @return the changed document
+     */
+    public IDocument getDocument() {
+        return fDocument;
+    }
+
+    /**
+     * Returns the change type of this event. This is either
+     * {@link DocumentRewriteSessionEvent#SESSION_START}or
+     * {@link DocumentRewriteSessionEvent#SESSION_STOP}.
+     *
+     * @return the change type of this event
+     */
+    public Object getChangeType() {
+        return fChangeType;
+    }
+
+    /**
+     * Returns the rewrite session.
+     *
+     * @return the rewrite session
+     */
+    public DocumentRewriteSession getSession() {
+        return fSession;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/DocumentRewriteSessionType.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.DocumentRewriteSessionType;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A document rewrite session type.
+ * <p>
+ * Allowed values are:
+ * <ul>
+ *  <li>{@link DocumentRewriteSessionType#UNRESTRICTED}</li>
+ *  <li>{@link DocumentRewriteSessionType#UNRESTRICTED_SMALL} (since 3.3)</li>
+ *  <li>{@link DocumentRewriteSessionType#SEQUENTIAL}</li>
+ *  <li>{@link DocumentRewriteSessionType#STRICTLY_SEQUENTIAL}</li>
+ * </ul>
+ * </p>
+ *
+ * @see org.eclipse.jface.text.IDocument
+ * @see org.eclipse.jface.text.IDocumentExtension4
+ * @see org.eclipse.jface.text.IDocumentRewriteSessionListener
+ * @since 3.1
+ */
+public class DocumentRewriteSessionType {
+
+    static this(){
+        UNRESTRICTED= new DocumentRewriteSessionType();
+        UNRESTRICTED_SMALL= new DocumentRewriteSessionType();
+        SEQUENTIAL= new DocumentRewriteSessionType();
+        STRICTLY_SEQUENTIAL= new DocumentRewriteSessionType();
+    }
+
+    /**
+     * An unrestricted rewrite session is a sequence of unrestricted replace operations. This
+     * session type should only be used for <em>large</em> operations that touch more than about
+     * fifty lines. Use {@link #UNRESTRICTED_SMALL} for small operations.
+     */
+    public const static DocumentRewriteSessionType UNRESTRICTED;
+    /**
+     * An small unrestricted rewrite session is a short sequence of unrestricted replace operations.
+     * This should be used for changes that touch less than about fifty lines.
+     *
+     * @since 3.3
+     */
+    public const static DocumentRewriteSessionType UNRESTRICTED_SMALL;
+    /**
+     * A sequential rewrite session is a sequence of non-overlapping replace
+     * operations starting at an arbitrary document offset.
+     */
+    public const static DocumentRewriteSessionType SEQUENTIAL;
+    /**
+     * A strictly sequential rewrite session is a sequence of non-overlapping
+     * replace operations from the start of the document to its end.
+     */
+    public const static DocumentRewriteSessionType STRICTLY_SEQUENTIAL;
+
+
+    /**
+     * Prohibit external object creation.
+     */
+    private this() {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/FindReplaceDocumentAdapter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,823 @@
+/*******************************************************************************
+ * 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
+ *     Cagatay Calli <ccalli@gmail.com> - [find/replace] retain caps when replacing - https://bugs.eclipse.org/bugs/show_bug.cgi?id=28949
+ *     Cagatay Calli <ccalli@gmail.com> - [find/replace] define & fix behavior of retain caps with other escapes and text before \C - https://bugs.eclipse.org/bugs/show_bug.cgi?id=217061
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.FindReplaceDocumentAdapter;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+import org.eclipse.dwtxhelper.regex;
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * Provides search and replace operations on
+ * {@link org.eclipse.jface.text.IDocument}.
+ * <p>
+ * Replaces
+ * {@link org.eclipse.jface.text.IDocument#search(int, String, bool, bool, bool)}.
+ *
+ * @since 3.0
+ */
+public class FindReplaceDocumentAdapter : CharSequence {
+
+    /**
+     * Internal type for operation codes.
+     */
+    private static class FindReplaceOperationCode {
+    }
+
+    // Find/replace operation codes.
+    private static FindReplaceOperationCode FIND_FIRST_;
+    private static FindReplaceOperationCode FIND_FIRST(){
+        if( FIND_FIRST_ is null ){
+            synchronized( FindReplaceDocumentAdapter.classinfo ){
+                if( FIND_FIRST_ is null ){
+                    FIND_FIRST_ = new FindReplaceOperationCode();
+                }
+            }
+        }
+        return FIND_FIRST_;
+    }
+
+    private static FindReplaceOperationCode FIND_NEXT_;
+    private static FindReplaceOperationCode FIND_NEXT(){
+        if( FIND_NEXT_ is null ){
+            synchronized( FindReplaceDocumentAdapter.classinfo ){
+                if( FIND_NEXT_ is null ){
+                    FIND_NEXT_ = new FindReplaceOperationCode();
+                }
+            }
+        }
+        return FIND_NEXT_;
+    }
+    private static FindReplaceOperationCode REPLACE_;
+    private static FindReplaceOperationCode REPLACE(){
+        if( REPLACE_ is null ){
+            synchronized( FindReplaceDocumentAdapter.classinfo ){
+                if( REPLACE_ is null ){
+                    REPLACE_ = new FindReplaceOperationCode();
+                }
+            }
+        }
+        return REPLACE_;
+    }
+    private static FindReplaceOperationCode REPLACE_FIND_NEXT_;
+    private static FindReplaceOperationCode REPLACE_FIND_NEXT(){
+        if( REPLACE_FIND_NEXT_ is null ){
+            synchronized( FindReplaceDocumentAdapter.classinfo ){
+                if( REPLACE_FIND_NEXT_ is null ){
+                    REPLACE_FIND_NEXT_ = new FindReplaceOperationCode();
+                }
+            }
+        }
+        return REPLACE_FIND_NEXT_;
+    }
+
+    /**
+     * Retain case mode constants.
+     * @since 3.4
+     */
+    private static const int RC_MIXED= 0;
+    private static const int RC_UPPER= 1;
+    private static const int RC_LOWER= 2;
+    private static const int RC_FIRSTUPPER= 3;
+
+
+    /**
+     * The adapted document.
+     */
+    private IDocument fDocument;
+
+    /**
+     * State for findReplace.
+     */
+    private FindReplaceOperationCode fFindReplaceState= null;
+
+    /**
+     * The matcher used in findReplace.
+     */
+    private Matcher fFindReplaceMatcher;
+
+    /**
+     * The match offset from the last findReplace call.
+     */
+    private int fFindReplaceMatchOffset;
+
+    /**
+     * Retain case mode
+     */
+    private int fRetainCaseMode;
+
+    /**
+     * Constructs a new find replace document adapter.
+     *
+     * @param document the adapted document
+     */
+    public this(IDocument document) {
+        Assert.isNotNull(cast(Object)document);
+        fDocument= document;
+    }
+
+    /**
+     * Returns the location of a given string in this adapter's document based on a set of search criteria.
+     *
+     * @param startOffset document offset at which search starts
+     * @param findString the string to find
+     * @param forwardSearch the search direction
+     * @param caseSensitive indicates whether lower and upper case should be distinguished
+     * @param wholeWord indicates whether the findString should be limited by white spaces as
+     *          defined by Character.isWhiteSpace. Must not be used in combination with <code>regExSearch</code>.
+     * @param regExSearch if <code>true</code> findString represents a regular expression
+     *          Must not be used in combination with <code>wholeWord</code>.
+     * @return the find or replace region or <code>null</code> if there was no match
+     * @throws BadLocationException if startOffset is an invalid document offset
+     * @throws PatternSyntaxException if a regular expression has invalid syntax
+     */
+    public IRegion find(int startOffset, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord, bool regExSearch)  {
+        Assert.isTrue(!(regExSearch && wholeWord));
+
+        // Adjust offset to special meaning of -1
+        if (startOffset is -1 && forwardSearch)
+            startOffset= 0;
+        if (startOffset is -1 && !forwardSearch)
+            startOffset= length() - 1;
+
+        return findReplace(FIND_FIRST, startOffset, findString, null, forwardSearch, caseSensitive, wholeWord, regExSearch);
+    }
+
+    /**
+     * Stateful findReplace executes a FIND, REPLACE, REPLACE_FIND or FIND_FIRST operation.
+     * In case of REPLACE and REPLACE_FIND it sends a <code>DocumentEvent</code> to all
+     * registered <code>IDocumentListener</code>.
+     *
+     * @param startOffset document offset at which search starts
+     *          this value is only used in the FIND_FIRST operation and otherwise ignored
+     * @param findString the string to find
+     *          this value is only used in the FIND_FIRST operation and otherwise ignored
+     * @param replaceText the string to replace the current match
+     *          this value is only used in the REPLACE and REPLACE_FIND operations and otherwise ignored
+     * @param forwardSearch the search direction
+     * @param caseSensitive indicates whether lower and upper case should be distinguished
+     * @param wholeWord indicates whether the findString should be limited by white spaces as
+     *          defined by Character.isWhiteSpace. Must not be used in combination with <code>regExSearch</code>.
+     * @param regExSearch if <code>true</code> this operation represents a regular expression
+     *          Must not be used in combination with <code>wholeWord</code>.
+     * @param operationCode specifies what kind of operation is executed
+     * @return the find or replace region or <code>null</code> if there was no match
+     * @throws BadLocationException if startOffset is an invalid document offset
+     * @throws IllegalStateException if a REPLACE or REPLACE_FIND operation is not preceded by a successful FIND operation
+     * @throws PatternSyntaxException if a regular expression has invalid syntax
+     */
+    private IRegion findReplace(FindReplaceOperationCode operationCode, int startOffset, String findString, String replaceText, bool forwardSearch, bool caseSensitive, bool wholeWord, bool regExSearch)  {
+
+        // Validate option combinations
+        Assert.isTrue(!(regExSearch && wholeWord));
+
+        // Validate state
+        if ((operationCode is REPLACE || operationCode is REPLACE_FIND_NEXT) && (fFindReplaceState !is FIND_FIRST && fFindReplaceState !is FIND_NEXT))
+            throw new IllegalStateException("illegal findReplace state: cannot replace without preceding find"); //$NON-NLS-1$
+
+        if (operationCode is FIND_FIRST) {
+            // Reset
+
+            if (findString is null || findString.length is 0)
+                return null;
+
+            // Validate start offset
+            if (startOffset < 0 || startOffset >= length())
+                throw new BadLocationException();
+
+            int patternFlags= 0;
+
+            if (regExSearch) {
+                patternFlags |= Pattern.MULTILINE;
+                findString= substituteLinebreak(findString);
+            }
+
+            if (!caseSensitive)
+                patternFlags |= Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE;
+
+            if (wholeWord)
+                findString= "\\b" ~ findString ~ "\\b"; //$NON-NLS-1$ //$NON-NLS-2$
+
+            if (!regExSearch && !wholeWord)
+                findString= asRegPattern(findString);
+
+            fFindReplaceMatchOffset= startOffset;
+            if (fFindReplaceMatcher !is null && fFindReplaceMatcher.pattern().pattern().equals(findString) && fFindReplaceMatcher.pattern().flags() is patternFlags) {
+                /*
+                 * Commented out for optimization:
+                 * The call is not needed since FIND_FIRST uses find(int) which resets the matcher
+                 */
+                // fFindReplaceMatcher.reset();
+            } else {
+                Pattern pattern= Pattern.compile(findString, patternFlags);
+                fFindReplaceMatcher= pattern.matcher(this);
+            }
+        }
+
+        // Set state
+        fFindReplaceState= operationCode;
+
+        if (operationCode is REPLACE || operationCode is REPLACE_FIND_NEXT) {
+            if (regExSearch) {
+                Pattern pattern= fFindReplaceMatcher.pattern();
+                String prevMatch= fFindReplaceMatcher.group();
+                try {
+                    replaceText= interpretReplaceEscapes(replaceText, prevMatch);
+                    Matcher replaceTextMatcher= pattern.matcher(prevMatch);
+                    replaceText= replaceTextMatcher.replaceFirst(replaceText);
+                } catch (IndexOutOfBoundsException ex) {
+                    throw new PatternSyntaxException(ex.msg/+getLocalizedMessage()+/, replaceText, -1);
+                }
+            }
+
+            int offset= fFindReplaceMatcher.start();
+            int length= fFindReplaceMatcher.group().length;
+
+            if (cast(IRepairableDocumentExtension)fDocument
+                    && (cast(IRepairableDocumentExtension)fDocument).isLineInformationRepairNeeded(offset, length, replaceText)) {
+                String message= TextMessages.getString("FindReplaceDocumentAdapter.incompatibleLineDelimiter"); //$NON-NLS-1$
+                throw new PatternSyntaxException(message, replaceText, offset);
+            }
+
+            fDocument.replace(offset, length, replaceText);
+
+            if (operationCode is REPLACE) {
+                return new Region(offset, replaceText.length);
+            }
+        }
+
+        if (operationCode !is REPLACE) {
+            if (forwardSearch) {
+
+                bool found= false;
+                if (operationCode is FIND_FIRST)
+                    found= fFindReplaceMatcher.find(startOffset);
+                else
+                    found= fFindReplaceMatcher.find();
+
+                if (operationCode is REPLACE_FIND_NEXT)
+                    fFindReplaceState= FIND_NEXT;
+
+                if (found && fFindReplaceMatcher.group().length > 0)
+                    return new Region(fFindReplaceMatcher.start(), fFindReplaceMatcher.group().length);
+                return null;
+            }
+
+            // backward search
+            bool found= fFindReplaceMatcher.find(0);
+            int index= -1;
+            int length= -1;
+            while (found && fFindReplaceMatcher.start() + fFindReplaceMatcher.group().length <= fFindReplaceMatchOffset + 1) {
+                index= fFindReplaceMatcher.start();
+                length= fFindReplaceMatcher.group().length;
+                found= fFindReplaceMatcher.find(index + 1);
+            }
+            fFindReplaceMatchOffset= index;
+            if (index > -1) {
+                // must set matcher to correct position
+                fFindReplaceMatcher.find(index);
+                return new Region(index, length);
+            }
+            return null;
+        }
+
+        return null;
+    }
+
+    /**
+     * Substitutes \R in a regex find pattern with (?>\r\n?|\n)
+     *
+     * @param findString the original find pattern
+     * @return the transformed find pattern
+     * @throws PatternSyntaxException if \R is added at an illegal position (e.g. in a character set)
+     * @since 3.4
+     */
+    private String substituteLinebreak(String findString)  {
+        int length= findString.length;
+        StringBuffer buf= new StringBuffer(length);
+
+        int inCharGroup= 0;
+        int inBraces= 0;
+        bool inQuote= false;
+        for (int i= 0; i < length; i++) {
+            char ch= .charAt(findString, i);
+            switch (ch) {
+                case '[':
+                    buf.append(ch);
+                    if (! inQuote)
+                        inCharGroup++;
+                    break;
+
+                case ']':
+                    buf.append(ch);
+                    if (! inQuote)
+                        inCharGroup--;
+                    break;
+
+                case '{':
+                    buf.append(ch);
+                    if (! inQuote && inCharGroup is 0)
+                        inBraces++;
+                    break;
+
+                case '}':
+                    buf.append(ch);
+                    if (! inQuote && inCharGroup is 0)
+                        inBraces--;
+                    break;
+
+                case '\\':
+                    if (i + 1 < length) {
+                        char ch1= .charAt(findString, i + 1);
+                        if (inQuote) {
+                            if (ch1 is 'E')
+                                inQuote= false;
+                            buf.append(ch).append(ch1);
+                            i++;
+
+                        } else if (ch1 is 'R') {
+                            if (inCharGroup > 0 || inBraces > 0) {
+                                String msg= TextMessages.getString("FindReplaceDocumentAdapter.illegalLinebreak"); //$NON-NLS-1$
+                                throw new PatternSyntaxException(msg, findString, i);
+                            }
+                            buf.append("(?>\\r\\n?|\\n)"); //$NON-NLS-1$
+                            i++;
+
+                        } else {
+                            if (ch1 is 'Q') {
+                                inQuote= true;
+                            }
+                            buf.append(ch).append(ch1);
+                            i++;
+                        }
+                    } else {
+                        buf.append(ch);
+                    }
+                    break;
+
+                default:
+                    buf.append(ch);
+                    break;
+            }
+
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Interprets current Retain Case mode (all upper-case,all lower-case,capitalized or mixed)
+     * and appends the character <code>ch</code> to <code>buf</code> after processing.
+     *
+     * @param buf the output buffer
+     * @param ch the character to process
+     * @since 3.4
+     */
+    private void interpretRetainCase(StringBuffer buf, dchar ch) {
+        if (fRetainCaseMode is RC_UPPER)
+            buf.append(dcharToString(Character.toUpperCase(ch)));
+        else if (fRetainCaseMode is RC_LOWER)
+            buf.append(dcharToString(Character.toLowerCase(ch)));
+        else if (fRetainCaseMode is RC_FIRSTUPPER) {
+            buf.append(dcharToString(Character.toUpperCase(ch)));
+            fRetainCaseMode= RC_MIXED;
+        } else
+            buf.append(dcharToString(ch));
+    }
+
+    /**
+     * Interprets escaped characters in the given replace pattern.
+     *
+     * @param replaceText the replace pattern
+     * @param foundText the found pattern to be replaced
+     * @return a replace pattern with escaped characters substituted by the respective characters
+     * @since 3.4
+     */
+    private String interpretReplaceEscapes(String replaceText, String foundText) {
+        int length= replaceText.length;
+        bool inEscape= false;
+        StringBuffer buf= new StringBuffer(length);
+
+        /* every string we did not check looks mixed at first
+         * so initialize retain case mode with RC_MIXED
+         */
+        fRetainCaseMode= RC_MIXED;
+
+        for (int i= 0; i < length; i++) {
+            char ch= .charAt(replaceText, i);
+            if (inEscape) {
+                i= interpretReplaceEscape(ch, i, buf, replaceText, foundText);
+                inEscape= false;
+
+            } else if (ch is '\\') {
+                inEscape= true;
+
+            } else if (ch is '$') {
+                buf.append(ch);
+
+                /*
+                 * Feature in java.util.regex.Matcher#replaceFirst(String):
+                 * $00, $000, etc. are interpreted as $0 and
+                 * $01, $001, etc. are interpreted as $1, etc. .
+                 * If we support \0 as replacement pattern for capturing group 0,
+                 * it would not be possible any more to write a replacement pattern
+                 * that appends 0 to a capturing group (like $0\0).
+                 * The fix is to interpret \00 and $00 as $0\0, and
+                 * \01 and $01 as $0\1, etc.
+                 */
+                if (i + 2 < length) {
+                    char ch1= .charAt(replaceText, i + 1);
+                    char ch2= .charAt(replaceText, i + 2);
+                    if (ch1 is '0' && '0' <= ch2 && ch2 <= '9') {
+                        buf.append("0\\"); //$NON-NLS-1$
+                        i++; // consume the 0
+                    }
+                }
+            } else {
+                interpretRetainCase(buf, ch);
+            }
+        }
+
+        if (inEscape) {
+            // '\' as last character is invalid, but we still add it to get an error message
+            buf.append('\\');
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Interprets the escaped character <code>ch</code> at offset <code>i</code>
+     * of the <code>replaceText</code> and appends the interpretation to <code>buf</code>.
+     *
+     * @param ch the escaped character
+     * @param i the offset
+     * @param buf the output buffer
+     * @param replaceText the original replace pattern
+     * @param foundText the found pattern to be replaced
+     * @return the new offset
+     * @since 3.4
+     */
+    private int interpretReplaceEscape(char ch, int i, StringBuffer buf, String replaceText, String foundText) {
+        int length= replaceText.length;
+        switch (ch) {
+            case 'r':
+                buf.append('\r');
+                break;
+            case 'n':
+                buf.append('\n');
+                break;
+            case 't':
+                buf.append('\t');
+                break;
+            case 'f':
+                buf.append('\f');
+                break;
+            case 'a':
+                buf.append("\u0007"c);
+                break;
+            case 'e':
+                buf.append("\u001B"c);
+                break;
+            case 'R': //see http://www.unicode.org/unicode/reports/tr18/#Line_Boundaries
+                buf.append(TextUtilities.getDefaultLineDelimiter(fDocument));
+                break;
+            /*
+             * \0 for octal is not supported in replace string, since it
+             * would conflict with capturing group \0, etc.
+             */
+            case '0':
+                buf.append('$').append(ch);
+                /*
+                 * See explanation in "Feature in java.util.regex.Matcher#replaceFirst(String)"
+                 * in interpretReplaceEscape(String) above.
+                 */
+                if (i + 1 < length) {
+                    char ch1= .charAt(replaceText, i + 1);
+                    if ('0' <= ch1 && ch1 <= '9') {
+                        buf.append('\\');
+                    }
+                }
+                break;
+
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                buf.append('$').append(ch);
+                break;
+
+            case 'c':
+                if (i + 1 < length) {
+                    char ch1= .charAt(replaceText, i + 1);
+                    interpretRetainCase(buf, cast(wchar)(ch1 ^ 64));
+                    i++;
+                } else {
+                    String msg= TextMessages.getFormattedString("FindReplaceDocumentAdapter.illegalControlEscape", stringcast("\\c")); //$NON-NLS-1$ //$NON-NLS-2$
+                    throw new PatternSyntaxException(msg, replaceText, i);
+                }
+                break;
+
+            case 'x':
+                if (i + 2 < length) {
+                    int parsedInt;
+                    try {
+                        parsedInt= Integer.parseInt(replaceText.substring(i + 1, i + 3), 16);
+                        if (parsedInt < 0)
+                            throw new NumberFormatException("");
+                    } catch (NumberFormatException e) {
+                        String msg= TextMessages.getFormattedString("FindReplaceDocumentAdapter.illegalHexEscape", stringcast(replaceText.substring(i - 1, i + 3))); //$NON-NLS-1$
+                        throw new PatternSyntaxException(msg, replaceText, i);
+                    }
+                    interpretRetainCase(buf, cast(wchar) parsedInt);
+                    i+= 2;
+                } else {
+                    String msg= TextMessages.getFormattedString("FindReplaceDocumentAdapter.illegalHexEscape", stringcast(replaceText.substring(i - 1, length))); //$NON-NLS-1$
+                    throw new PatternSyntaxException(msg, replaceText, i);
+                }
+                break;
+
+            case 'u':
+                if (i + 4 < length) {
+                    int parsedInt;
+                    try {
+                        parsedInt= Integer.parseInt(replaceText.substring(i + 1, i + 5), 16);
+                        if (parsedInt < 0)
+                            throw new NumberFormatException("");
+                    } catch (NumberFormatException e) {
+                        String msg= TextMessages.getFormattedString("FindReplaceDocumentAdapter.illegalUnicodeEscape", stringcast(replaceText.substring(i - 1, i + 5))); //$NON-NLS-1$
+                        throw new PatternSyntaxException(msg, replaceText, i);
+                    }
+                    interpretRetainCase(buf, cast(wchar) parsedInt);
+                    i+= 4;
+                } else {
+                    String msg= TextMessages.getFormattedString("FindReplaceDocumentAdapter.illegalUnicodeEscape", stringcast(replaceText.substring(i - 1, length))); //$NON-NLS-1$
+                    throw new PatternSyntaxException(msg, replaceText, i);
+                }
+                break;
+
+            case 'C':
+                if(foundText.toUpperCase().equals(foundText)) // is whole match upper-case?
+                    fRetainCaseMode= RC_UPPER;
+                else if (foundText.toLowerCase().equals(foundText)) // is whole match lower-case?
+                    fRetainCaseMode= RC_LOWER;
+                else if(Character.isUpperCase(.charAt(foundText,0))) // is first character upper-case?
+                    fRetainCaseMode= RC_FIRSTUPPER;
+                else
+                    fRetainCaseMode= RC_MIXED;
+                break;
+
+            default:
+                // unknown escape k: append uninterpreted \k
+                buf.append('\\').append(ch);
+                break;
+        }
+        return i;
+    }
+
+    /**
+     * Converts a non-regex string to a pattern
+     * that can be used with the regex search engine.
+     *
+     * @param string the non-regex pattern
+     * @return the string converted to a regex pattern
+     */
+    private String asRegPattern(String string) {
+        StringBuffer out_= new StringBuffer(string.length);
+        bool quoting= false;
+
+        for (int i= 0, length= string.length; i < length; i++) {
+            char ch= .charAt(string, i);
+            if (ch is '\\') {
+                if (quoting) {
+                    out_.append("\\E"); //$NON-NLS-1$
+                    quoting= false;
+                }
+                out_.append("\\\\"); //$NON-NLS-1$
+                continue;
+            }
+            if (!quoting) {
+                out_.append("\\Q"); //$NON-NLS-1$
+                quoting= true;
+            }
+            out_.append(ch);
+        }
+        if (quoting)
+            out_.append("\\E"); //$NON-NLS-1$
+
+        return out_.toString();
+    }
+
+    /**
+     * Substitutes the previous match with the given text.
+     * Sends a <code>DocumentEvent</code> to all registered <code>IDocumentListener</code>.
+     *
+     * @param text the substitution text
+     * @param regExReplace if <code>true</code> <code>text</code> represents a regular expression
+     * @return the replace region or <code>null</code> if there was no match
+     * @throws BadLocationException if startOffset is an invalid document offset
+     * @throws IllegalStateException if a REPLACE or REPLACE_FIND operation is not preceded by a successful FIND operation
+     * @throws PatternSyntaxException if a regular expression has invalid syntax
+     *
+     * @see DocumentEvent
+     * @see IDocumentListener
+     */
+    public IRegion replace(String text, bool regExReplace)  {
+        return findReplace(REPLACE, -1, null, text, false, false, false, regExReplace);
+    }
+
+    // ---------- CharSequence implementation ----------
+
+    /*
+     * @see java.lang.CharSequence#length()
+     */
+    public int length() {
+        return fDocument.getLength();
+    }
+
+    /*
+     * @see java.lang.CharSequence#charAt(int)
+     */
+    public override char charAt(int index) {
+        try {
+            return fDocument.getChar(index);
+        } catch (BadLocationException e) {
+            throw new IndexOutOfBoundsException();
+        }
+    }
+
+    /*
+     * @see java.lang.CharSequence#subSequence(int, int)
+     */
+    public CharSequence subSequence(int start, int end) {
+        try {
+            return new StringCharSequence(fDocument.get(start, end - start));
+        } catch (BadLocationException e) {
+            throw new IndexOutOfBoundsException();
+        }
+    }
+
+    /*
+     * @see java.lang.Object#toString()
+     */
+    public override String toString() {
+        return fDocument.get();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/GapTextStore.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,537 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.GapTextStore;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * Implements a gap managing text store. The gap text store relies on the assumption that
+ * consecutive changes to a document are co-located. The start of the gap is always moved to the
+ * location of the last change.
+ * <p>
+ * <strong>Performance:</strong> Typing-style changes perform in constant time unless re-allocation
+ * becomes necessary. Generally, a change that does not cause re-allocation will cause at most one
+ * {@linkplain System#arraycopy(Object, int, Object, int, int) arraycopy} operation of a length of
+ * about <var>d</var>, where <var>d</var> is the distance from the previous change. Let <var>a(x)</var>
+ * be the algorithmic performance of an <code>arraycopy</code> operation of the length <var>x</var>,
+ * then such a change then performs in <i>O(a(x))</i>,
+ * {@linkplain #get(int, int) get(int, <var>length</var>)} performs in <i>O(a(length))</i>,
+ * {@link #get(int)} in <i>O(1)</i>.
+ * <p>
+ * How frequently the array needs re-allocation is controlled by the constructor parameters.
+ * </p>
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ *
+ * @see CopyOnWriteTextStore for a copy-on-write text store wrapper
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class GapTextStore : ITextStore {
+    /**
+     * The minimum gap size allocated when re-allocation occurs.
+     * @since 3.3
+     */
+    private const int fMinGapSize;
+    /**
+     * The maximum gap size allocated when re-allocation occurs.
+     * @since 3.3
+     */
+    private const int fMaxGapSize;
+    /**
+     * The multiplier to compute the array size from the content length
+     * (1&nbsp;&lt;=&nbsp;fSizeMultiplier&nbsp;&lt;=&nbsp;2).
+     *
+     * @since 3.3
+     */
+    private const float fSizeMultiplier;
+
+    /** The store's content */
+    private char[] fContent;
+    /** Starting index of the gap */
+    private int fGapStart= 0;
+    /** End index of the gap */
+    private int fGapEnd= 0;
+    /**
+     * The current high water mark. If a change would cause the gap to grow larger than this, the
+     * array is re-allocated.
+     * @since 3.3
+     */
+    private int fThreshold= 0;
+
+    /**
+     * Creates a new empty text store using the specified low and high watermarks.
+     *
+     * @param lowWatermark unused - at the lower bound, the array is only resized when the content
+     *        does not fit
+     * @param highWatermark if the gap is ever larger than this, it will automatically be shrunken
+     *        (&gt;=&nbsp;0)
+     * @deprecated use {@link GapTextStore#GapTextStore(int, int, float)} instead
+     */
+    public this(int lowWatermark, int highWatermark) {
+        /*
+         * Legacy constructor. The API contract states that highWatermark is the upper bound for the
+         * gap size. Albeit this contract was not previously adhered to, it is now: The allocated
+         * gap size is fixed at half the highWatermark. Since the threshold is always twice the
+         * allocated gap size, the gap will never grow larger than highWatermark. Previously, the
+         * gap size was initialized to highWatermark, causing re-allocation if the content length
+         * shrunk right after allocation. The fixed gap size is now only half of the previous value,
+         * circumventing that problem (there was no API contract specifying the initial gap size).
+         *
+         * The previous implementation did not allow the gap size to become smaller than
+         * lowWatermark, which doesn't make any sense: that area of the gap was simply never ever
+         * used.
+         */
+        this(highWatermark / 2, highWatermark / 2, 0f);
+    }
+
+    /**
+     * Equivalent to
+     * {@linkplain GapTextStore#GapTextStore(int, int, float) new GapTextStore(256, 4096, 0.1f)}.
+     *
+     * @since 3.3
+     */
+    public this() {
+        this(256, 4096, 0.1f);
+    }
+
+    /**
+     * Creates an empty text store that uses re-allocation thresholds relative to the content
+     * length. Re-allocation is controlled by the <em>gap factor</em>, which is the quotient of
+     * the gap size and the array size. Re-allocation occurs if a change causes the gap factor to go
+     * outside <code>[0,&nbsp;maxGapFactor]</code>. When re-allocation occurs, the array is sized
+     * such that the gap factor is <code>0.5 * maxGapFactor</code>. The gap size computed in this
+     * manner is bounded by the <code>minSize</code> and <code>maxSize</code> parameters.
+     * <p>
+     * A <code>maxGapFactor</code> of <code>0</code> creates a text store that never has a gap
+     * at all (if <code>minSize</code> is 0); a <code>maxGapFactor</code> of <code>1</code>
+     * creates a text store that doubles its size with every re-allocation and that never shrinks.
+     * </p>
+     * <p>
+     * The <code>minSize</code> and <code>maxSize</code> parameters are absolute bounds to the
+     * allocated gap size. Use <code>minSize</code> to avoid frequent re-allocation for small
+     * documents. Use <code>maxSize</code> to avoid a huge gap being allocated for large
+     * documents.
+     * </p>
+     *
+     * @param minSize the minimum gap size to allocate (&gt;=&nbsp;0; use 0 for no minimum)
+     * @param maxSize the maximum gap size to allocate (&gt;=&nbsp;minSize; use
+     *        {@link Integer#MAX_VALUE} for no maximum)
+     * @param maxGapFactor is the maximum fraction of the array that is occupied by the gap (<code>0&nbsp;&lt;=&nbsp;maxGapFactor&nbsp;&lt;=&nbsp;1</code>)
+     * @since 3.3
+     */
+    public this(int minSize, int maxSize, float maxGapFactor) {
+        Assert.isLegal(0f <= maxGapFactor && maxGapFactor <= 1f);
+        Assert.isLegal(0 <= minSize && minSize <= maxSize);
+        fMinGapSize= minSize;
+        fMaxGapSize= maxSize;
+        fSizeMultiplier= 1 / (1 - maxGapFactor / 2);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#get(int)
+     */
+    public final char get(int offset) {
+        if (offset < fGapStart)
+            return fContent[offset];
+
+        return fContent[offset + gapSize()];
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#get(int, int)
+     */
+    public final String get(int offset, int length) {
+        if (fGapStart <= offset)
+            return new_String(fContent, offset + gapSize() , length);
+
+        final int end= offset + length;
+
+        if (end <= fGapStart)
+            return new_String(fContent, offset, length);
+
+        StringBuffer buf= new StringBuffer(length);
+        buf.append(fContent[ offset .. fGapStart ]);
+        buf.append(fContent[ fGapEnd .. end - fGapStart + fGapEnd ]);
+        return buf.toString();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#getLength()
+     */
+    public final int getLength() {
+        return fContent.length - gapSize();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#set(java.lang.String)
+     */
+    public final void set(String text) {
+        /*
+         * Moves the gap to the end of the content. There is no sensible prediction of where the
+         * next change will occur, but at least the next change will not trigger re-allocation. This
+         * is especially important when using the GapTextStore within a CopyOnWriteTextStore, where
+         * the GTS is only initialized right before a modification.
+         */
+        replace(0, getLength(), text);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#replace(int, int, java.lang.String)
+     */
+    public final void replace(int offset, int length, String text) {
+        if (text is null) {
+            adjustGap(offset, length, 0);
+        } else {
+            int textLength= text.length;
+            adjustGap(offset, length, textLength);
+            if (textLength !is 0)
+                text.getChars(0, textLength, fContent, offset);
+        }
+    }
+
+    /**
+     * Moves the gap to <code>offset + add</code>, moving any content after
+     * <code>offset + remove</code> behind the gap. The gap size is kept between 0 and
+     * {@link #fThreshold}, leading to re-allocation if needed. The content between
+     * <code>offset</code> and <code>offset + add</code> is undefined after this operation.
+     *
+     * @param offset the offset at which a change happens
+     * @param remove the number of character which are removed or overwritten at <code>offset</code>
+     * @param add the number of character which are inserted or overwriting at <code>offset</code>
+     */
+    private void adjustGap(int offset, int remove, int add) {
+        final int oldGapSize= gapSize();
+        final int newGapSize= oldGapSize - add + remove;
+        final bool reuseArray= 0 <= newGapSize && newGapSize <= fThreshold;
+
+        final int newGapStart= offset + add;
+        int newGapEnd;
+
+        if (reuseArray)
+            newGapEnd= moveGap(offset, remove, oldGapSize, newGapSize, newGapStart);
+        else
+            newGapEnd= reallocate(offset, remove, oldGapSize, newGapSize, newGapStart);
+
+        fGapStart= newGapStart;
+        fGapEnd= newGapEnd;
+    }
+
+    /**
+     * Moves the gap to <code>newGapStart</code>.
+     *
+     * @param offset the change offset
+     * @param remove the number of removed / overwritten characters
+     * @param oldGapSize the old gap size
+     * @param newGapSize the gap size after the change
+     * @param newGapStart the offset in the array to move the gap to
+     * @return the new gap end
+     * @since 3.3
+     */
+    private int moveGap(int offset, int remove, int oldGapSize, int newGapSize, int newGapStart) {
+        /*
+         * No re-allocation necessary. The area between the change offset and gap can be copied
+         * in at most one operation. Don't copy parts that will be overwritten anyway.
+         */
+        final int newGapEnd= newGapStart + newGapSize;
+        if (offset < fGapStart) {
+            int afterRemove= offset + remove;
+            if (afterRemove < fGapStart) {
+                final int betweenSize= fGapStart - afterRemove;
+                arrayCopy(afterRemove, fContent, newGapEnd, betweenSize);
+            }
+            // otherwise, only the gap gets enlarged
+        } else {
+            final int offsetShifted= offset + oldGapSize;
+            final int betweenSize= offsetShifted - fGapEnd; // in the typing case, betweenSize is 0
+            arrayCopy(fGapEnd, fContent, fGapStart, betweenSize);
+        }
+        return newGapEnd;
+    }
+
+    /**
+     * Reallocates a new array and copies the data from the previous one.
+     *
+     * @param offset the change offset
+     * @param remove the number of removed / overwritten characters
+     * @param oldGapSize the old gap size
+     * @param newGapSize the gap size after the change if no re-allocation would occur (can be negative)
+     * @param newGapStart the offset in the array to move the gap to
+     * @return the new gap end
+     * @since 3.3
+     */
+    private int reallocate(int offset, int remove, int oldGapSize, int newGapSize, int newGapStart) {
+        // the new content length (without any gap)
+        final int newLength= fContent.length - newGapSize;
+        // the new array size based on the gap factor
+        int newArraySize= cast(int) (newLength * fSizeMultiplier);
+        newGapSize= newArraySize - newLength;
+
+        // bound the gap size within min/max
+        if (newGapSize < fMinGapSize) {
+            newGapSize= fMinGapSize;
+            newArraySize= newLength + newGapSize;
+        } else if (newGapSize > fMaxGapSize) {
+            newGapSize= fMaxGapSize;
+            newArraySize= newLength + newGapSize;
+        }
+
+        // the upper threshold is always twice the gapsize
+        fThreshold= newGapSize * 2;
+        final char[] newContent= allocate(newArraySize);
+        final int newGapEnd= newGapStart + newGapSize;
+
+        /*
+         * Re-allocation: The old content can be copied in at most 3 operations to the newly allocated
+         * array. Either one of change offset and the gap may come first.
+         * - unchanged area before the change offset / gap
+         * - area between the change offset and the gap (either one may be first)
+         * - rest area after the change offset / after the gap
+         */
+        if (offset < fGapStart) {
+            // change comes before gap
+            arrayCopy(0, newContent, 0, offset);
+            int afterRemove= offset + remove;
+            if (afterRemove < fGapStart) {
+                // removal is completely before the gap
+                final int betweenSize= fGapStart - afterRemove;
+                arrayCopy(afterRemove, newContent, newGapEnd, betweenSize);
+                final int restSize= fContent.length - fGapEnd;
+                arrayCopy(fGapEnd, newContent, newGapEnd + betweenSize, restSize);
+            } else {
+                // removal encompasses the gap
+                afterRemove += oldGapSize;
+                final int restSize= fContent.length - afterRemove;
+                arrayCopy(afterRemove, newContent, newGapEnd, restSize);
+            }
+        } else {
+            // gap comes before change
+            arrayCopy(0, newContent, 0, fGapStart);
+            final int offsetShifted= offset + oldGapSize;
+            final int betweenSize= offsetShifted - fGapEnd;
+            arrayCopy(fGapEnd, newContent, fGapStart, betweenSize);
+            final int afterRemove= offsetShifted + remove;
+            final int restSize= fContent.length - afterRemove;
+            arrayCopy(afterRemove, newContent, newGapEnd, restSize);
+        }
+
+        fContent= newContent;
+        return newGapEnd;
+    }
+
+    /**
+     * Allocates a new <code>char[size]</code>.
+     *
+     * @param size the length of the new array.
+     * @return a newly allocated char array
+     * @since 3.3
+     */
+    private char[] allocate(int size) {
+        return new char[size];
+    }
+
+    /*
+     * Executes System.arraycopy if length !is 0. A length < 0 cannot happen -> don't hide coding
+     * errors by checking for negative lengths.
+     * @since 3.3
+     */
+    private void arrayCopy(int srcPos, char[] dest, int destPos, int length) {
+        if (length !is 0)
+            System.arraycopy(fContent, srcPos, dest, destPos, length);
+    }
+
+    /**
+     * Returns the gap size.
+     *
+     * @return the gap size
+     * @since 3.3
+     */
+    private int gapSize() {
+        return fGapEnd - fGapStart;
+    }
+
+    /**
+     * Returns a copy of the content of this text store.
+     * For internal use only.
+     *
+     * @return a copy of the content of this text store
+     */
+    protected String getContentAsString() {
+        return new_String(fContent);
+    }
+
+    /**
+     * Returns the start index of the gap managed by this text store.
+     * For internal use only.
+     *
+     * @return the start index of the gap managed by this text store
+     */
+    protected int getGapStartIndex() {
+        return fGapStart;
+    }
+
+    /**
+     * Returns the end index of the gap managed by this text store.
+     * For internal use only.
+     *
+     * @return the end index of the gap managed by this text store
+     */
+    protected int getGapEndIndex() {
+        return fGapEnd;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocument.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,664 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IDocument;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * An <code>IDocument</code> represents text providing support for
+ * <ul>
+ * <li> text manipulation
+ * <li> positions
+ * <li> partitions
+ * <li> line information
+ * <li> document change listeners
+ * <li> document partition change listeners
+ * </ul>
+ *
+ * A document allows to set its content and to manipulate it. For manipulation
+ * a document provides the <code>replace</code> method which substitutes a given
+ * string for a specified text range in the document. On each document change, all
+ * registered document listeners are informed exactly once.
+ * <p>
+ * Positions are stickers to the document's text that are updated when the
+ * document is changed. Positions are updated by {@link org.eclipse.jface.text.IPositionUpdater}s. Position
+ * updaters are managed as a list. The list defines the sequence in which position
+ * updaters are invoked. This way, position updaters may rely on each other.
+ * Positions are grouped into categories.  A category is a ordered list of positions.
+ * the document defines the order of position in a category based on the position's offset
+ * based on the implementation of the method <code>computeIndexInCategory</code>.
+ * Each document must support a default position category whose name is specified by this
+ * interface.</p>
+ * <p>
+ * A document can be considered consisting of a sequence of not overlapping partitions.
+ * A partition is defined by its offset, its length, and its type. Partitions are
+ * updated on every document manipulation and ensured to be up-to-date when the document
+ * listeners are informed. A document uses an <code>IDocumentPartitioner</code> to
+ * manage its partitions. A document may be unpartitioned which happens when there is no
+ * partitioner. In this case, the document is considered as one single partition of a
+ * default type. The default type is specified by this interface. If a document change
+ * changes the document's partitioning all registered partitioning listeners are
+ * informed exactly once. The extension interface {@link org.eclipse.jface.text.IDocumentExtension3}
+ * introduced in version 3.0 extends the concept of partitions and allows a document to
+ * not only manage one but multiple partitioning. Each partitioning has an id which must
+ * be used to refer to a particular partitioning.</p>
+ * <p>
+ * An <code>IDocument</code> provides methods to map line numbers and character
+ * positions onto each other based on the document's line delimiters. When moving text
+ * between documents using different line delimiters, the text must be converted to
+ * use the target document's line delimiters.</p>
+ * <p>
+ * An <code>IDocument</code> does not care about mixed line delimiters. Clients who
+ * want to ensure a single line delimiter in their document should use the line
+ * delimiter returned by {@link org.eclipse.jface.text.TextUtilities#getDefaultLineDelimiter(IDocument)}.</p>
+ * <p>
+ * <code>IDocument</code> throws <code>BadLocationException</code> if the parameters of
+ * queries or manipulation requests are not inside the bounds of the document. The purpose
+ * of this style of exception handling is
+ * <ul>
+ * <li> prepare document for multi-thread access
+ * <li> allow clients to implement backtracking recovery methods
+ * <li> prevent clients from up-front contract checking when dealing with documents.
+ * </ul></p>
+ * <p>
+ * A document support for searching has deprecated since version 3.0. The recommended way
+ * for searching is to use a {@link org.eclipse.jface.text.FindReplaceDocumentAdapter}.</p>
+ * <p>
+ * In order to provide backward compatibility for clients of <code>IDocument</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces
+ * exist:
+ * <ul>
+ * <li> {@link org.eclipse.jface.text.IDocumentExtension} since version 2.0 introducing the concept
+ *      of post notification replaces in order to allow document listeners to manipulate the document
+ *      while receiving a document change notification </li>
+ * <li> {@link org.eclipse.jface.text.IDocumentExtension2} since version 2.1 introducing configuration
+ *      methods for post notification replaces and document change notification. </li>
+ * <li> {@link org.eclipse.jface.text.IDocumentExtension3} since version 3.0 replacing the original
+ *      partitioning concept by allowing multiple partitionings at the same time and introducing zero-
+ *      length partitions in conjunction with the distinction between open and closed partitions. </li>
+ * <li> {@link org.eclipse.jface.text.IDocumentExtension4} since version 3.1 introducing the
+ *      concept of rewrite sessions. A rewrite session is a sequence of document replace operations
+ *      that form a semantic unit. It also introduces a modification stamp and the ability to
+ *      set the initial line delimiter and to query the default line delimiter.</li>
+ * </ul></p>
+ * <p>
+ * Clients may implement this interface and its extension interfaces or use the default
+ * implementation provided by <code>AbstractDocument</code> and <code>Document</code>.</p>
+ *
+ * @see org.eclipse.jface.text.IDocumentExtension
+ * @see org.eclipse.jface.text.IDocumentExtension2
+ * @see org.eclipse.jface.text.IDocumentExtension3
+ * @see org.eclipse.jface.text.IDocumentExtension4
+ * @see org.eclipse.jface.text.Position
+ * @see org.eclipse.jface.text.IPositionUpdater
+ * @see org.eclipse.jface.text.IDocumentPartitioner
+ * @see org.eclipse.jface.text.ILineTracker
+ * @see org.eclipse.jface.text.IDocumentListener
+ * @see org.eclipse.jface.text.IDocumentPartitioningListener
+ */
+public interface IDocument {
+
+
+    /**
+     * The identifier of the default position category.
+     */
+    final static String DEFAULT_CATEGORY= "__dflt_position_category"; //$NON-NLS-1$
+
+    /**
+     * The identifier of the default partition content type.
+     */
+    final static String DEFAULT_CONTENT_TYPE= "__dftl_partition_content_type"; //$NON-NLS-1$
+
+
+
+
+    /* --------------- text access and manipulation --------------------------- */
+
+    /**
+     * Returns the character at the given document offset in this document.
+     *
+     * @param offset a document offset
+     * @return the character at the offset
+     * @exception BadLocationException if the offset is invalid in this document
+     */
+    char getChar(int offset) ;
+
+    /**
+     * Returns the number of characters in this document.
+     *
+     * @return the number of characters in this document
+     */
+    int getLength();
+
+    /**
+     * Returns this document's complete text.
+     *
+     * @return the document's complete text
+     */
+    String get();
+
+    /**
+     * Returns this document's text for the specified range.
+     *
+     * @param offset the document offset
+     * @param length the length of the specified range
+     * @return the document's text for the specified range
+     * @exception BadLocationException if the range is invalid in this document
+     */
+    String get(int offset, int length) ;
+
+    /**
+     * Replaces the content of the document with the given text.
+     * Sends a <code>DocumentEvent</code> to all registered <code>IDocumentListener</code>.
+     * This method is a convenience method for <code>replace(0, getLength(), text)</code>.
+     *
+     * @param text the new content of the document
+     *
+     * @see DocumentEvent
+     * @see IDocumentListener
+     */
+    void set(String text);
+
+    /**
+     * Substitutes the given text for the specified document range.
+     * Sends a <code>DocumentEvent</code> to all registered <code>IDocumentListener</code>.
+     *
+     * @param offset the document offset
+     * @param length the length of the specified range
+     * @param text the substitution text
+     * @exception BadLocationException if the offset is invalid in this document
+     *
+     * @see DocumentEvent
+     * @see IDocumentListener
+     */
+    void replace(int offset, int length, String text) ;
+
+    /**
+     * Registers the document listener with the document. After registration
+     * the IDocumentListener is informed about each change of this document.
+     * If the listener is already registered nothing happens.<p>
+     * An <code>IDocumentListener</code> may call back to this method
+     * when being inside a document notification.
+     *
+     * @param listener the listener to be registered
+     */
+    void addDocumentListener(IDocumentListener listener);
+
+    /**
+     * Removes the listener from the document's list of document listeners.
+     * If the listener is not registered with the document nothing happens.<p>
+     * An <code>IDocumentListener</code> may call back to this method
+     * when being inside a document notification.
+     *
+     * @param listener the listener to be removed
+     */
+    void removeDocumentListener(IDocumentListener listener);
+
+    /**
+     * Adds the given document listener as one which is notified before
+     * those document listeners added with <code>addDocumentListener</code>
+     * are notified. If the given listener is also registered using
+     * <code>addDocumentListener</code> it will be notified twice.
+     * If the listener is already registered nothing happens.<p>
+     *
+     * This method is not for public use.
+     *
+     * @param documentAdapter the listener to be added as pre-notified document listener
+     *
+     * @see #removePrenotifiedDocumentListener(IDocumentListener)
+     */
+    void addPrenotifiedDocumentListener(IDocumentListener documentAdapter);
+
+    /**
+     * Removes the given document listener from the document's list of
+     * pre-notified document listeners. If the listener is not registered
+     * with the document nothing happens. <p>
+     *
+     * This method is not for public use.
+     *
+     * @param documentAdapter the listener to be removed
+     *
+     * @see #addPrenotifiedDocumentListener(IDocumentListener)
+     */
+    void removePrenotifiedDocumentListener(IDocumentListener documentAdapter);
+
+
+
+    /* -------------------------- positions ----------------------------------- */
+
+    /**
+     * Adds a new position category to the document. If the position category
+     * already exists nothing happens.
+     *
+     * @param category the category to be added
+     */
+    void addPositionCategory(String category);
+
+    /**
+     * Deletes the position category from the document. All positions
+     * in this category are thus deleted as well.
+     *
+     * @param category the category to be removed
+     * @exception BadPositionCategoryException if category is undefined in this document
+     */
+    void removePositionCategory(String category) ;
+
+    /**
+     * Returns all position categories of this document. This
+     * includes the default position category.
+     *
+     * @return the document's position categories
+     */
+    String[] getPositionCategories();
+
+    /**
+     * Checks the presence of the specified position category.
+     *
+     * @param category the category to check
+     * @return <code>true</code> if category is defined
+     */
+    bool containsPositionCategory(String category);
+
+    /**
+     * Adds the position to the document's default position category.
+     * This is a convenience method for <code>addPosition(DEFAULT_CATEGORY, position)</code>.
+     *
+     * @param position the position to be added
+     * @exception BadLocationException if position describes an invalid range in this document
+     */
+    void addPosition(Position position) ;
+
+    /**
+     * Removes the given position from the document's default position category.
+     * This is a convenience method for <code>removePosition(DEFAULT_CATEGORY, position)</code>.
+     *
+     * @param position the position to be removed
+     */
+    void removePosition(Position position);
+
+    /**
+     * Adds the position to the specified position category of the document.
+     * Positions may be added multiple times. The order of the category is
+     * maintained.
+     * <p>
+     * <strong>Note:</strong> The position is only updated on each change
+     * applied to the document if a {@link IPositionUpdater} has been
+     * registered that handles the given category.
+     * </p>
+     *
+     * @param category the category to which to add
+     * @param position the position to be added
+     * @throws BadLocationException if position describes an invalid range in this document
+     * @throws BadPositionCategoryException if the category is undefined in this document
+     */
+    void addPosition(String category, Position position);
+
+    /**
+     * Removes the given position from the specified position category.
+     * If the position is not part of the specified category nothing happens.
+     * If the position has been added multiple times, only the first occurrence is deleted.
+     *
+     * @param category the category from which to delete
+     * @param position the position to be deleted
+     * @exception BadPositionCategoryException if category is undefined in this document
+     */
+    void removePosition(String category, Position position) ;
+
+    /**
+     * Returns all positions of the given position category.
+     * The positions are ordered according to the category's order.
+     * Manipulating this list does not affect the document, but manipulating the
+     * position does affect the document.
+     *
+     * @param category the category
+     * @return the list of all positions
+     * @exception BadPositionCategoryException if category is undefined in this document
+     */
+    Position[] getPositions(String category) ;
+
+    /**
+     * Determines whether a position described by the parameters is managed by this document.
+     *
+     * @param category the category to check
+     * @param offset the offset of the position to find
+     * @param length the length of the position to find
+     * @return <code>true</code> if position is found
+     */
+    bool containsPosition(String category, int offset, int length);
+
+    /**
+     * Computes the index at which a <code>Position</code> with the
+     * specified offset would be inserted into the given category. As the
+     * ordering inside a category only depends on the offset, the index must be
+     * chosen to be the first of all positions with the same offset.
+     *
+     * @param category the category in which would be added
+     * @param offset the position offset to be considered
+     * @return the index into the category
+     * @exception BadLocationException if offset is invalid in this document
+     * @exception BadPositionCategoryException if category is undefined in this document
+     */
+    int computeIndexInCategory(String category, int offset);
+
+    /**
+     * Appends a new position updater to the document's list of position updaters.
+     * Position updaters may be added multiple times.<p>
+     * An <code>IPositionUpdater</code> may call back to this method
+     * when being inside a document notification.
+     *
+     * @param updater the updater to be added
+     */
+    void addPositionUpdater(IPositionUpdater updater);
+
+    /**
+     * Removes the position updater from the document's list of position updaters.
+     * If the position updater has multiple occurrences only the first occurrence is
+     * removed. If the position updater is not registered with this document, nothing
+     * happens.<p>
+     * An <code>IPositionUpdater</code> may call back to this method
+     * when being inside a document notification.
+     *
+     * @param updater the updater to be removed
+     */
+    void removePositionUpdater(IPositionUpdater updater);
+
+    /**
+     * Inserts the position updater at the specified index in the document's
+     * list of position updaters. Positions updaters may be inserted multiple times.<p>
+     * An <code>IPositionUpdater</code> may call back to this method
+     * when being inside a document notification.
+     *
+     * @param updater the updater to be inserted
+     * @param index the index in the document's updater list
+     */
+    void insertPositionUpdater(IPositionUpdater updater, int index);
+
+    /**
+     * Returns the list of position updaters attached to the document.
+     *
+     * @return the list of position updaters
+     */
+    IPositionUpdater[] getPositionUpdaters();
+
+
+
+
+    /* -------------------------- partitions ---------------------------------- */
+
+    /**
+     * Returns the set of legal content types of document partitions.
+     * This set can be empty. The set can contain more content types than
+     * contained by the result of <code>getPartitioning(0, getLength())</code>.
+     * <p>
+     * Use {@link IDocumentExtension3#getLegalContentTypes(String)} when the document
+     * supports multiple partitionings. In that case this method is equivalent to:
+     * <pre>
+     *    IDocumentExtension3 extension= cast(IDocumentExtension3) document;
+     *    return extension.getLegalContentTypes(IDocumentExtension3.DEFAULT_PARTITIONING);
+     * </pre>
+     *
+     * @return the set of legal content types
+     */
+    String[] getLegalContentTypes();
+
+    /**
+     * Returns the type of the document partition containing the given offset.
+     * This is a convenience method for <code>getPartition(offset).getType()</code>.
+     * <p>
+     * Use {@link IDocumentExtension3#getContentType(String, int, bool)} when
+     * the document supports multiple partitionings. In that case this method is
+     * equivalent to:
+     * <pre>
+     *    IDocumentExtension3 extension= cast(IDocumentExtension3) document;
+     *    return extension.getContentType(IDocumentExtension3.DEFAULT_PARTITIONING, offset, false);
+     * </pre>
+     *
+     * @param offset the document offset
+     * @return the partition type
+     * @exception BadLocationException if offset is invalid in this document
+     */
+    String getContentType(int offset) ;
+
+    /**
+     * Returns the document partition in which the position is located.
+     * <p>
+     * Use {@link IDocumentExtension3#getPartition(String, int, bool)} when
+     * the document supports multiple partitionings. In that case this method is
+     * equivalent:
+     * <pre>
+     *    IDocumentExtension3 extension= cast(IDocumentExtension3) document;
+     *    return extension.getPartition(IDocumentExtension3.DEFAULT_PARTITIONING, offset, false);
+     * </pre>
+     *
+     * @param offset the document offset
+     * @return a specification of the partition
+     * @exception BadLocationException if offset is invalid in this document
+     */
+    ITypedRegion getPartition(int offset) ;
+
+    /**
+     * Computes the partitioning of the given document range using the
+     * document's partitioner.
+     * <p>
+     * Use {@link IDocumentExtension3#computePartitioning(String, int, int, bool)} when
+     * the document supports multiple partitionings. In that case this method is
+     * equivalent:
+     * <pre>
+     *    IDocumentExtension3 extension= cast(IDocumentExtension3) document;
+     *    return extension.computePartitioning(IDocumentExtension3.DEFAULT_PARTITIONING, offset, length, false);
+     * </pre>
+     *
+     * @param offset the document offset at which the range starts
+     * @param length the length of the document range
+     * @return a specification of the range's partitioning
+     * @exception BadLocationException if the range is invalid in this document
+     */
+    ITypedRegion[] computePartitioning(int offset, int length) ;
+
+    /**
+     * Registers the document partitioning listener with the document. After registration
+     * the document partitioning listener is informed about each partition change
+     * cause by a document manipulation or by changing the document's partitioner.
+     * If a document partitioning listener is also
+     * a document listener, the following notification sequence is guaranteed if a
+     * document manipulation changes the document partitioning:
+     * <ul>
+     * <li>listener.documentAboutToBeChanged(DocumentEvent);
+     * <li>listener.documentPartitioningChanged();
+     * <li>listener.documentChanged(DocumentEvent);
+     * </ul>
+     * If the listener is already registered nothing happens.<p>
+     * An <code>IDocumentPartitioningListener</code> may call back to this method
+     * when being inside a document notification.
+     *
+     * @param listener the listener to be added
+     */
+    void addDocumentPartitioningListener(IDocumentPartitioningListener listener);
+
+    /**
+     * Removes the listener from this document's list of document partitioning
+     * listeners. If the listener is not registered with the document nothing
+     * happens.<p>
+     * An <code>IDocumentPartitioningListener</code> may call back to this method
+     * when being inside a document notification.
+     *
+     * @param listener the listener to be removed
+     */
+    void removeDocumentPartitioningListener(IDocumentPartitioningListener listener);
+
+    /**
+     * Sets this document's partitioner. The caller of this method is responsible for
+     * disconnecting the document's old partitioner from the document and to
+     * connect the new partitioner to the document. Informs all document partitioning
+     * listeners about this change.
+     * <p>
+     * Use {@link IDocumentExtension3#setDocumentPartitioner(String, IDocumentPartitioner)} when
+     * the document supports multiple partitionings. In that case this method is equivalent to:
+     * <pre>
+     *    IDocumentExtension3 extension= cast(IDocumentExtension3) document;
+     *    extension.setDocumentPartitioner(IDocumentExtension3.DEFAULT_PARTITIONING, partitioner);
+     * </pre>
+     *
+     * @param partitioner the document's new partitioner
+     *
+     * @see IDocumentPartitioningListener
+     */
+    void setDocumentPartitioner(IDocumentPartitioner partitioner);
+
+    /**
+     * Returns this document's partitioner.
+     * <p>
+     * Use {@link IDocumentExtension3#getDocumentPartitioner(String)} when
+     * the document supports multiple partitionings. In that case this method is
+     * equivalent to:
+     * <pre>
+     *    IDocumentExtension3 extension= cast(IDocumentExtension3) document;
+     *    return extension.getDocumentPartitioner(IDocumentExtension3.DEFAULT_PARTITIONING);
+     * </pre>
+     *
+     * @return this document's partitioner
+     */
+    IDocumentPartitioner getDocumentPartitioner();
+
+
+
+    /* ---------------------- line information -------------------------------- */
+
+    /**
+     * Returns the length of the given line including the line's delimiter.
+     *
+     * @param line the line of interest
+     * @return the length of the line
+     * @exception BadLocationException if the line number is invalid in this document
+     */
+    int getLineLength(int line) ;
+
+    /**
+     * Returns the number of the line at which the character of the specified position is located.
+     * The first line has the line number 0. A new line starts directly after a line
+     * delimiter. <code>(offset is document length)</code> is a valid argument although there is no
+     * corresponding character.
+     *
+     * @param offset the document offset
+     * @return the number of the line
+     * @exception BadLocationException if the offset is invalid in this document
+     */
+    int getLineOfOffset(int offset) ;
+
+    /**
+     * Determines the offset of the first character of the given line.
+     *
+     * @param line the line of interest
+     * @return the document offset
+     * @exception BadLocationException if the line number is invalid in this document
+     */
+    int getLineOffset(int line) ;
+
+    /**
+     * Returns a description of the specified line. The line is described by its
+     * offset and its length excluding the line's delimiter.
+     *
+     * @param line the line of interest
+     * @return a line description
+     * @exception BadLocationException if the line number is invalid in this document
+     */
+    IRegion getLineInformation(int line) ;
+
+    /**
+     * Returns a description of the line at the given offset.
+     * The description contains the offset and the length of the line
+     * excluding the line's delimiter.
+     *
+     * @param offset the offset whose line should be described
+     * @return a region describing the line
+     * @exception BadLocationException if offset is invalid in this document
+     */
+    IRegion getLineInformationOfOffset(int offset) ;
+
+    /**
+     * Returns the number of lines in this document
+     *
+     * @return the number of lines in this document
+     */
+    int getNumberOfLines();
+
+    /**
+     * Returns the number of lines which are occupied by a given text range.
+     *
+     * @param offset the offset of the specified text range
+     * @param length the length of the specified text range
+     * @return the number of lines occupied by the specified range
+     * @exception BadLocationException if specified range is invalid in this tracker
+     */
+    int getNumberOfLines(int offset, int length) ;
+
+    /**
+     * Computes the number of lines in the given text. For a given
+     * implementer of this interface this method returns the same
+     * result as <code>set(text); getNumberOfLines()</code>.
+     *
+     * @param text the text whose number of lines should be computed
+     * @return the number of lines in the given text
+     */
+    int computeNumberOfLines(String text);
+
+
+    /* ------------------ line delimiter conversion --------------------------- */
+
+    /**
+     * Returns the document's legal line delimiters.
+     *
+     * @return the document's legal line delimiters
+     */
+    String[] getLegalLineDelimiters();
+
+    /**
+     * Returns the line delimiter of that line or <code>null</code> if the
+     * line is not closed with a line delimiter.
+     *
+     * @param line the line of interest
+     * @return the line's delimiter or <code>null</code> if line does not have a delimiter
+     * @exception BadLocationException if the line number is invalid in this document
+     */
+    String getLineDelimiter(int line) ;
+
+
+    /* ---------------------------- search ------------------------------------ */
+
+    /**
+     * Returns the offset of a given search string in the document based on a set of search criteria.
+     *
+     * @param startOffset document offset at which search starts
+     * @param findString the string to find
+     * @param forwardSearch the search direction
+     * @param caseSensitive indicates whether lower and upper case should be distinguished
+     * @param wholeWord indicates whether the findString should be limited by white spaces as
+     *      defined by Character.isWhiteSpace
+     * @return the offset of the first occurrence of findString based on the parameters or -1 if no match is found
+     * @exception BadLocationException if startOffset is an invalid document offset
+     * @deprecated as of 3.0 search is provided by {@link FindReplaceDocumentAdapter}
+     */
+    int search(int startOffset, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord) ;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+
+module org.eclipse.jface.text.IDocumentExtension;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IDocument}.<p>
+ *
+ * It introduces the notion of sequentially rewriting a document. This is to tell a
+ * document that a sequence of non-overlapping replace operation is about to be
+ * performed. Implementers can use this knowledge for internal optimization.<p>
+ *
+ * Is also introduces the concept of post notification replaces. This is, a document
+ * listener who is informed about a document change can cause a derived document
+ * change. As the listener is not allowed to directly modify the document, it can
+ * register a replace operation that is performed directly after all document listeners
+ * have been notified.
+ *
+ * @since 2.0
+ */
+public interface IDocumentExtension {
+
+    /**
+     * Interface for a post notification replace operation.
+     */
+    public interface IReplace {
+
+        /**
+         * Executes the replace operation on the given document.
+         *
+         * @param document the document to be changed
+         * @param owner the owner of this replace operation
+         */
+        void perform(IDocument document, IDocumentListener owner);
+    }
+
+    /**
+     * Callback for document listeners to be used inside <code>documentChanged</code>
+     * to register a post notification replace operation on the document notifying them.
+     *
+     * @param owner the owner of the replace operation
+     * @param replace the replace operation to be executed
+     * @exception UnsupportedOperationException if <code>registerPostNotificationReplace</code>
+     *  is not supported by this document
+     */
+    void registerPostNotificationReplace(IDocumentListener owner, IReplace replace) ;
+
+    /**
+     * Stops the processing of registered post notification replace operations until
+     * <code>resumePostNotificationProcessing</code> is called.
+     */
+    void stopPostNotificationProcessing();
+
+    /**
+     * Resumes the processing of post notification replace operations. If the queue of registered
+     * <code>IDocumentExtension.IReplace</code> objects is not empty, they are immediately processed if the
+     * document is not inside a replace operation. If the document is inside a replace operation,
+     * they are processed directly after the replace operation has finished.
+     */
+    void resumePostNotificationProcessing();
+
+    /**
+     * Tells the document that it is about to be sequentially rewritten. That is a
+     * sequence of non-overlapping replace operations will be performed on it. The
+     * <code>normalize</code> flag indicates whether the rewrite is performed from
+     * the start of the document to its end or from an arbitrary start offset. <p>
+     *
+     * The document is considered being in sequential rewrite mode as long as
+     * <code>stopSequentialRewrite</code> has not been called.
+     *
+     * @param normalize <code>true</code> if performed from the start to the end of the document
+     * @deprecated since 3.1. Use {@link IDocumentExtension4#startRewriteSession(DocumentRewriteSessionType)} instead.
+     */
+    void startSequentialRewrite(bool normalize);
+
+    /**
+     * Tells the document that the sequential rewrite has been finished. This method
+     * has only any effect if <code>startSequentialRewrite</code> has been called before.
+     * @deprecated since 3.1. Use {@link IDocumentExtension4#stopRewriteSession(DocumentRewriteSession)} instead.
+     */
+    void stopSequentialRewrite();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.IDocumentExtension2;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IDocument}.<p>
+ *
+ * It adds configuration methods to post notification replaces and document
+ * listener notification.
+ *
+ * @since 2.1
+ */
+public interface IDocumentExtension2 {
+
+    /**
+     * Tells the receiver to ignore calls to
+     * <code>registerPostNotificationReplace</code> until
+     * <code>acceptPostNotificationReplaces</code> is called.
+     */
+    void ignorePostNotificationReplaces();
+
+    /**
+     * Tells the receiver to accept calls to
+     * <code>registerPostNotificationReplace</code> until
+     * <code>ignorePostNotificationReplaces</code> is called.
+     */
+    void acceptPostNotificationReplaces();
+
+    /**
+     * Can be called prior to a <code>replace</code> operation. After the
+     * <code>replace</code> <code>resumeListenerNotification</code> must be
+     * called. The affect of these calls is that no document listener is notified
+     * until <code>resumeListenerNotification</code> is called. This allows clients
+     * to update structure before any listener is informed about the change.<p>
+     * Listener notification can only be stopped for a single <code>replace</code> operation.
+     * Otherwise, document change notifications will be lost.
+     */
+    void stopListenerNotification();
+
+    /**
+     * Resumes the notification of document listeners which must previously
+     * have been stopped by a call to <code>stopListenerNotification</code>.
+     */
+    void resumeListenerNotification();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentExtension3.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,303 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IDocumentExtension3;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IDocument}.
+ * <p>
+ * Adds the concept of multiple partitionings and the concept of zero-length
+ * partitions in conjunction with open and delimited partitions. A delimited
+ * partition has a well defined start delimiter and a well defined end
+ * delimiter. Between two delimited partitions there may be an open partition of
+ * length zero.
+ * <p>
+ *
+ * In order to fulfill the contract of this interface, the document must be
+ * configured with a document partitioner implementing
+ * {@link org.eclipse.jface.text.IDocumentPartitionerExtension2}.
+ *
+ * @see org.eclipse.jface.text.IDocumentPartitionerExtension2
+ * @since 3.0
+ */
+public interface IDocumentExtension3 {
+
+    /**
+     * The identifier of the default partitioning.
+     */
+    final static String DEFAULT_PARTITIONING= "__dftl_partitioning"; //$NON-NLS-1$
+
+
+    /**
+     * Returns the existing partitionings for this document. This includes
+     * the default partitioning.
+     *
+     * @return the existing partitionings for this document
+     */
+    String[] getPartitionings();
+
+    /**
+     * Returns the set of legal content types of document partitions for the given partitioning
+     * This set can be empty. The set can contain more content types than  contained by the
+     * result of <code>getPartitioning(partitioning, 0, getLength())</code>.
+     *
+     * @param partitioning the partitioning for which to return the legal content types
+     * @return the set of legal content types
+     * @exception BadPartitioningException if partitioning is invalid for this document
+     */
+    String[] getLegalContentTypes(String partitioning) ;
+
+
+    /**
+     * Returns the type of the document partition containing the given offset
+     * for the given partitioning. This is a convenience method for
+     * <code>getPartition(partitioning, offset, bool).getType()</code>.
+     * <p>
+     * If <code>preferOpenPartitions</code> is <code>true</code>,
+     * precedence is given to an open partition ending at <code>offset</code>
+     * over a delimited partition starting at <code>offset</code>. If it is
+     * <code>false</code>, precedence is given to the partition that does not
+     * end at <code>offset</code>.
+     * </p>
+     * This is only supported if the connected <code>IDocumentPartitioner</code>
+     * supports it, i.e. implements <code>IDocumentPartitionerExtension2</code>.
+     * Otherwise, <code>preferOpenPartitions</code> is ignored.
+     * </p>
+     *
+     * @param partitioning the partitioning
+     * @param offset the document offset
+     * @param preferOpenPartitions <code>true</code> if precedence should be
+     *        given to a open partition ending at <code>offset</code> over a
+     *        closed partition starting at <code>offset</code>
+     * @return the partition type
+     * @exception BadLocationException if offset is invalid in this document
+     * @exception BadPartitioningException if partitioning is invalid for this document
+     */
+    String getContentType(String partitioning, int offset, bool preferOpenPartitions);
+
+    /**
+     * Returns the document partition of the given partitioning in which the
+     * given offset is located.
+     * <p>
+     * If <code>preferOpenPartitions</code> is <code>true</code>,
+     * precedence is given to an open partition ending at <code>offset</code>
+     * over a delimited partition starting at <code>offset</code>. If it is
+     * <code>false</code>, precedence is given to the partition that does not
+     * end at <code>offset</code>.
+     * </p>
+     * This is only supported if the connected <code>IDocumentPartitioner</code>
+     * supports it, i.e. implements <code>IDocumentPartitionerExtension2</code>.
+     * Otherwise, <code>preferOpenPartitions</code> is ignored.
+     * </p>
+     *
+     * @param partitioning the partitioning
+     * @param offset the document offset
+     * @param preferOpenPartitions <code>true</code> if precedence should be
+     *        given to a open partition ending at <code>offset</code> over a
+     *        closed partition starting at <code>offset</code>
+     * @return a specification of the partition
+     * @exception BadLocationException if offset is invalid in this document
+     * @exception BadPartitioningException if partitioning is invalid for this document
+     */
+    ITypedRegion getPartition(String partitioning, int offset, bool preferOpenPartitions);
+
+    /**
+     * Computes the partitioning of the given document range based on the given
+     * partitioning type.
+     * <p>
+     * If <code>includeZeroLengthPartitions</code> is <code>true</code>, a
+     * zero-length partition of an open partition type (usually the default
+     * partition) is included between two closed partitions. If it is
+     * <code>false</code>, no zero-length partitions are included.
+     * </p>
+     * This is only supported if the connected <code>IDocumentPartitioner</code>
+     * supports it, i.e. implements <code>IDocumentPartitionerExtension2</code>.
+     * Otherwise, <code>includeZeroLengthPartitions</code> is ignored.
+     * </p>
+     *
+     * @param partitioning the document's partitioning type
+     * @param offset the document offset at which the range starts
+     * @param length the length of the document range
+     * @param includeZeroLengthPartitions <code>true</code> if zero-length
+     *        partitions should be returned as part of the computed partitioning
+     * @return a specification of the range's partitioning
+     * @exception BadLocationException if the range is invalid in this document$
+     * @exception BadPartitioningException if partitioning is invalid for this document
+     */
+    ITypedRegion[] computePartitioning(String partitioning, int offset, int length, bool includeZeroLengthPartitions);
+
+    /**
+     * Sets this document's partitioner. The caller of this method is responsible for
+     * disconnecting the document's old partitioner from the document and to
+     * connect the new partitioner to the document. Informs all document partitioning
+     * listeners about this change.
+     *
+     * @param  partitioning the partitioning for which to set the partitioner
+     * @param partitioner the document's new partitioner
+     * @see IDocumentPartitioningListener
+     */
+    void setDocumentPartitioner(String partitioning, IDocumentPartitioner partitioner);
+
+    /**
+     * Returns the partitioner for the given partitioning or <code>null</code> if
+     * no partitioner is registered.
+     *
+     * @param  partitioning the partitioning for which to set the partitioner
+     * @return the partitioner for the given partitioning
+     */
+    IDocumentPartitioner getDocumentPartitioner(String partitioning);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentExtension4.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,300 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IDocumentExtension4;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IDocument}. It adds the
+ * following concepts:
+ * <ul>
+ *   <li>Rewrite sessions. A rewrite session is a sequence of replace operations
+ *       that form a semantic unit.</li>
+ *   <li>A modification stamp on the document</li>
+ *   <li>The ability to set the initial line delimiter and to query the default
+ *       line delimiter</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public interface IDocumentExtension4 {
+
+    /**
+     * Tells the document that it is about to be rewritten. That is, a sequence
+     * of replace operations that form a semantic unit will be performed on this
+     * document. A specification of the nature of the operation sequence is
+     * given in form of the session type.
+     * <p>
+     * The document is considered being in rewrite mode as long as
+     * <code>stopRewriteSession</code> has not been called.
+     *
+     * @param sessionType the session type
+     * @return the started rewrite session
+     * @throws IllegalStateException in case there is already an active rewrite session
+     */
+    DocumentRewriteSession startRewriteSession(DocumentRewriteSessionType sessionType) ;
+
+    /**
+     * Tells the document to stop the rewrite session. This method has only any
+     * effect if <code>startRewriteSession</code> has been called before.
+     * <p>
+     * This method does not have any effect if the given session is not the
+     * active rewrite session.
+     *
+     * @param session the session to stop
+     */
+    void stopRewriteSession(DocumentRewriteSession session);
+
+    /**
+     * Returns the active rewrite session of this document or <code>null</code>.
+     *
+     * @return the active rewrite session or <code>null</code>
+     */
+    DocumentRewriteSession getActiveRewriteSession();
+
+    /**
+     * Registers the document rewrite session listener with the document. After
+     * registration the <code>IDocumentRewriteSessionListener</code> is
+     * informed about each state change of rewrite sessions performed on this
+     * document.
+     * <p>
+     * If the listener is already registered nothing happens.
+     * <p>
+     * An <code>IRewriteSessionDocumentListener</code> may call back to this
+     * document when being inside a document notification.
+     *
+     * @param listener the listener to be registered
+     */
+    void addDocumentRewriteSessionListener(IDocumentRewriteSessionListener listener);
+
+    /**
+     * Removes the listener from the document's list of document rewrite session
+     * listeners. If the listener is not registered with the document nothing
+     * happens.
+     * <p>
+     * An <code>IDocumentRewriteSessionListener</code> may call back to this
+     * document when being inside a document notification.
+     *
+     * @param listener the listener to be removed
+     */
+    void removeDocumentRewriteSessionListener(IDocumentRewriteSessionListener listener);
+
+    /**
+     * Substitutes the given text for the specified document range.
+     * Sends a <code>DocumentEvent</code> to all registered <code>IDocumentListener</code>.
+     *
+     * @param offset the document offset
+     * @param length the length of the specified range
+     * @param text the substitution text
+     * @param modificationStamp of the document after replacing
+     * @exception BadLocationException if the offset is invalid in this document
+     *
+     * @see DocumentEvent
+     * @see IDocumentListener
+     */
+    void replace(int offset, int length, String text, long modificationStamp) ;
+
+    /**
+     * Replaces the content of the document with the given text.
+     * Sends a <code>DocumentEvent</code> to all registered <code>IDocumentListener</code>.
+     * This method is a convenience method for <code>replace(0, getLength(), text)</code>.
+     *
+     * @param text the new content of the document
+     * @param modificationStamp of the document after setting the content
+     *
+     * @see DocumentEvent
+     * @see IDocumentListener
+     */
+    void set(String text, long modificationStamp);
+
+    /**
+     * The unknown modification stamp.
+     */
+    static const long UNKNOWN_MODIFICATION_STAMP= -1;
+
+    /**
+     * Returns the modification stamp of this document. The modification stamp
+     * is updated each time a modifying operation is called on this document. If
+     * two modification stamps of the same document are identical then the document
+     * content is too, however, same content does not imply same modification stamp.
+     * <p>
+     * The magnitude or sign of the numerical difference between two modification stamps
+     * is not significant.
+     * </p>
+     *
+     * @return the modification stamp of this document or <code>UNKNOWN_MODIFICATION_STAMP</code>
+     */
+    long getModificationStamp();
+
+    /**
+     * Returns this document's default line delimiter.
+     * <p>
+     * This default line delimiter should be used by clients who
+     * want unique delimiters (e.g. 'CR's) in the document.</p>
+     *
+     * @return the default line delimiter or <code>null</code> if none
+     */
+    String getDefaultLineDelimiter();
+
+    /**
+     * Sets this document's initial line delimiter i.e. the one
+     * which is returned by <code>getDefaultLineDelimiter</code>
+     * if the document does not yet contain any line delimiter.
+     *
+     * @param lineDelimiter the default line delimiter
+     */
+    void setInitialLineDelimiter(String lineDelimiter);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentInformationMapping.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,270 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IDocumentInformationMapping;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * A <code>IDocumentInformationMapping</code>  represents a mapping between the coordinates of two
+ * <code>IDocument</code> objects: the original and the image. The document information mapping
+ * can translate document information such as line numbers or character ranges given for the original into
+ * the corresponding information of the image and vice versa.
+ *
+ * In order to provided backward compatibility for clients of <code>IDocumentInformationMapping</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces
+ * exist:
+ * <ul>
+ * <li> {@link org.eclipse.jface.text.IDocumentInformationMappingExtension} since version 3.0 extending the
+ *      degree of detail of the mapping information.</li>
+ * <li> {@link org.eclipse.jface.text.IDocumentInformationMappingExtension2} since version 3.1, adding lenient
+ *      image region computation.</li>
+ * </ul>
+ *
+ * @since 2.1
+ */
+public interface IDocumentInformationMapping {
+
+    /**
+     * Returns the minimal region of the original document that completely comprises all of the image document
+     * or <code>null</code> if there is no such region.
+     *
+     * @return the minimal region of the original document comprising the image document or <code>null</code>
+     */
+    IRegion getCoverage();
+
+    /**
+     * Returns the offset in the original document that corresponds to the given offset in the image document
+     * or <code>-1</code> if there is no such offset
+     *
+     * @param imageOffset the offset in the image document
+     * @return the corresponding offset in the original document or <code>-1</code>
+     * @throws BadLocationException if <code>imageOffset</code> is not a valid offset in the image document
+     */
+    int toOriginOffset(int imageOffset) ;
+
+    /**
+     * Returns the minimal region of the original document that completely comprises the given region of the
+     * image document or <code>null</code> if there is no such region.
+     *
+     * @param imageRegion the region of the image document
+     * @return the minimal region of the original document comprising the given region of the image document or <code>null</code>
+     * @throws BadLocationException if <code>imageRegion</code> is not a valid region of the image document
+     */
+    IRegion toOriginRegion(IRegion imageRegion) ;
+
+    /**
+     * Returns the range of lines of the original document that corresponds to the given line of the image document or
+     * <code>null</code> if there are no such lines.
+     *
+     * @param imageLine the line of the image document
+     * @return the corresponding lines of the original document or <code>null</code>
+     * @throws BadLocationException if <code>imageLine</code> is not a valid line number in the image document
+     */
+    IRegion toOriginLines(int imageLine) ;
+
+    /**
+     * Returns the line of the original document that corresponds to the given line of the image document or
+     * <code>-1</code> if there is no such line.
+     *
+     * @param imageLine the line of the image document
+     * @return the corresponding line of the original document or <code>-1</code>
+     * @throws BadLocationException if <code>imageLine</code> is not a valid line number in the image document
+     */
+    int toOriginLine(int imageLine) ;
+
+
+
+    /**
+     * Returns the offset in the image document that corresponds to the given offset in the original document
+     * or <code>-1</code> if there is no such offset
+     *
+     * @param originOffset the offset in the original document
+     * @return the corresponding offset in the image document or <code>-1</code>
+     * @throws BadLocationException if <code>originOffset</code> is not a valid offset in the original document
+     */
+    int toImageOffset(int originOffset) ;
+
+    /**
+     * Returns the minimal region of the image document that completely comprises the given region of the
+     * original document or <code>null</code> if there is no such region.
+     *
+     * @param originRegion the region of the original document
+     * @return the minimal region of the image document comprising the given region of the original document or <code>null</code>
+     * @throws BadLocationException if <code>originRegion</code> is not a valid region of the original document
+     */
+    IRegion toImageRegion(IRegion originRegion) ;
+
+    /**
+     * Returns the line of the image document that corresponds to the given line of the original document or
+     * <code>-1</code> if there is no such line.
+     *
+     * @param originLine the line of the original document
+     * @return the corresponding line of the image document or <code>-1</code>
+     * @throws BadLocationException if <code>originLine</code> is not a valid line number in the original document
+     */
+    int toImageLine(int originLine) ;
+
+    /**
+     * Returns the line of the image document whose corresponding line in the original document
+     * is closest to the given line in the original document.
+     *
+     * @param originLine the line in the original document
+     * @return the line in the image document that corresponds best to the given line in the original document
+     * @throws BadLocationException if <code>originLine</code>is not a valid line in the original document
+     */
+    int toClosestImageLine(int originLine) ;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentInformationMappingExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,226 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IDocumentInformationMappingExtension;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Extension to {@link org.eclipse.jface.text.IDocumentInformationMapping}.
+ * <p>
+ * Extends the information available in the mapping by providing explicit access
+ * to the isomorphic portion of the basically homomorphic information mapping.
+ *
+ * @see org.eclipse.jface.text.IDocumentInformationMapping
+ * @since 3.0
+ */
+public interface IDocumentInformationMappingExtension {
+
+    /**
+     * Adheres to
+     * <code>originRegion=toOriginRegion(toExactImageRegion(originRegion))</code>,
+     * if <code>toExactImageRegion(originRegion) !is null</code>. Returns
+     * <code>null</code> if there is no image for the given origin region.
+     *
+     * @param originRegion the origin region
+     * @return the exact image region or <code>null</code>
+     * @throws BadLocationException if origin region is not a valid region in
+     *             the origin document
+     */
+    IRegion toExactImageRegion(IRegion originRegion) ;
+
+    /**
+     * Returns the segments of the image document that exactly correspond to the
+     * given region of the original document. Returns <code>null</code> if
+     * there are no such image regions.
+     *
+     * @param originRegion the region in the origin document
+     * @return the segments in the image document or <code>null</code>
+     * @throws BadLocationException in case the given origin region is not valid
+     *             in the original document
+     */
+    IRegion[] toExactImageRegions(IRegion originRegion) ;
+
+    /**
+     * Returns the fragments of the original document that exactly correspond to
+     * the given region of the image document.
+     *
+     * @param imageRegion the region in the image document
+     * @return the fragments in the origin document
+     * @throws BadLocationException in case the given image region is not valid
+     *             in the image document
+     */
+    IRegion[] toExactOriginRegions(IRegion imageRegion) ;
+
+    /**
+     * Returns the length of the image document.
+     *
+     * @return the length of the image document
+     */
+    int getImageLength();
+
+    /**
+     * Returns the maximal sub-regions of the given origin region which are
+     * completely covered. I.e. each offset in a sub-region has a corresponding
+     * image offset. Returns <code>null</code> if there are no such
+     * sub-regions.
+     *
+     * @param originRegion the region in the origin document
+     * @return the sub-regions with complete coverage or <code>null</code>
+     * @throws BadLocationException in case the given origin region is not valid
+     *             in the original document
+     */
+    IRegion[] getExactCoverage(IRegion originRegion) ;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentInformationMappingExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IDocumentInformationMappingExtension2;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Extension to {@link org.eclipse.jface.text.IDocumentInformationMapping}.
+ * <p>
+ * Extends the information available in the mapping by providing access
+ * to the closest image region of an origin region.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.IDocumentInformationMapping
+ * @since 3.1
+ */
+public interface IDocumentInformationMappingExtension2 {
+
+    /**
+     * Returns the minimal region of the image document that completely
+     * comprises the given region of the original document. The difference to
+     * {@link IDocumentInformationMapping#toImageRegion(IRegion)} is that this
+     * method will always return an image region for a valid origin region. If
+     * <code>originRegion</code> has no corresponding image region, the
+     * zero-length region at the offset between its surrounding fragments is
+     * returned.
+     *
+     * @param originRegion the region of the original document
+     * @return the minimal region of the image document comprising the given
+     *         region of the original document
+     * @throws BadLocationException if <code>originRegion</code> is not a
+     *         valid region of the original document
+     */
+    IRegion toClosestImageRegion(IRegion originRegion) ;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.IDocumentListener;
+
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+
+import java.lang.all;
+
+
+
+/**
+ * Interface for objects which are interested in getting informed about
+ * document changes. A listener is informed about document changes before
+ * they are applied and after they have been applied. It is ensured that
+ * the document event passed into the listener is the same for the two
+ * notifications, i.e. the two document events can be checked using object identity.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.IDocument
+ */
+public interface IDocumentListener {
+
+
+    /**
+     * The manipulation described by the document event will be performed.
+     *
+     * @param event the document event describing the document change
+     */
+    void documentAboutToBeChanged(DocumentEvent event);
+
+    /**
+     * The manipulation described by the document event has been performed.
+     *
+     * @param event the document event describing the document change
+     */
+    void documentChanged(DocumentEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioner.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,308 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IDocumentPartitioner;
+
+// import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+// import org.eclipse.jface.text.DefaultTextHover; // packageimport
+// import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+// import org.eclipse.jface.text.TextUtilities; // packageimport
+// import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+// import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+// import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+// import org.eclipse.jface.text.ITextSelection; // packageimport
+// import org.eclipse.jface.text.Document; // packageimport
+// import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+// import org.eclipse.jface.text.ITextListener; // packageimport
+// import org.eclipse.jface.text.BadPartitioningException; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+// import org.eclipse.jface.text.IUndoManager; // packageimport
+// import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+// import org.eclipse.jface.text.IRepairableDocument; // packageimport
+// import org.eclipse.jface.text.IRewriteTarget; // packageimport
+// import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+// import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+// import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+// import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+// import org.eclipse.jface.text.TextViewer; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+// import org.eclipse.jface.text.RegExMessages; // packageimport
+// import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+// import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+// import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+// import org.eclipse.jface.text.IViewportListener; // packageimport
+// import org.eclipse.jface.text.GapTextStore; // packageimport
+// import org.eclipse.jface.text.MarkSelection; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+// import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+// import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+// import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+// import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+// import org.eclipse.jface.text.TypedRegion; // packageimport
+// import org.eclipse.jface.text.ISynchronizable; // packageimport
+// import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+// import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+// import org.eclipse.jface.text.IRegion; // packageimport
+// import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+// import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+// import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+// import org.eclipse.jface.text.Assert; // packageimport
+// import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+// import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+// import org.eclipse.jface.text.DocumentClone; // packageimport
+// import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+// import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+// import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+// import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+// import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+// import org.eclipse.jface.text.TextSelection; // packageimport
+// import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+// import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+// import org.eclipse.jface.text.IPainter; // packageimport
+// import org.eclipse.jface.text.IInformationControl; // packageimport
+// import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+// import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+// import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+// import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+// import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+// import org.eclipse.jface.text.ITextHover; // packageimport
+// import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+// import org.eclipse.jface.text.ILineTracker; // packageimport
+// import org.eclipse.jface.text.Line; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+// import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+// import org.eclipse.jface.text.TextEvent; // packageimport
+// import org.eclipse.jface.text.BadLocationException; // packageimport
+// import org.eclipse.jface.text.AbstractDocument; // packageimport
+// import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+// import org.eclipse.jface.text.TreeLineTracker; // packageimport
+// import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+// import org.eclipse.jface.text.Region; // packageimport
+// import org.eclipse.jface.text.ITextViewer; // packageimport
+// import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+// import org.eclipse.jface.text.MarginPainter; // packageimport
+// import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+// import org.eclipse.jface.text.TextPresentation; // packageimport
+// import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+// import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+// import org.eclipse.jface.text.ISelectionValidator; // packageimport
+// import org.eclipse.jface.text.IDocumentExtension; // packageimport
+// import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+// import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+// import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+// import org.eclipse.jface.text.IDocumentListener; // packageimport
+// import org.eclipse.jface.text.PaintManager; // packageimport
+// import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+// import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+// import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+// import org.eclipse.jface.text.Position; // packageimport
+// import org.eclipse.jface.text.TextMessages; // packageimport
+// import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+// import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+// import org.eclipse.jface.text.IPositionUpdater; // packageimport
+// import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+// import org.eclipse.jface.text.ListLineTracker; // packageimport
+// import org.eclipse.jface.text.ITextInputListener; // packageimport
+// import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+// import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+// import org.eclipse.jface.text.IInputChangedListener; // packageimport
+// import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+// import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+// import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+// import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+// import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+// import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+// import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+// import org.eclipse.jface.text.CursorLinePainter; // packageimport
+// import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+// import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+// import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+// import org.eclipse.jface.text.DocumentCommand; // packageimport
+// import org.eclipse.jface.text.TypedPosition; // packageimport
+// import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+// import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+// import org.eclipse.jface.text.IEditingSupport; // packageimport
+// import org.eclipse.jface.text.IMarkSelection; // packageimport
+// import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+// import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+// import org.eclipse.jface.text.ITextStore; // packageimport
+// import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+// import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+// import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+// import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+// import org.eclipse.jface.text.TextAttribute; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+/**
+ * A document partitioner divides a document into a set
+ * of disjoint text partitions. Each partition has a content type, an
+ * offset, and a length. The document partitioner is connected to one document
+ * and informed about all changes of this document before any of the
+ * document's document listeners. A document partitioner can thus
+ * incrementally update on the receipt of a document change event.<p>
+ *
+ * In order to provided backward compatibility for clients of <code>IDocumentPartitioner</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces
+ * exist:
+ * <ul>
+ * <li> {@link org.eclipse.jface.text.IDocumentPartitionerExtension} since version 2.0 replacing
+ *      the <code>documentChanged</code> method with a new one returning the minimal document region
+ *      comprising all partition changes.</li>
+ * <li> {@link org.eclipse.jface.text.IDocumentPartitionerExtension2} since version 3.0
+ *      introducing zero-length partitions in conjunction with the distinction between
+ *      open and closed partitions. Also provides inside in the implementation of the partitioner
+ *      by exposing the position category used for managing the partitioning information.</li>
+ * <li> {@link org.eclipse.jface.text.IDocumentPartitionerExtension3} since version 3.1 introducing
+ *      rewrite session. It also replaces the existing {@link #connect(IDocument)} method with
+ *      a new one: {@link org.eclipse.jface.text.IDocumentPartitionerExtension3#connect(IDocument, bool)}.
+ * </ul>
+ * <p>
+ * Clients may implement this interface and its extension interfaces or use the standard
+ * implementation <code>DefaultPartitioner</code>.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.IDocumentPartitionerExtension
+ * @see org.eclipse.jface.text.IDocumentPartitionerExtension2
+ * @see org.eclipse.jface.text.IDocument
+ */
+public interface IDocumentPartitioner {
+
+    /**
+     * Connects the partitioner to a document.
+     * Connect indicates the begin of the usage of the receiver
+     * as partitioner of the given document. Thus, resources the partitioner
+     * needs to be operational for this document should be allocated.<p>
+     *
+     * The caller of this method must ensure that this partitioner is
+     * also set as the document's document partitioner.<p>
+     *
+     * This method has been replaced with {@link IDocumentPartitionerExtension3#connect(IDocument, bool)}.
+     * Implementers should default a call <code>connect(document)</code> to
+     * <code>connect(document, false)</code> in order to sustain the same semantics.
+     *
+     * @param document the document to be connected to
+     */
+    void connect(IDocument document);
+
+    /**
+     * Disconnects the partitioner from the document it is connected to.
+     * Disconnect indicates the end of the usage of the receiver as
+     * partitioner of the connected document. Thus, resources the partitioner
+     * needed to be operation for its connected document should be deallocated.<p>
+     * The caller of this method should also must ensure that this partitioner is
+     * no longer the document's partitioner.
+     */
+    void disconnect();
+
+    /**
+     * Informs about a forthcoming document change. Will be called by the
+     * connected document and is not intended to be used by clients
+     * other than the connected document.
+     *
+     * @param event the event describing the forthcoming change
+     */
+    void documentAboutToBeChanged(DocumentEvent event);
+
+    /**
+     * The document has been changed. The partitioner updates
+     * the document's partitioning and returns whether the structure of the
+     * document partitioning has been changed, i.e. whether partitions
+     * have been added or removed. Will be called by the connected document and
+     * is not intended to be used by clients other than the connected document.<p>
+     *
+     * This method has been replaced by {@link IDocumentPartitionerExtension#documentChanged2(DocumentEvent)}.
+     *
+     * @param event the event describing the document change
+     * @return <code>true</code> if partitioning changed
+     */
+    bool documentChanged(DocumentEvent event);
+
+    /**
+     * Returns the set of all legal content types of this partitioner.
+     * I.e. any result delivered by this partitioner may not contain a content type
+     * which would not be included in this method's result.
+     *
+     * @return the set of legal content types
+     */
+    String[] getLegalContentTypes();
+
+    /**
+     * Returns the content type of the partition containing the
+     * given offset in the connected document. There must be a
+     * document connected to this partitioner.<p>
+     *
+     * Use {@link IDocumentPartitionerExtension2#getContentType(int, bool)} when
+     * zero-length partitions are supported. In that case this method is
+     * equivalent:
+     * <pre>
+     *    IDocumentPartitionerExtension2 extension= cast(IDocumentPartitionerExtension2) partitioner;
+     *    return extension.getContentType(offset, false);
+     * </pre>
+     *
+     * @param offset the offset in the connected document
+     * @return the content type of the offset's partition
+     */
+    String getContentType(int offset);
+
+    /**
+     * Returns the partitioning of the given range of the connected
+     * document. There must be a document connected to this partitioner.<p>
+     *
+     * Use {@link IDocumentPartitionerExtension2#computePartitioning(int, int, bool)} when
+     * zero-length partitions are supported. In that case this method is
+     * equivalent:
+     * <pre>
+     *    IDocumentPartitionerExtension2 extension= cast(IDocumentPartitionerExtension2) partitioner;
+     *    return extension.computePartitioning(offset, length, false);
+     * </pre>
+     *
+     * @param offset the offset of the range of interest
+     * @param length the length of the range of interest
+     * @return the partitioning of the range
+     */
+    ITypedRegion[] computePartitioning(int offset, int length);
+
+    /**
+     * Returns the partition containing the given offset of
+     * the connected document. There must be a document connected to this
+     * partitioner.<p>
+     *
+     * Use {@link IDocumentPartitionerExtension2#getPartition(int, bool)} when
+     * zero-length partitions are supported. In that case this method is
+     * equivalent:
+     * <pre>
+     *    IDocumentPartitionerExtension2 extension= cast(IDocumentPartitionerExtension2) partitioner;
+     *    return extension.getPartition(offset, false);
+     * </pre>
+     *
+     * @param offset the offset for which to determine the partition
+     * @return the partition containing the offset
+     */
+    ITypedRegion getPartition(int offset);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitionerExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.IDocumentPartitionerExtension;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IDocumentPartitioner}.
+ * <p>
+ * Replaces the original concept of the document partitioner by returning the
+ * minimal region that includes all partition changes caused by the invocation
+ * of the document partitioner.
+ * The method <code>documentChanged2</code> is considered the replacement of
+ * {@link org.eclipse.jface.text.IDocumentPartitioner#documentChanged(DocumentEvent)}.
+ *
+ * @since 2.0
+ */
+public interface IDocumentPartitionerExtension {
+
+    /**
+     * The document has been changed. The partitioner updates the document's
+     * partitioning and returns the minimal region that comprises all partition
+     * changes caused in response to the given document event. This method
+     * returns <code>null</code> if the partitioning did not change.
+     * <p>
+     *
+     * Will be called by the connected document and is not intended to be used
+     * by clients other than the connected document.
+     * <p>
+     * Replaces {@link IDocumentPartitioner#documentChanged(DocumentEvent)}.
+     *
+     * @param event the event describing the document change
+     * @return the region of the document in which the partition type changed or <code>null</code>
+     */
+    IRegion documentChanged2(DocumentEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitionerExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,266 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IDocumentPartitionerExtension2;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IDocumentPartitioner}.
+ * <p>
+ * Extends the original concept of a document partitioner to answer the position
+ * categories that are used to manage the partitioning information.
+ * <p>
+ * This extension also introduces the concept of open and delimited partitions.
+ * A delimited partition has a predefined textual token delimiting its start and
+ * end, while an open partition can fill any space between two delimited
+ * partitions.
+ * </p>
+ * <p>
+ * An open partition of length zero can occur between two delimited partitions,
+ * thus having the same offset as the following delimited partition. The
+ * document start and end are considered to be delimiters of open partitions,
+ * i.e. there may be a zero-length partition between the document start and a
+ * delimited partition starting at offset 0.
+ * </p>
+ *
+ * @since 3.0
+ */
+public interface IDocumentPartitionerExtension2 {
+
+    /**
+     * Returns the position categories that this partitioners uses in order to manage
+     * the partitioning information of the documents. Returns <code>null</code> if
+     * no position category is used.
+     *
+     * @return the position categories used to manage partitioning information or <code>null</code>
+     */
+    String[] getManagingPositionCategories();
+
+
+    /* zero-length partition support */
+
+    /**
+     * Returns the content type of the partition containing the given offset in
+     * the connected document. There must be a document connected to this
+     * partitioner.
+     * <p>
+     * If <code>preferOpenPartitions</code> is <code>true</code>,
+     * precedence is given to an open partition ending at <code>offset</code>
+     * over a delimited partition starting at <code>offset</code>.
+     * <p>
+     * This method replaces {@link IDocumentPartitioner#getContentType(int)}and
+     * behaves like it when <code>prepreferOpenPartitions</code> is
+     * <code>false</code>, i.e. precedence is always given to the partition
+     * that does not end at <code>offset</code>.
+     * </p>
+     *
+     * @param offset the offset in the connected document
+     * @param preferOpenPartitions <code>true</code> if precedence should be
+     *            given to a open partition ending at <code>offset</code> over
+     *            a delimited partition starting at <code>offset</code>
+     * @return the content type of the offset's partition
+     */
+    String getContentType(int offset, bool preferOpenPartitions);
+
+    /**
+     * Returns the partition containing the given offset of the connected
+     * document. There must be a document connected to this partitioner.
+     * <p>
+     * If <code>preferOpenPartitions</code> is <code>true</code>,
+     * precedence is given to an open partition ending at <code>offset</code>
+     * over a delimited partition starting at <code>offset</code>.
+     * <p>
+     * This method replaces {@link IDocumentPartitioner#getPartition(int)}and
+     * behaves like it when <preferOpenPartitions</code> is <code>false
+     * </code>, i.e. precedence is always given to the partition that does not
+     * end at <code>offset</code>.
+     * </p>
+     *
+     * @param offset the offset for which to determine the partition
+     * @param preferOpenPartitions <code>true</code> if precedence should be
+     *            given to a open partition ending at <code>offset</code> over
+     *            a delimited partition starting at <code>offset</code>
+     * @return the partition containing the offset
+     */
+    ITypedRegion getPartition(int offset, bool preferOpenPartitions);
+
+    /**
+     * Returns the partitioning of the given range of the connected document.
+     * There must be a document connected to this partitioner.
+     * <p>
+     * If <code>includeZeroLengthPartitions</code> is <code>true</code>, a
+     * zero-length partition of an open partition type (usually the default
+     * partition) is included between two delimited partitions. If it is
+     * <code>false</code>, no zero-length partitions are included.
+     * </p>
+     * <p>
+     * This method replaces
+     * {@link IDocumentPartitioner#computePartitioning(int, int)}and behaves
+     * like it when <code>includeZeroLengthPartitions</code> is
+     * <code>false</code>.
+     * </p>
+     *
+     * @param offset the offset of the range of interest
+     * @param length the length of the range of interest
+     * @param includeZeroLengthPartitions <code>true</code> if zero-length
+     *            partitions should be returned as part of the computed
+     *            partitioning
+     * @return the partitioning of the range
+     */
+    ITypedRegion[] computePartitioning(int offset, int length, bool includeZeroLengthPartitions);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitionerExtension3.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IDocumentPartitionerExtension3;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IDocumentPartitioner}. Adds the
+ * concept of rewrite sessions. A rewrite session is a sequence of replace
+ * operations that form a semantic unit.
+ *
+ * @since 3.1
+ */
+public interface IDocumentPartitionerExtension3 {
+
+    /**
+     * Tells the document partitioner that a rewrite session started. A rewrite
+     * session is a sequence of replace operations that form a semantic unit.
+     * The document partitioner is allowed to use that information for internal
+     * optimization.
+     *
+     * @param session the rewrite session
+     * @throws IllegalStateException in case there is already an active rewrite session
+     */
+    void startRewriteSession(DocumentRewriteSession session) ;
+
+    /**
+     * Tells the document partitioner that the rewrite session has finished.
+     * This method is only called when <code>startRewriteSession</code> has
+     * been called before.
+     *
+     * @param session the rewrite session
+     */
+    void stopRewriteSession(DocumentRewriteSession session);
+
+    /**
+     * Returns the active rewrite session of this document or <code>null</code>.
+     *
+     * @return the active rewrite session or <code>null</code>
+     */
+    DocumentRewriteSession getActiveRewriteSession();
+
+    /**
+     * Connects this partitioner to a document. Connect indicates the begin of
+     * the usage of the receiver as partitioner of the given document. Thus,
+     * resources the partitioner needs to be operational for this document
+     * should be allocated.
+     * <p>
+     * The caller of this method must ensure that this partitioner is also set
+     * as the document's document partitioner.
+     * <p>
+     * <code>delayInitialization</code> indicates whether the partitioner is
+     * allowed to delay it initial computation of the document's partitioning
+     * until it has to answer the first query.
+     *
+     * Replaces {@link IDocumentPartitioner#connect(IDocument)}.
+     *
+     * @param document the document to be connected to
+     * @param delayInitialization <code>true</code> if initialization can be delayed, <code>false</code> otherwise
+     */
+    void connect(IDocument document, bool delayInitialization);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.IDocumentPartitioningListener;
+
+// import org.eclipse.jface.text.DefaultTextHover; // packageimport
+// import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+// import org.eclipse.jface.text.TextUtilities; // packageimport
+// import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+// import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+// import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+// import org.eclipse.jface.text.ITextSelection; // packageimport
+// import org.eclipse.jface.text.Document; // packageimport
+// import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+// import org.eclipse.jface.text.ITextListener; // packageimport
+// import org.eclipse.jface.text.BadPartitioningException; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+// import org.eclipse.jface.text.IUndoManager; // packageimport
+// import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+// import org.eclipse.jface.text.IRepairableDocument; // packageimport
+// import org.eclipse.jface.text.IRewriteTarget; // packageimport
+// import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+// import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+// import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+// import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+// import org.eclipse.jface.text.TextViewer; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+// import org.eclipse.jface.text.RegExMessages; // packageimport
+// import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+// import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+// import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+// import org.eclipse.jface.text.IViewportListener; // packageimport
+// import org.eclipse.jface.text.GapTextStore; // packageimport
+// import org.eclipse.jface.text.MarkSelection; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+// import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+// import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+// import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+// import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+// import org.eclipse.jface.text.TypedRegion; // packageimport
+// import org.eclipse.jface.text.ISynchronizable; // packageimport
+// import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+// import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+// import org.eclipse.jface.text.IRegion; // packageimport
+// import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+// import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+// import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+// import org.eclipse.jface.text.Assert; // packageimport
+// import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+// import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+// import org.eclipse.jface.text.DocumentClone; // packageimport
+// import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+// import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+// import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+// import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+// import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+// import org.eclipse.jface.text.TextSelection; // packageimport
+// import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+// import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+// import org.eclipse.jface.text.IPainter; // packageimport
+// import org.eclipse.jface.text.IInformationControl; // packageimport
+// import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+// import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+// import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+// import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+// import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+// import org.eclipse.jface.text.ITextHover; // packageimport
+// import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+// import org.eclipse.jface.text.ILineTracker; // packageimport
+// import org.eclipse.jface.text.Line; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+// import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+// import org.eclipse.jface.text.TextEvent; // packageimport
+// import org.eclipse.jface.text.BadLocationException; // packageimport
+// import org.eclipse.jface.text.AbstractDocument; // packageimport
+// import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+// import org.eclipse.jface.text.TreeLineTracker; // packageimport
+// import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+// import org.eclipse.jface.text.Region; // packageimport
+// import org.eclipse.jface.text.ITextViewer; // packageimport
+// import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+// import org.eclipse.jface.text.MarginPainter; // packageimport
+// import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+// import org.eclipse.jface.text.TextPresentation; // packageimport
+// import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+// import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+// import org.eclipse.jface.text.ISelectionValidator; // packageimport
+// import org.eclipse.jface.text.IDocumentExtension; // packageimport
+// import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+// import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+// import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+// import org.eclipse.jface.text.IDocumentListener; // packageimport
+// import org.eclipse.jface.text.PaintManager; // packageimport
+// import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+// import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+// import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+// import org.eclipse.jface.text.Position; // packageimport
+// import org.eclipse.jface.text.TextMessages; // packageimport
+// import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+// import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+// import org.eclipse.jface.text.IPositionUpdater; // packageimport
+// import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+// import org.eclipse.jface.text.ListLineTracker; // packageimport
+// import org.eclipse.jface.text.ITextInputListener; // packageimport
+// import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+// import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+// import org.eclipse.jface.text.IInputChangedListener; // packageimport
+// import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+// import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+// import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+// import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+// import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+// import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+// import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+// import org.eclipse.jface.text.CursorLinePainter; // packageimport
+// import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+// import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+// import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+// import org.eclipse.jface.text.DocumentCommand; // packageimport
+// import org.eclipse.jface.text.TypedPosition; // packageimport
+// import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+// import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+// import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+// import org.eclipse.jface.text.IEditingSupport; // packageimport
+// import org.eclipse.jface.text.IMarkSelection; // packageimport
+// import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+// import org.eclipse.jface.text.DocumentEvent; // packageimport
+// import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+// import org.eclipse.jface.text.ITextStore; // packageimport
+// import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+// import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+// import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+// import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+// import org.eclipse.jface.text.TextAttribute; // packageimport
+// import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+// import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+
+/**
+ * Interface of objects which are interested in getting informed
+ * about changes of a document's partitioning.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ * <p>
+ * In order to provided backward compatibility for clients of <code>IDocumentPartitioningListener</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces
+ * exist:
+ * <ul>
+ * <li> {@link org.eclipse.jface.text.IDocumentPartitioningListenerExtension} since version 2.0 replacing the original
+ *      notification mechanism.</li>
+ * <li> {@link org.eclipse.jface.text.IDocumentPartitioningListenerExtension2} since version 3.0 replacing all previous
+ *      notification mechanisms. Thus, implementers up-to-date with version 3.0 do not have to implement
+ *      {@link org.eclipse.jface.text.IDocumentPartitioningListenerExtension}.</li>
+ * </ul>
+ * </p>
+ * @see org.eclipse.jface.text.IDocumentPartitioningListenerExtension
+ * @see org.eclipse.jface.text.IDocumentPartitioningListenerExtension2
+ * @see org.eclipse.jface.text.IDocument
+ * @see org.eclipse.jface.text.IDocumentPartitioner
+ */
+public interface IDocumentPartitioningListener {
+
+    /**
+     * The partitioning of the given document changed.
+     * <p>
+     * In version 2.0 this method has been replaces by
+     * {@link IDocumentPartitioningListenerExtension#documentPartitioningChanged(IDocument, IRegion)}.
+     * <p>
+     * In version 3.0 this method has been replaces by
+     * {@link IDocumentPartitioningListenerExtension2#documentPartitioningChanged(DocumentPartitioningChangedEvent)}<p>
+     *
+     * @param document the document whose partitioning changed
+     *
+     * @see IDocumentPartitioningListenerExtension#documentPartitioningChanged(IDocument, IRegion)
+     * @see IDocumentPartitioningListenerExtension2#documentPartitioningChanged(DocumentPartitioningChangedEvent)
+     * @see IDocument#addDocumentPartitioningListener(IDocumentPartitioningListener)
+     */
+    void documentPartitioningChanged(IDocument document);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListenerExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IDocumentPartitioningListenerExtension;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+
+
+/**
+ * Extension interface for
+ * {@link org.eclipse.jface.text.IDocumentPartitioningListener}.
+ * <p>
+ * Replaces the original notification mechanism by telling the listener the
+ * minimal region that comprises all partitioning changes.
+ *
+ * @see org.eclipse.jface.text.IDocumentPartitionerExtension
+ * @since 2.0
+ */
+public interface IDocumentPartitioningListenerExtension {
+
+    /**
+     * The partitioning of the given document changed in the given region.
+     * <p>
+     * In version 3.0, this method has been replaced with
+     * {@link IDocumentPartitioningListenerExtension2#documentPartitioningChanged(DocumentPartitioningChangedEvent)}.
+     *
+     * @param document the document whose partitioning changed
+     * @param region the region in which the partitioning changed
+     * @see IDocumentPartitioningListenerExtension2#documentPartitioningChanged(DocumentPartitioningChangedEvent)
+     * @see IDocument#addDocumentPartitioningListener(IDocumentPartitioningListener)
+     */
+    void documentPartitioningChanged(IDocument document, IRegion region);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListenerExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.IDocumentPartitioningListenerExtension2;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Extension interface to
+ * {@link org.eclipse.jface.text.IDocumentPartitioningListener}.
+ * <p>
+ *
+ * Replaces the previous notification mechanisms by introducing an explicit
+ * document partitioning changed event.
+ *
+ * @see org.eclipse.jface.text.DocumentPartitioningChangedEvent
+ * @since 3.0
+ */
+public interface IDocumentPartitioningListenerExtension2 {
+
+    /**
+     * Signals the change of document partitionings.
+     * <p>
+     * This method replaces
+     * {@link IDocumentPartitioningListener#documentPartitioningChanged(IDocument)}
+     * and
+     * {@link IDocumentPartitioningListenerExtension#documentPartitioningChanged(IDocument, IRegion)}
+     *
+     * @param event the event describing the change
+     * @see IDocument#addDocumentPartitioningListener(IDocumentPartitioningListener)
+     */
+    void documentPartitioningChanged(DocumentPartitioningChangedEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentRewriteSessionListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IDocumentRewriteSessionListener;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Interface for objects which are interested in getting informed about document
+ * rewrite sessions.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.IDocument
+ * @see org.eclipse.jface.text.IDocumentExtension4
+ * @since 3.1
+ */
+public interface IDocumentRewriteSessionListener {
+
+    /**
+     * Signals a change in a document's rewrite session state.
+     *
+     * @param event the event describing the document rewrite session state change
+     */
+    void documentRewriteSessionChanged(DocumentRewriteSessionEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ILineTracker.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,292 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ILineTracker;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * A line tracker maps character positions to line numbers and vice versa.
+ * Initially the line tracker is informed about its underlying text in order to
+ * initialize the mapping information. After that, the line tracker is informed
+ * about all changes of the underlying text allowing for incremental updates of
+ * the mapping information. It is the client's responsibility to actively inform
+ * the line tacker about text changes. For example, when using a line tracker in
+ * combination with a document the document controls the line tracker.
+ * <p>
+ * In order to provide backward compatibility for clients of <code>ILineTracker</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces
+ * exist:
+ * <ul>
+ * <li> {@link org.eclipse.jface.text.ILineTrackerExtension} since version 3.1 introducing the concept
+ *      of rewrite sessions.</li>
+ * </ul>
+ * <p>
+ * Clients may implement this interface or use the standard implementation
+ * </p>
+ * {@link org.eclipse.jface.text.DefaultLineTracker}or
+ * {@link org.eclipse.jface.text.ConfigurableLineTracker}.
+ */
+public interface ILineTracker {
+
+    /**
+     * Returns the strings this tracker considers as legal line delimiters.
+     *
+     * @return the legal line delimiters
+     */
+    String[] getLegalLineDelimiters();
+
+    /**
+     * Returns the line delimiter of the specified line. Returns <code>null</code> if the
+     * line is not closed with a line delimiter.
+     *
+     * @param line the line whose line delimiter is queried
+     * @return the line's delimiter or <code>null</code> if line does not have a delimiter
+     * @exception BadLocationException if the line number is invalid in this tracker's line structure
+     */
+    String getLineDelimiter(int line) ;
+
+    /**
+     * Computes the number of lines in the given text.
+     *
+     * @param text the text whose number of lines should be computed
+     * @return the number of lines in the given text
+     */
+    int computeNumberOfLines(String text);
+
+    /**
+     * Returns the number of lines.
+     *
+     * @return the number of lines in this tracker's line structure
+     */
+    int getNumberOfLines();
+
+    /**
+     * Returns the number of lines which are occupied by a given text range.
+     *
+     * @param offset the offset of the specified text range
+     * @param length the length of the specified text range
+     * @return the number of lines occupied by the specified range
+     * @exception BadLocationException if specified range is unknown to this tracker
+     */
+    int getNumberOfLines(int offset, int length) ;
+
+    /**
+     * Returns the position of the first character of the specified line.
+     *
+     * @param line the line of interest
+     * @return offset of the first character of the line
+     * @exception BadLocationException if the line is unknown to this tracker
+     */
+    int getLineOffset(int line) ;
+
+    /**
+     * Returns length of the specified line including the line's delimiter.
+     *
+     * @param line the line of interest
+     * @return the length of the line
+     * @exception BadLocationException if line is unknown to this tracker
+     */
+    int getLineLength(int line) ;
+
+    /**
+     * Returns the line number the character at the given offset belongs to.
+     *
+     * @param offset the offset whose line number to be determined
+     * @return the number of the line the offset is on
+     * @exception BadLocationException if the offset is invalid in this tracker
+     */
+    int getLineNumberOfOffset(int offset) ;
+
+    /**
+     * Returns a line description of the line at the given offset.
+     * The description contains the start offset and the length of the line
+     * excluding the line's delimiter.
+     *
+     * @param offset the offset whose line should be described
+     * @return a region describing the line
+     * @exception BadLocationException if offset is invalid in this tracker
+     */
+    IRegion getLineInformationOfOffset(int offset) ;
+
+    /**
+     * Returns a line description of the given line. The description
+     * contains the start offset and the length of the line excluding the line's
+     * delimiter.
+     *
+     * @param line the line that should be described
+     * @return a region describing the line
+     * @exception BadLocationException if line is unknown to this tracker
+     */
+    IRegion getLineInformation(int line) ;
+
+    /**
+     * Informs the line tracker about the specified change in the tracked text.
+     *
+     * @param offset the offset of the replaced text
+     * @param length the length of the replaced text
+     * @param text the substitution text
+     * @exception BadLocationException if specified range is unknown to this tracker
+     */
+    void replace(int offset, int length, String text) ;
+
+    /**
+     * Sets the tracked text to the specified text.
+     *
+     * @param text the new tracked text
+     */
+    void set(String text);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ILineTrackerExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.ILineTrackerExtension;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.ILineTracker}. Adds the
+ * concept of rewrite sessions. A rewrite session is a sequence of replace
+ * operations that form a semantic unit.
+ *
+ * @since 3.1
+ */
+public interface ILineTrackerExtension {
+
+    /**
+     * Tells the line tracker that a rewrite session started. A rewrite session
+     * is a sequence of replace operations that form a semantic unit. The line
+     * tracker is allowed to use that information for internal optimization.
+     *
+     * @param session the rewrite session
+     * @throws IllegalStateException in case there is already an active rewrite
+     *             session
+     */
+    void startRewriteSession(DocumentRewriteSession session) ;
+
+    /**
+     * Tells the line tracker that the rewrite session has finished. This method
+     * is only called when <code>startRewriteSession</code> has been called
+     * before. The text resulting from the rewrite session is passed to the line
+     * tracker.
+     *
+     * @param session the rewrite session
+     * @param text the text with which to re-initialize the line tracker
+     */
+    void stopRewriteSession(DocumentRewriteSession session, String text);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IPositionUpdater.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IPositionUpdater;
+
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+
+import java.lang.all;
+
+
+/**
+ * A position updater is responsible for adapting document positions. When
+ * installed on a document, the position updater updates the document's
+ * positions to changes applied to this document. Document updaters can be
+ * selective, i.e. they might only update positions of a certain category.
+ * <p>
+ * Position updaters are of primary importance for the definition of the
+ * semantics of positions.
+ * <p>
+ * Clients may implement this interface or use the standard implementation
+ * {@link org.eclipse.jface.text.DefaultPositionUpdater}.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.IDocument
+ * @see org.eclipse.jface.text.Position
+ */
+public interface IPositionUpdater {
+
+    /**
+     * Adapts positions to the change specified by the document event.
+     * It is ensured that the document's partitioning has been adapted to
+     * this document change and that all the position updaters which have
+     * a smaller index in the document's position updater list have been called.
+     *
+     * @param event the document event describing the document change
+     */
+    void update(DocumentEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IRegion.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.IRegion;
+
+import java.lang.all;
+
+/**
+ * A region describes a certain range in an indexed text store. Text stores are
+ * for example documents or strings. A region is defined by its offset into the
+ * text store and its length.
+ * <p>
+ * A region is considered a value object. Its offset and length do not change
+ * over time.
+ * <p>
+ * Clients may implement this interface or use the standard implementation
+ * {@link org.eclipse.jface.text.Region}.
+ * </p>
+ */
+public interface IRegion {
+
+    /**
+     * Returns the length of the region.
+     *
+     * @return the length of the region
+     */
+    int getLength();
+
+    /**
+     * Returns the offset of the region.
+     *
+     * @return the offset of the region
+     */
+    int getOffset();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IRepairableDocument.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IRepairableDocument;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Tagging interface to be implemented by
+ * {@link org.eclipse.jface.text.IDocument} implementers that offer a line
+ * repair method on the documents.
+ * <p>
+ * In order to provide backward compatibility for clients of
+ * <code>IRepairableDocument</code>, extension interfaces are used to provide
+ * a means of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li> {@link org.eclipse.jface.text.IRepairableDocumentExtension} since version 3.4
+ *      adds the ability to query whether the repairable document needs to be repaired.</li>
+ * </ul>
+ * 
+ * 
+ * @see org.eclipse.jface.text.IDocument
+ * @see org.eclipse.jface.text.IRepairableDocumentExtension
+ * @since 3.0
+ */
+public interface IRepairableDocument {
+
+    /**
+     * Repairs the line information of the document implementing this interface.
+     */
+    void repairLineInformation();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IRepairableDocumentExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.IRepairableDocumentExtension;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.IRepairableDocument}.
+ * <p>
+ * Adds the ability to query whether the repairable document needs to be
+ * repaired.
+ * 
+ * @see org.eclipse.jface.text.IRepairableDocument
+ * @since 3.4
+ */
+public interface IRepairableDocumentExtension {
+
+    /**
+     * Tells whether the line information of the document implementing this
+     * interface needs to be repaired.
+     * 
+     * @param offset the document offset
+     * @param length the length of the specified range
+     * @param text the substitution text to check
+     * @return <code>true</code> if the line information must be repaired
+     * @throws BadLocationException if the offset is invalid in this document
+     * @see IRepairableDocument#repairLineInformation()
+     */
+    bool isLineInformationRepairNeeded(int offset, int length, String text) ;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ISlaveDocumentManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,250 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ISlaveDocumentManager;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Slave documents are documents whose contents is defined in terms of a master
+ * document. Thus, slave documents usually reflect a projection of the master document.
+ * Slave documents are causally connected to the master document. This means, changes
+ * of the master document have immediate effect on the slave document and vice versa.
+ * <p>
+ * A slave document manager creates slave documents for given master documents, manages the
+ * life cycle of the slave documents, and keeps track of the information flow between
+ * master and slave documents. The slave document manager defines the construction rules of the
+ * slave documents in terms of the master document.</p>
+ * <p>
+* In order to provided backward compatibility for clients of <code>ISlaveDocumentManager</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces
+ * exist:
+ * <ul>
+ * <li> {@link org.eclipse.jface.text.ISlaveDocumentManagerExtension} since version 3.0 extending the protocol
+ *      with an access to all managed slave document for a given master document. </li>
+ * </ul>
+ * </p>
+ *
+ * @see org.eclipse.jface.text.IDocument
+ * @since 2.1
+ */
+public interface ISlaveDocumentManager {
+
+    /**
+     * Creates a new slave document for the given master document. The slave document
+     * is causally connected to its master document until <code>freeSlaveDocument</code>
+     * is called. The connection between the newly created slave document and the master
+     * document is managed by this slave document manager.
+     *
+     * @param master the master document
+     * @return the newly created slave document
+     * @see #freeSlaveDocument(IDocument)
+     */
+    IDocument createSlaveDocument(IDocument master);
+
+    /**
+     * Frees the given slave document. If the given document is not a slave document known
+     * to this slave document manager, this call does not have any effect. A slave
+     * document is known to this slave document manager if it has been created by
+     * this manager using <code>createSlaveDocument</code>.
+     *
+     * @param slave the slave document to be freed
+     * @see #createSlaveDocument(IDocument)
+     */
+    void freeSlaveDocument(IDocument slave);
+
+    /**
+     * Creates a new document information mapping between the given slave document and
+     * its master document. Returns <code>null</code> if the given document is unknown
+     * to this slave document manager.
+     *
+     * @param slave the slave document
+     * @return a document information mapping between the slave document and its master document or
+     *      <code>null</code>
+     */
+    IDocumentInformationMapping createMasterSlaveMapping(IDocument slave);
+
+    /**
+     * Returns the master document of the given slave document or <code>null</code> if the
+     * given document is unknown to this slave document manager.
+     *
+     * @param slave the slave document
+     * @return the master document of the given slave document or <code>null</code>
+     */
+    IDocument getMasterDocument(IDocument slave);
+
+    /**
+     * Returns whether the given document is a slave document known to this slave document manager. A slave document
+     * is known to this slave document manager, if the document has been created by this manager.
+     *
+     * @param document the document to be checked whether it is a slave document known to this manager
+     * @return <code>true</code> if the document is a slave document, <code>false</code> otherwise
+     */
+    bool isSlaveDocument(IDocument document);
+
+    /**
+     * Sets the given slave document's auto expand mode. In auto expand mode, a
+     * slave document is automatically adapted to reflect all changes applied to it's master document.
+     * Assume a master document contains 30 lines and the slave is defined to contain the lines 11-20.
+     * In auto expand mode, when the master document is changed at line 8, the slave document is expanded
+     * to contain the lines 8-20.<p>
+     * This call is without effect if the given document is unknown to this slave document manager.
+     *
+     * @param slave the slave whose auto expand mode should be set
+     * @param autoExpand <code>true</code> for auto expand, <code>false</code> otherwise
+     */
+    void setAutoExpandMode(IDocument slave, bool autoExpand);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ISlaveDocumentManagerExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.ISlaveDocumentManagerExtension;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Extension interface for {@link org.eclipse.jface.text.ISlaveDocumentManager}.
+ * <p>
+ * Adds access to the list of all slave documents for a given master document.
+ *
+ * @see org.eclipse.jface.text.ISlaveDocumentManager
+ * @since 3.0
+ */
+public interface ISlaveDocumentManagerExtension {
+
+    /**
+     * Returns the list of slave documents for the given master document or
+     * <code>null</code> if there are no such slave document.
+     *
+     * @param master the master document
+     * @return the list of slave documents or <code>null</code>
+     */
+    IDocument[] getSlaveDocuments(IDocument master);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ISynchronizable.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,198 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.ISynchronizable;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Interface for text related objects which may be used in the multi-threaded
+ * context and thus must provide a way to prevent concurrent access and
+ * manipulation.
+ * <p>
+ * In order to reduce the probability of dead locks clients should synchronize
+ * their access to these objects by using the provided lock object rather than
+ * the object itself.</p>
+ * <p>
+ * Managing objects can use the <code>setLockObject</code> method in order to
+ * synchronize whole sets of objects.</p>
+ *
+ * @since 3.0
+ */
+public interface ISynchronizable {
+
+    /**
+     * Sets the lock object for this object. If the lock object is not
+     * <code>null</code> subsequent calls to specified methods of this object
+     * are synchronized on this lock object. Which methods are synchronized is
+     * specified by the implementer.
+     * <p>
+     * <em>You should not override an existing lock object unless you own
+     * that lock object yourself. Use the existing lock object instead.</em>
+     * </p>
+     *
+     * @param lockObject the lock object. May be <code>null</code>.
+     */
+    void setLockObject(Object lockObject);
+
+    /**
+     * Returns the lock object or <code>null</code> if there is none. Clients
+     * should use the lock object in order to synchronize concurrent access to
+     * the implementer.
+     *
+     * @return the lock object or <code>null</code>
+     */
+    Object getLockObject();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ITextStore.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,214 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.ITextStore;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Interface for storing and managing text.
+ * <p>
+ * Provides access to the stored text and allows to manipulate it.</p>
+ * <p>
+ * Clients may
+ * implement this interface or use {@link org.eclipse.jface.text.GapTextStore} or
+ * {@link org.eclipse.jface.text.CopyOnWriteTextStore}.</p>
+ */
+public interface ITextStore {
+
+    /**
+     * Returns the character at the specified offset.
+     *
+     * @param offset the offset in this text store
+     * @return the character at this offset
+     */
+    char get(int offset);
+
+    /**
+     * Returns the text of the specified character range.
+     *
+     * @param offset the offset of the range
+     * @param length the length of the range
+     * @return the text of the range
+     */
+    String get(int offset, int length);
+
+    /**
+     * Returns number of characters stored in this text store.
+     *
+     * @return the number of characters stored in this text store
+     */
+    int getLength();
+
+    /**
+     * Replaces the specified character range with the given text.
+     * <code>replace(getLength(), 0, "some text")</code> is a valid
+     * call and appends text to the end of the text store.
+     *
+     * @param offset the offset of the range to be replaced
+     * @param length the number of characters to be replaced
+     * @param text the substitution text
+     */
+    void replace(int offset, int length, String text);
+
+    /**
+     * Replace the content of the text store with the given text.
+     * Convenience method for <code>replace(0, getLength(), text</code>.
+     *
+     * @param text the new content of the text store
+     */
+    void set(String text);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ITypedRegion.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.ITypedRegion;
+
+import org.eclipse.jface.text.IRegion; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+/**
+ * Describes a region of an indexed text store such as a document or a string.
+ * The region consists of offset, length, and type. The region type is defined
+ * as a string.
+ * <p>
+ * A typed region can, e.g., be used to described document partitions.</p>
+ * <p>
+ * Clients may implement this interface or use the standard implementation
+ * {@link org.eclipse.jface.text.TypedRegion}.</p>
+ */
+public interface ITypedRegion : IRegion {
+
+    /**
+     * Returns the content type of the region.
+     *
+     * @return the content type of the region
+     */
+    String getType();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/Line.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.Line;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Describes a line as a particular number of characters beginning at
+ * a particular offset, consisting of a particular number of characters,
+ * and being closed with a particular line delimiter.
+ */
+final class Line : IRegion {
+
+    /** The offset of the line */
+    public int offset;
+    /** The length of the line */
+    public int length;
+    /** The delimiter of this line */
+    public const String delimiter;
+
+    /**
+     * Creates a new Line.
+     *
+     * @param offset the offset of the line
+     * @param end the last including character offset of the line
+     * @param delimiter the line's delimiter
+     */
+    public this(int offset, int end, String delimiter) {
+        this.offset= offset;
+        this.length= (end - offset) +1;
+        this.delimiter= delimiter;
+    }
+
+    /**
+     * Creates a new Line.
+     *
+     * @param offset the offset of the line
+     * @param length the length of the line
+     */
+    public this(int offset, int length) {
+        this.offset= offset;
+        this.length= length;
+        this.delimiter= null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IRegion#getOffset()
+     */
+    public int getOffset() {
+        return offset;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IRegion#getLength()
+     */
+    public int getLength() {
+        return length;
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ListLineTracker.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,507 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.ListLineTracker;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+import org.eclipse.jface.text.AbstractLineTracker;
+
+/**
+ * Abstract, read-only implementation of <code>ILineTracker</code>. It lets the definition of
+ * line delimiters to subclasses. Assuming that '\n' is the only line delimiter, this abstract
+ * implementation defines the following line scheme:
+ * <ul>
+ * <li> "" -> [0,0]
+ * <li> "a" -> [0,1]
+ * <li> "\n" -> [0,1], [1,0]
+ * <li> "a\n" -> [0,2], [2,0]
+ * <li> "a\nb" -> [0,2], [2,1]
+ * <li> "a\nbc\n" -> [0,2], [2,3], [5,0]
+ * </ul>
+ * This class must be subclassed.
+ *
+ * @since 3.2
+ */
+abstract class ListLineTracker : ILineTracker {
+
+    /** The line information */
+    private const List fLines;
+    /** The length of the tracked text */
+    private int fTextLength;
+
+    /**
+     * Creates a new line tracker.
+     */
+    protected this() {
+        fLines= new ArrayList();
+    }
+
+    /**
+     * Binary search for the line at a given offset.
+     *
+     * @param offset the offset whose line should be found
+     * @return the line of the offset
+     */
+    private int findLine(int offset) {
+
+        if (fLines.size() is 0)
+            return -1;
+
+        int left= 0;
+        int right= fLines.size() - 1;
+        int mid= 0;
+        Line line= null;
+
+        while (left < right) {
+
+            mid= (left + right) / 2;
+
+            line= cast(Line) fLines.get(mid);
+            if (offset < line.offset) {
+                if (left is mid)
+                    right= left;
+                else
+                    right= mid - 1;
+            } else if (offset > line.offset) {
+                if (right is mid)
+                    left= right;
+                else
+                    left= mid + 1;
+            } else if (offset is line.offset) {
+                left= right= mid;
+            }
+        }
+
+        line= cast(Line) fLines.get(left);
+        if (line.offset > offset)
+            --left;
+        return left;
+    }
+
+    /**
+     * Returns the number of lines covered by the specified text range.
+     *
+     * @param startLine the line where the text range starts
+     * @param offset the start offset of the text range
+     * @param length the length of the text range
+     * @return the number of lines covered by this text range
+     * @exception BadLocationException if range is undefined in this tracker
+     */
+    private int getNumberOfLines(int startLine, int offset, int length)  {
+
+        if (length is 0)
+            return 1;
+
+        int target= offset + length;
+
+        Line l= cast(Line) fLines.get(startLine);
+
+        if (l.delimiter is null)
+            return 1;
+
+        if (l.offset + l.length > target)
+            return 1;
+
+        if (l.offset + l.length is target)
+            return 2;
+
+        return getLineNumberOfOffset(target) - startLine + 1;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineLength(int)
+     */
+    public final int getLineLength(int line)  {
+        int lines= fLines.size();
+
+        if (line < 0 || line > lines)
+            throw new BadLocationException();
+
+        if (lines is 0 || lines is line)
+            return 0;
+
+        Line l= cast(Line) fLines.get(line);
+        return l.length;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineNumberOfOffset(int)
+     */
+    public final int getLineNumberOfOffset(int position)  {
+        if (position < 0 || position > fTextLength)
+            throw new BadLocationException();
+
+        if (position is fTextLength) {
+
+            int lastLine= fLines.size() - 1;
+            if (lastLine < 0)
+                return 0;
+
+            Line l= cast(Line) fLines.get(lastLine);
+            return (l.delimiter !is null ? lastLine + 1 : lastLine);
+        }
+
+        return findLine(position);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineInformationOfOffset(int)
+     */
+    public final IRegion getLineInformationOfOffset(int position)  {
+        if (position > fTextLength)
+            throw new BadLocationException();
+
+        if (position is fTextLength) {
+            int size= fLines.size();
+            if (size is 0)
+                return new Region(0, 0);
+            Line l= cast(Line) fLines.get(size - 1);
+            return (l.delimiter !is null ? new Line(fTextLength, 0) : new Line(fTextLength - l.length, l.length));
+        }
+
+        return getLineInformation(findLine(position));
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineInformation(int)
+     */
+    public final IRegion getLineInformation(int line)  {
+        int lines= fLines.size();
+
+        if (line < 0 || line > lines)
+            throw new BadLocationException();
+
+        if (lines is 0)
+            return new Line(0, 0);
+
+        if (line is lines) {
+            Line l= cast(Line) fLines.get(line - 1);
+            return new Line(l.offset + l.length, 0);
+        }
+
+        Line l= cast(Line) fLines.get(line);
+        return (l.delimiter !is null ? new Line(l.offset, l.length - l.delimiter.length()) : l);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineOffset(int)
+     */
+    public final int getLineOffset(int line)  {
+        int lines= fLines.size();
+
+        if (line < 0 || line > lines)
+            throw new BadLocationException();
+
+        if (lines is 0)
+            return 0;
+
+        if (line is lines) {
+            Line l= cast(Line) fLines.get(line - 1);
+            if (l.delimiter !is null)
+                return l.offset + l.length;
+            throw new BadLocationException();
+        }
+
+        Line l= cast(Line) fLines.get(line);
+        return l.offset;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getNumberOfLines()
+     */
+    public final int getNumberOfLines() {
+        int lines= fLines.size();
+
+        if (lines is 0)
+            return 1;
+
+        Line l= cast(Line) fLines.get(lines - 1);
+        return (l.delimiter !is null ? lines + 1 : lines);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getNumberOfLines(int, int)
+     */
+    public final int getNumberOfLines(int position, int length)  {
+
+        if (position < 0 || position + length > fTextLength)
+            throw new BadLocationException();
+
+        if (length is 0) // optimization
+            return 1;
+
+        return getNumberOfLines(getLineNumberOfOffset(position), position, length);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#computeNumberOfLines(java.lang.String)
+     */
+    public final int computeNumberOfLines(String text) {
+        int count= 0;
+        int start= 0;
+        AbstractLineTracker_DelimiterInfo delimiterInfo= nextDelimiterInfo(text, start);
+        while (delimiterInfo !is null && delimiterInfo.delimiterIndex > -1) {
+            ++count;
+            start= delimiterInfo.delimiterIndex + delimiterInfo.delimiterLength;
+            delimiterInfo= nextDelimiterInfo(text, start);
+        }
+        return count;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineDelimiter(int)
+     */
+    public final String getLineDelimiter(int line)  {
+        int lines= fLines.size();
+
+        if (line < 0 || line > lines)
+            throw new BadLocationException();
+
+        if (lines is 0)
+            return null;
+
+        if (line is lines)
+            return null;
+
+        Line l= cast(Line) fLines.get(line);
+        return l.delimiter;
+    }
+
+    /**
+     * Returns the information about the first delimiter found in the given text starting at the
+     * given offset.
+     *
+     * @param text the text to be searched
+     * @param offset the offset in the given text
+     * @return the information of the first found delimiter or <code>null</code>
+     */
+    protected abstract AbstractLineTracker_DelimiterInfo nextDelimiterInfo(String text, int offset);
+
+    /**
+     * Creates the line structure for the given text. Newly created lines are inserted into the line
+     * structure starting at the given position. Returns the number of newly created lines.
+     *
+     * @param text the text for which to create a line structure
+     * @param insertPosition the position at which the newly created lines are inserted into the
+     *        tracker's line structure
+     * @param offset the offset of all newly created lines
+     * @return the number of newly created lines
+     */
+    private int createLines(String text, int insertPosition, int offset) {
+
+        int count= 0;
+        int start= 0;
+        AbstractLineTracker_DelimiterInfo delimiterInfo= nextDelimiterInfo(text, 0);
+
+        while (delimiterInfo !is null && delimiterInfo.delimiterIndex > -1) {
+
+            int index= delimiterInfo.delimiterIndex + (delimiterInfo.delimiterLength - 1);
+
+            if (insertPosition + count >= fLines.size())
+                fLines.add(new Line(offset + start, offset + index, delimiterInfo.delimiter));
+            else
+                fLines.add(insertPosition + count, new Line(offset + start, offset + index, delimiterInfo.delimiter));
+
+            ++count;
+            start= index + 1;
+            delimiterInfo= nextDelimiterInfo(text, start);
+        }
+
+        if (start < text.length()) {
+            if (insertPosition + count < fLines.size()) {
+                // there is a line below the current
+                Line l= cast(Line) fLines.get(insertPosition + count);
+                int delta= text.length() - start;
+                l.offset-= delta;
+                l.length+= delta;
+            } else {
+                fLines.add(new Line(offset + start, offset + text.length() - 1, null));
+                ++count;
+            }
+        }
+
+        return count;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#replace(int, int, java.lang.String)
+     */
+    public final void replace(int position, int length, String text)  {
+        throw new UnsupportedOperationException();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#set(java.lang.String)
+     */
+    public final void set(String text) {
+        fLines.clear();
+        if (text !is null) {
+            fTextLength= text.length();
+            createLines(text, 0, 0);
+        }
+    }
+
+    /**
+     * Returns the internal data structure, a {@link List} of {@link Line}s. Used only by
+     * {@link TreeLineTracker#TreeLineTracker(ListLineTracker)}.
+     *
+     * @return the internal list of lines.
+     */
+    final List getLines() {
+        return fLines;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/Position.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,343 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.Position;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * Positions describe text ranges of a document. Positions are adapted to
+ * changes applied to that document. The text range is specified by an offset
+ * and a length. Positions can be marked as deleted. Deleted positions are
+ * considered to no longer represent a valid text range in the managing
+ * document.
+ * <p>
+ * Positions attached to documents are usually updated by position updaters.
+ * Because position updaters are freely definable and because of the frequency
+ * in which they are used, the fields of a position are made publicly
+ * accessible. Clients other than position updaters are not allowed to access
+ * these public fields.
+ * </p>
+ * <p>
+ * Positions cannot be used as keys in hash tables as they override
+ * <code>equals</code> and <code>hashCode</code> as they would be value
+ * objects.
+ * </p>
+ *
+ * @see org.eclipse.jface.text.IDocument
+ */
+public class Position {
+
+    /** The offset of the position */
+    public int offset;
+    /** The length of the position */
+    public int length;
+    /** Indicates whether the position has been deleted */
+    public bool isDeleted_;
+
+    /**
+     * Creates a new position with the given offset and length 0.
+     *
+     * @param offset the position offset, must be >= 0
+     */
+    public this(int offset) {
+        this(offset, 0);
+    }
+
+    /**
+     * Creates a new position with the given offset and length.
+     *
+     * @param offset the position offset, must be >= 0
+     * @param length the position length, must be >= 0
+     */
+    public this(int offset, int length) {
+        Assert.isTrue(offset >= 0);
+        Assert.isTrue(length >= 0);
+        this.offset= offset;
+        this.length= length;
+    }
+
+    /**
+     * Creates a new, not initialized position.
+     */
+    protected this() {
+    }
+
+     /*
+     * @see java.lang.Object#hashCode()
+     */
+    public override hash_t toHash() {
+        int deleted= isDeleted_ ? 0 : 1;
+        return (offset << 24) | (length << 16) | deleted;
+     }
+
+    /**
+     * Marks this position as deleted.
+     */
+    public void delete_() {
+        isDeleted_= true;
+    }
+
+    /**
+     * Marks this position as not deleted.
+     *
+     * @since 2.0
+     */
+    public void undelete() {
+        isDeleted_= false;
+    }
+
+    /*
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public override int opEquals(Object other) {
+        if ( auto rp = cast(Position)other ) {
+            return (rp.offset is offset) && (rp.length is length);
+        }
+        return super.opEquals(other);
+    }
+
+    /**
+     * Returns the length of this position.
+     *
+     * @return the length of this position
+     */
+    public int getLength() {
+        return length;
+    }
+
+    /**
+     * Returns the offset of this position.
+     *
+     * @return the offset of this position
+     */
+    public int getOffset() {
+        return offset;
+    }
+
+    /**
+     * Checks whether the given index is inside
+     * of this position's text range.
+     *
+     * @param index the index to check
+     * @return <code>true</code> if <code>index</code> is inside of this position
+     */
+    public bool includes(int index) {
+
+        if (isDeleted_)
+            return false;
+
+        return (this.offset <= index) && (index < this.offset + length);
+    }
+
+    /**
+     * Checks whether the intersection of the given text range
+     * and the text range represented by this position is empty
+     * or not.
+     *
+     * @param rangeOffset the offset of the range to check
+     * @param rangeLength the length of the range to check
+     * @return <code>true</code> if intersection is not empty
+     */
+    public bool overlapsWith(int rangeOffset, int rangeLength) {
+
+        if (isDeleted_)
+            return false;
+
+        int end= rangeOffset + rangeLength;
+        int thisEnd= this.offset + this.length;
+
+        if (rangeLength > 0) {
+            if (this.length > 0)
+                return this.offset < end && rangeOffset < thisEnd;
+            return  rangeOffset <= this.offset && this.offset < end;
+        }
+
+        if (this.length > 0)
+            return this.offset <= rangeOffset && rangeOffset < thisEnd;
+        return this.offset is rangeOffset;
+    }
+
+    /**
+     * Returns whether this position has been deleted or not.
+     *
+     * @return <code>true</code> if position has been deleted
+     */
+    public bool isDeleted() {
+        return isDeleted_;
+    }
+
+    /**
+     * Changes the length of this position to the given length.
+     *
+     * @param length the new length of this position
+     */
+    public void setLength(int length) {
+        Assert.isTrue(length >= 0);
+        this.length= length;
+    }
+
+    /**
+     * Changes the offset of this position to the given offset.
+     *
+     * @param offset the new offset of this position
+     */
+    public void setOffset(int offset) {
+        Assert.isTrue(offset >= 0);
+        this.offset= offset;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/Region.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,221 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.Region;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+import tango.text.convert.Format;
+
+
+/**
+ * The default implementation of the {@link org.eclipse.jface.text.IRegion} interface.
+ */
+public class Region : IRegion {
+
+    /** The region offset */
+    private int fOffset;
+    /** The region length */
+    private int fLength;
+
+    /**
+     * Create a new region.
+     *
+     * @param offset the offset of the region
+     * @param length the length of the region
+     */
+    public this(int offset, int length) {
+        fOffset= offset;
+        fLength= length;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IRegion#getLength()
+     */
+    public int getLength() {
+        return fLength;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IRegion#getOffset()
+     */
+    public int getOffset() {
+        return fOffset;
+    }
+
+    /*
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public bool equals(Object o) {
+        if ( cast(IRegion)o ) {
+            IRegion r= cast(IRegion) o;
+            return r.getOffset() is fOffset && r.getLength() is fLength;
+        }
+        return false;
+    }
+
+    /*
+     * @see java.lang.Object#hashCode()
+     */
+    public override hash_t toHash() {
+        return (fOffset << 24) | (fLength << 16);
+    }
+
+    /*
+     * @see java.lang.Object#toString()
+     */
+    public override String toString() {
+        return Format("[{}+{}]", fOffset, fLength ); //$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/RewriteSessionEditProcessor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,260 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.jface.text.RewriteSessionEditProcessor;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.text.edits.CopyTargetEdit;
+import org.eclipse.text.edits.DeleteEdit;
+import org.eclipse.text.edits.InsertEdit;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.MoveTargetEdit;
+import org.eclipse.text.edits.ReplaceEdit;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.text.edits.TextEditProcessor;
+import org.eclipse.text.edits.TextEditVisitor;
+import org.eclipse.text.edits.UndoEdit;
+
+/**
+ * A text edit processor that brackets the application of edits into a document rewrite session.
+ *
+ * @since 3.3
+ */
+public final class RewriteSessionEditProcessor : TextEditProcessor {
+    /** The threshold for <em>large</em> text edits. */
+    private static const int THRESHOLD= 1000;
+
+    /**
+     * Text edit visitor that estimates the compound size of an edit tree in characters.
+     */
+    private static final class SizeVisitor : TextEditVisitor {
+        int fSize= 0;
+
+        public bool visit(CopyTargetEdit edit) {
+            fSize += edit.getLength();
+            return super.visit(edit);
+        }
+
+        public bool visit(DeleteEdit edit) {
+            fSize += edit.getLength();
+            return super.visit(edit);
+        }
+
+        public bool visit(InsertEdit edit) {
+            fSize += edit.getText().length();
+            return super.visit(edit);
+        }
+
+        public bool visit(MoveTargetEdit edit) {
+            fSize += edit.getLength();
+            return super.visit(edit);
+        }
+
+        public bool visit(ReplaceEdit edit) {
+            fSize += Math.max(edit.getLength(), edit.getText().length());
+            return super.visit(edit);
+        }
+    }
+
+    /**
+     * Constructs a new edit processor for the given document.
+     *
+     * @param document the document to manipulate
+     * @param root the root of the text edit tree describing the modifications. By passing a text
+     *        edit a a text edit processor the ownership of the edit is transfered to the text edit
+     *        processors. Clients must not modify the edit (e.g adding new children) any longer.
+     * @param style {@link TextEdit#NONE}, {@link TextEdit#CREATE_UNDO} or
+     *        {@link TextEdit#UPDATE_REGIONS})
+     */
+    public this(IDocument document, TextEdit root, int style) {
+        super(document, root, style);
+    }
+
+    /*
+     * @see org.eclipse.text.edits.TextEditProcessor#performEdits()
+     */
+    public UndoEdit performEdits()  {
+        IDocument document= getDocument();
+        if (!( cast(IDocumentExtension4)document ))
+            return super.performEdits();
+
+        IDocumentExtension4 extension= cast(IDocumentExtension4) document;
+        bool isLargeEdit= isLargeEdit(getRoot());
+        DocumentRewriteSessionType type= isLargeEdit ? DocumentRewriteSessionType.UNRESTRICTED : DocumentRewriteSessionType.UNRESTRICTED_SMALL;
+
+        DocumentRewriteSession session= extension.startRewriteSession(type);
+        try {
+            return super.performEdits();
+        } finally {
+            extension.stopRewriteSession(session);
+        }
+    }
+
+    /**
+     * Returns <code>true</code> if the passed edit is considered <em>large</em>,
+     * <code>false</code> otherwise.
+     *
+     * @param edit the edit to check
+     * @return <code>true</code> if <code>edit</code> is considered <em>large</em>,
+     *         <code>false</code> otherwise
+     * @since 3.3
+     */
+    public static bool isLargeEdit(TextEdit edit) {
+        SizeVisitor sizeVisitor= new SizeVisitor();
+        edit.accept(sizeVisitor);
+        return sizeVisitor.fSize > THRESHOLD;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/SequentialRewriteTextStore.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,419 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.SequentialRewriteTextStore;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.util.Set;
+
+
+
+
+
+/**
+ * A text store that optimizes a given source text store for sequential rewriting.
+ * While rewritten it keeps a list of replace command that serve as patches for
+ * the source store. Only on request, the source store is indeed manipulated
+ * by applying the patch commands to the source text store.
+ *
+ * @since 2.0
+ * @deprecated since 3.3 as {@link GapTextStore} performs better even for sequential rewrite scenarios
+ */
+public class SequentialRewriteTextStore : ITextStore {
+
+    /**
+     * A buffered replace command.
+     */
+    private static class Replace {
+        public int newOffset;
+        public const int offset;
+        public const int length;
+        public const String text;
+
+        public this(int offset, int newOffset, int length, String text) {
+            this.newOffset= newOffset;
+            this.offset= offset;
+            this.length= length;
+            this.text= text;
+        }
+    }
+
+    /** The list of buffered replacements. */
+    private LinkedList fReplaceList;
+    /** The source text store */
+    private ITextStore fSource;
+    /** A flag to enforce sequential access. */
+    private static const bool ASSERT_SEQUENTIALITY= false;
+
+
+    /**
+     * Creates a new sequential rewrite store for the given source store.
+     *
+     * @param source the source text store
+     */
+    public this(ITextStore source) {
+        fReplaceList= new LinkedList();
+        fSource= source;
+    }
+
+    /**
+     * Returns the source store of this rewrite store.
+     *
+     * @return  the source store of this rewrite store
+     */
+    public ITextStore getSourceStore() {
+        commit();
+        return fSource;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#replace(int, int, java.lang.String)
+     */
+    public void replace(int offset, int length, String text) {
+        if (text is null)
+            text= ""; //$NON-NLS-1$
+
+        if (fReplaceList.size() is 0) {
+            fReplaceList.add(new Replace(offset, offset, length, text));
+
+        } else {
+            Replace firstReplace= cast(Replace) fReplaceList.getFirst();
+            Replace lastReplace= cast(Replace) fReplaceList.getLast();
+
+            // backward
+            if (offset + length <= firstReplace.newOffset) {
+                int delta= text.length - length;
+                if (delta !is 0) {
+                    for (Iterator i= fReplaceList.iterator(); i.hasNext(); ) {
+                        Replace replace= cast(Replace) i.next();
+                        replace.newOffset += delta;
+                    }
+                }
+
+                fReplaceList.addFirst(new Replace(offset, offset, length, text));
+
+            // forward
+            } else if (offset >= lastReplace.newOffset + lastReplace.text.length) {
+                int delta= getDelta(lastReplace);
+                fReplaceList.add(new Replace(offset - delta, offset, length, text));
+
+            } else if (ASSERT_SEQUENTIALITY) {
+                throw new IllegalArgumentException(null);
+
+            } else {
+                commit();
+                fSource.replace(offset, length, text);
+            }
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#set(java.lang.String)
+     */
+    public void set(String text) {
+        fSource.set(text);
+        fReplaceList.clear();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#get(int, int)
+     */
+    public String get(int offset, int length) {
+
+        if (fReplaceList.isEmpty())
+            return fSource.get(offset, length);
+
+
+        Replace firstReplace= cast(Replace) fReplaceList.getFirst();
+        Replace lastReplace= cast(Replace) fReplaceList.getLast();
+
+        // before
+        if (offset + length <= firstReplace.newOffset) {
+            return fSource.get(offset, length);
+
+            // after
+        } else if (offset >= lastReplace.newOffset + lastReplace.text.length) {
+            int delta= getDelta(lastReplace);
+            return fSource.get(offset - delta, length);
+
+        } else if (ASSERT_SEQUENTIALITY) {
+            throw new IllegalArgumentException(null);
+
+        } else {
+
+            int delta= 0;
+            for (Iterator i= fReplaceList.iterator(); i.hasNext(); ) {
+                Replace replace= cast(Replace) i.next();
+
+                if (offset + length < replace.newOffset) {
+                    return fSource.get(offset - delta, length);
+
+                } else if (offset >= replace.newOffset && offset + length <= replace.newOffset + replace.text.length) {
+                    return replace.text.substring(offset - replace.newOffset, offset - replace.newOffset + length);
+
+                } else if (offset >= replace.newOffset + replace.text.length) {
+                    delta= getDelta(replace);
+                    continue;
+
+                } else {
+                    commit();
+                    return fSource.get(offset, length);
+                }
+            }
+
+            return fSource.get(offset - delta, length);
+        }
+
+    }
+
+    /**
+     * Returns the difference between the offset in the source store and the "same" offset in the
+     * rewrite store after the replace operation.
+     *
+     * @param replace the replace command
+     * @return the difference
+     */
+    private static final int getDelta(Replace replace) {
+        return replace.newOffset - replace.offset + replace.text.length() - replace.length;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#get(int)
+     */
+    public char get(int offset) {
+        if (fReplaceList.isEmpty())
+            return fSource.get(offset);
+
+        Replace firstReplace= cast(Replace) fReplaceList.getFirst();
+        Replace lastReplace= cast(Replace) fReplaceList.getLast();
+
+        // before
+        if (offset < firstReplace.newOffset) {
+            return fSource.get(offset);
+
+            // after
+        } else if (offset >= lastReplace.newOffset + lastReplace.text.length()) {
+            int delta= getDelta(lastReplace);
+            return fSource.get(offset - delta);
+
+        } else if (ASSERT_SEQUENTIALITY) {
+            throw new IllegalArgumentException(null);
+
+        } else {
+
+            int delta= 0;
+            for (Iterator i= fReplaceList.iterator(); i.hasNext(); ) {
+                Replace replace= cast(Replace) i.next();
+
+                if (offset < replace.newOffset)
+                    return fSource.get(offset - delta);
+
+                else if (offset < replace.newOffset + replace.text.length())
+                    return replace.text.charAt(offset - replace.newOffset);
+
+                delta= getDelta(replace);
+            }
+
+            return fSource.get(offset - delta);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#getLength()
+     */
+    public int getLength() {
+        if (fReplaceList.isEmpty())
+            return fSource.getLength();
+
+        Replace lastReplace= cast(Replace) fReplaceList.getLast();
+        return fSource.getLength() + getDelta(lastReplace);
+    }
+
+    /**
+     * Disposes this rewrite store.
+     */
+    public void dispose() {
+        fReplaceList= null;
+        fSource= null;
+    }
+
+    /**
+     * Commits all buffered replace commands.
+     */
+    private void commit() {
+
+        if (fReplaceList.isEmpty())
+            return;
+
+        StringBuffer buffer= new StringBuffer();
+
+        int delta= 0;
+        for (Iterator i= fReplaceList.iterator(); i.hasNext(); ) {
+            Replace replace= cast(Replace) i.next();
+
+            int offset= buffer.length() - delta;
+            buffer.append(fSource.get(offset, replace.offset - offset));
+            buffer.append(replace.text);
+            delta= getDelta(replace);
+        }
+
+        int offset= buffer.length() - delta;
+        buffer.append(fSource.get(offset, fSource.getLength() - offset));
+
+        fSource.set(buffer.toString());
+        fReplaceList.clear();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/SlaveDocumentEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,192 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.SlaveDocumentEvent;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * A slave document event represents a master document event as a slave-relative
+ * document event. It also carries the master document event.
+ */
+public class SlaveDocumentEvent : DocumentEvent {
+
+    /** The master document event */
+    private DocumentEvent fMasterEvent;
+
+    /**
+     * Creates a new slave document event.
+     *
+     * @param doc the slave document
+     * @param offset the offset in the slave document
+     * @param length the length in the slave document
+     * @param text the substitution text
+     * @param masterEvent the master document event
+     */
+    public this(IDocument doc, int offset, int length, String text, DocumentEvent masterEvent) {
+        super(doc, offset, length, text);
+        fMasterEvent= masterEvent;
+    }
+
+    /**
+     * Returns this event's master event.
+     *
+     * @return this event's master event
+     */
+    public DocumentEvent getMasterEvent() {
+        return fMasterEvent;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/TextMessages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 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 org.eclipse.jface.text.TextMessages;
+
+import java.lang.all;
+
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+import java.text.MessageFormat;
+
+
+/**
+ * Helper class to get NLSed messages.
+ *
+ * @since 3.4
+ */
+class TextMessages {
+//     private static const String BUNDLE_NAME= "org.eclipse.jface.text.TextMessages"; //$NON-NLS-1$
+
+    private static ResourceBundle RESOURCE_BUNDLE_;//= ResourceBundle.getBundle(BUNDLE_NAME);
+    private static ResourceBundle RESOURCE_BUNDLE(){
+        if( RESOURCE_BUNDLE_ is null ){
+            synchronized(TextMessages.classinfo ){
+                if( RESOURCE_BUNDLE_ is null ){
+                    RESOURCE_BUNDLE_ = ResourceBundle.getBundle(
+                        getImportData!("org.eclipse.jface.text.TextMessages.properties"));
+                }
+            }
+        }
+        return RESOURCE_BUNDLE_;
+    }
+
+    private this() {
+    }
+
+    public static String getString(String key) {
+        try {
+            return RESOURCE_BUNDLE.getString(key);
+        } catch (MissingResourceException e) {
+            return '!' ~ key ~ '!';
+        }
+    }
+
+    public static String getFormattedString(String key, Object[] args...) {
+        return MessageFormat.format(getString(key), args);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/TextUtilities.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,724 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.TextUtilities;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+// import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.ListIterator;
+import java.util.List;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+
+
+
+
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * A collection of text functions.
+ * <p>
+ * This class is neither intended to be instantiated nor subclassed.
+ * </p>
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class TextUtilities {
+
+    /**
+     * Default line delimiters used by the text functions of this class.
+     */
+    public const static String[] DELIMITERS= [ "\n", "\r", "\r\n" ]; //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
+
+    /**
+     * Default line delimiters used by these text functions.
+     *
+     * @deprecated use DELIMITERS instead
+     */
+    public const static String[] fgDelimiters= DELIMITERS;
+
+
+
+    /**
+     * Determines which one of default line delimiters appears first in the list. If none of them the
+     * hint is returned.
+     *
+     * @param text the text to be checked
+     * @param hint the line delimiter hint
+     * @return the line delimiter
+     */
+    public static String determineLineDelimiter(String text, String hint) {
+        try {
+            int[] info= indexOf(DELIMITERS, text, 0);
+            return DELIMITERS[info[1]];
+        } catch (ArrayIndexOutOfBoundsException x) {
+        }
+        return hint;
+    }
+
+    /**
+     * Returns the starting position and the index of the first matching search string
+     * in the given text that is greater than the given offset. If more than one search
+     * string matches with the same starting position then the longest one is returned.
+     *
+     * @param searchStrings the strings to search for
+     * @param text the text to be searched
+     * @param offset the offset at which to start the search
+     * @return an <code>int[]</code> with two elements where the first is the starting offset, the second the index of the found
+     *      search string in the given <code>searchStrings</code> array, returns <code>[-1, -1]</code> if no match exists
+     */
+    public static int[] indexOf(String[] searchStrings, String text, int offset) {
+
+        int[] result= [ -1, -1 ];
+        int zeroIndex= -1;
+
+        for (int i= 0; i < searchStrings.length; i++) {
+
+            int length= searchStrings[i].length();
+
+            if (length is 0) {
+                zeroIndex= i;
+                continue;
+            }
+
+            int index= .indexOf( text, searchStrings[i], offset);
+            if (index >= 0) {
+
+                if (result[0] is -1) {
+                    result[0]= index;
+                    result[1]= i;
+                } else if (index < result[0]) {
+                    result[0]= index;
+                    result[1]= i;
+                } else if (index is result[0] && length > searchStrings[result[1]].length) {
+                    result[0]= index;
+                    result[1]= i;
+                }
+            }
+        }
+
+        if (zeroIndex > -1 && result[0] is -1) {
+            result[0]= 0;
+            result[1]= zeroIndex;
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns the index of the longest search string with which the given text ends or
+     * <code>-1</code> if none matches.
+     *
+     * @param searchStrings the strings to search for
+     * @param text the text to search
+     * @return the index in <code>searchStrings</code> of the longest string with which <code>text</code> ends or <code>-1</code>
+     */
+    public static int endsWith(String[] searchStrings, String text) {
+
+        int index= -1;
+
+        for (int i= 0; i < searchStrings.length; i++) {
+            if (.endsWith(text, searchStrings[i])) {
+                if (index is -1 || searchStrings[i].length() > searchStrings[index].length())
+                    index= i;
+            }
+        }
+
+        return index;
+    }
+
+    /**
+     * Returns the index of the longest search string with which the given text starts or <code>-1</code>
+     * if none matches.
+     *
+     * @param searchStrings the strings to search for
+     * @param text the text to search
+     * @return the index in <code>searchStrings</code> of the longest string with which <code>text</code> starts or <code>-1</code>
+     */
+    public static int startsWith(String[] searchStrings, String text) {
+
+        int index= -1;
+
+        for (int i= 0; i < searchStrings.length; i++) {
+            if (.startsWith(text, searchStrings[i])) {
+                if (index is -1 || searchStrings[i].length() > searchStrings[index].length())
+                    index= i;
+            }
+        }
+
+        return index;
+    }
+
+    /**
+     * Returns the index of the first compare string that equals the given text or <code>-1</code>
+     * if none is equal.
+     *
+     * @param compareStrings the strings to compare with
+     * @param text the text to check
+     * @return the index of the first equal compare string or <code>-1</code>
+     */
+    public static int equals(String[] compareStrings, String text) {
+        for (int i= 0; i < compareStrings.length; i++) {
+            if (.equals(text,compareStrings[i]))
+                return i;
+        }
+        return -1;
+    }
+
+    /**
+     * Returns a document event which is an accumulation of a list of document events,
+     * <code>null</code> if the list of documentEvents is empty.
+     * The document of the document events are ignored.
+     *
+     * @param unprocessedDocument the document to which the document events would be applied
+     * @param documentEvents the list of document events to merge
+     * @return returns the merged document event
+     * @throws BadLocationException might be thrown if document is not in the correct state with respect to document events
+     */
+    public static DocumentEvent mergeUnprocessedDocumentEvents(IDocument unprocessedDocument, List documentEvents)  {
+
+        if (documentEvents.size() is 0)
+            return null;
+
+        final Iterator iterator= documentEvents.iterator();
+        final DocumentEvent firstEvent= cast(DocumentEvent) iterator.next();
+
+        // current merged event
+        final IDocument document= unprocessedDocument;
+        int offset= firstEvent.getOffset();
+        int length= firstEvent.getLength();
+        final StringBuffer text= new StringBuffer(firstEvent.getText() is null ? "" : firstEvent.getText()); //$NON-NLS-1$
+
+        while (iterator.hasNext()) {
+
+            final int delta= text.length() - length;
+
+            final DocumentEvent event= cast(DocumentEvent) iterator.next();
+            final int eventOffset= event.getOffset();
+            final int eventLength= event.getLength();
+            final String eventText= event.getText() is null ? "" : event.getText(); //$NON-NLS-1$
+
+            // event is right from merged event
+            if (eventOffset > offset + length + delta) {
+                final String string= document.get(offset + length, (eventOffset - delta) - (offset + length));
+                text.append(string);
+                text.append(eventText);
+
+                length= (eventOffset - delta) + eventLength - offset;
+
+            // event is left from merged event
+            } else if (eventOffset + eventLength < offset) {
+                final String string= document.get(eventOffset + eventLength, offset - (eventOffset + eventLength));
+                text.select(0,0);
+                text.replace(string);
+                text.select(0,0);
+                text.replace(eventText);
+
+                length= offset + length - eventOffset;
+                offset= eventOffset;
+
+            // events overlap each other
+            } else {
+                final int start= Math.max(0, eventOffset - offset);
+                final int end= Math.min(text.length(), eventLength + eventOffset - offset);
+                text.select(start, end);
+                text.replace(eventText);
+
+                offset= Math.min(offset, eventOffset);
+                final int totalDelta= delta + eventText.length - eventLength;
+                length= text.length() - totalDelta;
+            }
+        }
+
+        return new DocumentEvent(document, offset, length, text.toString());
+    }
+
+    /**
+     * Returns a document event which is an accumulation of a list of document events,
+     * <code>null</code> if the list of document events is empty.
+     * The document events being merged must all refer to the same document, to which
+     * the document changes have been already applied.
+     *
+     * @param documentEvents the list of document events to merge
+     * @return returns the merged document event
+     * @throws BadLocationException might be thrown if document is not in the correct state with respect to document events
+     */
+    public static DocumentEvent mergeProcessedDocumentEvents(List documentEvents)  {
+
+        if (documentEvents.size() is 0)
+            return null;
+
+        final ListIterator iterator= documentEvents.listIterator(documentEvents.size());
+        final DocumentEvent firstEvent= cast(DocumentEvent) iterator.previous();
+
+        // current merged event
+        final IDocument document= firstEvent.getDocument();
+        int offset= firstEvent.getOffset();
+        int length= firstEvent.getLength();
+        int textLength= firstEvent.getText() is null ? 0 : firstEvent.getText().length;
+
+        while (iterator.hasPrevious()) {
+
+            final int delta= length - textLength;
+
+            final DocumentEvent event= cast(DocumentEvent) iterator.previous();
+            final int eventOffset= event.getOffset();
+            final int eventLength= event.getLength();
+            final int eventTextLength= event.getText() is null ? 0 : event.getText().length;
+
+            // event is right from merged event
+            if (eventOffset > offset + textLength + delta) {
+                length= (eventOffset - delta) - (offset + textLength) + length + eventLength;
+                textLength= (eventOffset - delta) + eventTextLength - offset;
+
+            // event is left from merged event
+            } else if (eventOffset + eventTextLength < offset) {
+                length= offset - (eventOffset + eventTextLength) + length + eventLength;
+                textLength= offset + textLength - eventOffset;
+                offset= eventOffset;
+
+            // events overlap each other
+            } else {
+                final int start= Math.max(0, eventOffset - offset);
+                final int end= Math.min(length, eventTextLength + eventOffset - offset);
+                length += eventLength - (end - start);
+
+                offset= Math.min(offset, eventOffset);
+                final int totalDelta= delta + eventLength - eventTextLength;
+                textLength= length - totalDelta;
+            }
+        }
+
+        final String text= document.get(offset, textLength);
+        return new DocumentEvent(document, offset, length, text);
+    }
+
+    /**
+     * Removes all connected document partitioners from the given document and stores them
+     * under their partitioning name in a map. This map is returned. After this method has been called
+     * the given document is no longer connected to any document partitioner.
+     *
+     * @param document the document
+     * @return the map containing the removed partitioners
+     */
+    public static Map removeDocumentPartitioners(IDocument document) {
+        Map partitioners= new HashMap();
+        if (cast(IDocumentExtension3)document ) {
+            IDocumentExtension3 extension3= cast(IDocumentExtension3) document;
+            String[] partitionings= extension3.getPartitionings();
+            for (int i= 0; i < partitionings.length; i++) {
+                IDocumentPartitioner partitioner= extension3.getDocumentPartitioner(partitionings[i]);
+                if (partitioner !is null) {
+                    extension3.setDocumentPartitioner(partitionings[i], null);
+                    partitioner.disconnect();
+                    partitioners.put(stringcast(partitionings[i]), cast(Object)partitioner);
+                }
+            }
+        } else {
+            IDocumentPartitioner partitioner= document.getDocumentPartitioner();
+            if (partitioner !is null) {
+                document.setDocumentPartitioner(null);
+                partitioner.disconnect();
+                partitioners.put(stringcast(IDocumentExtension3.DEFAULT_PARTITIONING), cast(Object)partitioner);
+            }
+        }
+        return partitioners;
+    }
+
+    /**
+     * Connects the given document with all document partitioners stored in the given map under
+     * their partitioning name. This method cleans the given map.
+     *
+     * @param document the document
+     * @param partitioners the map containing the partitioners to be connected
+     * @since 3.0
+     */
+    public static void addDocumentPartitioners(IDocument document, Map partitioners) {
+        if (cast(IDocumentExtension3)document ) {
+            IDocumentExtension3 extension3= cast(IDocumentExtension3) document;
+            Iterator e= partitioners.keySet().iterator();
+            while (e.hasNext()) {
+                String partitioning= stringcast( e.next() );
+                IDocumentPartitioner partitioner= cast(IDocumentPartitioner) partitioners.get(partitioning);
+                partitioner.connect(document);
+                extension3.setDocumentPartitioner(partitioning, partitioner);
+            }
+            partitioners.clear();
+        } else {
+            IDocumentPartitioner partitioner= cast(IDocumentPartitioner) partitioners.get(IDocumentExtension3.DEFAULT_PARTITIONING);
+            partitioner.connect(document);
+            document.setDocumentPartitioner(partitioner);
+        }
+    }
+
+    /**
+     * Returns the content type at the given offset of the given document.
+     *
+     * @param document the document
+     * @param partitioning the partitioning to be used
+     * @param offset the offset
+     * @param preferOpenPartitions <code>true</code> if precedence should be
+     *        given to a open partition ending at <code>offset</code> over a
+     *        closed partition starting at <code>offset</code>
+     * @return the content type at the given offset of the document
+     * @throws BadLocationException if offset is invalid in the document
+     * @since 3.0
+     */
+    public static String getContentType(IDocument document, String partitioning, int offset, bool preferOpenPartitions)  {
+        if (cast(IDocumentExtension3)document ) {
+            IDocumentExtension3 extension3= cast(IDocumentExtension3) document;
+            try {
+                return extension3.getContentType(partitioning, offset, preferOpenPartitions);
+            } catch (BadPartitioningException x) {
+                return IDocument.DEFAULT_CONTENT_TYPE;
+            }
+        }
+
+        return document.getContentType(offset);
+    }
+
+    /**
+     * Returns the partition of the given offset of the given document.
+     *
+     * @param document the document
+     * @param partitioning the partitioning to be used
+     * @param offset the offset
+     * @param preferOpenPartitions <code>true</code> if precedence should be
+     *        given to a open partition ending at <code>offset</code> over a
+     *        closed partition starting at <code>offset</code>
+     * @return the content type at the given offset of this viewer's input
+     *         document
+     * @throws BadLocationException if offset is invalid in the given document
+     * @since 3.0
+     */
+    public static ITypedRegion getPartition(IDocument document, String partitioning, int offset, bool preferOpenPartitions)  {
+        if (cast(IDocumentExtension3)document ) {
+            IDocumentExtension3 extension3= cast(IDocumentExtension3) document;
+            try {
+                return extension3.getPartition(partitioning, offset, preferOpenPartitions);
+            } catch (BadPartitioningException x) {
+                return new TypedRegion(0, document.getLength(), IDocument.DEFAULT_CONTENT_TYPE);
+            }
+        }
+
+        return document.getPartition(offset);
+    }
+
+    /**
+     * Computes and returns the partitioning for the given region of the given
+     * document for the given partitioning name.
+     *
+     * @param document the document
+     * @param partitioning the partitioning name
+     * @param offset the region offset
+     * @param length the region length
+     * @param includeZeroLengthPartitions whether to include zero-length partitions
+     * @return the partitioning for the given region of the given document for
+     *         the given partitioning name
+     * @throws BadLocationException if the given region is invalid for the given
+     *         document
+     * @since 3.0
+     */
+    public static ITypedRegion[] computePartitioning(IDocument document, String partitioning, int offset, int length, bool includeZeroLengthPartitions)  {
+        if (cast(IDocumentExtension3)document ) {
+            IDocumentExtension3 extension3= cast(IDocumentExtension3) document;
+            try {
+                return extension3.computePartitioning(partitioning, offset, length, includeZeroLengthPartitions);
+            } catch (BadPartitioningException x) {
+                return new ITypedRegion[0];
+            }
+        }
+
+        return document.computePartitioning(offset, length);
+    }
+
+    /**
+     * Computes and returns the partition managing position categories for the
+     * given document or <code>null</code> if this was impossible.
+     *
+     * @param document the document
+     * @return the partition managing position categories or <code>null</code>
+     * @since 3.0
+     */
+    public static String[] computePartitionManagingCategories(IDocument document) {
+        if (cast(IDocumentExtension3)document ) {
+            IDocumentExtension3 extension3= cast(IDocumentExtension3) document;
+            String[] partitionings= extension3.getPartitionings();
+            if (partitionings !is null) {
+                Set categories= new HashSet();
+                for (int i= 0; i < partitionings.length; i++) {
+                    IDocumentPartitioner p= extension3.getDocumentPartitioner(partitionings[i]);
+                    if (cast(IDocumentPartitionerExtension2)p ) {
+                        IDocumentPartitionerExtension2 extension2= cast(IDocumentPartitionerExtension2) p;
+                        String[] c= extension2.getManagingPositionCategories();
+                        if (c !is null) {
+                            for (int j= 0; j < c.length; j++)
+                                categories.add(c[j]);
+                        }
+                    }
+                }
+                return stringcast(categories.toArray());
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the default line delimiter for the given document. This is either the delimiter of the first line, or the platform line delimiter if it is
+     * a legal line delimiter or the first one of the legal line delimiters. The default line delimiter should be used when performing document
+     * manipulations that span multiple lines.
+     *
+     * @param document the document
+     * @return the document's default line delimiter
+     * @since 3.0
+     */
+    public static String getDefaultLineDelimiter(IDocument document) {
+
+        if (cast(IDocumentExtension4)document )
+            return (cast(IDocumentExtension4)document).getDefaultLineDelimiter();
+
+        String lineDelimiter= null;
+
+        try {
+            lineDelimiter= document.getLineDelimiter(0);
+        } catch (BadLocationException x) {
+        }
+
+        if (lineDelimiter !is null)
+            return lineDelimiter;
+
+        String sysLineDelimiter= System.getProperty("line.separator"); //$NON-NLS-1$
+        String[] delimiters= document.getLegalLineDelimiters();
+        Assert.isTrue(delimiters.length > 0);
+        for (int i= 0; i < delimiters.length; i++) {
+            if (.equals(delimiters[i], sysLineDelimiter)) {
+                lineDelimiter= sysLineDelimiter;
+                break;
+            }
+        }
+
+        if (lineDelimiter is null)
+            lineDelimiter= delimiters[0];
+
+        return lineDelimiter;
+    }
+
+    /**
+     * Returns <code>true</code> if the two regions overlap. Returns <code>false</code> if one of the
+     * arguments is <code>null</code>.
+     *
+     * @param left the left region
+     * @param right the right region
+     * @return <code>true</code> if the two regions overlap, <code>false</code> otherwise
+     * @since 3.0
+     */
+    public static bool overlaps(IRegion left, IRegion right) {
+
+        if (left is null || right is null)
+            return false;
+
+        int rightEnd= right.getOffset() + right.getLength();
+        int leftEnd= left.getOffset()+ left.getLength();
+
+        if (right.getLength() > 0) {
+            if (left.getLength() > 0)
+                return left.getOffset() < rightEnd && right.getOffset() < leftEnd;
+            return  right.getOffset() <= left.getOffset() && left.getOffset() < rightEnd;
+        }
+
+        if (left.getLength() > 0)
+            return left.getOffset() <= right.getOffset() && right.getOffset() < leftEnd;
+
+        return left.getOffset() is right.getOffset();
+    }
+
+    /**
+     * Returns a copy of the given string array.
+     *
+     * @param array the string array to be copied
+     * @return a copy of the given string array or <code>null</code> when <code>array</code> is <code>null</code>
+     * @since 3.1
+     */
+    public static String[] copy(String[] array) {
+        if (array !is null) {
+            String[] copy= new String[array.length];
+            System.arraycopy(array, 0, copy, 0, array.length);
+            return copy;
+        }
+        return null;
+    }
+
+    /**
+     * Returns a copy of the given integer array.
+     *
+     * @param array the integer array to be copied
+     * @return a copy of the given integer array or <code>null</code> when <code>array</code> is <code>null</code>
+     * @since 3.1
+     */
+    public static int[] copy(int[] array) {
+        if (array !is null) {
+            int[] copy= new int[array.length];
+            System.arraycopy(array, 0, copy, 0, array.length);
+            return copy;
+        }
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/TreeLineTracker.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1535 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.TreeLineTracker;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+import java.lang.all;
+import java.util.ListIterator;
+import java.util.Arrays;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Set;
+import tango.text.convert.Format;
+import tango.core.Exception;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.AbstractLineTracker;
+
+/**
+ * Abstract implementation of <code>ILineTracker</code>. It lets the definition of line
+ * delimiters to subclasses. Assuming that '\n' is the only line delimiter, this abstract
+ * implementation defines the following line scheme:
+ * <ul>
+ * <li> "" -> [0,0]
+ * <li> "a" -> [0,1]
+ * <li> "\n" -> [0,1], [1,0]
+ * <li> "a\n" -> [0,2], [2,0]
+ * <li> "a\nb" -> [0,2], [2,1]
+ * <li> "a\nbc\n" -> [0,2], [2,3], [5,0]
+ * </ul>
+ * <p>
+ * This class must be subclassed.
+ * </p>
+ * <p>
+ * <strong>Performance:</strong> The query operations perform in <i>O(log n)</i> where <var>n</var>
+ * is the number of lines in the document. The modification operations roughly perform in <i>O(l *
+ * log n)</i> where <var>n</var> is the number of lines in the document and <var>l</var> is the
+ * sum of the number of removed, added or modified lines.
+ * </p>
+ *
+ * @since 3.2
+ */
+abstract class TreeLineTracker : ILineTracker {
+    /*
+     * Differential Balanced Binary Tree
+     *
+     * Assumption: lines cannot overlap => there exists a total ordering of the lines by their offset,
+     * which is the same as the ordering by line number
+     *
+     * Base idea: store lines in a binary search tree
+     *   - the key is the line number / line offset
+     *     -> lookup_line is O(log n)
+     *     -> lookup_offset is O(log n)
+     *   - a change in a line somewhere will change any succeeding line numbers / line offsets
+     *     -> replace is O(n)
+     *
+     * Differential tree: instead of storing the key (line number, line offset) directly, every node
+     * stores the difference between its key and its parent's key
+     *   - the sort key is still the line number / line offset, but it remains "virtual"
+     *   - inserting a node (a line) really increases the virtual key of all succeeding nodes (lines), but this
+     *     fact will not be realized in the key information encoded in the nodes.
+     *     -> any change only affects the nodes in the node's parent chain, although more bookkeeping
+     *         has to be done when changing a node or balancing the tree
+     *        -> replace is O(log n)
+     *     -> line offsets and line numbers have to be computed when walking the tree from the root /
+     *         from a node
+     *        -> still O(log n)
+     *
+     * The balancing algorithm chosen does not depend on the differential tree property. An AVL tree
+     * implementation has been chosen for simplicity.
+     */
+
+    /*
+     * Turns assertions on/off. Don't make this a a debug option for performance reasons - this way
+     * the compiler can optimize the asserts away.
+     */
+    private static const bool ASSERT= false;
+
+    /**
+     * The empty delimiter of the last line. The last line and only the last line must have this
+     * zero-length delimiter.
+     */
+    private static const String NO_DELIM= ""; //$NON-NLS-1$
+
+    /**
+     * A node represents one line. Its character and line offsets are 0-based and relative to the
+     * subtree covered by the node. All nodes under the left subtree represent lines before, all
+     * nodes under the right subtree lines after the current node.
+     */
+    private static final class Node {
+        this(int length, String delimiter) {
+            this.length= length;
+            this.delimiter= delimiter;
+        }
+        /**
+         * The line index in this node's line tree, or equivalently, the number of lines in the left
+         * subtree.
+         */
+        int line;
+        /**
+         * The line offset in this node's line tree, or equivalently, the number of characters in
+         * the left subtree.
+         */
+        int offset;
+        /** The number of characters in this line. */
+        int length;
+        /** The line delimiter of this line, needed to answer the delimiter query. */
+        String delimiter;
+        /** The parent node, <code>null</code> if this is the root node. */
+        Node parent;
+        /** The left subtree, possibly <code>null</code>. */
+        Node left;
+        /** The right subtree, possibly <code>null</code>. */
+        Node right;
+        /** The balance factor. */
+        byte balance;
+
+        /*
+         * @see java.lang.Object#toString()
+         */
+        public final override String toString() {
+            String bal;
+            switch (balance) {
+                case 0:
+                    bal= "="; //$NON-NLS-1$
+                    break;
+                case 1:
+                    bal= "+"; //$NON-NLS-1$
+                    break;
+                case 2:
+                    bal= "++"; //$NON-NLS-1$
+                    break;
+                case -1:
+                    bal= "-"; //$NON-NLS-1$
+                    break;
+                case -2:
+                    bal= "--"; //$NON-NLS-1$
+                    break;
+                default:
+                    bal= Byte.toString(balance);
+            }
+            return Format("[{}+{}+{}|{}|{}]", offset, pureLength(), delimiter.length, line, bal ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
+        }
+
+        /**
+         * Returns the pure (without the line delimiter) length of this line.
+         *
+         * @return the pure line length
+         */
+        int pureLength() {
+            return length - delimiter.length;
+        }
+    }
+
+    /**
+     * The root node of the tree, never <code>null</code>.
+     */
+    private Node fRoot;
+
+    /**
+     * Creates a new line tracker.
+     */
+    protected this() {
+        fRoot= new Node(0, NO_DELIM);
+    }
+
+    /**
+     * Package visible constructor for creating a tree tracker from a list tracker.
+     *
+     * @param tracker
+     */
+    this(ListLineTracker tracker) {
+        fRoot= new Node(0, NO_DELIM);
+        final List lines= tracker.getLines();
+        final int n= lines.size();
+        if (n is 0)
+            return;
+
+        Line line= cast(Line) lines.get(0);
+        String delim= line.delimiter;
+        if (delim is null)
+            delim= NO_DELIM;
+        int length= line.length;
+        fRoot= new Node(length, delim);
+        Node node= fRoot;
+
+        for (int i= 1; i < n; i++) {
+            line= cast(Line) lines.get(i);
+            delim= line.delimiter;
+            if (delim is null)
+                delim= NO_DELIM;
+            length= line.length;
+            node= insertAfter(node, length, delim);
+        }
+
+        if (node.delimiter !is NO_DELIM)
+            insertAfter(node, 0, NO_DELIM);
+
+        if (ASSERT) checkTree();
+    }
+
+    /**
+     * Returns the node (line) including a certain offset. If the offset is between two
+     * lines, the line starting at <code>offset</code> is returned.
+     * <p>
+     * This means that for offsets smaller than the length, the following holds:
+     * </p>
+     * <p>
+     * <code>line.offset <= offset < line.offset + offset.length</code>.
+     * </p>
+     * <p>
+     * If <code>offset</code> is the document length, then this is true:
+     * </p>
+     * <p>
+     * <code>offset= line.offset + line.length</code>.
+     * </p>
+     *
+     * @param offset a document offset
+     * @return the line starting at or containing <code>offset</code>
+     * @throws BadLocationException if the offset is invalid
+     */
+    private Node nodeByOffset(int offset)  {
+        /*
+         * Works for any binary search tree.
+         */
+        int remaining= offset;
+        Node node= fRoot;
+        int line= 0;
+
+        while (true) {
+            if (node is null)
+                fail(offset);
+
+            if (remaining < node.offset) {
+                node= node.left;
+            } else {
+                remaining -= node.offset;
+                line+= node.line;
+                if (remaining < node.length
+                        || remaining is node.length && node.right is null) { // last line
+                    break;
+                }
+                remaining -= node.length;
+                line ++;
+                node= node.right;
+            }
+        }
+
+        return node;
+    }
+    /**
+     * Returns the line number for the given offset. If the offset is between two lines, the line
+     * starting at <code>offset</code> is returned. The last line is returned if
+     * <code>offset</code> is equal to the document length.
+     *
+     * @param offset a document offset
+     * @return the line number starting at or containing <code>offset</code>
+     * @throws BadLocationException if the offset is invalid
+     */
+    private int lineByOffset(int offset)  {
+        /*
+         * Works for any binary search tree.
+         */
+        int remaining= offset;
+        Node node= fRoot;
+        int line= 0;
+
+        while (true) {
+            if (node is null)
+                fail(offset);
+
+            if (remaining < node.offset) {
+                node= node.left;
+            } else {
+                remaining -= node.offset;
+                line+= node.line;
+                if (remaining < node.length || remaining is node.length && node.right is null) // last line
+                    return  line;
+
+                remaining -= node.length;
+                line ++;
+                node= node.right;
+            }
+        }
+    }
+
+    /**
+     * Returns the node (line) with the given line number. Note that the last line is always
+     * incomplete, i.e. has the {@link #NO_DELIM} delimiter.
+     *
+     * @param line a line number
+     * @return the line with the given line number
+     * @throws BadLocationException if the line is invalid
+     */
+    private Node nodeByLine(int line)  {
+        /*
+         * Works for any binary search tree.
+         */
+        int remaining= line;
+        int offset= 0;
+        Node node= fRoot;
+
+        while (true) {
+            if (node is null)
+                fail(line);
+
+            if (remaining is node.line)
+                break;
+            if (remaining < node.line) {
+                node= node.left;
+            } else {
+                remaining -= node.line + 1;
+                offset += node.offset + node.length;
+                node= node.right;
+            }
+        }
+
+        return node;
+    }
+
+    /**
+     * Returns the offset for the given line number. Note that the
+     * last line is always incomplete, i.e. has the {@link #NO_DELIM} delimiter.
+     *
+     * @param line a line number
+     * @return the line offset with the given line number
+     * @throws BadLocationException if the line is invalid
+     */
+    private int offsetByLine(int line)  {
+        /*
+         * Works for any binary search tree.
+         */
+        int remaining= line;
+        int offset= 0;
+        Node node= fRoot;
+
+        while (true) {
+            if (node is null)
+                fail(line);
+
+            if (remaining is node.line)
+                return offset + node.offset;
+
+            if (remaining < node.line) {
+                node= node.left;
+            } else {
+                remaining -= node.line + 1;
+                offset += node.offset + node.length;
+                node= node.right;
+            }
+        }
+    }
+
+    /**
+     * Left rotation - the given node is rotated down, its right child is rotated up, taking the
+     * previous structural position of <code>node</code>.
+     *
+     * @param node the node to rotate around
+     */
+    private void rotateLeft(Node node) {
+        if (ASSERT) Assert.isNotNull(node);
+        Node child= node.right;
+        if (ASSERT) Assert.isNotNull(child);
+        bool leftChild= node.parent is null || node is node.parent.left;
+
+        // restructure
+        setChild(node.parent, child, leftChild);
+
+        setChild(node, child.left, false);
+        setChild(child, node, true);
+
+        // update relative info
+        // child becomes the new parent, its line and offset counts increase as the former parent
+        // moves under child's left subtree
+        child.line += node.line + 1;
+        child.offset += node.offset + node.length;
+    }
+
+    /**
+     * Right rotation - the given node is rotated down, its left child is rotated up, taking the
+     * previous structural position of <code>node</code>.
+     *
+     * @param node the node to rotate around
+     */
+    private void rotateRight(Node node) {
+        if (ASSERT) Assert.isNotNull(node);
+        Node child= node.left;
+        if (ASSERT) Assert.isNotNull(child);
+        bool leftChild= node.parent is null || node is node.parent.left;
+
+        setChild(node.parent, child, leftChild);
+
+        setChild(node, child.right, true);
+        setChild(child, node, false);
+
+        // update relative info
+        // node loses its left subtree, except for what it keeps in its new subtree
+        // this is exactly the amount in child
+        node.line -= child.line + 1;
+        node.offset -= child.offset + child.length;
+    }
+
+    /**
+     * Helper method for moving a child, ensuring that parent pointers are set correctly.
+     *
+     * @param parent the new parent of <code>child</code>, <code>null</code> to replace the
+     *        root node
+     * @param child the new child of <code>parent</code>, may be <code>null</code>
+     * @param isLeftChild <code>true</code> if <code>child</code> shall become
+     *        <code>parent</code>'s left child, <code>false</code> if it shall become
+     *        <code>parent</code>'s right child
+     */
+    private void setChild(Node parent, Node child, bool isLeftChild) {
+        if (parent is null) {
+            if (child is null)
+                fRoot= new Node(0, NO_DELIM);
+            else
+                fRoot= child;
+        } else {
+            if (isLeftChild)
+                parent.left= child;
+            else
+                parent.right= child;
+        }
+        if (child !is null)
+            child.parent= parent;
+    }
+
+    /**
+     * A left rotation around <code>parent</code>, whose structural position is replaced by
+     * <code>node</code>.
+     *
+     * @param node the node moving up and left
+     * @param parent the node moving left and down
+     */
+    private void singleLeftRotation(Node node, Node parent) {
+        rotateLeft(parent);
+        node.balance= 0;
+        parent.balance= 0;
+    }
+
+    /**
+     * A right rotation around <code>parent</code>, whose structural position is replaced by
+     * <code>node</code>.
+     *
+     * @param node the node moving up and right
+     * @param parent the node moving right and down
+     */
+    private void singleRightRotation(Node node, Node parent) {
+        rotateRight(parent);
+        node.balance= 0;
+        parent.balance= 0;
+    }
+
+    /**
+     * A double left rotation, first rotating right around <code>node</code>, then left around
+     * <code>parent</code>.
+     *
+     * @param node the node that will be rotated right
+     * @param parent the node moving left and down
+     */
+    private void rightLeftRotation(Node node, Node parent) {
+        Node child= node.left;
+        rotateRight(node);
+        rotateLeft(parent);
+        if (child.balance is 1) {
+            node.balance= 0;
+            parent.balance= -1;
+            child.balance= 0;
+        } else if (child.balance is 0) {
+            node.balance= 0;
+            parent.balance= 0;
+        } else if (child.balance is -1) {
+            node.balance= 1;
+            parent.balance= 0;
+            child.balance= 0;
+        }
+    }
+
+    /**
+     * A double right rotation, first rotating left around <code>node</code>, then right around
+     * <code>parent</code>.
+     *
+     * @param node the node that will be rotated left
+     * @param parent the node moving right and down
+     */
+    private void leftRightRotation(Node node, Node parent) {
+        Node child= node.right;
+        rotateLeft(node);
+        rotateRight(parent);
+        if (child.balance is -1) {
+            node.balance= 0;
+            parent.balance= 1;
+            child.balance= 0;
+        } else if (child.balance is 0) {
+            node.balance= 0;
+            parent.balance= 0;
+        } else if (child.balance is 1) {
+            node.balance= -1;
+            parent.balance= 0;
+            child.balance= 0;
+        }
+    }
+
+    /**
+     * Inserts a line with the given length and delimiter after <code>node</code>.
+     *
+     * @param node the predecessor of the inserted node
+     * @param length the line length of the inserted node
+     * @param delimiter the delimiter of the inserted node
+     * @return the inserted node
+     */
+    private Node insertAfter(Node node, int length, String delimiter) {
+        /*
+         * An insertion really shifts the key of all succeeding nodes. Hence we insert the added node
+         * between node and the successor of node. The added node becomes either the right child
+         * of the predecessor node, or the left child of the successor node.
+         */
+        Node added= new Node(length, delimiter);
+
+        if (node.right is null)
+            setChild(node, added, false);
+        else
+            setChild(successorDown(node.right), added, true);
+
+        // parent chain update
+        updateParentChain(added, length, 1);
+        updateParentBalanceAfterInsertion(added);
+
+        return added;
+    }
+
+    /**
+     * Updates the balance information in the parent chain of node until it reaches the root or
+     * finds a node whose balance violates the AVL constraint, which is the re-balanced.
+     *
+     * @param node the child of the first node that needs balance updating
+     */
+    private void updateParentBalanceAfterInsertion(Node node) {
+        Node parent= node.parent;
+        while (parent !is null) {
+            if (node is parent.left)
+                parent.balance--;
+            else
+                parent.balance++;
+
+            switch (parent.balance) {
+                case 1:
+                case -1:
+                    node= parent;
+                    parent= node.parent;
+                    continue;
+                case -2:
+                    rebalanceAfterInsertionLeft(node);
+                    break;
+                case 2:
+                    rebalanceAfterInsertionRight(node);
+                    break;
+                case 0:
+                    break;
+                default:
+                    if (ASSERT)
+                        Assert.isTrue(false);
+            }
+            return;
+        }
+    }
+
+    /**
+     * Re-balances a node whose parent has a double positive balance.
+     *
+     * @param node the node to re-balance
+     */
+    private void rebalanceAfterInsertionRight(Node node) {
+        Node parent= node.parent;
+        if (node.balance is 1) {
+            singleLeftRotation(node, parent);
+        } else if (node.balance is -1) {
+            rightLeftRotation(node, parent);
+        } else if (ASSERT) {
+            Assert.isTrue(false);
+        }
+    }
+
+    /**
+     * Re-balances a node whose parent has a double negative balance.
+     *
+     * @param node the node to re-balance
+     */
+    private void rebalanceAfterInsertionLeft(Node node) {
+        Node parent= node.parent;
+        if (node.balance is -1) {
+            singleRightRotation(node, parent);
+        } else if (node.balance is 1) {
+            leftRightRotation(node, parent);
+        } else if (ASSERT) {
+            Assert.isTrue(false);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#replace(int, int, java.lang.String)
+     */
+    public final void replace(int offset, int length, String text)  {
+        if (ASSERT) checkTree();
+
+        // Inlined nodeByOffset as we need both node and offset
+        int remaining= offset;
+        Node first= fRoot;
+        int firstNodeOffset;
+
+        while (true) {
+            if (first is null)
+                fail(offset);
+
+            if (remaining < first.offset) {
+                first= first.left;
+            } else {
+                remaining -= first.offset;
+                if (remaining < first.length
+                        || remaining is first.length && first.right is null) { // last line
+                    firstNodeOffset= offset - remaining;
+                    break;
+                }
+                remaining -= first.length;
+                first= first.right;
+            }
+        }
+        // Inline nodeByOffset end
+        if (ASSERT) Assert.isTrue(first !is null);
+
+        Node last;
+        if (offset + length < firstNodeOffset + first.length)
+            last= first;
+        else
+            last= nodeByOffset(offset + length);
+        if (ASSERT) Assert.isTrue(last !is null);
+
+        int firstLineDelta= firstNodeOffset + first.length - offset;
+        if (first is last)
+            replaceInternal(first, text, length, firstLineDelta);
+        else
+            replaceFromTo(first, last, text, length, firstLineDelta);
+
+        if (ASSERT) checkTree();
+    }
+
+    /**
+     * Replace happening inside a single line.
+     *
+     * @param node the affected node
+     * @param text the added text
+     * @param length the replace length, &lt; <code>firstLineDelta</code>
+     * @param firstLineDelta the number of characters from the replacement offset to the end of
+     *        <code>node</code> &gt; <code>length</code>
+     */
+    private void replaceInternal(Node node, String text, int length, int firstLineDelta) {
+        // 1) modification on a single line
+
+        AbstractLineTracker_DelimiterInfo info= text is null ? null : nextDelimiterInfo(text, 0);
+
+        if (info is null || info.delimiter is null) {
+            // a) trivial case: insert into a single node, no line mangling
+            int added= text is null ? 0 : text.length;
+            updateLength(node, added - length);
+        } else {
+            // b) more lines to add between two chunks of the first node
+            // remember what we split off the first line
+            int remainder= firstLineDelta - length;
+            String remDelim= node.delimiter;
+
+            // join the first line with the first added
+            int consumed= info.delimiterIndex + info.delimiterLength;
+            int delta= consumed - firstLineDelta;
+            updateLength(node, delta);
+            node.delimiter= info.delimiter;
+
+            // Inline addlines start
+            info= nextDelimiterInfo(text, consumed);
+            while (info !is null) {
+                int lineLen= info.delimiterIndex - consumed + info.delimiterLength;
+                node= insertAfter(node, lineLen, info.delimiter);
+                consumed += lineLen;
+                info= nextDelimiterInfo(text, consumed);
+            }
+            // Inline addlines end
+
+            // add remaining chunk merged with last (incomplete) additional line
+            insertAfter(node, remainder + text.length - consumed, remDelim);
+        }
+    }
+
+    /**
+     * Replace spanning from one node to another.
+     *
+     * @param node the first affected node
+     * @param last the last affected node
+     * @param text the added text
+     * @param length the replace length, &gt;= <code>firstLineDelta</code>
+     * @param firstLineDelta the number of characters removed from the replacement offset to the end
+     *        of <code>node</code>, &lt;= <code>length</code>
+     */
+    private void replaceFromTo(Node node, Node last, String text, int length, int firstLineDelta) {
+        // 2) modification covers several lines
+
+        // delete intermediate nodes
+        // TODO could be further optimized: replace intermediate lines with intermediate added lines
+        // to reduce re-balancing
+        Node successor_= successor(node);
+        while (successor_ !is last) {
+            length -= successor_.length;
+            Node toDelete= successor_;
+            successor_= successor(successor_);
+            updateLength(toDelete, -toDelete.length);
+        }
+
+        AbstractLineTracker_DelimiterInfo info= text is null ? null : nextDelimiterInfo(text, 0);
+
+        if (info is null || info.delimiter is null) {
+            int added= text is null ? 0 : text.length;
+
+            // join the two lines if there are no lines added
+            join(node, last, added - length);
+
+        } else {
+
+            // join the first line with the first added
+            int consumed= info.delimiterIndex + info.delimiterLength;
+            updateLength(node, consumed - firstLineDelta);
+            node.delimiter= info.delimiter;
+            length -= firstLineDelta;
+
+            // Inline addLines start
+            info= nextDelimiterInfo(text, consumed);
+            while (info !is null) {
+                int lineLen= info.delimiterIndex - consumed + info.delimiterLength;
+                node= insertAfter(node, lineLen, info.delimiter);
+                consumed += lineLen;
+                info= nextDelimiterInfo(text, consumed);
+            }
+            // Inline addLines end
+
+            updateLength(last, text.length - consumed - length);
+        }
+    }
+
+    /**
+     * Joins two consecutive node lines, additionally adjusting the resulting length of the combined
+     * line by <code>delta</code>. The first node gets deleted.
+     *
+     * @param one the first node to join
+     * @param two the second node to join
+     * @param delta the delta to apply to the remaining single node
+     */
+    private void join(Node one, Node two, int delta) {
+        int oneLength= one.length;
+        updateLength(one, -oneLength);
+        updateLength(two, oneLength + delta);
+    }
+
+    /**
+     * Adjusts the length of a node by <code>delta</code>, also adjusting the parent chain of
+     * <code>node</code>. If the node's length becomes zero and is not the last (incomplete)
+     * node, it is deleted after the update.
+     *
+     * @param node the node to adjust
+     * @param delta the character delta to add to the node's length
+     */
+    private void updateLength(Node node, int delta) {
+        if (ASSERT) Assert.isTrue(node.length  + delta >= 0);
+
+        // update the node itself
+        node.length += delta;
+
+        // check deletion
+        int lineDelta;
+        bool delete__= node.length is 0 && node.delimiter !is NO_DELIM;
+        if (delete__)
+            lineDelta= -1;
+        else
+            lineDelta= 0;
+
+        // update parent chain
+        if (delta !is 0 || lineDelta !is 0)
+            updateParentChain(node, delta, lineDelta);
+
+        if (delete__)
+            delete_(node);
+    }
+
+    /**
+     * Updates the differential indices following the parent chain. All nodes from
+     * <code>from.parent</code> to the root are updated.
+     *
+     * @param node the child of the first node to update
+     * @param deltaLength the character delta
+     * @param deltaLines the line delta
+     */
+    private void updateParentChain(Node node, int deltaLength, int deltaLines) {
+        updateParentChain(node, null, deltaLength, deltaLines);
+    }
+
+    /**
+     * Updates the differential indices following the parent chain. All nodes from
+     * <code>from.parent</code> to <code>to</code> (exclusive) are updated.
+     *
+     * @param from the child of the first node to update
+     * @param to the first node not to update
+     * @param deltaLength the character delta
+     * @param deltaLines the line delta
+     */
+    private void updateParentChain(Node from, Node to, int deltaLength, int deltaLines) {
+        Node parent= from.parent;
+        while (parent !is to) {
+            // only update node if update comes from left subtree
+            if (from is parent.left) {
+                parent.offset += deltaLength;
+                parent.line += deltaLines;
+            }
+            from= parent;
+            parent= from.parent;
+        }
+    }
+
+    /**
+     * Deletes a node from the tree, re-balancing it if necessary. The differential indices in the
+     * node's parent chain have to be updated in advance to calling this method. Generally, don't
+     * call <code>delete</code> directly, but call
+     * {@link #updateLength(Node, int) update_length(node, -node.length)} to properly remove a
+     * node.
+     *
+     * @param node the node to delete.
+     */
+    private void delete_(Node node) {
+        if (ASSERT) Assert.isTrue(node !is null);
+        if (ASSERT) Assert.isTrue(node.length is 0);
+
+        Node parent= node.parent;
+        Node toUpdate; // the parent of the node that lost a child
+        bool lostLeftChild;
+        bool isLeftChild= parent is null || node is parent.left;
+
+        if (node.left is null || node.right is null) {
+            // 1) node has one child at max - replace parent's pointer with the only child
+            // also handles the trivial case of no children
+            Node replacement= node.left is null ? node.right : node.left;
+            setChild(parent, replacement, isLeftChild);
+            toUpdate= parent;
+            lostLeftChild= isLeftChild;
+            // no updates to do - subtrees stay as they are
+        } else if (node.right.left is null) {
+            // 2a) node's right child has no left child - replace node with right child, giving node's
+            // left subtree to the right child
+            Node replacement= node.right;
+            setChild(parent, replacement, isLeftChild);
+            setChild(replacement, node.left, true);
+            replacement.line= node.line;
+            replacement.offset= node.offset;
+            replacement.balance= node.balance;
+            toUpdate= replacement;
+            lostLeftChild= false;
+//      } else if (node.left.right is null) {
+//          // 2b) symmetric case
+//          Node replacement= node.left;
+//          set_child(parent, replacement, isLeftChild);
+//          set_child(replacement, node.right, false);
+//          replacement.balance= node.balance;
+//          toUpdate= replacement;
+//          lostLeftChild= true;
+        } else {
+            // 3) hard case - replace node with its successor
+            Node successor_= successor(node);
+
+            // successor exists (otherwise node would not have right child, case 1)
+            if (ASSERT) Assert.isNotNull(successor_);
+            // successor has no left child (a left child would be the real successor of node)
+            if (ASSERT) Assert.isTrue(successor_.left is null);
+            if (ASSERT) Assert.isTrue(successor_.line is 0);
+            // successor is the left child of its parent (otherwise parent would be smaller and
+            // hence the real successor)
+            if (ASSERT) Assert.isTrue(successor_ is successor_.parent.left);
+            // successor is not a child of node (would have been covered by 2a)
+            if (ASSERT) Assert.isTrue(successor_.parent !is node);
+
+            toUpdate= successor_.parent;
+            lostLeftChild= true;
+
+            // update relative indices
+            updateParentChain(successor_, node, -successor_.length, -1);
+
+            // delete successor from its current place - like 1)
+            setChild(toUpdate, successor_.right, true);
+
+            // move node's subtrees to its successor
+            setChild(successor_, node.right, false);
+            setChild(successor_, node.left, true);
+
+            // replace node by successor in its parent
+            setChild(parent, successor_, isLeftChild);
+
+            // update the successor
+            successor_.line= node.line;
+            successor_.offset= node.offset;
+            successor_.balance= node.balance;
+        }
+
+        updateParentBalanceAfterDeletion(toUpdate, lostLeftChild);
+    }
+
+    /**
+     * Updates the balance information in the parent chain of node.
+     *
+     * @param node the first node that needs balance updating
+     * @param wasLeftChild <code>true</code> if the deletion happened on <code>node</code>'s
+     *        left subtree, <code>false</code> if it occurred on <code>node</code>'s right
+     *        subtree
+     */
+    private void updateParentBalanceAfterDeletion(Node node, bool wasLeftChild) {
+        while (node !is null) {
+            if (wasLeftChild)
+                node.balance++;
+            else
+                node.balance--;
+
+            Node parent= node.parent;
+            if (parent !is null)
+                wasLeftChild= node is parent.left;
+
+            switch (node.balance) {
+                case 1:
+                case -1:
+                    return; // done, no tree change
+                case -2:
+                    if (rebalanceAfterDeletionRight(node.left))
+                        return;
+                    break; // propagate up
+                case 2:
+                    if (rebalanceAfterDeletionLeft(node.right))
+                        return;
+                    break; // propagate up
+                case 0:
+                    break; // propagate up
+                default:
+                    if (ASSERT)
+                        Assert.isTrue(false);
+            }
+
+            node= parent;
+        }
+    }
+
+    /**
+     * Re-balances a node whose parent has a double positive balance.
+     *
+     * @param node the node to re-balance
+     * @return <code>true</code> if the re-balancement leaves the height at
+     *         <code>node.parent</code> constant, <code>false</code> if the height changed
+     */
+    private bool rebalanceAfterDeletionLeft(Node node) {
+        Node parent= node.parent;
+        if (node.balance is 1) {
+            singleLeftRotation(node, parent);
+            return false;
+        } else if (node.balance is -1) {
+            rightLeftRotation(node, parent);
+            return false;
+        } else if (node.balance is 0) {
+            rotateLeft(parent);
+            node.balance= -1;
+            parent.balance= 1;
+            return true;
+        } else {
+            if (ASSERT) Assert.isTrue(false);
+            return true;
+        }
+    }
+
+    /**
+     * Re-balances a node whose parent has a double negative balance.
+     *
+     * @param node the node to re-balance
+     * @return <code>true</code> if the re-balancement leaves the height at
+     *         <code>node.parent</code> constant, <code>false</code> if the height changed
+     */
+    private bool rebalanceAfterDeletionRight(Node node) {
+        Node parent= node.parent;
+        if (node.balance is -1) {
+            singleRightRotation(node, parent);
+            return false;
+        } else if (node.balance is 1) {
+            leftRightRotation(node, parent);
+            return false;
+        } else if (node.balance is 0) {
+            rotateRight(parent);
+            node.balance= 1;
+            parent.balance= -1;
+            return true;
+        } else {
+            if (ASSERT) Assert.isTrue(false);
+            return true;
+        }
+    }
+
+    /**
+     * Returns the successor of a node, <code>null</code> if node is the last node.
+     *
+     * @param node a node
+     * @return the successor of <code>node</code>, <code>null</code> if there is none
+     */
+    private Node successor(Node node) {
+        if (node.right !is null)
+            return successorDown(node.right);
+
+        return successorUp(node);
+    }
+
+    /**
+     * Searches the successor of <code>node</code> in its parent chain.
+     *
+     * @param node a node
+     * @return the first node in <code>node</code>'s parent chain that is reached from its left
+     *         subtree, <code>null</code> if there is none
+     */
+    private Node successorUp(Node node) {
+        Node child= node;
+        Node parent= child.parent;
+        while (parent !is null) {
+            if (child is parent.left)
+                return parent;
+            child= parent;
+            parent= child.parent;
+        }
+        if (ASSERT) Assert.isTrue(node.delimiter is NO_DELIM);
+        return null;
+    }
+
+    /**
+     * Searches the left-most node in a given subtree.
+     *
+     * @param node a node
+     * @return the left-most node in the given subtree
+     */
+    private Node successorDown(Node node) {
+        Node child= node.left;
+        while (child !is null) {
+            node= child;
+            child= node.left;
+        }
+        return node;
+    }
+
+    /* miscellaneous */
+
+    /**
+     * Throws an exception.
+     *
+     * @param offset the illegal character or line offset that caused the exception
+     * @throws BadLocationException always
+     */
+    private void fail(int offset)  {
+        throw new BadLocationException();
+    }
+
+    /**
+     * Returns the information about the first delimiter found in the given
+     * text starting at the given offset.
+     *
+     * @param text the text to be searched
+     * @param offset the offset in the given text
+     * @return the information of the first found delimiter or <code>null</code>
+     */
+    protected abstract AbstractLineTracker_DelimiterInfo nextDelimiterInfo(String text, int offset);
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineDelimiter(int)
+     */
+    public final String getLineDelimiter(int line)  {
+        Node node= nodeByLine(line);
+        return node.delimiter is NO_DELIM ? null : node.delimiter;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#computeNumberOfLines(java.lang.String)
+     */
+    public final int computeNumberOfLines(String text) {
+        int count= 0;
+        int start= 0;
+        AbstractLineTracker_DelimiterInfo delimiterInfo= nextDelimiterInfo(text, start);
+        while (delimiterInfo !is null && delimiterInfo.delimiterIndex > -1) {
+            ++count;
+            start= delimiterInfo.delimiterIndex + delimiterInfo.delimiterLength;
+            delimiterInfo= nextDelimiterInfo(text, start);
+        }
+        return count;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getNumberOfLines()
+     */
+    public final int getNumberOfLines() {
+        // TODO track separately?
+        Node node= fRoot;
+        int lines= 0;
+        while (node !is null) {
+            lines += node.line + 1;
+            node= node.right;
+        }
+        return lines;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getNumberOfLines(int, int)
+     */
+    public final int getNumberOfLines(int offset, int length)  {
+        if (length is 0)
+            return 1;
+
+        int startLine= lineByOffset(offset);
+        int endLine= lineByOffset(offset + length);
+
+        return endLine - startLine + 1;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineOffset(int)
+     */
+    public final int getLineOffset(int line)  {
+        return offsetByLine(line);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineLength(int)
+     */
+    public final int getLineLength(int line)  {
+        Node node= nodeByLine(line);
+        return node.length;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineNumberOfOffset(int)
+     */
+    public final int getLineNumberOfOffset(int offset)  {
+        return lineByOffset(offset);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineInformationOfOffset(int)
+     */
+    public final IRegion getLineInformationOfOffset(int offset)  {
+        // Inline nodeByOffset start as we need both node and offset
+        int remaining= offset;
+        Node node= fRoot;
+        int lineOffset;
+
+        while (true) {
+            if (node is null)
+                fail(offset);
+
+            if (remaining < node.offset) {
+                node= node.left;
+            } else {
+                remaining -= node.offset;
+                if (remaining < node.length
+                        || remaining is node.length && node.right is null) { // last line
+                    lineOffset= offset - remaining;
+                    break;
+                }
+                remaining -= node.length;
+                node= node.right;
+            }
+        }
+        // Inline nodeByOffset end
+        return new Region(lineOffset, node.pureLength());
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#getLineInformation(int)
+     */
+    public final IRegion getLineInformation(int line)  {
+        try {
+            // Inline nodeByLine start
+            int remaining= line;
+            int offset= 0;
+            Node node= fRoot;
+
+            while (true) {
+                if (node is null)
+                    fail(line);
+
+                if (remaining is node.line) {
+                    offset += node.offset;
+                    break;
+                }
+                if (remaining < node.line) {
+                    node= node.left;
+                } else {
+                    remaining -= node.line + 1;
+                    offset += node.offset + node.length;
+                    node= node.right;
+                }
+            }
+            // Inline nodeByLine end
+            return new Region(offset, node.pureLength());
+        } catch (BadLocationException x) {
+            /*
+             * FIXME: this really strange behavior is mandated by the previous line tracker
+             * implementation and included here for compatibility. See
+             * LineTrackerTest3#testFunnyLastLineCompatibility().
+             */
+            if (line > 0 && line is getNumberOfLines()) {
+                line= line - 1;
+                // Inline nodeByLine start
+                int remaining= line;
+                int offset= 0;
+                Node node= fRoot;
+
+                while (true) {
+                    if (node is null)
+                        fail(line);
+
+                    if (remaining is node.line) {
+                        offset+= node.offset;
+                        break;
+                    }
+                    if (remaining < node.line) {
+                        node= node.left;
+                    } else {
+                        remaining -= node.line + 1;
+                        offset += node.offset + node.length;
+                        node= node.right;
+                    }
+                }
+                Node last= node;
+                // Inline nodeByLine end
+                if (last.length > 0)
+                    return new Region(offset + last.length, 0);
+            }
+            throw x;
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ILineTracker#set(java.lang.String)
+     */
+    public final void set(String text) {
+        fRoot= new Node(0, NO_DELIM);
+        try {
+            replace(0, 0, text);
+        } catch (BadLocationException x) {
+            throw new AssertException(__FILE__,__LINE__);
+        }
+    }
+
+    /*
+     * @see java.lang.Object#toString()
+     */
+    public override String toString() {
+        int depth= computeDepth(fRoot);
+        int WIDTH= 30;
+        int leaves= cast(int) Math.pow( cast(real)2, cast(uint) depth - 1);
+        int width= WIDTH * leaves;
+        String empty= "."; //$NON-NLS-1$
+
+        List roots= new LinkedList();
+        roots.add(fRoot);
+        StringBuffer buf= new StringBuffer((width + 1) * depth);
+        int nodes= 1;
+        int indents= leaves;
+        char[] space= new char[leaves * WIDTH / 2];
+        Arrays.fill(space, ' ');
+        for(int d= 0; d < depth; d++) {
+            // compute indent
+            indents /= 2;
+            int spaces= Math.max(0, indents * WIDTH - WIDTH / 2);
+            // print nodes
+            for (ListIterator it= roots.listIterator(); it.hasNext();) {
+                // pad before
+                buf.append(space[ 0 .. spaces]);
+
+                Node node= cast(Node) it.next();
+                String box;
+                // replace the node with its children
+                if (node is null) {
+                    it.add(cast(Object)null);
+                    box= empty;
+                } else {
+                    it.set(node.left);
+                    it.add(node.right);
+                    box= node.toString();
+                }
+
+                // draw the node, pad to WIDTH
+                int pad_left= (WIDTH - box.length() + 1) / 2;
+                int pad_right= WIDTH - box.length() - pad_left;
+                buf.append(space[ 0 .. pad_left]);
+                buf.append(box);
+                buf.append(space[ 0 .. pad_right]);
+
+                // pad after
+                buf.append(space[ 0 .. spaces]);
+            }
+
+            buf.append('\n');
+            nodes *= 2;
+        }
+
+        return buf.toString();
+    }
+
+    /**
+     * Recursively computes the depth of the tree. Only used by {@link #toString()}.
+     *
+     * @param root the subtree to compute the depth of, may be <code>null</code>
+     * @return the depth of the given tree, 0 if it is <code>null</code>
+     */
+    private byte computeDepth(Node root) {
+        if (root is null)
+            return 0;
+
+        return cast(byte) (Math.max(computeDepth(root.left), computeDepth(root.right)) + 1);
+    }
+
+    /**
+     * Debug-only method that checks the tree structure and the differential offsets.
+     */
+    private void checkTree() {
+        checkTreeStructure(fRoot);
+
+        try {
+            checkTreeOffsets(nodeByOffset(0), [0, 0], null);
+        } catch (BadLocationException x) {
+            throw new AssertException(__FILE__,__LINE__);
+        }
+    }
+
+    /**
+     * Debug-only method that validates the tree structure below <code>node</code>. I.e. it
+     * checks whether all parent/child pointers are consistent and whether the AVL balance
+     * information is correct.
+     *
+     * @param node the node to validate
+     * @return the depth of the tree under <code>node</code>
+     */
+    private byte checkTreeStructure(Node node) {
+        if (node is null)
+            return 0;
+
+        byte leftDepth= checkTreeStructure(node.left);
+        byte rightDepth= checkTreeStructure(node.right);
+        Assert.isTrue(node.balance is rightDepth - leftDepth);
+        Assert.isTrue(node.left is null || node.left.parent is node);
+        Assert.isTrue(node.right is null || node.right.parent is node);
+
+        return cast(byte) (Math.max(rightDepth, leftDepth) + 1);
+    }
+
+    /**
+     * Debug-only method that checks the differential offsets of the tree, starting at
+     * <code>node</code> and continuing until <code>last</code>.
+     *
+     * @param node the first <code>Node</code> to check, may be <code>null</code>
+     * @param offLen an array of length 2, with <code>offLen[0]</code> the expected offset of
+     *        <code>node</code> and <code>offLen[1]</code> the expected line of
+     *        <code>node</code>
+     * @param last the last <code>Node</code> to check, may be <code>null</code>
+     * @return an <code>int[]</code> of length 2, with the first element being the character
+     *         length of <code>node</code>'s subtree, and the second element the number of lines
+     *         in <code>node</code>'s subtree
+     */
+    private int[] checkTreeOffsets(Node node, int[] offLen, Node last) {
+        if (node is last)
+            return offLen;
+
+        Assert.isTrue(node.offset is offLen[0]);
+        Assert.isTrue(node.line is offLen[1]);
+
+        if (node.right !is null) {
+            int[] result= checkTreeOffsets(successorDown(node.right), new int[2], node);
+            offLen[0] += result[0];
+            offLen[1] += result[1];
+        }
+
+        offLen[0] += node.length;
+        offLen[1]++;
+        return checkTreeOffsets(node.parent, offLen, last);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/TypedPosition.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,225 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.TypedPosition;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TypedRegion; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Convenience class for positions that have a type, similar to
+ * {@link org.eclipse.jface.text.ITypedRegion}.
+ * <p>
+ * As {@link org.eclipse.jface.text.Position},<code>TypedPosition</code> can
+ * not be used as key in hash tables as it overrides <code>equals</code> and
+ * <code>hashCode</code> as it would be a value object.
+ */
+public class TypedPosition : Position {
+
+    /** The type of the region described by this position */
+    private String fType;
+
+    /**
+     * Creates a position along the given specification.
+     *
+     * @param offset the offset of this position
+     * @param length the length of this position
+     * @param type the content type of this position
+     */
+    public this(int offset, int length, String type) {
+        super(offset, length);
+        fType= type;
+    }
+
+    /**
+     * Creates a position based on the typed region.
+     *
+     * @param region the typed region
+     */
+    public this(ITypedRegion region) {
+        super(region.getOffset(), region.getLength());
+        fType= region.getType();
+    }
+
+    /**
+     * Returns the content type of the region.
+     *
+     * @return the content type of the region
+     */
+    public String getType() {
+        return fType;
+    }
+
+    /*
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public override int opEquals(Object o) {
+        if ( cast(TypedPosition)o ) {
+            if (super.opEquals(o)) {
+                TypedPosition p= cast(TypedPosition) o;
+                return (fType is null && p.getType() is null) || fType.equals(p.getType());
+            }
+        }
+        return false;
+    }
+
+     /*
+     * @see java.lang.Object#hashCode()
+     */
+    public override hash_t toHash() {
+        int type= fType is null ? 0 : .toHash(fType);
+        return super.toHash() | type;
+     }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/TypedRegion.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.TypedRegion;
+
+import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
+import org.eclipse.jface.text.DefaultTextHover; // packageimport
+import org.eclipse.jface.text.AbstractInformationControl; // packageimport
+import org.eclipse.jface.text.TextUtilities; // packageimport
+import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
+import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
+import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ITextSelection; // packageimport
+import org.eclipse.jface.text.Document; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
+import org.eclipse.jface.text.ITextListener; // packageimport
+import org.eclipse.jface.text.BadPartitioningException; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
+import org.eclipse.jface.text.IUndoManager; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
+import org.eclipse.jface.text.IRepairableDocument; // packageimport
+import org.eclipse.jface.text.IRewriteTarget; // packageimport
+import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
+import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
+import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
+import org.eclipse.jface.text.TextViewer; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
+import org.eclipse.jface.text.RegExMessages; // packageimport
+import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
+import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
+import org.eclipse.jface.text.IViewportListener; // packageimport
+import org.eclipse.jface.text.GapTextStore; // packageimport
+import org.eclipse.jface.text.MarkSelection; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension; // packageimport
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
+import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
+import org.eclipse.jface.text.IInformationControlCreator; // packageimport
+import org.eclipse.jface.text.ISynchronizable; // packageimport
+import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
+import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
+import org.eclipse.jface.text.IRegion; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentExtension4; // packageimport
+import org.eclipse.jface.text.IDocumentExtension2; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
+import org.eclipse.jface.text.Assert; // packageimport
+import org.eclipse.jface.text.DefaultInformationControl; // packageimport
+import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
+import org.eclipse.jface.text.DocumentClone; // packageimport
+import org.eclipse.jface.text.DefaultUndoManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
+import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
+import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
+import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
+import org.eclipse.jface.text.TextSelection; // packageimport
+import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
+import org.eclipse.jface.text.IPainter; // packageimport
+import org.eclipse.jface.text.IInformationControl; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
+import org.eclipse.jface.text.DefaultLineTracker; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
+import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
+import org.eclipse.jface.text.ITextHover; // packageimport
+import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
+import org.eclipse.jface.text.ILineTracker; // packageimport
+import org.eclipse.jface.text.Line; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension; // packageimport
+import org.eclipse.jface.text.IDocumentAdapter; // packageimport
+import org.eclipse.jface.text.TextEvent; // packageimport
+import org.eclipse.jface.text.BadLocationException; // packageimport
+import org.eclipse.jface.text.AbstractDocument; // packageimport
+import org.eclipse.jface.text.AbstractLineTracker; // packageimport
+import org.eclipse.jface.text.TreeLineTracker; // packageimport
+import org.eclipse.jface.text.ITextPresentationListener; // packageimport
+import org.eclipse.jface.text.Region; // packageimport
+import org.eclipse.jface.text.ITextViewer; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
+import org.eclipse.jface.text.MarginPainter; // packageimport
+import org.eclipse.jface.text.IPaintPositionManager; // packageimport
+import org.eclipse.jface.text.TextPresentation; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
+import org.eclipse.jface.text.ISelectionValidator; // packageimport
+import org.eclipse.jface.text.IDocumentExtension; // packageimport
+import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
+import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
+import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
+import org.eclipse.jface.text.IDocumentListener; // packageimport
+import org.eclipse.jface.text.PaintManager; // packageimport
+import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
+import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.IDocumentExtension3; // packageimport
+import org.eclipse.jface.text.Position; // packageimport
+import org.eclipse.jface.text.TextMessages; // packageimport
+import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
+import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
+import org.eclipse.jface.text.IPositionUpdater; // packageimport
+import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
+import org.eclipse.jface.text.ListLineTracker; // packageimport
+import org.eclipse.jface.text.ITextInputListener; // packageimport
+import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
+import org.eclipse.jface.text.IInputChangedListener; // packageimport
+import org.eclipse.jface.text.ITextOperationTarget; // packageimport
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
+import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
+import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
+import org.eclipse.jface.text.JFaceTextUtil; // packageimport
+import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
+import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
+import org.eclipse.jface.text.CursorLinePainter; // packageimport
+import org.eclipse.jface.text.ITextHoverExtension; // packageimport
+import org.eclipse.jface.text.IEventConsumer; // packageimport
+import org.eclipse.jface.text.IDocument; // packageimport
+import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
+import org.eclipse.jface.text.DocumentCommand; // packageimport
+import org.eclipse.jface.text.TypedPosition; // packageimport
+import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
+import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
+import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
+import org.eclipse.jface.text.IEditingSupport; // packageimport
+import org.eclipse.jface.text.IMarkSelection; // packageimport
+import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
+import org.eclipse.jface.text.DocumentEvent; // packageimport
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
+import org.eclipse.jface.text.ITextStore; // packageimport
+import org.eclipse.jface.text.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
+import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
+import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
+import org.eclipse.jface.text.TextAttribute; // packageimport
+import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
+import org.eclipse.jface.text.ITypedRegion; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Default implementation of {@link org.eclipse.jface.text.ITypedRegion}. A
+ * <code>TypedRegion</code> is a value object.
+ */
+public class TypedRegion : Region , ITypedRegion {
+
+    /** The region's type */
+    private String fType;
+
+    /**
+     * Creates a typed region based on the given specification.
+     *
+     * @param offset the region's offset
+     * @param length the region's length
+     * @param type the region's type
+     */
+    public this(int offset, int length, String type) {
+        super(offset, length);
+        fType= type;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITypedRegion#getType()
+     */
+    public String getType() {
+        return fType;
+    }
+
+    /*
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public override int opEquals(Object o) {
+        if ( cast(TypedRegion)o ) {
+            TypedRegion r= cast(TypedRegion) o;
+            return super.opEquals(r) && ((fType is null && r.getType() is null) || fType ==/+eq+/r.getType());
+        }
+        return false;
+    }
+
+     /*
+     * @see java.lang.Object#hashCode()
+     */
+    public override hash_t toHash() {
+        int type= fType is null ? 0 : .toHash(fType);
+        return super.toHash() | type;
+     }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/link/ILinkedModeListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.link.ILinkedModeListener;
+
+import org.eclipse.jface.text.link.LinkedModeModel; // packageimport
+import org.eclipse.jface.text.link.LinkedPosition; // packageimport
+import org.eclipse.jface.text.link.TabStopIterator; // packageimport
+import org.eclipse.jface.text.link.LinkedModeUI; // packageimport
+import org.eclipse.jface.text.link.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionGroup; // packageimport
+import org.eclipse.jface.text.link.LinkedModeManager; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionAnnotations; // packageimport
+import org.eclipse.jface.text.link.ProposalPosition; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * Protocol used by {@link LinkedModeModel}s to communicate state changes, such
+ * as leaving linked mode, suspending it due to a child mode coming up, and
+ * resuming after a child mode has left.
+ * <p>
+ * This interface may implemented by clients.
+ * </p>
+ *
+ * @since 3.0
+ */
+public interface ILinkedModeListener {
+
+    /** Flag to <code>leave</code> specifying no special action. */
+    static const int NONE= 0;
+    /**
+     * Flag to <code>leave</code> specifying that all nested modes should
+     * exit.
+     */
+    static const int EXIT_ALL= 1 << 0;
+    /**
+     * Flag to <code>leave</code> specifying that the caret should be moved to
+     * the exit position.
+     */
+    static const int UPDATE_CARET= 1 << 1;
+    /**
+     * Flag to <code>leave</code> specifying that a UI of a parent mode should
+     * select the current position.
+     */
+    static const int SELECT= 1 << 2;
+    /**
+     * Flag to <code>leave</code> specifying that document content outside of
+     * a linked position was modified.
+     */
+    static const int EXTERNAL_MODIFICATION= 1 << 3;
+
+    /**
+     * The leave event occurs when linked is left.
+     *
+     * @param model the model being left
+     * @param flags the reason and commands for leaving linked mode
+     */
+    void left(LinkedModeModel model, int flags);
+
+    /**
+     * The suspend event occurs when a nested linked mode is installed within
+     * <code>model</code>.
+     *
+     * @param model the model being suspended due to a nested model being
+     *        installed
+     */
+    void suspend(LinkedModeModel model);
+
+    /**
+     * The resume event occurs when a nested linked mode exits.
+     *
+     * @param model the linked mode model being resumed due to a nested mode
+     *        exiting
+     * @param flags the commands to execute when resuming after suspend
+     */
+    void resume(LinkedModeModel model, int flags);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/link/InclusivePositionUpdater.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.link.InclusivePositionUpdater;
+
+import org.eclipse.jface.text.link.LinkedModeModel; // packageimport
+import org.eclipse.jface.text.link.LinkedPosition; // packageimport
+import org.eclipse.jface.text.link.ILinkedModeListener; // packageimport
+import org.eclipse.jface.text.link.TabStopIterator; // packageimport
+import org.eclipse.jface.text.link.LinkedModeUI; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionGroup; // packageimport
+import org.eclipse.jface.text.link.LinkedModeManager; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionAnnotations; // packageimport
+import org.eclipse.jface.text.link.ProposalPosition; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IPositionUpdater;
+import org.eclipse.jface.text.Position;
+
+
+/**
+ * Position updater that considers any change in
+ * <code>[p.offset,&nbsp;p.offset&nbsp;+&nbsp;p.length]</code> of a {@link Position}
+ * <code>p</code> as belonging to the position.
+ * <p>
+ * Internal class. Do not use. Public for testing purposes only.
+ * </p>
+ *
+ * @since 3.0
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class InclusivePositionUpdater : IPositionUpdater {
+
+    /** The position category. */
+    private const String fCategory;
+
+    /**
+     * Creates a new updater for the given <code>category</code>.
+     *
+     * @param category the new category.
+     */
+    public this(String category) {
+        fCategory= category;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent)
+     */
+    public void update(DocumentEvent event) {
+
+        int eventOffset= event.getOffset();
+        int eventOldLength= event.getLength();
+        int eventNewLength= event.getText() is null ? 0 : event.getText().length();
+        int deltaLength= eventNewLength - eventOldLength;
+
+        try {
+            Position[] positions= event.getDocument().getPositions(fCategory);
+
+            for (int i= 0; i !is positions.length; i++) {
+
+                Position position= positions[i];
+
+                if (position.isDeleted())
+                    continue;
+
+                int offset= position.getOffset();
+                int length= position.getLength();
+                int end= offset + length;
+
+                if (offset > eventOffset + eventOldLength)
+                    // position comes way
+                    // after change - shift
+                    position.setOffset(offset + deltaLength);
+                else if (end < eventOffset) {
+                    // position comes way before change -
+                    // leave alone
+                } else if (offset <= eventOffset && end >= eventOffset + eventOldLength) {
+                    // event completely internal to the position - adjust length
+                    position.setLength(length + deltaLength);
+                } else if (offset < eventOffset) {
+                    // event extends over end of position - adjust length
+                    int newEnd= eventOffset + eventNewLength;
+                    position.setLength(newEnd - offset);
+                } else if (end > eventOffset + eventOldLength) {
+                    // event extends from before position into it - adjust offset
+                    // and length
+                    // offset becomes end of event, length adjusted accordingly
+                    // we want to recycle the overlapping part
+                    position.setOffset(eventOffset);
+                    int deleted= eventOffset + eventOldLength - offset;
+                    position.setLength(length - deleted + eventNewLength);
+                } else {
+                    // event consumes the position - delete it
+                    position.delete_();
+                }
+            }
+        } catch (BadPositionCategoryException e) {
+            // ignore and return
+        }
+    }
+
+    /**
+     * Returns the position category.
+     *
+     * @return the position category
+     */
+    public String getCategory() {
+        return fCategory;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/link/LinkedModeManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,267 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.link.LinkedModeManager;
+
+import org.eclipse.jface.text.link.LinkedModeModel; // packageimport
+import org.eclipse.jface.text.link.LinkedPosition; // packageimport
+import org.eclipse.jface.text.link.ILinkedModeListener; // packageimport
+import org.eclipse.jface.text.link.TabStopIterator; // packageimport
+import org.eclipse.jface.text.link.LinkedModeUI; // packageimport
+import org.eclipse.jface.text.link.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionGroup; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionAnnotations; // packageimport
+import org.eclipse.jface.text.link.ProposalPosition; // packageimport
+
+
+import java.lang.all;
+import java.util.Stack;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+
+
+
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * A linked mode manager ensures exclusive access of linked position infrastructures to documents. There
+ * is at most one <code>LinkedModeManager</code> installed on the same document. The <code>getManager</code>
+ * methods will return the existing instance if any of the specified documents already have an installed
+ * manager.
+ *
+ * @since 3.0
+ */
+class LinkedModeManager {
+
+    /**
+     * Our implementation of <code>ILinkedModeListener</code>.
+     */
+    private class Listener : ILinkedModeListener {
+
+        /*
+         * @see org.eclipse.jdt.internal.ui.text.link2.LinkedModeModel.ILinkedModeListener#left(org.eclipse.jdt.internal.ui.text.link2.LinkedModeModel, int)
+         */
+        public void left(LinkedModeModel model, int flags) {
+            this.outer.left(model, flags);
+        }
+
+        /*
+         * @see org.eclipse.jdt.internal.ui.text.link2.LinkedModeModel.ILinkedModeListener#suspend(org.eclipse.jdt.internal.ui.text.link2.LinkedModeModel)
+         */
+        public void suspend(LinkedModeModel model) {
+            // not interested
+        }
+
+        /*
+         * @see org.eclipse.jdt.internal.ui.text.link2.LinkedModeModel.ILinkedModeListener#resume(org.eclipse.jdt.internal.ui.text.link2.LinkedModeModel, int)
+         */
+        public void resume(LinkedModeModel model, int flags) {
+            // not interested
+        }
+
+    }
+
+    /** Global map from documents to managers. */
+    private static Map fgManagers_;
+    private static Map fgManagers(){
+        if( fgManagers_ is null ){
+            synchronized( LinkedModeManager.classinfo ){
+                if( fgManagers_ is null ){
+                    fgManagers_= new HashMap();
+                }
+            }
+        }
+        return fgManagers_;
+    }
+    /**
+     * Returns whether there exists a <code>LinkedModeManager</code> on <code>document</code>.
+     *
+     * @param document the document of interest
+     * @return <code>true</code> if there exists a <code>LinkedModeManager</code> on <code>document</code>, <code>false</code> otherwise
+     */
+    public static bool hasManager(IDocument document) {
+        return fgManagers.get(cast(Object)document) !is null;
+    }
+
+    /**
+     * Returns whether there exists a <code>LinkedModeManager</code> on any of the <code>documents</code>.
+     *
+     * @param documents the documents of interest
+     * @return <code>true</code> if there exists a <code>LinkedModeManager</code> on any of the <code>documents</code>, <code>false</code> otherwise
+     */
+    public static bool hasManager(IDocument[] documents) {
+        for (int i= 0; i < documents.length; i++) {
+            if (hasManager(documents[i]))
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the manager for the given documents. If <code>force</code> is
+     * <code>true</code>, any existing conflicting managers are canceled, otherwise,
+     * the method may return <code>null</code> if there are conflicts.
+     *
+     * @param documents the documents of interest
+     * @param force whether to kill any conflicting managers
+     * @return a manager able to cover the requested documents, or <code>null</code> if there is a conflict and <code>force</code> was set to <code>false</code>
+     */
+    public static LinkedModeManager getLinkedManager(IDocument[] documents, bool force) {
+        if (documents is null || documents.length is 0)
+            return null;
+
+        Set mgrs= new HashSet();
+        LinkedModeManager mgr= null;
+        for (int i= 0; i < documents.length; i++) {
+            mgr= cast(LinkedModeManager) fgManagers.get(cast(Object)documents[i]);
+            if (mgr !is null)
+                mgrs.add(mgr);
+        }
+        if (mgrs.size() > 1)
+            if (force) {
+                for (Iterator it= mgrs.iterator(); it.hasNext(); ) {
+                    LinkedModeManager m= cast(LinkedModeManager) it.next();
+                    m.closeAllEnvironments();
+                }
+            } else {
+                return null;
+            }
+
+        if (mgrs.size() is 0)
+            mgr= new LinkedModeManager();
+
+        for (int i= 0; i < documents.length; i++)
+            fgManagers.put(cast(Object)documents[i], mgr);
+
+        return mgr;
+    }
+
+    /**
+     * Cancels any linked mode manager for the specified document.
+     *
+     * @param document the document whose <code>LinkedModeManager</code> should be canceled
+     */
+    public static void cancelManager(IDocument document) {
+        LinkedModeManager mgr= cast(LinkedModeManager) fgManagers.get(cast(Object)document);
+        if (mgr !is null)
+            mgr.closeAllEnvironments();
+    }
+
+    /** The hierarchy of environments managed by this manager. */
+    private Stack fEnvironments;
+    private Listener fListener;
+
+    this(){
+        fEnvironments= new Stack();
+        fListener= new Listener();
+    }
+
+    /**
+     * Notify the manager about a leaving model.
+     *
+     * @param model
+     * @param flags
+     */
+    private void left(LinkedModeModel model, int flags) {
+        if (!fEnvironments.contains(model))
+            return;
+
+        while (!fEnvironments.isEmpty()) {
+            LinkedModeModel env= cast(LinkedModeModel) fEnvironments.pop();
+            if (env is model)
+                break;
+            env.exit(ILinkedModeListener.NONE);
+        }
+
+        if (fEnvironments.isEmpty()) {
+            removeManager();
+        }
+    }
+
+    private void closeAllEnvironments() {
+        while (!fEnvironments.isEmpty()) {
+            LinkedModeModel env= cast(LinkedModeModel) fEnvironments.pop();
+            env.exit(ILinkedModeListener.NONE);
+        }
+
+        removeManager();
+    }
+
+    private void removeManager() {
+        for (Iterator it= fgManagers.keySet().iterator(); it.hasNext();) {
+            IDocument doc= cast(IDocument) it.next();
+            if (fgManagers.get(cast(Object)doc) is this)
+                it.remove();
+        }
+    }
+
+    /**
+     * Tries to nest the given <code>LinkedModeModel</code> onto the top of
+     * the stack of environments managed by the receiver. If <code>force</code>
+     * is <code>true</code>, any environments on the stack that create a conflict
+     * are killed.
+     *
+     * @param model the model to nest
+     * @param force whether to force the addition of the model
+     * @return <code>true</code> if nesting was successful, <code>false</code> otherwise (only possible if <code>force</code> is <code>false</code>
+     */
+    public bool nestEnvironment(LinkedModeModel model, bool force) {
+        Assert.isNotNull(model);
+
+        try {
+            while (true) {
+                if (fEnvironments.isEmpty()) {
+                    model.addLinkingListener(fListener);
+                    fEnvironments.push(model);
+                    return true;
+                }
+
+                LinkedModeModel top= cast(LinkedModeModel) fEnvironments.peek();
+                if (model.canNestInto(top)) {
+                    model.addLinkingListener(fListener);
+                    fEnvironments.push(model);
+                    return true;
+                } else if (!force) {
+                    return false;
+                } else { // force
+                    fEnvironments.pop();
+                    top.exit(ILinkedModeListener.NONE);
+                    // continue;
+                }
+            }
+        } finally {
+            // if we remove any, make sure the new one got inserted
+            Assert.isTrue(fEnvironments.size() > 0);
+        }
+    }
+
+    /**
+     * Returns the <code>LinkedModeModel</code> that is on top of the stack of
+     * environments managed by the receiver.
+     *
+     * @return the topmost <code>LinkedModeModel</code>
+     */
+    public LinkedModeModel getTopEnvironment() {
+        if (fEnvironments.isEmpty())
+            return null;
+        return cast(LinkedModeModel) fEnvironments.peek();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/link/LinkedModeModel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,797 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.link.LinkedModeModel;
+
+import org.eclipse.jface.text.link.LinkedPosition; // packageimport
+import org.eclipse.jface.text.link.ILinkedModeListener; // packageimport
+import org.eclipse.jface.text.link.TabStopIterator; // packageimport
+import org.eclipse.jface.text.link.LinkedModeUI; // packageimport
+import org.eclipse.jface.text.link.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionGroup; // packageimport
+import org.eclipse.jface.text.link.LinkedModeManager; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionAnnotations; // packageimport
+import org.eclipse.jface.text.link.ProposalPosition; // packageimport
+
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.HashSet;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IPositionUpdater;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.IDocumentExtension;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.TextEdit;
+
+
+/**
+ * The model for linked mode, umbrellas several
+ * {@link LinkedPositionGroup}s. Once installed, the model
+ * propagates any changes to a position to all its siblings in the same position
+ * group.
+ * <p>
+ * Setting up a model consists of first adding
+ * <code>LinkedPositionGroup</code>s to it, and then installing the
+ * model by either calling {@link #forceInstall()} or
+ * {@link #tryInstall()}. After installing the model, it becomes
+ * <em>sealed</em> and no more groups may be added.
+ * </p>
+ * <p>
+ * If a document change occurs that would modify more than one position
+ * group or that would invalidate the disjointness requirement of the positions,
+ * the model is torn down and all positions are deleted. The same happens
+ * upon calling {@link #exit(int)}.
+ * </p>
+ * <h4>Nesting</h4>
+ * <p>
+ * A <code>LinkedModeModel</code> may be nested into another model. This
+ * happens when installing a model the positions of which all fit into a
+ * single position in a parent model that has previously been installed on
+ * the same document(s).
+ * </p>
+ * <p>
+ * Clients may instantiate instances of this class.
+ * </p>
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class LinkedModeModel {
+
+    /**
+     * Checks whether there is already a model installed on <code>document</code>.
+     *
+     * @param document the <code>IDocument</code> of interest
+     * @return <code>true</code> if there is an existing model, <code>false</code>
+     *         otherwise
+     */
+    public static bool hasInstalledModel(IDocument document) {
+        // if there is a manager, there also is a model
+        return LinkedModeManager.hasManager(document);
+    }
+
+    /**
+     * Checks whether there is already a linked mode model installed on any of
+     * the <code>documents</code>.
+     *
+     * @param documents the <code>IDocument</code>s of interest
+     * @return <code>true</code> if there is an existing model, <code>false</code>
+     *         otherwise
+     */
+    public static bool hasInstalledModel(IDocument[] documents) {
+        // if there is a manager, there also is a model
+        return LinkedModeManager.hasManager(documents);
+    }
+
+    /**
+     * Cancels any linked mode model on the specified document. If there is no
+     * model, nothing happens.
+     *
+     * @param document the document whose <code>LinkedModeModel</code> should
+     *        be canceled
+     */
+    public static void closeAllModels(IDocument document) {
+        LinkedModeManager.cancelManager(document);
+    }
+
+    /**
+     * Returns the model currently active on <code>document</code> at
+     * <code>offset</code>, or <code>null</code> if there is none.
+     *
+     * @param document the document for which the caller asks for a
+     *        model
+     * @param offset the offset into <code>document</code>, as there may be
+     *        several models on a document
+     * @return the model currently active on <code>document</code>, or
+     *         <code>null</code>
+     */
+    public static LinkedModeModel getModel(IDocument document, int offset) {
+        if (!hasInstalledModel(document))
+            return null;
+
+        LinkedModeManager mgr= LinkedModeManager.getLinkedManager([document], false);
+        if (mgr !is null)
+            return mgr.getTopEnvironment();
+        return null;
+    }
+
+    /**
+     * Encapsulates the edition triggered by a change to a linking position. Can
+     * be applied to a document as a whole.
+     */
+    private class Replace : IDocumentExtension.IReplace {
+
+        /** The edition to apply on a document. */
+        private TextEdit fEdit;
+
+        /**
+         * Creates a new instance.
+         *
+         * @param edit the edition to apply to a document.
+         */
+        public this(TextEdit edit) {
+            fEdit= edit;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IDocumentExtension.IReplace#perform(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocumentListener)
+         */
+        public void perform(IDocument document, IDocumentListener owner)  {
+            document.removeDocumentListener(owner);
+            fIsChanging= true;
+            try {
+                fEdit.apply(document, TextEdit.UPDATE_REGIONS | TextEdit.CREATE_UNDO);
+            } catch (BadLocationException e) {
+                /* XXX: perform should really throw a BadLocationException
+                 *      see https://bugs.eclipse.org/bugs/show_bug.cgi?id=52950
+                 */
+                throw new RuntimeException(e);
+            } finally {
+                document.addDocumentListener(owner);
+                fIsChanging= false;
+            }
+        }
+
+    }
+
+    /**
+     * The document listener triggering the linked updating of positions
+     * managed by this model.
+     */
+    private class DocumentListener : IDocumentListener {
+
+        private bool fExit= false;
+
+        /**
+         * Checks whether <code>event</code> occurs within any of the positions
+         * managed by this model. If not, the linked mode is left.
+         *
+         * @param event {@inheritDoc}
+         */
+        public void documentAboutToBeChanged(DocumentEvent event) {
+            // don't react on changes executed by the parent model
+            if (fParentEnvironment !is null && fParentEnvironment.isChanging())
+                return;
+
+            for (Iterator it= fGroups.iterator(); it.hasNext(); ) {
+                LinkedPositionGroup group= cast(LinkedPositionGroup) it.next();
+                if (!group.isLegalEvent(event)) {
+                    fExit= true;
+                    return;
+                }
+            }
+        }
+
+        /**
+         * Propagates a change to a linked position to all its sibling positions.
+         *
+         * @param event {@inheritDoc}
+         */
+        public void documentChanged(DocumentEvent event) {
+            if (fExit) {
+                this.outer.exit(ILinkedModeListener.EXTERNAL_MODIFICATION);
+                return;
+            }
+            fExit= false;
+
+            // don't react on changes executed by the parent model
+            if (fParentEnvironment !is null && fParentEnvironment.isChanging())
+                return;
+
+            // collect all results
+            Map result= null;
+            for (Iterator it= fGroups.iterator(); it.hasNext();) {
+                LinkedPositionGroup group= cast(LinkedPositionGroup) it.next();
+
+                Map map= group.handleEvent(event);
+                if (result !is null && map !is null) {
+                    // exit if more than one position was changed
+                    this.outer.exit(ILinkedModeListener.EXTERNAL_MODIFICATION);
+                    return;
+                }
+                if (map !is null)
+                    result= map;
+            }
+
+            if (result !is null) {
+                // edit all documents
+                for (Iterator it2= result.keySet().iterator(); it2.hasNext(); ) {
+                    IDocument doc= cast(IDocument) it2.next();
+                    TextEdit edit= cast(TextEdit) result.get(cast(Object)doc);
+                    Replace replace= new Replace(edit);
+
+                    // apply the edition, either as post notification replace
+                    // on the calling document or directly on any other
+                    // document
+                    if (doc is event.getDocument()) {
+                        if ( cast(IDocumentExtension)doc ) {
+                            (cast(IDocumentExtension) doc).registerPostNotificationReplace(this, replace);
+                        } else {
+                            // ignore - there is no way we can log from JFace text...
+                        }
+                    } else {
+                        replace.perform(doc, this);
+                    }
+                }
+            }
+        }
+
+    }
+
+    /** The set of linked position groups. */
+    private const List fGroups;
+    /** The set of documents spanned by this group. */
+    private const Set fDocuments;
+    /** The position updater for linked positions. */
+    private const IPositionUpdater fUpdater;
+    /** The document listener on the documents affected by this model. */
+    private const DocumentListener fDocumentListener;
+    /** The parent model for a hierarchical set up, or <code>null</code>. */
+    private LinkedModeModel fParentEnvironment;
+    /**
+     * The position in <code>fParentEnvironment</code> that includes all
+     * positions in this object, or <code>null</code> if there is no parent
+     * model.
+     */
+    private LinkedPosition fParentPosition= null;
+    /**
+     * A model is sealed once it has children - no more positions can be
+     * added.
+     */
+    private bool fIsSealed= false;
+    /** <code>true</code> when this model is changing documents. */
+    private bool fIsChanging= false;
+    /** The linked listeners. */
+    private const List fListeners;
+    /** Flag telling whether we have exited: */
+    private bool fIsActive= true;
+    /**
+     * The sequence of document positions as we are going to iterate through
+     * them.
+     */
+    private List fPositionSequence;
+
+    /**
+     * Whether we are in the process of editing documents (set by <code>Replace</code>,
+     * read by <code>DocumentListener</code>.
+     *
+     * @return <code>true</code> if we are in the process of editing a
+     *         document, <code>false</code> otherwise
+     */
+    private bool isChanging() {
+        return fIsChanging || fParentEnvironment !is null && fParentEnvironment.isChanging();
+    }
+
+    /**
+     * Throws a <code>BadLocationException</code> if <code>group</code>
+     * conflicts with this model's groups.
+     *
+     * @param group the group being checked
+     * @throws BadLocationException if <code>group</code> conflicts with this
+     *         model's groups
+     */
+    private void enforceDisjoint(LinkedPositionGroup group)  {
+        for (Iterator it= fGroups.iterator(); it.hasNext(); ) {
+            LinkedPositionGroup g= cast(LinkedPositionGroup) it.next();
+            g.enforceDisjoint(group);
+        }
+    }
+
+    /**
+     * Causes this model to exit. Called either if an illegal document change
+     * is detected, or by the UI.
+     *
+     * @param flags the exit flags as defined in {@link ILinkedModeListener}
+     */
+    public void exit(int flags) {
+        if (!fIsActive)
+            return;
+
+        fIsActive= false;
+
+        for (Iterator it= fDocuments.iterator(); it.hasNext(); ) {
+            IDocument doc= cast(IDocument) it.next();
+            try {
+                doc.removePositionCategory(getCategory());
+            } catch (BadPositionCategoryException e) {
+                // won't happen
+                Assert.isTrue(false);
+            }
+            doc.removePositionUpdater(fUpdater);
+            doc.removeDocumentListener(fDocumentListener);
+        }
+
+        fDocuments.clear();
+        fGroups.clear();
+
+        List listeners= new ArrayList(fListeners);
+        fListeners.clear();
+        for (Iterator it= listeners.iterator(); it.hasNext(); ) {
+            ILinkedModeListener listener= cast(ILinkedModeListener) it.next();
+            listener.left(this, flags);
+        }
+
+
+        if (fParentEnvironment !is null)
+            fParentEnvironment.resume(flags);
+    }
+
+    /**
+     * Causes this model to stop forwarding updates. The positions are not
+     * unregistered however, which will only happen when <code>exit</code>
+     * is called, or after the next document change.
+     *
+     * @param flags the exit flags as defined in {@link ILinkedModeListener}
+     * @since 3.1
+     */
+    public void stopForwarding(int flags) {
+        fDocumentListener.fExit= true;
+    }
+
+    /**
+     * Puts <code>document</code> into the set of managed documents. This
+     * involves registering the document listener and adding our position
+     * category.
+     *
+     * @param document the new document
+     */
+    private void manageDocument(IDocument document) {
+        if (!fDocuments.contains(cast(Object)document)) {
+            fDocuments.add(cast(Object)document);
+            document.addPositionCategory(getCategory());
+            document.addPositionUpdater(fUpdater);
+            document.addDocumentListener(fDocumentListener);
+        }
+
+    }
+
+    /**
+     * Returns the position category used by this model.
+     *
+     * @return the position category used by this model
+     */
+    private String getCategory() {
+        return toString();
+    }
+
+    /**
+     * Adds a position group to this <code>LinkedModeModel</code>. This
+     * method may not be called if the model has been installed. Also, if
+     * a UI has been set up for this model, it may not pick up groups
+     * added afterwards.
+     * <p>
+     * If the positions in <code>group</code> conflict with any other group in
+     * this model, a <code>BadLocationException</code> is thrown. Also,
+     * if this model is nested inside another one, all positions in all
+     * groups of the child model have to reside within a single position in the
+     * parent model, otherwise a <code>BadLocationException</code> is thrown.
+     * </p>
+     * <p>
+     * If <code>group</code> already exists, nothing happens.
+     * </p>
+     *
+     * @param group the group to be added to this model
+     * @throws BadLocationException if the group conflicts with the other groups
+     *         in this model or violates the nesting requirements.
+     * @throws IllegalStateException if the method is called when the
+     *         model is already sealed
+     */
+    public void addGroup(LinkedPositionGroup group)  {
+        if (group is null)
+            throw new IllegalArgumentException("group may not be null"); //$NON-NLS-1$
+        if (fIsSealed)
+            throw new IllegalStateException("model is already installed"); //$NON-NLS-1$
+        if (fGroups.contains(group))
+            // nothing happens
+            return;
+
+        enforceDisjoint(group);
+        group.seal();
+        fGroups.add(group);
+    }
+
+    /**
+     * Creates a new model.
+     * @since 3.1
+     */
+    public this() {
+        // SWT inst init
+        fGroups= new ArrayList();
+        fDocuments= new HashSet();
+        fUpdater= new InclusivePositionUpdater(getCategory());
+        fDocumentListener= new DocumentListener();
+        fListeners= new ArrayList();
+        fPositionSequence= new ArrayList();
+
+    }
+
+    /**
+     * Installs this model, which includes registering as document
+     * listener on all involved documents and storing global information about
+     * this model. Any conflicting model already present will be
+     * closed.
+     * <p>
+     * If an exception is thrown, the installation failed and
+     * the model is unusable.
+     * </p>
+     *
+     * @throws BadLocationException if some of the positions of this model
+     *         were not valid positions on their respective documents
+     */
+    public void forceInstall()  {
+        if (!install(true))
+            Assert.isTrue(false);
+    }
+
+    /**
+     * Installs this model, which includes registering as document
+     * listener on all involved documents and storing global information about
+     * this model. If there is another model installed on the
+     * document(s) targeted by the receiver that conflicts with it, installation
+     * may fail.
+     * <p>
+     * The return value states whether installation was
+     * successful; if not, the model is not installed and will not work.
+     * </p>
+     *
+     * @return <code>true</code> if installation was successful,
+     *         <code>false</code> otherwise
+     * @throws BadLocationException if some of the positions of this model
+     *         were not valid positions on their respective documents
+     */
+    public bool tryInstall()  {
+        return install(false);
+    }
+
+    /**
+     * Installs this model, which includes registering as document
+     * listener on all involved documents and storing global information about
+     * this model. The return value states whether installation was
+     * successful; if not, the model is not installed and will not work.
+     * The return value can only then become <code>false</code> if
+     * <code>force</code> was set to <code>false</code> as well.
+     *
+     * @param force if <code>true</code>, any other model that cannot
+     *        coexist with this one is canceled; if <code>false</code>,
+     *        install will fail when conflicts occur and return false
+     * @return <code>true</code> if installation was successful,
+     *         <code>false</code> otherwise
+     * @throws BadLocationException if some of the positions of this model
+     *         were not valid positions on their respective documents
+     */
+    private bool install(bool force)  {
+        if (fIsSealed)
+            throw new IllegalStateException("model is already installed"); //$NON-NLS-1$
+        enforceNotEmpty();
+
+        IDocument[] documents= getDocuments();
+        LinkedModeManager manager= LinkedModeManager.getLinkedManager(documents, force);
+        // if we force creation, we require a valid manager
+        Assert.isTrue(!(force && manager is null));
+        if (manager is null)
+            return false;
+
+        if (!manager.nestEnvironment(this, force))
+            if (force)
+                Assert.isTrue(false);
+            else
+                return false;
+
+        // we set up successfully. After this point, exit has to be called to
+        // remove registered listeners...
+        fIsSealed= true;
+        if (fParentEnvironment !is null)
+            fParentEnvironment.suspend();
+
+        // register positions
+        try {
+            for (Iterator it= fGroups.iterator(); it.hasNext(); ) {
+                LinkedPositionGroup group= cast(LinkedPositionGroup) it.next();
+                group.register(this);
+            }
+            return true;
+        } catch (BadLocationException e){
+            // if we fail to add, make sure to release all listeners again
+            exit(ILinkedModeListener.NONE);
+            throw e;
+        }
+    }
+
+    /**
+     * Asserts that there is at least one linked position in this linked mode
+     * model, throws an IllegalStateException otherwise.
+     */
+    private void enforceNotEmpty() {
+        bool hasPosition= false;
+        for (Iterator it= fGroups.iterator(); it.hasNext(); )
+            if (!(cast(LinkedPositionGroup) it.next()).isEmpty()) {
+                hasPosition= true;
+                break;
+            }
+        if (!hasPosition)
+            throw new IllegalStateException("must specify at least one linked position"); //$NON-NLS-1$
+
+    }
+
+    /**
+     * Collects all the documents that contained positions are set upon.
+     * @return the set of documents affected by this model
+     */
+    private IDocument[] getDocuments() {
+        Set docs= new HashSet();
+        for (Iterator it= fGroups.iterator(); it.hasNext(); ) {
+            LinkedPositionGroup group= cast(LinkedPositionGroup) it.next();
+            docs.addAll(Arrays.asList(arraycast!(Object)(group.getDocuments())));
+        }
+        return arraycast!(IDocument)( docs.toArray());
+    }
+
+    /**
+     * Returns whether the receiver can be nested into the given <code>parent</code>
+     * model. If yes, the parent model and its position that the receiver
+     * fits in are remembered.
+     *
+     * @param parent the parent model candidate
+     * @return <code>true</code> if the receiver can be nested into <code>parent</code>, <code>false</code> otherwise
+     */
+    bool canNestInto(LinkedModeModel parent) {
+        for (Iterator it= fGroups.iterator(); it.hasNext(); ) {
+            LinkedPositionGroup group= cast(LinkedPositionGroup) it.next();
+            if (!enforceNestability(group, parent)) {
+                fParentPosition= null;
+                return false;
+            }
+        }
+
+        Assert.isNotNull(fParentPosition);
+        fParentEnvironment= parent;
+        return true;
+    }
+
+    /**
+     * Called by nested models when a group is added to them. All
+     * positions in all groups of a nested model have to fit inside a
+     * single position in the parent model.
+     *
+     * @param group the group of the nested model to be adopted.
+     * @param model the model to check against
+     * @return <code>false</code> if it failed to enforce nestability
+     */
+    private bool enforceNestability(LinkedPositionGroup group, LinkedModeModel model) {
+        Assert.isNotNull(model);
+        Assert.isNotNull(group);
+
+        try {
+            for (Iterator it= model.fGroups.iterator(); it.hasNext(); ) {
+                LinkedPositionGroup pg= cast(LinkedPositionGroup) it.next();
+                LinkedPosition pos;
+                pos= pg.adopt(group);
+                if (pos !is null && fParentPosition !is null && fParentPosition !is pos)
+                    return false; // group does not fit into one parent position, which is illegal
+                else if (fParentPosition is null && pos !is null)
+                    fParentPosition= pos;
+            }
+        } catch (BadLocationException e) {
+            return false;
+        }
+
+        // group must fit into exactly one of the parent's positions
+        return fParentPosition !is null;
+    }
+
+    /**
+     * Returns whether this model is nested.
+     *
+     * <p>
+     * This method is part of the private protocol between
+     * <code>LinkedModeUI</code> and <code>LinkedModeModel</code>.
+     * </p>
+     *
+     * @return <code>true</code> if this model is nested,
+     *         <code>false</code> otherwise
+     */
+    public bool isNested() {
+        return fParentEnvironment !is null;
+    }
+
+    /**
+     * Returns the positions in this model that have a tab stop, in the
+     * order they were added.
+     *
+     * <p>
+     * This method is part of the private protocol between
+     * <code>LinkedModeUI</code> and <code>LinkedModeModel</code>.
+     * </p>
+     *
+     * @return the positions in this model that have a tab stop, in the
+     *         order they were added
+     */
+    public List getTabStopSequence() {
+        return fPositionSequence;
+    }
+
+    /**
+     * Adds <code>listener</code> to the set of listeners that are informed
+     * upon state changes.
+     *
+     * @param listener the new listener
+     */
+    public void addLinkingListener(ILinkedModeListener listener) {
+        Assert.isNotNull(cast(Object)listener);
+        if (!fListeners.contains(cast(Object)listener))
+            fListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes <code>listener</code> from the set of listeners that are
+     * informed upon state changes.
+     *
+     * @param listener the new listener
+     */
+    public void removeLinkingListener(ILinkedModeListener listener) {
+        fListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Finds the position in this model that is closest after
+     * <code>toFind</code>. <code>toFind</code> needs not be a position in
+     * this model and serves merely as an offset.
+     *
+     * <p>
+     * This method part of the private protocol between
+     * <code>LinkedModeUI</code> and <code>LinkedModeModel</code>.
+     * </p>
+     *
+     * @param toFind the position to search from
+     * @return the closest position in the same document as <code>toFind</code>
+     *         after the offset of <code>toFind</code>, or <code>null</code>
+     */
+    public LinkedPosition findPosition(LinkedPosition toFind) {
+        LinkedPosition position= null;
+        for (Iterator it= fGroups.iterator(); it.hasNext(); ) {
+            LinkedPositionGroup group= cast(LinkedPositionGroup) it.next();
+            position= group.getPosition(toFind);
+            if (position !is null)
+                break;
+        }
+        return position;
+    }
+
+    /**
+     * Registers a <code>LinkedPosition</code> with this model. Called
+     * by <code>PositionGroup</code>.
+     *
+     * @param position the position to register
+     * @throws BadLocationException if the position cannot be added to its
+     *         document
+     */
+    void register(LinkedPosition position)  {
+        Assert.isNotNull(position);
+
+        IDocument document= position.getDocument();
+        manageDocument(document);
+        try {
+            document.addPosition(getCategory(), position);
+        } catch (BadPositionCategoryException e) {
+            // won't happen as the category has been added by manageDocument()
+            Assert.isTrue(false);
+        }
+        int seqNr= position.getSequenceNumber();
+        if (seqNr !is LinkedPositionGroup.NO_STOP) {
+            fPositionSequence.add(position);
+        }
+    }
+
+    /**
+     * Suspends this model.
+     */
+    private void suspend() {
+        List l= new ArrayList(fListeners);
+        for (Iterator it= l.iterator(); it.hasNext(); ) {
+            ILinkedModeListener listener= cast(ILinkedModeListener) it.next();
+            listener.suspend(this);
+        }
+    }
+
+    /**
+     * Resumes this model. <code>flags</code> can be <code>NONE</code>
+     * or <code>SELECT</code>.
+     *
+     * @param flags <code>NONE</code> or <code>SELECT</code>
+     */
+    private void resume(int flags) {
+        List l= new ArrayList(fListeners);
+        for (Iterator it= l.iterator(); it.hasNext(); ) {
+            ILinkedModeListener listener= cast(ILinkedModeListener) it.next();
+            listener.resume(this, flags);
+        }
+    }
+
+    /**
+     * Returns whether an offset is contained by any position in this
+     * model.
+     *
+     * @param offset the offset to check
+     * @return <code>true</code> if <code>offset</code> is included by any
+     *         position (see {@link LinkedPosition#includes(int)}) in this
+     *         model, <code>false</code> otherwise
+     */
+    public bool anyPositionContains(int offset) {
+        for (Iterator it= fGroups.iterator(); it.hasNext(); ) {
+            LinkedPositionGroup group= cast(LinkedPositionGroup) it.next();
+            if (group.contains(offset))
+                // take the first hit - exclusion is guaranteed by enforcing
+                // disjointness when adding positions
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the linked position group that contains <code>position</code>,
+     * or <code>null</code> if <code>position</code> is not contained in any
+     * group within this model. Group containment is tested by calling
+     * <code>group.contains(position)</code> for every <code>group</code> in
+     * this model.
+     *
+     * <p>
+     * This method part of the private protocol between
+     * <code>LinkedModeUI</code> and <code>LinkedModeModel</code>.
+     * </p>
+     *
+     * @param position the position the group of which is requested
+     * @return the first group in this model for which
+     *         <code>group.contains(position)</code> returns <code>true</code>,
+     *         or <code>null</code> if no group contains <code>position</code>
+     */
+    public LinkedPositionGroup getGroupForPosition(Position position) {
+        for (Iterator it= fGroups.iterator(); it.hasNext(); ) {
+            LinkedPositionGroup group= cast(LinkedPositionGroup) it.next();
+            if (group.contains(position))
+                return group;
+        }
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/link/LinkedPosition.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.link.LinkedPosition;
+
+import org.eclipse.jface.text.link.LinkedModeModel; // packageimport
+import org.eclipse.jface.text.link.ILinkedModeListener; // packageimport
+import org.eclipse.jface.text.link.TabStopIterator; // packageimport
+import org.eclipse.jface.text.link.LinkedModeUI; // packageimport
+import org.eclipse.jface.text.link.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionGroup; // packageimport
+import org.eclipse.jface.text.link.LinkedModeManager; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionAnnotations; // packageimport
+import org.eclipse.jface.text.link.ProposalPosition; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.Position;
+
+/**
+ * A <code>Position</code> on a document that knows which document it is
+ * registered with and has a sequence number for tab stops.
+ * <p>
+ * Clients may extend this class.
+ * </p>
+ * @since 3.0
+ */
+public class LinkedPosition : Position {
+    alias Position.overlapsWith overlapsWith;
+    /** The document this position belongs to. */
+    private IDocument fDocument;
+    private int fSequenceNumber;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param document the document
+     * @param offset the offset of the position
+     * @param length the length of the position
+     * @param sequence the iteration sequence rank
+     */
+    public this(IDocument document, int offset, int length, int sequence) {
+        super(offset, length);
+        Assert.isNotNull(cast(Object)document);
+        fDocument= document;
+        fSequenceNumber= sequence;
+    }
+
+    /**
+     * Creates a new instance. Equivalent to calling
+     * <code>LinkedPosition(document, offset, length, LinkedPositionGroup.NO_STOP)</code>
+     *
+     * @param document the document
+     * @param offset the offset of the position
+     * @param length the length of the position
+     */
+    public this(IDocument document, int offset, int length) {
+        this(document, offset, length, LinkedPositionGroup.NO_STOP);
+    }
+
+    /**
+     * @return Returns the document.
+     */
+    public IDocument getDocument() {
+        return fDocument;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.Position#equals(java.lang.Object)
+     */
+    public bool equals(Object other) {
+        if ( cast(LinkedPosition)other ) {
+            LinkedPosition p= cast(LinkedPosition) other;
+            return p.offset is offset && p.length is length && p.fDocument is fDocument;
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether this position overlaps with <code>position</code>.
+     *
+     * @param position the position to check.
+     * @return <code>true</code> if this position overlaps with
+     *         <code>position</code>,<code>false</code> otherwise
+     */
+    public bool overlapsWith(LinkedPosition position) {
+        return position.getDocument() is fDocument && overlapsWith(position.getOffset(), position.getLength());
+    }
+
+    /**
+     * Returns whether this position includes <code>event</code>.
+     *
+     * @param event the event to check.
+     * @return <code>true</code> if this position includes <code>event</code>,
+     *         <code>false</code> otherwise
+     */
+    public bool includes(DocumentEvent event) {
+        return includes(event.getDocument(), event.getOffset(), event.getLength());
+    }
+
+    /**
+     * Returns whether this position includes <code>position</code>.
+     *
+     * @param position the position to check.
+     * @return <code>true</code> if this position includes
+     *         <code>position</code>,<code>false</code> otherwise
+     */
+    public bool includes(LinkedPosition position) {
+        return includes(position.getDocument(), position.getOffset(), position.getLength());
+    }
+
+    /**
+     * Overrides {@link Position#includes(int)}so every offset is considered
+     * included that lies in between the first and last offset of this position,
+     * and offsets that are right at the end of the position.
+     *
+     * @param pOffset the offset to check
+     * @return <code>true</code> if <code>pOffset</code> is in
+     *         <code>[offset, offset + length]</code>
+     */
+    public bool includes(int pOffset) {
+        return this.offset <= pOffset && pOffset <= this.offset + this.length;
+    }
+
+    /**
+     * Returns whether this position includes the range given by
+     * <code>offset</code> and <code>length</code>. A range is included by
+     * a <code>LinkedPosition</code> if {@link #includes(int) includes(offset)}
+     * returns true for every offset in the range, including the borders of the
+     * range.
+     *
+     * @param doc the document that the given range refers to, may be <code>null</code>
+     * @param off the offset of the range, referring to <code>document</code>
+     * @param len the length of the range
+     * @return <code>true</code> if <code>doc</code> is the same document as
+     *         this position refers to, and if the entire range is included in
+     *         this position
+     */
+    protected bool includes(IDocument doc, int off, int len) {
+        return doc is fDocument && off >= offset && len + off <= offset + length;
+
+    }
+
+    /**
+     * Returns the content of this position on the referenced document.
+     *
+     * @return the content of the document at this position
+     * @throws BadLocationException if the position is not valid
+     */
+    public String getContent()  {
+        return fDocument.get(offset, length);
+    }
+
+    /**
+     * Returns the sequence number of this position.
+     *
+     * @return the sequence number of this position
+     */
+    public int getSequenceNumber() {
+        return fSequenceNumber;
+    }
+
+    /**
+     * Sets the sequence number of this position.
+     *
+     * @param sequence the new sequence number
+     */
+    public void setSequenceNumber(int sequence) {
+        fSequenceNumber= sequence;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.Position#hashCode()
+     */
+    public override hash_t toHash() {
+        alias .toHash toHash;
+        return (cast(Object)fDocument).toHash() | super.toHash() | fSequenceNumber;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/link/LinkedPositionGroup.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,435 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.link.LinkedPositionGroup;
+
+import org.eclipse.jface.text.link.LinkedModeModel; // packageimport
+import org.eclipse.jface.text.link.LinkedPosition; // packageimport
+import org.eclipse.jface.text.link.ILinkedModeListener; // packageimport
+import org.eclipse.jface.text.link.TabStopIterator; // packageimport
+import org.eclipse.jface.text.link.LinkedModeUI; // packageimport
+import org.eclipse.jface.text.link.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.link.LinkedModeManager; // packageimport
+import org.eclipse.jface.text.link.LinkedPositionAnnotations; // packageimport
+import org.eclipse.jface.text.link.ProposalPosition; // packageimport
+
+import java.lang.all;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import tango.text.convert.Format;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.ReplaceEdit;
+import org.eclipse.text.edits.TextEdit;
+
+/**
+ * A group of positions in multiple documents that are simultaneously modified -
+ * if one gets edited, all other positions in a group are edited the same way.
+ * All linked positions in a group have the same content.
+ * <p>
+ * Normally, new positions are given a tab stop weight which can be used by
+ * clients, e.g. the UI. If no weight is given, a position will not be visited.
+ * If no weights are used at all, the first position in a document is taken as
+ * the only stop as to comply with the behavior of the old linked position
+ * infrastructure.
+ * </p>
+ * <p>
+ * Clients may instantiate this class.
+ * </p>
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class LinkedPositionGroup {
+
+    /** Sequence constant declaring that a position should not be stopped by. */
+    public static const int NO_STOP= -1;
+
+    /* members */
+
+    /** The linked positions of this group. */
+    private const List fPositions;
+    /** Whether we are sealed or not. */
+    private bool fIsSealed= false;
+    /**
+     * <code>true</code> if there are custom iteration weights. For backward
+     * compatibility.
+     */
+    private bool fHasCustomIteration= false;
+
+    /*
+     * iteration variables, set to communicate state between isLegalEvent and
+     * handleEvent
+     */
+    /** The position including the most recent <code>DocumentEvent</code>. */
+    private LinkedPosition fLastPosition;
+    /** The region covered by <code>fLastPosition</code> before the document
+     * change.
+     */
+    private IRegion fLastRegion;
+
+    this(){
+        fPositions= new LinkedList();
+    }
+
+    /**
+     * Adds a position to this group. The document region defined by the
+     * position must contain the same content (and thus have the same length) as
+     * any of the other positions already in this group. Additionally, all
+     * positions added must be disjoint; otherwise a
+     * <code>BadLocationException</code> is thrown.
+     * <p>
+     * Positions added using this method are owned by this group afterwards and
+     * may not be updated or modified thereafter.
+     * </p>
+     * <p>
+     * Once a group has been added to a <code>LinkedModeModel</code>, it
+     * becomes <em>sealed</em> and no positions may be added any more.
+     * </p>
+     *
+     * @param position the position to add
+     * @throws BadLocationException if the position is invalid or conflicts with
+     *         other positions in the group
+     * @throws IllegalStateException if the group has already been added to a
+     *         model
+     */
+    public void addPosition(LinkedPosition position)  {
+        /*
+         * Enforces constraints and sets the custom iteration flag. If the
+         * position is already in this group, nothing happens.
+         */
+        Assert.isNotNull(position);
+        if (fIsSealed)
+            throw new IllegalStateException("cannot add positions after the group is added to an model"); //$NON-NLS-1$
+
+        if (!fPositions.contains(position)) {
+            enforceDisjoint(position);
+            enforceEqualContent(position);
+            fPositions.add(position);
+            fHasCustomIteration |= position.getSequenceNumber() !is LinkedPositionGroup.NO_STOP;
+        } else
+            return; // nothing happens
+    }
+
+    /**
+     * Enforces the invariant that all positions must contain the same string.
+     *
+     * @param position the position to check
+     * @throws BadLocationException if the equal content check fails
+     */
+    private void enforceEqualContent(LinkedPosition position)  {
+        if (fPositions.size() > 0) {
+            LinkedPosition groupPosition= cast(LinkedPosition) fPositions.get(0);
+            String groupContent= groupPosition.getContent();
+            String positionContent= position.getContent();
+            if (!groupContent.equals(positionContent))
+                throw new BadLocationException(Format( "First position: '{}' at {}, this position: '{}' at {}",
+                        groupContent, groupPosition.getOffset(), //$NON-NLS-1$ //$NON-NLS-2$
+                        positionContent, position.getOffset())); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+    }
+
+    /**
+     * Enforces the invariant that all positions must be disjoint.
+     *
+     * @param position the position to check
+     * @throws BadLocationException if the disjointness check fails
+     */
+    private void enforceDisjoint(LinkedPosition position)  {
+        for (Iterator it= fPositions.iterator(); it.hasNext(); ) {
+            LinkedPosition p= cast(LinkedPosition) it.next();
+            if (p.overlapsWith(position))
+                throw new BadLocationException();
+        }
+    }
+
+    /**
+     * Enforces the disjointness for another group
+     *
+     * @param group the group to check
+     * @throws BadLocationException if the disjointness check fails
+     */
+    void enforceDisjoint(LinkedPositionGroup group)  {
+        Assert.isNotNull(group);
+        for (Iterator it= group.fPositions.iterator(); it.hasNext(); ) {
+            LinkedPosition p= cast(LinkedPosition) it.next();
+            enforceDisjoint(p);
+        }
+    }
+
+    /**
+     * Checks whether <code>event</code> is a legal event for this group. An
+     * event is legal if it touches at most one position contained within this
+     * group.
+     *
+     * @param event the document event to check
+     * @return <code>true</code> if <code>event</code> is legal
+     */
+    bool isLegalEvent(DocumentEvent event) {
+        fLastPosition= null;
+        fLastRegion= null;
+
+        for (Iterator it= fPositions.iterator(); it.hasNext(); ) {
+            LinkedPosition pos= cast(LinkedPosition) it.next();
+            if (overlapsOrTouches(pos, event)) {
+                if (fLastPosition !is null) {
+                    fLastPosition= null;
+                    fLastRegion= null;
+                    return false;
+                }
+
+                fLastPosition= pos;
+                fLastRegion= new Region(pos.getOffset(), pos.getLength());
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks whether the given event touches the given position. To touch means
+     * to overlap or come up to the borders of the position.
+     *
+     * @param position the position
+     * @param event the event
+     * @return <code>true</code> if <code>position</code> and
+     *         <code>event</code> are not absolutely disjoint
+     * @since 3.1
+     */
+    private bool overlapsOrTouches(LinkedPosition position, DocumentEvent event) {
+        return (cast(Object)position.getDocument()).opEquals(cast(Object)event.getDocument()) && position.getOffset() <= event.getOffset() + event.getLength() && position.getOffset() + position.getLength() >= event.getOffset();
+    }
+
+    /**
+     * Creates an edition of a document change that will forward any
+     * modification in one position to all linked siblings. The return value is
+     * a map from <code>IDocument</code> to <code>TextEdit</code>.
+     *
+     * @param event the document event to check
+     * @return a map of edits, grouped by edited document, or <code>null</code>
+     *         if there are no edits
+     */
+    Map handleEvent(DocumentEvent event) {
+
+        if (fLastPosition !is null) {
+
+            Map map= new HashMap();
+
+
+            int relativeOffset= event.getOffset() - fLastRegion.getOffset();
+            if (relativeOffset < 0) {
+                relativeOffset= 0;
+            }
+
+            int eventEnd= event.getOffset() + event.getLength();
+            int lastEnd= fLastRegion.getOffset() + fLastRegion.getLength();
+            int length;
+            if (eventEnd > lastEnd)
+                length= lastEnd - relativeOffset - fLastRegion.getOffset();
+            else
+                length= eventEnd - relativeOffset - fLastRegion.getOffset();
+
+            String text= event.getText();
+            if (text is null)
+                text= ""; //$NON-NLS-1$
+
+            for (Iterator it= fPositions.iterator(); it.hasNext(); ) {
+                LinkedPosition p= cast(LinkedPosition) it.next();
+                if (p is fLastPosition || p.isDeleted())
+                    continue; // don't re-update the origin of the change
+
+                List edits= cast(List) map.get(cast(Object)p.getDocument());
+                if (edits is null) {
+                    edits= new ArrayList();
+                    map.put(cast(Object)p.getDocument(), cast(Object)edits);
+                }
+
+                edits.add(new ReplaceEdit(p.getOffset() + relativeOffset, length, text));
+            }
+
+            try {
+                for (Iterator it= map.keySet().iterator(); it.hasNext();) {
+                    IDocument d= cast(IDocument) it.next();
+                    TextEdit edit= new MultiTextEdit(0, d.getLength());
+                    edit.addChildren(arraycast!(TextEdit)( (cast(List) map.get(cast(Object)d)).toArray()));
+                    map.put(cast(Object)d, edit);
+                }
+
+                return map;
+            } catch (MalformedTreeException x) {
+                // may happen during undo, as LinkedModeModel does not know
+                // that the changes technically originate from a parent environment
+                // if this happens, post notification changes are not accepted anyway and
+                // we can simply return null - any changes will be undone by the undo
+                // manager
+                return null;
+            }
+
+        }
+
+        return null;
+    }
+
+    /**
+     * Sets the model of this group. Once a model has been set, no
+     * more positions can be added and the model cannot be changed.
+     */
+    void seal() {
+        Assert.isTrue(!fIsSealed);
+        fIsSealed= true;
+
+        if (fHasCustomIteration is false && fPositions.size() > 0) {
+            (cast(LinkedPosition) fPositions.get(0)).setSequenceNumber(0);
+        }
+    }
+
+    IDocument[] getDocuments() {
+        IDocument[] docs= new IDocument[fPositions.size()];
+        int i= 0;
+        for (Iterator it= fPositions.iterator(); it.hasNext(); i++) {
+            LinkedPosition pos= cast(LinkedPosition) it.next();
+            docs[i]= pos.getDocument();
+        }
+        return docs;
+    }
+
+    void register(LinkedModeModel model)  {
+        for (Iterator it= fPositions.iterator(); it.hasNext(); ) {
+            LinkedPosition pos= cast(LinkedPosition) it.next();
+            model.register(pos);
+        }
+    }
+
+    /**
+     * Returns the position in this group that encompasses all positions in
+     * <code>group</code>.
+     *
+     * @param group the group to be adopted
+     * @return a position in the receiver that contains all positions in <code>group</code>,
+     *         or <code>null</code> if none can be found
+     * @throws BadLocationException if more than one position are affected by
+     *         <code>group</code>
+     */
+    LinkedPosition adopt(LinkedPositionGroup group)  {
+        LinkedPosition found= null;
+        for (Iterator it= group.fPositions.iterator(); it.hasNext(); ) {
+            LinkedPosition pos= cast(LinkedPosition) it.next();
+            LinkedPosition localFound= null;
+            for (Iterator it2= fPositions.iterator(); it2.hasNext(); ) {
+                LinkedPosition myPos= cast(LinkedPosition) it2.next();
+                if (myPos.includes(pos)) {
+                    if (found is null)
+                        found= myPos;
+                    else if (found !is myPos)
+                        throw new BadLocationException();
+                    if (localFound is null)
+                        localFound= myPos;
+                }
+            }
+
+            if (localFound !is found)
+                throw new BadLocationException();
+        }
+        return found;
+    }
+
+    /**
+     * Finds the closest position to <code>toFind</code>.
+     *
+     * @param toFind the linked position for which to find the closest position
+     * @return the closest position to <code>toFind</code>.
+     */
+    LinkedPosition getPosition(LinkedPosition toFind) {
+        for (Iterator it= fPositions.iterator(); it.hasNext(); ) {
+            LinkedPosition p= cast(LinkedPosition) it.next();
+            if (p.includes(toFind))
+                return p;
+        }
+        return null;
+    }
+
+    /**
+     * Returns <code>true</code> if <code>offset</code> is contained in any
+     * position in this group.
+     *
+     * @param offset the offset to check
+     * @return <code>true</code> if offset is contained by this group
+     */
+    bool contains(int offset) {
+        for (Iterator it= fPositions.iterator(); it.hasNext(); ) {
+            LinkedPosition pos= cast(LinkedPosition) it.next();
+            if (pos.includes(offset)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether this group contains any positions.
+     *
+     * @return <code>true</code> if this group is empty, <code>false</code> otherwise
+     * @since 3.1
+     */
+    public bool isEmpty() {
+        return fPositions.size() is 0;
+    }
+
+    /**
+     * Returns whether this group contains any positions.
+     *
+     * @return <code>true</code> if this group is empty, <code>false</code> otherwise
+     * @deprecated As of 3.1, replaced by {@link #isEmpty()}
+     */
+    public bool isEmtpy() {
+        return isEmpty();
+    }
+
+    /**
+     * Returns the positions contained in the receiver as an array. The
+     * positions are the actual positions and must not be modified; the array
+     * is a copy of internal structures.
+     *
+     * @return the positions of this group in no particular order
+     */
+    public LinkedPosition[] getPositions() {
+        return arraycast!(LinkedPosition) (fPositions.toArray());
+    }
+
+    /**
+     * Returns <code>true</code> if the receiver contains <code>position</code>.
+     *
+     * @param position the position to check
+     * @return <code>true</code> if the receiver contains <code>position</code>
+     */
+    bool contains(Position position) {
+        for (Iterator it= fPositions.iterator(); it.hasNext(); ) {
+            LinkedPosition p= cast(LinkedPosition) it.next();
+            if (position.opEquals(p))
+                return true;
+        }
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/projection/ChildDocument.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.projection.ChildDocument;
+
+import org.eclipse.jface.text.projection.ProjectionMapping; // packageimport
+import org.eclipse.jface.text.projection.ChildDocumentManager; // packageimport
+import org.eclipse.jface.text.projection.SegmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.Segment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocument; // packageimport
+import org.eclipse.jface.text.projection.FragmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentEvent; // packageimport
+import org.eclipse.jface.text.projection.IMinimalMapping; // packageimport
+import org.eclipse.jface.text.projection.Fragment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionTextStore; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Position;
+
+/**
+ * Implementation of a child document based on
+ * {@link org.eclipse.jface.text.projection.ProjectionDocument}. This class
+ * exists for compatibility reasons.
+ * <p>
+ * Internal class. This class is not intended to be used by clients.</p>
+ *
+ * @since 3.0
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ChildDocument : ProjectionDocument {
+
+    /**
+     * Position reflecting a visible region. The exclusive end offset of the position
+     * is considered being overlapping with the visible region.
+     */
+    static private class VisibleRegion : Position {
+
+        /**
+         * Creates a new visible region.
+         *
+         * @param regionOffset the offset of the region
+         * @param regionLength the length of the region
+         */
+        public this(int regionOffset, int regionLength) {
+            super(regionOffset, regionLength);
+        }
+
+        /**
+         * If <code>regionOffset</code> is the end of the visible region and the <code>regionLength is 0</code>,
+         * the <code>regionOffset</code> is considered overlapping with the visible region.
+         *
+         * @see org.eclipse.jface.text.Position#overlapsWith(int, int)
+         */
+        public bool overlapsWith(int regionOffset, int regionLength) {
+            bool appending= (regionOffset is offset + length) && regionLength is 0;
+            return appending || super.overlapsWith(regionOffset, regionLength);
+        }
+    }
+
+    /**
+     * Creates a new child document.
+     *
+     * @param masterDocument @inheritDoc
+     */
+    public this(IDocument masterDocument) {
+        super(masterDocument);
+    }
+
+    /**
+     * Returns the parent document of this child document.
+     *
+     * @return the parent document of this child document
+     * @see ProjectionDocument#getMasterDocument()
+     */
+    public IDocument getParentDocument() {
+        return getMasterDocument();
+    }
+
+    /**
+     * Sets the parent document range covered by this child document to the
+     * given range.
+     *
+     * @param offset the offset of the range
+     * @param length the length of the range
+     * @throws BadLocationException if the given range is not valid
+     */
+    public void setParentDocumentRange(int offset, int length)  {
+        replaceMasterDocumentRanges(offset, length);
+    }
+
+    /**
+     * Returns the parent document range of this child document.
+     *
+     * @return the parent document range of this child document
+     */
+    public Position getParentDocumentRange() {
+        IRegion coverage= getDocumentInformationMapping().getCoverage();
+        return new VisibleRegion(coverage.getOffset(), coverage.getLength());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/projection/ChildDocumentManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.projection.ChildDocumentManager;
+
+import org.eclipse.jface.text.projection.ProjectionMapping; // packageimport
+import org.eclipse.jface.text.projection.SegmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.Segment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocument; // packageimport
+import org.eclipse.jface.text.projection.FragmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentEvent; // packageimport
+import org.eclipse.jface.text.projection.ChildDocument; // packageimport
+import org.eclipse.jface.text.projection.IMinimalMapping; // packageimport
+import org.eclipse.jface.text.projection.Fragment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionTextStore; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentManager; // packageimport
+
+
+import java.lang.all;
+
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * Implementation of a child document manager based on
+ * {@link org.eclipse.jface.text.projection.ProjectionDocumentManager}. This
+ * class exists for compatibility reasons.
+ * <p>
+ * Internal class. This class is not intended to be used by clients outside
+ * the Platform Text framework.</p>
+ *
+ * @since 3.0
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ChildDocumentManager : ProjectionDocumentManager {
+
+    /*
+     * @see org.eclipse.jface.text.projection.ProjectionDocumentManager#createProjectionDocument(org.eclipse.jface.text.IDocument)
+     */
+    protected ProjectionDocument createProjectionDocument(IDocument master) {
+        return new ChildDocument(master);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/projection/Fragment.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.projection.Fragment;
+
+import org.eclipse.jface.text.projection.ProjectionMapping; // packageimport
+import org.eclipse.jface.text.projection.ChildDocumentManager; // packageimport
+import org.eclipse.jface.text.projection.SegmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.Segment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocument; // packageimport
+import org.eclipse.jface.text.projection.FragmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentEvent; // packageimport
+import org.eclipse.jface.text.projection.ChildDocument; // packageimport
+import org.eclipse.jface.text.projection.IMinimalMapping; // packageimport
+import org.eclipse.jface.text.projection.ProjectionTextStore; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.Position;
+
+
+/**
+ * Internal class. Do not use. Only public for testing purposes.
+ * <p>
+ * A fragment is a range of the master document that has an image, the so called
+ * segment, in a projection document.</p>
+ *
+ * @since 3.0
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class Fragment : Position {
+
+    /**
+     * The corresponding segment of this fragment.
+     */
+    public Segment segment;
+
+    /**
+     * Creates a new fragment covering the given range.
+     *
+     * @param offset the offset of the fragment
+     * @param length the length of the fragment
+     */
+    public this(int offset, int length) {
+        super(offset, length);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/projection/FragmentUpdater.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.projection.FragmentUpdater;
+
+import org.eclipse.jface.text.projection.ProjectionMapping; // packageimport
+import org.eclipse.jface.text.projection.ChildDocumentManager; // packageimport
+import org.eclipse.jface.text.projection.SegmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.Segment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocument; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentEvent; // packageimport
+import org.eclipse.jface.text.projection.ChildDocument; // packageimport
+import org.eclipse.jface.text.projection.IMinimalMapping; // packageimport
+import org.eclipse.jface.text.projection.Fragment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionTextStore; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DefaultPositionUpdater;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.Position;
+
+
+/**
+ * The position updater used to adapt the fragments of a master document. If an
+ * insertion happens at a fragment's offset, the fragment is extended rather
+ * than shifted. Also, the last fragment is extended if an insert operation
+ * happens at the end of the fragment.
+ *
+ * @since 3.0
+ */
+class FragmentUpdater : DefaultPositionUpdater {
+
+    /** Indicates whether the position being updated represents the last fragment. */
+    private bool fIsLast= false;
+
+    /**
+     * Creates the fragment updater for the given category.
+     *
+     * @param fragmentCategory the position category used for managing the fragments of a document
+     */
+    /+protected+/ this(String fragmentCategory) {
+        super(fragmentCategory);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent)
+     */
+    public void update(DocumentEvent event) {
+
+        try {
+
+            Position[] category= event.getDocument().getPositions(getCategory());
+
+            fOffset= event.getOffset();
+            fLength= event.getLength();
+            fReplaceLength= (event.getText() is null ? 0 : event.getText().length());
+            fDocument= event.getDocument();
+
+            for (int i= 0; i < category.length; i++) {
+
+                fPosition= category[i];
+                fIsLast= (i is category.length -1);
+
+                fOriginalPosition.offset= fPosition.offset;
+                fOriginalPosition.length= fPosition.length;
+
+                if (notDeleted())
+                    adaptToReplace();
+            }
+
+        } catch (BadPositionCategoryException x) {
+            // do nothing
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.DefaultPositionUpdater#adaptToInsert()
+     */
+    protected void adaptToInsert() {
+        int myStart= fPosition.offset;
+        int myEnd= Math.max(myStart, fPosition.offset + fPosition.length - (fIsLast || isAffectingReplace() ? 0 : 1));
+
+        if (myEnd < fOffset)
+            return;
+
+        if (fLength <= 0) {
+
+            if (myStart <= fOffset)
+                fPosition.length += fReplaceLength;
+            else
+                fPosition.offset += fReplaceLength;
+
+        } else {
+
+            if (myStart <= fOffset && fOriginalPosition.offset <= fOffset)
+                fPosition.length += fReplaceLength;
+            else
+                fPosition.offset += fReplaceLength;
+        }
+    }
+
+    /**
+     * Returns whether this updater considers any position affected by the given document event. A
+     * position is affected if <code>event</code> {@link Position#overlapsWith(int, int) overlaps}
+     * with it but not if the position is only shifted.
+     *
+     * @param event the event
+     * @return <code>true</code> if there is any affected position, <code>false</code> otherwise
+     */
+    public bool affectsPositions(DocumentEvent event) {
+        IDocument document= event.getDocument();
+        try {
+
+            int index= document.computeIndexInCategory(getCategory(), event.getOffset());
+            Position[] fragments= document.getPositions(getCategory());
+
+            if (0 < index) {
+                Position fragment= fragments[index - 1];
+                if (fragment.overlapsWith(event.getOffset(), event.getLength()))
+                    return true;
+                if (index is fragments.length && fragment.offset + fragment.length is event.getOffset())
+                    return true;
+            }
+
+            if (index < fragments.length) {
+                Position fragment= fragments[index];
+                return fragment.overlapsWith(event.getOffset(), event.getLength());
+            }
+
+        } catch (BadLocationException x) {
+        } catch (BadPositionCategoryException x) {
+        }
+
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/projection/IMinimalMapping.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.projection.IMinimalMapping;
+
+import org.eclipse.jface.text.projection.ProjectionMapping; // packageimport
+import org.eclipse.jface.text.projection.ChildDocumentManager; // packageimport
+import org.eclipse.jface.text.projection.SegmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.Segment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocument; // packageimport
+import org.eclipse.jface.text.projection.FragmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentEvent; // packageimport
+import org.eclipse.jface.text.projection.ChildDocument; // packageimport
+import org.eclipse.jface.text.projection.Fragment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionTextStore; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IRegion;
+
+
+/**
+ * Internal interface for defining the exact subset of
+ * {@link org.eclipse.jface.text.projection.ProjectionMapping} that the
+ * {@link org.eclipse.jface.text.projection.ProjectionTextStore} is allowed to
+ * access.
+ *
+ * @since 3.0
+ */
+interface IMinimalMapping {
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMapping#getCoverage()
+     */
+    IRegion getCoverage();
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginRegion(IRegion)
+     */
+    IRegion toOriginRegion(IRegion region) ;
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginOffset(int)
+     */
+    int toOriginOffset(int offset) ;
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMappingExtension#toExactOriginRegions(IRegion)
+     */
+    IRegion[] toExactOriginRegions(IRegion region) ;
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMappingExtension#getImageLength()
+     */
+    int getImageLength();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/projection/ProjectionDocument.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,921 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.projection.ProjectionDocument;
+
+import org.eclipse.jface.text.projection.ProjectionMapping; // packageimport
+import org.eclipse.jface.text.projection.ChildDocumentManager; // packageimport
+import org.eclipse.jface.text.projection.SegmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.Segment; // packageimport
+import org.eclipse.jface.text.projection.FragmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentEvent; // packageimport
+import org.eclipse.jface.text.projection.ChildDocument; // packageimport
+import org.eclipse.jface.text.projection.IMinimalMapping; // packageimport
+import org.eclipse.jface.text.projection.Fragment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionTextStore; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentManager; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+
+import org.eclipse.jface.text.AbstractDocument;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DefaultLineTracker;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension;
+import org.eclipse.jface.text.IDocumentInformationMapping;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.ILineTracker;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextStore;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
+
+
+/**
+ * A <code>ProjectionDocument</code> represents a projection of its master
+ * document. The contents of a projection document is a sequence of fragments of
+ * the master document, i.e. the projection document can be thought as being
+ * constructed from the master document by not copying the whole master document
+ * but omitting several ranges of the master document.
+ * <p>
+ * The projection document indirectly utilizes its master document as
+ * <code>ITextStore</code> by means of a <code>ProjectionTextStore</code>.
+ * <p>
+ * The content of a projection document can be changed in two ways. Either by a
+ * text replace applied to the master document or the projection document. Or by
+ * changing the projection between the master document and the projection
+ * document. For the latter the two methods <code>addMasterDocumentRange</code>
+ * and <code>removeMasterDocumentRange</code> are provided. For any
+ * manipulation, the projection document sends out a
+ * {@link org.eclipse.jface.text.projection.ProjectionDocumentEvent} describing
+ * the change.
+ * <p>
+ * Clients are not supposed to directly instantiate this class. In order to
+ * obtain a projection document, a
+ * {@link org.eclipse.jface.text.projection.ProjectionDocumentManager}should be
+ * used. This class is not intended to be subclassed outside of its origin
+ * package.</p>
+ *
+ * @since 3.0
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ProjectionDocument : AbstractDocument {
+
+
+    /**
+     * Prefix of the name of the position category used to keep track of the master
+     * document's fragments that correspond to the segments of the projection
+     * document.
+     */
+    private const static String FRAGMENTS_CATEGORY_PREFIX= "__fragmentsCategory"; //$NON-NLS-1$
+
+    /**
+     * Name of the position category used to keep track of the project
+     * document's segments that correspond to the fragments of the master
+     * document.
+     */
+    private const static String SEGMENTS_CATEGORY= "__segmentsCategory"; //$NON-NLS-1$
+
+
+    /** The master document */
+    private IDocument fMasterDocument;
+    /** The master document as document extension */
+    private IDocumentExtension fMasterDocumentExtension;
+    /** The fragments' position category */
+    private String fFragmentsCategory;
+    /** The segment's position category */
+    private String fSegmentsCategory;
+    /** The document event issued by the master document */
+    private DocumentEvent fMasterEvent;
+    /** The document event to be issued by the projection document */
+    private ProjectionDocumentEvent fSlaveEvent;
+    /** The original document event generated by a direct manipulation of this projection document */
+    private DocumentEvent fOriginalEvent;
+    /** Indicates whether the projection document initiated a master document update or not */
+    private bool fIsUpdating= false;
+    /** Indicated whether the projection document is in auto expand mode nor not */
+    private bool fIsAutoExpanding= false;
+    /** The position updater for the segments */
+    private SegmentUpdater fSegmentUpdater;
+    /** The position updater for the fragments */
+    private FragmentUpdater fFragmentsUpdater;
+    /** The projection mapping */
+    private ProjectionMapping fMapping;
+
+    /**
+     * Creates a projection document for the given master document.
+     *
+     * @param masterDocument the master document
+     */
+    public this(IDocument masterDocument) {
+        super();
+
+        fMasterDocument= masterDocument;
+        if ( cast(IDocumentExtension)fMasterDocument )
+            fMasterDocumentExtension= cast(IDocumentExtension) fMasterDocument;
+
+        fSegmentsCategory= SEGMENTS_CATEGORY;
+        fFragmentsCategory= FRAGMENTS_CATEGORY_PREFIX ~ Integer.toString(toHash());
+        fMasterDocument.addPositionCategory(fFragmentsCategory);
+        fFragmentsUpdater= new FragmentUpdater(fFragmentsCategory);
+        fMasterDocument.addPositionUpdater(fFragmentsUpdater);
+
+        fMapping= new ProjectionMapping(masterDocument, fFragmentsCategory, this, fSegmentsCategory);
+
+        ITextStore s= new ProjectionTextStore(masterDocument, fMapping);
+        ILineTracker tracker= new DefaultLineTracker();
+
+        setTextStore(s);
+        setLineTracker(tracker);
+
+        completeInitialization();
+
+        initializeProjection();
+        tracker.set(s.get(0, s.getLength()));
+    }
+
+    /**
+     * Disposes this projection document.
+     */
+    public void dispose() {
+        fMasterDocument.removePositionUpdater(fFragmentsUpdater);
+        try {
+            fMasterDocument.removePositionCategory(fFragmentsCategory);
+        } catch (BadPositionCategoryException x) {
+            // allow multiple dispose calls
+        }
+    }
+
+    private void internalError() {
+        throw new IllegalStateException();
+    }
+
+    /**
+     * Returns the fragments of the master documents.
+     *
+     * @return the fragment of the master document
+     */
+    protected final Position[] getFragments() {
+        try {
+            return fMasterDocument.getPositions(fFragmentsCategory);
+        } catch (BadPositionCategoryException e) {
+            internalError();
+        }
+        // unreachable
+        return null;
+    }
+
+    /**
+     * Returns the segments of this projection document.
+     *
+     * @return the segments of this projection document
+     */
+    protected final Position[] getSegments() {
+        try {
+            return getPositions(fSegmentsCategory);
+        } catch (BadPositionCategoryException e) {
+            internalError();
+        }
+        // unreachable
+        return null;
+    }
+
+    /**
+     * Returns the projection mapping used by this document.
+     *
+     * @return the projection mapping used by this document
+     * @deprecated As of 3.4, replaced by {@link #getDocumentInformationMapping()}
+     */
+    public ProjectionMapping getProjectionMapping(){
+        return fMapping;
+    }
+
+    /**
+     * Returns the projection mapping used by this document.
+     *
+     * @return the projection mapping used by this document
+     * @since 3.4
+     */
+    public IDocumentInformationMapping getDocumentInformationMapping() {
+        return fMapping;
+    }
+
+    /**
+     * Returns the master document of this projection document.
+     *
+     * @return the master document of this projection document
+     */
+    public IDocument getMasterDocument() {
+        return fMasterDocument;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentExtension4#getDefaultLineDelimiter()
+     * @since 3.1
+     */
+    public String getDefaultLineDelimiter() {
+        return TextUtilities.getDefaultLineDelimiter(fMasterDocument);
+    }
+
+    /**
+     * Initializes the projection document from the master document based on
+     * the master's fragments.
+     */
+    private void initializeProjection() {
+
+        try {
+
+            addPositionCategory(fSegmentsCategory);
+            fSegmentUpdater= new SegmentUpdater(fSegmentsCategory);
+            addPositionUpdater(fSegmentUpdater);
+
+            int offset= 0;
+            Position[] fragments= getFragments();
+            for (int i= 0; i < fragments.length; i++) {
+                Fragment fragment= cast(Fragment) fragments[i];
+                Segment segment= new Segment(offset, fragment.getLength());
+                segment.fragment= fragment;
+                addPosition(fSegmentsCategory, segment);
+                offset += fragment.length;
+            }
+
+        } catch (BadPositionCategoryException x) {
+            internalError();
+        } catch (BadLocationException x) {
+            internalError();
+        }
+    }
+
+    /**
+     * Creates a segment for the given fragment at the given position inside the list of segments.
+     *
+     * @param fragment the corresponding fragment
+     * @param index the index in the list of segments
+     * @return the created segment
+     * @throws BadLocationException in case the fragment is invalid
+     * @throws BadPositionCategoryException in case the segment category is invalid
+     */
+    private Segment createSegmentFor(Fragment fragment, int index)  {
+
+        int offset= 0;
+        if (index > 0) {
+            Position[] segments= getSegments();
+            Segment segment= cast(Segment) segments[index - 1];
+            offset= segment.getOffset() + segment.getLength();
+        }
+
+        Segment segment= new Segment(offset, 0);
+        segment.fragment= fragment;
+        fragment.segment= segment;
+        addPosition(fSegmentsCategory, segment);
+        return segment;
+    }
+
+    /**
+     * Adds the given range of the master document to this projection document.
+     *
+     * @param offsetInMaster offset of the master document range
+     * @param lengthInMaster length of the master document range
+     * @param masterDocumentEvent the master document event that causes this
+     *            projection change or <code>null</code> if none
+     * @throws BadLocationException if the given range is invalid in the master
+     *             document
+     */
+    private void internalAddMasterDocumentRange(int offsetInMaster, int lengthInMaster, DocumentEvent masterDocumentEvent)  {
+        if (lengthInMaster is 0)
+            return;
+
+        try {
+
+            Position[] fragments= getFragments();
+            int index= fMasterDocument.computeIndexInCategory(fFragmentsCategory, offsetInMaster);
+
+            Fragment left= null;
+            Fragment right= null;
+
+            if (index < fragments.length) {
+                Fragment fragment= cast(Fragment) fragments[index];
+                if (offsetInMaster is fragment.offset)
+                    if (fragment.length is 0) // the fragment does not overlap - it is a zero-length fragment at the same offset
+                        left= fragment;
+                    else
+                        throw new IllegalArgumentException("overlaps with existing fragment"); //$NON-NLS-1$
+                if (offsetInMaster + lengthInMaster is fragment.offset)
+                    right= fragment;
+            }
+
+            if (0 < index && index <= fragments.length) {
+                Fragment fragment= cast(Fragment) fragments[index - 1];
+                if (fragment.includes(offsetInMaster))
+                    throw new IllegalArgumentException("overlaps with existing fragment"); //$NON-NLS-1$
+                if (fragment.getOffset() + fragment.getLength() is offsetInMaster)
+                    left= fragment;
+            }
+
+            int offsetInSlave= 0;
+            if (index > 0) {
+                Fragment fragment= cast(Fragment) fragments[index - 1];
+                Segment segment= fragment.segment;
+                offsetInSlave= segment.getOffset() + segment.getLength();
+            }
+
+            ProjectionDocumentEvent event= new ProjectionDocumentEvent(this, offsetInSlave, 0, fMasterDocument.get(offsetInMaster, lengthInMaster), offsetInMaster, lengthInMaster, masterDocumentEvent);
+            super.fireDocumentAboutToBeChanged(event);
+
+            // check for neighboring fragment
+            if (left !is null && right !is null) {
+
+                int endOffset= right.getOffset() + right.getLength();
+                left.setLength(endOffset - left.getOffset());
+                left.segment.setLength(left.segment.getLength() + right.segment.getLength());
+
+                removePosition(fSegmentsCategory, right.segment);
+                fMasterDocument.removePosition(fFragmentsCategory, right);
+
+            } else if (left !is null) {
+                int endOffset= offsetInMaster +lengthInMaster;
+                left.setLength(endOffset - left.getOffset());
+                left.segment.markForStretch();
+
+            } else if (right !is null) {
+                right.setOffset(right.getOffset() - lengthInMaster);
+                right.setLength(right.getLength() + lengthInMaster);
+                right.segment.markForStretch();
+
+            } else {
+                // create a new segment
+                Fragment fragment= new Fragment(offsetInMaster, lengthInMaster);
+                fMasterDocument.addPosition(fFragmentsCategory, fragment);
+                Segment segment= createSegmentFor(fragment, index);
+                segment.markForStretch();
+            }
+
+            getTracker().replace(event.getOffset(), event.getLength(), event.getText());
+            super.fireDocumentChanged(event);
+
+        } catch (BadPositionCategoryException x) {
+            internalError();
+        }
+    }
+
+    /**
+     * Finds the fragment of the master document that represents the given range.
+     *
+     * @param offsetInMaster the offset of the range in the master document
+     * @param lengthInMaster the length of the range in the master document
+     * @return the fragment representing the given master document range
+     */
+    private Fragment findFragment(int offsetInMaster, int lengthInMaster) {
+        Position[] fragments= getFragments();
+        for (int i= 0; i < fragments.length; i++) {
+            Fragment f= cast(Fragment) fragments[i];
+            if (f.getOffset() <= offsetInMaster && offsetInMaster + lengthInMaster <= f.getOffset() + f.getLength())
+                return f;
+        }
+        return null;
+    }
+
+    /**
+     * Removes the given range of the master document from this projection
+     * document.
+     *
+     * @param offsetInMaster the offset of the range in the master document
+     * @param lengthInMaster the length of the range in the master document
+     *
+     * @throws BadLocationException if the given range is not valid in the
+     *             master document
+     * @throws IllegalArgumentException if the given range is not projected in
+     *             this projection document or is not completely comprised by
+     *             an existing fragment
+     */
+    private void internalRemoveMasterDocumentRange(int offsetInMaster, int lengthInMaster)  {
+        try {
+
+            IRegion imageRegion= fMapping.toExactImageRegion(new Region(offsetInMaster, lengthInMaster));
+            if (imageRegion is null)
+                throw new IllegalArgumentException(null);
+
+            Fragment fragment= findFragment(offsetInMaster, lengthInMaster);
+            if (fragment is null)
+                throw new IllegalArgumentException(null);
+
+            ProjectionDocumentEvent event= new ProjectionDocumentEvent(this, imageRegion.getOffset(), imageRegion.getLength(), "", offsetInMaster, lengthInMaster); //$NON-NLS-1$
+            super.fireDocumentAboutToBeChanged(event);
+
+            if (fragment.getOffset() is offsetInMaster) {
+                fragment.setOffset(offsetInMaster + lengthInMaster);
+                fragment.setLength(fragment.getLength() - lengthInMaster);
+            } else if (fragment.getOffset() + fragment.getLength() is offsetInMaster + lengthInMaster) {
+                fragment.setLength(fragment.getLength() - lengthInMaster);
+            } else {
+                // split fragment into three fragments, let position updater remove it
+
+                // add fragment for the region to be removed
+                Fragment newFragment= new Fragment(offsetInMaster, lengthInMaster);
+                Segment segment= new Segment(imageRegion.getOffset(), imageRegion.getLength());
+                newFragment.segment= segment;
+                segment.fragment= newFragment;
+                fMasterDocument.addPosition(fFragmentsCategory, newFragment);
+                addPosition(fSegmentsCategory, segment);
+
+                // add fragment for the remainder right of the deleted range in the original fragment
+                int offset= offsetInMaster + lengthInMaster;
+                newFragment= new Fragment(offset, fragment.getOffset() + fragment.getLength() - offset);
+                offset= imageRegion.getOffset() + imageRegion.getLength();
+                segment= new Segment(offset, fragment.segment.getOffset() + fragment.segment.getLength() - offset);
+                newFragment.segment= segment;
+                segment.fragment= newFragment;
+                fMasterDocument.addPosition(fFragmentsCategory, newFragment);
+                addPosition(fSegmentsCategory, segment);
+
+                // adjust length of initial fragment (the left one)
+                fragment.setLength(offsetInMaster - fragment.getOffset());
+                fragment.segment.setLength(imageRegion.getOffset() - fragment.segment.getOffset());
+            }
+
+            getTracker().replace(event.getOffset(), event.getLength(), event.getText());
+            super.fireDocumentChanged(event);
+
+        } catch (BadPositionCategoryException x) {
+            internalError();
+        }
+    }
+
+    /**
+     * Returns the sequence of all master document regions which are contained
+     * in the given master document range and which are not yet part of this
+     * projection document.
+     *
+     * @param offsetInMaster the range offset in the master document
+     * @param lengthInMaster the range length in the master document
+     * @return the sequence of regions which are not yet part of the projection
+     *         document
+     * @throws BadLocationException in case the given range is invalid in the
+     *         master document
+     */
+    public final IRegion[] computeUnprojectedMasterRegions(int offsetInMaster, int lengthInMaster)  {
+
+        IRegion[] fragments= null;
+        IRegion imageRegion= fMapping.toImageRegion(new Region(offsetInMaster, lengthInMaster));
+        if (imageRegion !is null)
+            fragments= fMapping.toExactOriginRegions(imageRegion);
+
+        if (fragments is null || fragments.length is 0)
+            return [ new Region(offsetInMaster, lengthInMaster) ];
+
+        List gaps= new ArrayList();
+
+        IRegion region= fragments[0];
+        if (offsetInMaster < region.getOffset())
+            gaps.add(new Region(offsetInMaster, region.getOffset() - offsetInMaster));
+
+        for (int i= 0; i < fragments.length - 1; i++) {
+            IRegion left= fragments[i];
+            IRegion right= fragments[i + 1];
+            int leftEnd= left.getOffset() + left.getLength();
+            if (leftEnd < right.getOffset())
+                gaps.add(new Region(leftEnd, right.getOffset() - leftEnd));
+        }
+
+        region= fragments[fragments.length - 1];
+        int leftEnd= region.getOffset() + region.getLength();
+        int rightEnd= offsetInMaster + lengthInMaster;
+        if (leftEnd < rightEnd)
+            gaps.add(new Region(leftEnd, rightEnd - leftEnd));
+
+        return arraycast!(IRegion)(gaps.toArray());
+    }
+
+    /**
+     * Returns the first master document region which is contained in the given
+     * master document range and which is not yet part of this projection
+     * document.
+     *
+     * @param offsetInMaster the range offset in the master document
+     * @param lengthInMaster the range length in the master document
+     * @return the first region that is not yet part of the projection document
+     * @throws BadLocationException in case the given range is invalid in the
+     *         master document
+     * @since 3.1
+     */
+    private IRegion computeFirstUnprojectedMasterRegion(int offsetInMaster, int lengthInMaster)  {
+
+        IRegion[] fragments= null;
+        IRegion imageRegion= fMapping.toImageRegion(new Region(offsetInMaster, lengthInMaster));
+        if (imageRegion !is null)
+            fragments= fMapping.toExactOriginRegions(imageRegion);
+
+        if (fragments is null || fragments.length is 0)
+            return new Region(offsetInMaster, lengthInMaster);
+
+        IRegion region= fragments[0];
+        if (offsetInMaster < region.getOffset())
+            return new Region(offsetInMaster, region.getOffset() - offsetInMaster);
+
+        for (int i= 0; i < fragments.length - 1; i++) {
+            IRegion left= fragments[i];
+            IRegion right= fragments[i + 1];
+            int leftEnd= left.getOffset() + left.getLength();
+            if (leftEnd < right.getOffset())
+                return new Region(leftEnd, right.getOffset() - leftEnd);
+        }
+
+        region= fragments[fragments.length - 1];
+        int leftEnd= region.getOffset() + region.getLength();
+        int rightEnd= offsetInMaster + lengthInMaster;
+        if (leftEnd < rightEnd)
+            return new Region(leftEnd, rightEnd - leftEnd);
+
+        return null;
+    }
+
+    /**
+     * Ensures that the given range of the master document is part of this
+     * projection document.
+     *
+     * @param offsetInMaster the offset of the master document range
+     * @param lengthInMaster the length of the master document range
+     * @throws BadLocationException in case the master event is not valid
+     */
+    public void addMasterDocumentRange(int offsetInMaster, int lengthInMaster)  {
+        addMasterDocumentRange(offsetInMaster, lengthInMaster, null);
+    }
+
+    /**
+     * Ensures that the given range of the master document is part of this
+     * projection document.
+     *
+     * @param offsetInMaster the offset of the master document range
+     * @param lengthInMaster the length of the master document range
+     * @param masterDocumentEvent the master document event which causes this
+     *            projection change, or <code>null</code> if none
+     * @throws BadLocationException in case the master event is not valid
+     */
+    private void addMasterDocumentRange(int offsetInMaster, int lengthInMaster, DocumentEvent masterDocumentEvent)  {
+        /*
+         * Calling internalAddMasterDocumentRange may cause other master ranges
+         * to become unfolded, resulting in re-entrant calls to this method. In
+         * order to not add a region twice, we have to compute the next region
+         * to add in every iteration.
+         *
+         * To place an upper bound on the number of iterations, we use the number
+         * of fragments * 2 as the limit.
+         */
+        int limit= Math.max(getFragments().length * 2, 20);
+        while (true) {
+            if (limit-- < 0)
+                throw new IllegalArgumentException("safety loop termination"); //$NON-NLS-1$
+
+            IRegion gap= computeFirstUnprojectedMasterRegion(offsetInMaster, lengthInMaster);
+            if (gap is null)
+                return;
+
+            internalAddMasterDocumentRange(gap.getOffset(), gap.getLength(), masterDocumentEvent);
+        }
+    }
+
+    /**
+     * Ensures that the given range of the master document is not part of this
+     * projection document.
+     *
+     * @param offsetInMaster the offset of the master document range
+     * @param lengthInMaster the length of the master document range
+     * @throws BadLocationException in case the master event is not valid
+     */
+    public void removeMasterDocumentRange(int offsetInMaster, int lengthInMaster)  {
+        IRegion[] fragments= computeProjectedMasterRegions(offsetInMaster, lengthInMaster);
+        if (fragments is null || fragments.length is 0)
+            return;
+
+        for (int i= 0; i < fragments.length; i++) {
+            IRegion fragment= fragments[i];
+            internalRemoveMasterDocumentRange(fragment.getOffset(), fragment.getLength());
+        }
+    }
+
+    /**
+     * Returns the sequence of all master document regions with are contained in the given master document
+     * range and which are part of this projection document. May return <code>null</code> if no such
+     * regions exist.
+     *
+     * @param offsetInMaster the range offset in the master document
+     * @param lengthInMaster the range length in the master document
+     * @return the sequence of regions which are part of the projection document or <code>null</code>
+     * @throws BadLocationException in case the given range is invalid in the master document
+     */
+    public final IRegion[] computeProjectedMasterRegions(int offsetInMaster, int lengthInMaster)  {
+        IRegion imageRegion= fMapping.toImageRegion(new Region(offsetInMaster, lengthInMaster));
+        return imageRegion !is null ? fMapping.toExactOriginRegions(imageRegion) : null;
+    }
+
+    /**
+     * Returns whether this projection is being updated.
+     *
+     * @return <code>true</code> if the document is updating
+     */
+    protected bool isUpdating() {
+        return fIsUpdating;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#replace(int, int, java.lang.String)
+     */
+    public void replace(int offset, int length, String text)  {
+        try {
+            fIsUpdating= true;
+            if (fMasterDocumentExtension !is null)
+                fMasterDocumentExtension.stopPostNotificationProcessing();
+
+            super.replace(offset, length, text);
+
+        } finally {
+            fIsUpdating= false;
+            if (fMasterDocumentExtension !is null)
+                fMasterDocumentExtension.resumePostNotificationProcessing();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocument#set(java.lang.String)
+     */
+    public void set(String text) {
+        try {
+            fIsUpdating= true;
+            if (fMasterDocumentExtension !is null)
+                fMasterDocumentExtension.stopPostNotificationProcessing();
+
+            super.set(text);
+
+        } finally {
+            fIsUpdating= false;
+            if (fMasterDocumentExtension !is null)
+                fMasterDocumentExtension.resumePostNotificationProcessing();
+        }
+    }
+
+    /**
+     * Transforms a document event of the master document into a projection
+     * document based document event.
+     *
+     * @param masterEvent the master document event
+     * @return the slave document event
+     * @throws BadLocationException in case the master event is not valid
+     */
+    private ProjectionDocumentEvent normalize(DocumentEvent masterEvent)  {
+        if (!isUpdating()) {
+            IRegion imageRegion= fMapping.toExactImageRegion(new Region(masterEvent.getOffset(), masterEvent.getLength()));
+            if (imageRegion !is null)
+                return new ProjectionDocumentEvent(this, imageRegion.getOffset(), imageRegion.getLength(), masterEvent.getText(), masterEvent);
+            return null;
+        }
+
+        ProjectionDocumentEvent event= new ProjectionDocumentEvent(this, fOriginalEvent.getOffset(), fOriginalEvent.getLength(), fOriginalEvent.getText(), masterEvent);
+        fOriginalEvent= null;
+        return event;
+    }
+
+    /**
+     * Ensures that when the master event affects this projection document, that the whole region described by the
+     * event is part of this projection document.
+     *
+     * @param masterEvent the master document event
+     * @return <code>true</code> if masterEvent affects this projection document
+     * @throws BadLocationException in case the master event is not valid
+     */
+    protected final bool adaptProjectionToMasterChange(DocumentEvent masterEvent)  {
+        if (!isUpdating() && fFragmentsUpdater.affectsPositions(masterEvent) || fIsAutoExpanding && masterEvent.getLength() > 0) {
+
+            addMasterDocumentRange(masterEvent.getOffset(), masterEvent.getLength(), masterEvent);
+            return true;
+
+        } else if (fMapping.getImageLength() is 0 && masterEvent.getLength() is 0) {
+
+            Position[] fragments= getFragments();
+            if (fragments.length is 0) {
+                // there is no segment in this projection document, thus one must be created
+                // need to bypass the usual infrastructure as the new segment/fragment would be of length 0 and thus the segmentation be not well formed
+                try {
+                    Fragment fragment= new Fragment(0, 0);
+                    fMasterDocument.addPosition(fFragmentsCategory, fragment);
+                    createSegmentFor(fragment, 0);
+                } catch (BadPositionCategoryException x) {
+                    internalError();
+                }
+            }
+        }
+
+        return isUpdating();
+    }
+
+    /**
+     * When called, this projection document is informed about a forthcoming
+     * change of its master document. This projection document checks whether
+     * the master document change affects it and if so informs all document
+     * listeners.
+     *
+     * @param masterEvent the master document event
+     */
+    public void masterDocumentAboutToBeChanged(DocumentEvent masterEvent) {
+        try {
+
+            bool assertNotNull= adaptProjectionToMasterChange(masterEvent);
+            fSlaveEvent= normalize(masterEvent);
+            if (assertNotNull && fSlaveEvent is null)
+                internalError();
+
+            fMasterEvent= masterEvent;
+            if (fSlaveEvent !is null)
+                delayedFireDocumentAboutToBeChanged();
+
+        } catch (BadLocationException e) {
+            internalError();
+        }
+    }
+
+    /**
+     * When called, this projection document is informed about a change of its
+     * master document. If this projection document is affected it informs all
+     * of its document listeners.
+     *
+     * @param masterEvent the master document event
+     */
+    public void masterDocumentChanged(DocumentEvent masterEvent) {
+        if ( !isUpdating() && masterEvent is fMasterEvent) {
+            if (fSlaveEvent !is null) {
+                try {
+                    getTracker().replace(fSlaveEvent.getOffset(), fSlaveEvent.getLength(), fSlaveEvent.getText());
+                    fireDocumentChanged(fSlaveEvent);
+                } catch (BadLocationException e) {
+                    internalError();
+                }
+            } else if (ensureWellFormedSegmentation(masterEvent.getOffset()))
+                fMapping.projectionChanged();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractDocument#fireDocumentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
+     */
+    protected void fireDocumentAboutToBeChanged(DocumentEvent event) {
+        fOriginalEvent= event;
+        // delay it until there is a notification from the master document
+        // at this point, it is expensive to construct the master document information
+    }
+
+    /**
+     * Fires the slave document event as about-to-be-changed event to all registered listeners.
+     */
+    private void delayedFireDocumentAboutToBeChanged() {
+        super.fireDocumentAboutToBeChanged(fSlaveEvent);
+    }
+
+    /**
+     * Ignores the given event and sends the semantically equal slave document event instead.
+     *
+     * @param event the event to be ignored
+     */
+    protected void fireDocumentChanged(DocumentEvent event) {
+        super.fireDocumentChanged(fSlaveEvent);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.AbstractDocument#updateDocumentStructures(org.eclipse.jface.text.DocumentEvent)
+     */
+    protected void updateDocumentStructures(DocumentEvent event) {
+        super.updateDocumentStructures(event);
+        ensureWellFormedSegmentation(computeAnchor(event));
+        fMapping.projectionChanged();
+    }
+
+    private int computeAnchor(DocumentEvent event) {
+        if ( cast(ProjectionDocumentEvent)event ) {
+            ProjectionDocumentEvent slave= cast(ProjectionDocumentEvent) event;
+            Object changeType= slave.getChangeType();
+            if (ProjectionDocumentEvent.CONTENT_CHANGE is changeType) {
+                DocumentEvent master= slave.getMasterEvent();
+                if (master !is null)
+                    return master.getOffset();
+            } else if (ProjectionDocumentEvent.PROJECTION_CHANGE is changeType) {
+                return slave.getMasterOffset();
+            }
+        }
+        return -1;
+    }
+
+    private bool ensureWellFormedSegmentation(int anchorOffset) {
+        bool changed= false;
+        Position[] segments= getSegments();
+        for (int i= 0; i < segments.length; i++) {
+            Segment segment= cast(Segment) segments[i];
+            if (segment.isDeleted() || segment.getLength() is 0) {
+                try {
+                    removePosition(fSegmentsCategory, segment);
+                    fMasterDocument.removePosition(fFragmentsCategory, segment.fragment);
+                    changed= true;
+                } catch (BadPositionCategoryException e) {
+                    internalError();
+                }
+            } else if (i < segments.length - 1) {
+                Segment next= cast(Segment) segments[i + 1];
+                if (next.isDeleted() || next.getLength() is 0)
+                    continue;
+                Fragment fragment= segment.fragment;
+                if (fragment.getOffset() + fragment.getLength() is next.fragment.getOffset()) {
+                    // join fragments and their corresponding segments
+                    segment.setLength(segment.getLength() + next.getLength());
+                    fragment.setLength(fragment.getLength() + next.fragment.getLength());
+                    next.delete_();
+                }
+            }
+        }
+
+        if (changed && anchorOffset !is -1) {
+            Position[] changedSegments= getSegments();
+            if (changedSegments is null || changedSegments.length is 0) {
+                Fragment fragment= new Fragment(anchorOffset, 0);
+                try {
+                    fMasterDocument.addPosition(fFragmentsCategory, fragment);
+                    createSegmentFor(fragment, 0);
+                } catch (BadLocationException e) {
+                    internalError();
+                } catch (BadPositionCategoryException e) {
+                    internalError();
+                }
+            }
+        }
+
+        return changed;
+    }
+
+    /*
+     * @see IDocumentExtension#registerPostNotificationReplace(IDocumentListener, IDocumentExtension.IReplace)
+     */
+    public void registerPostNotificationReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) {
+        if (!isUpdating())
+            throw new UnsupportedOperationException();
+        super.registerPostNotificationReplace(owner, replace);
+    }
+
+    /**
+     * Sets the auto expand mode for this document.
+     *
+     * @param autoExpandMode <code>true</code> if auto-expanding
+     */
+    public void setAutoExpandMode(bool autoExpandMode) {
+        fIsAutoExpanding= autoExpandMode;
+    }
+
+    /**
+     * Replaces all master document ranges with the given master document range.
+     *
+     * @param offsetInMaster the offset in the master document
+     * @param lengthInMaster the length in the master document
+     * @throws BadLocationException if the given range of the master document is not valid
+     */
+    public void replaceMasterDocumentRanges(int offsetInMaster, int lengthInMaster)  {
+        try {
+
+            ProjectionDocumentEvent event= new ProjectionDocumentEvent(this, 0, fMapping.getImageLength(), fMasterDocument.get(offsetInMaster, lengthInMaster), offsetInMaster, lengthInMaster);
+            super.fireDocumentAboutToBeChanged(event);
+
+            Position[] fragments= getFragments();
+            for (int i= 0; i < fragments.length; i++) {
+                Fragment fragment= cast(Fragment) fragments[i];
+                fMasterDocument.removePosition(fFragmentsCategory, fragment);
+                removePosition(fSegmentsCategory, fragment.segment);
+            }
+
+            Fragment fragment= new Fragment(offsetInMaster, lengthInMaster);
+            Segment segment= new Segment(0, 0);
+            segment.fragment= fragment;
+            fragment.segment= segment;
+            fMasterDocument.addPosition(fFragmentsCategory, fragment);
+            addPosition(fSegmentsCategory, segment);
+
+            getTracker().set(fMasterDocument.get(offsetInMaster, lengthInMaster));
+            super.fireDocumentChanged(event);
+
+        } catch (BadPositionCategoryException x) {
+            internalError();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/projection/ProjectionDocumentEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.projection.ProjectionDocumentEvent;
+
+import org.eclipse.jface.text.projection.ProjectionMapping; // packageimport
+import org.eclipse.jface.text.projection.ChildDocumentManager; // packageimport
+import org.eclipse.jface.text.projection.SegmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.Segment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocument; // packageimport
+import org.eclipse.jface.text.projection.FragmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.ChildDocument; // packageimport
+import org.eclipse.jface.text.projection.IMinimalMapping; // packageimport
+import org.eclipse.jface.text.projection.Fragment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionTextStore; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.SlaveDocumentEvent;
+
+
+/**
+ * This event is sent out by an
+ * {@link org.eclipse.jface.text.projection.ProjectionDocument}when it is
+ * manipulated. The manipulation is either a content manipulation or a change of
+ * the projection between the master and the slave. Clients can determine the
+ * type of change by asking the projection document event for its change type
+ * (see {@link #getChangeType()}) and comparing it with the predefined types
+ * {@link #PROJECTION_CHANGE}and {@link #CONTENT_CHANGE}.
+ * <p>
+ * Clients are not supposed to create instances of this class. Instances are
+ * created by {@link org.eclipse.jface.text.projection.ProjectionDocument}
+ * instances. This class is not intended to be subclassed.</p>
+ *
+ * @since 3.0
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ProjectionDocumentEvent : SlaveDocumentEvent {
+
+    /** The change type indicating a projection change */
+    private static Object PROJECTION_CHANGE_;
+    public static Object PROJECTION_CHANGE(){
+        if( PROJECTION_CHANGE_ is null ) {
+            synchronized( ProjectionDocumentEvent.classinfo ) {
+                if( PROJECTION_CHANGE_ is null ) {
+                    PROJECTION_CHANGE_ = new Object();
+                }
+            }
+        }
+        return PROJECTION_CHANGE_;
+    }
+    /** The change type indicating a content change */
+    private static Object CONTENT_CHANGE_;
+    public static Object CONTENT_CHANGE(){
+        if( CONTENT_CHANGE_ is null ) {
+            synchronized( ProjectionDocumentEvent.classinfo ) {
+                if( CONTENT_CHANGE_ is null ) {
+                    CONTENT_CHANGE_ = new Object();
+                }
+            }
+        }
+        return CONTENT_CHANGE_;
+    }
+
+    /** The change type */
+    private Object fChangeType;
+    /** The offset of the change in the master document */
+    private int fMasterOffset= -1;
+    /** The length of the change in the master document */
+    private int fMasterLength= -1;
+
+    /**
+     * Creates a new content change event caused by the given master document
+     * change. Instances created using this constructor return <code>-1</code>
+     * when calling <code>getMasterOffset</code> or
+     * <code>getMasterLength</code>. This information can be obtained by
+     * accessing the master event.
+     *
+     * @param doc the changed projection document
+     * @param offset the offset in the projection document
+     * @param length the length in the projection document
+     * @param text the replacement text
+     * @param masterEvent the original master event
+     */
+    public this(IDocument doc, int offset, int length, String text, DocumentEvent masterEvent) {
+        super(doc, offset, length, text, masterEvent);
+        fChangeType= CONTENT_CHANGE;
+    }
+
+    /**
+     * Creates a new projection change event for the given properties. Instances
+     * created with this constructor return the given master document offset and
+     * length but do not have an associated master document event.
+     *
+     * @param doc the projection document
+     * @param offset the offset in the projection document
+     * @param length the length in the projection document
+     * @param text the replacement text
+     * @param masterOffset the offset in the master document
+     * @param masterLength the length in the master document
+     */
+    public this(IDocument doc, int offset, int length, String text, int masterOffset, int masterLength) {
+        super(doc, offset, length, text, null);
+        fChangeType= PROJECTION_CHANGE;
+        fMasterOffset= masterOffset;
+        fMasterLength= masterLength;
+    }
+
+    /**
+     * Creates a new projection document event for the given properties. The
+     * projection change is caused by a manipulation of the master document. In
+     * order to accommodate the master document change, the projection document
+     * had to change the projection. Instances created with this constructor
+     * return the given master document offset and length and also have an
+     * associated master document event.
+     *
+     * @param doc the projection document
+     * @param offset the offset in the projection document
+     * @param length the length in the projection document
+     * @param text the replacement text
+     * @param masterOffset the offset in the master document
+     * @param masterLength the length in the master document
+     * @param masterEvent the master document event
+     */
+    public this(IDocument doc, int offset, int length, String text, int masterOffset, int masterLength, DocumentEvent masterEvent) {
+        super(doc, offset, length, text, masterEvent);
+        fChangeType= PROJECTION_CHANGE;
+        fMasterOffset= masterOffset;
+        fMasterLength= masterLength;
+    }
+
+    /**
+     * Returns the change type of this event. This is either {@link #PROJECTION_CHANGE} or
+     * {@link #CONTENT_CHANGE}.
+     *
+     * @return the change type of this event
+     */
+    public Object getChangeType() {
+        return fChangeType;
+    }
+
+    /**
+     * Returns the offset of the master document range that has been added or removed in case this
+     * event describes a projection change, otherwise it returns <code>-1</code>.
+     *
+     * @return the master document offset of the projection change or <code>-1</code>
+     */
+    public int getMasterOffset() {
+        return fMasterOffset;
+    }
+
+    /**
+     * Returns the length of the master document range that has been added or removed in case this event
+     * describes a projection changed, otherwise <code>-1</code>.
+     *
+     * @return the master document length of the projection change or <code>-1</code>
+     */
+    public int getMasterLength() {
+        return fMasterLength;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/projection/ProjectionDocumentManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,247 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.projection.ProjectionDocumentManager;
+
+import org.eclipse.jface.text.projection.ProjectionMapping; // packageimport
+import org.eclipse.jface.text.projection.ChildDocumentManager; // packageimport
+import org.eclipse.jface.text.projection.SegmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.Segment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocument; // packageimport
+import org.eclipse.jface.text.projection.FragmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentEvent; // packageimport
+import org.eclipse.jface.text.projection.ChildDocument; // packageimport
+import org.eclipse.jface.text.projection.IMinimalMapping; // packageimport
+import org.eclipse.jface.text.projection.Fragment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionTextStore; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+
+
+
+
+
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentInformationMapping;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.ISlaveDocumentManager;
+import org.eclipse.jface.text.ISlaveDocumentManagerExtension;
+
+
+/**
+ * A <code>ProjectionDocumentManager</code> is one particular implementation
+ * of {@link org.eclipse.jface.text.ISlaveDocumentManager}. This manager
+ * creates so called projection documents (see
+ * {@link org.eclipse.jface.text.projection.ProjectionDocument}as slave
+ * documents for given master documents.
+ * <p>
+ * A projection document represents a particular projection of the master
+ * document and is accordingly adapted to changes of the master document. Vice
+ * versa, the master document is accordingly adapted to changes of its slave
+ * documents. The manager does not maintain any particular management structure
+ * but utilizes mechanisms given by {@link org.eclipse.jface.text.IDocument}
+ * such as position categories and position updaters.
+ * <p>
+ * Clients can instantiate this class. This class is not intended to be
+ * subclassed.</p>
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ProjectionDocumentManager : IDocumentListener, ISlaveDocumentManager, ISlaveDocumentManagerExtension {
+
+    /** Registry for master documents and their projection documents. */
+    private Map fProjectionRegistry;
+
+    this(){
+        fProjectionRegistry= new HashMap();
+    }
+
+    /**
+     * Registers the given projection document for the given master document.
+     *
+     * @param master the master document
+     * @param projection the projection document
+     */
+    private void add(IDocument master, ProjectionDocument projection) {
+        List list= cast(List) fProjectionRegistry.get(cast(Object)master);
+        if (list is null) {
+            list= new ArrayList(1);
+            fProjectionRegistry.put(cast(Object)master, cast(Object)list);
+        }
+        list.add(projection);
+    }
+
+    /**
+     * Unregisters the given projection document from its master.
+     *
+     * @param master the master document
+     * @param projection the projection document
+     */
+    private void remove(IDocument master, ProjectionDocument projection) {
+        List list= cast(List) fProjectionRegistry.get(cast(Object)master);
+        if (list !is null) {
+            list.remove(projection);
+            if (list.size() is 0)
+                fProjectionRegistry.remove(cast(Object)master);
+        }
+    }
+
+    /**
+     * Returns whether the given document is a master document.
+     *
+     * @param master the document
+     * @return <code>true</code> if the given document is a master document known to this manager
+     */
+    private bool hasProjection(IDocument master) {
+        return ( null !is cast(List)fProjectionRegistry.get(cast(Object)master) );
+    }
+
+    /**
+     * Returns an iterator enumerating all projection documents registered for the given document or
+     * <code>null</code> if the document is not a known master document.
+     *
+     * @param master the document
+     * @return an iterator for all registered projection documents or <code>null</code>
+     */
+    private Iterator getProjectionsIterator(IDocument master) {
+        List list= cast(List) fProjectionRegistry.get(cast(Object)master);
+        if (list !is null)
+            return list.iterator();
+        return null;
+    }
+
+    /**
+     * Informs all projection documents of the master document that issued the given document event.
+     *
+     * @param about indicates whether the change is about to happen or happened already
+     * @param masterEvent the document event which will be processed to inform the projection documents
+     */
+    protected void fireDocumentEvent(bool about, DocumentEvent masterEvent) {
+        IDocument master= masterEvent.getDocument();
+        Iterator e= getProjectionsIterator(master);
+        if (e is null)
+            return;
+
+        while (e.hasNext()) {
+            ProjectionDocument document= cast(ProjectionDocument) e.next();
+            if (about)
+                document.masterDocumentAboutToBeChanged(masterEvent);
+            else
+                document.masterDocumentChanged(masterEvent);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
+     */
+    public void documentChanged(DocumentEvent event) {
+        fireDocumentEvent(false, event);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
+     */
+    public void documentAboutToBeChanged(DocumentEvent event) {
+        fireDocumentEvent(true, event);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ISlaveDocumentManager#createMasterSlaveMapping(org.eclipse.jface.text.IDocument)
+     */
+    public IDocumentInformationMapping createMasterSlaveMapping(IDocument slave) {
+        if ( cast(ProjectionDocument)slave ) {
+            ProjectionDocument projectionDocument= cast(ProjectionDocument) slave;
+            return projectionDocument.getDocumentInformationMapping();
+        }
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ISlaveDocumentManager#createSlaveDocument(org.eclipse.jface.text.IDocument)
+     */
+    public IDocument createSlaveDocument(IDocument master) {
+        if (!hasProjection(master))
+            master.addDocumentListener(this);
+        ProjectionDocument slave= createProjectionDocument(master);
+        add(master, slave);
+        return slave;
+    }
+
+    /**
+     * Factory method for projection documents.
+     *
+     * @param master the master document
+     * @return the newly created projection document
+     */
+    protected ProjectionDocument createProjectionDocument(IDocument master) {
+        return new ProjectionDocument(master);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ISlaveDocumentManager#freeSlaveDocument(org.eclipse.jface.text.IDocument)
+     */
+    public void freeSlaveDocument(IDocument slave) {
+        if ( cast(ProjectionDocument)slave ) {
+            ProjectionDocument projectionDocument= cast(ProjectionDocument) slave;
+            IDocument master= projectionDocument.getMasterDocument();
+            remove(master, projectionDocument);
+            projectionDocument.dispose();
+            if (!hasProjection(master))
+                master.removeDocumentListener(this);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ISlaveDocumentManager#getMasterDocument(org.eclipse.jface.text.IDocument)
+     */
+    public IDocument getMasterDocument(IDocument slave) {
+        if ( cast(ProjectionDocument)slave )
+            return (cast(ProjectionDocument) slave).getMasterDocument();
+        return null;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ISlaveDocumentManager#isSlaveDocument(org.eclipse.jface.text.IDocument)
+     */
+    public bool isSlaveDocument(IDocument document) {
+        return ( null !is cast(ProjectionDocument)document );
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ISlaveDocumentManager#setAutoExpandMode(org.eclipse.jface.text.IDocument, bool)
+     */
+    public void setAutoExpandMode(IDocument slave, bool autoExpanding) {
+        if ( cast(ProjectionDocument)slave )
+            (cast(ProjectionDocument) slave).setAutoExpandMode(autoExpanding);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ISlaveDocumentManagerExtension#getSlaveDocuments(org.eclipse.jface.text.IDocument)
+     */
+    public IDocument[] getSlaveDocuments(IDocument master) {
+        List list= cast(List) fProjectionRegistry.get(cast(Object)master);
+        if (list !is null) {
+            return arraycast!(IDocument)(list.toArray());
+        }
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/projection/ProjectionMapping.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,725 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.projection.ProjectionMapping;
+
+import org.eclipse.jface.text.projection.ChildDocumentManager; // packageimport
+import org.eclipse.jface.text.projection.SegmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.Segment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocument; // packageimport
+import org.eclipse.jface.text.projection.FragmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentEvent; // packageimport
+import org.eclipse.jface.text.projection.ChildDocument; // packageimport
+import org.eclipse.jface.text.projection.IMinimalMapping; // packageimport
+import org.eclipse.jface.text.projection.Fragment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionTextStore; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentInformationMapping;
+import org.eclipse.jface.text.IDocumentInformationMappingExtension;
+import org.eclipse.jface.text.IDocumentInformationMappingExtension2;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+
+
+/**
+ * Internal class. Do not use. Only public for testing purposes.
+ * <p>
+ * Implementation of {@link org.eclipse.jface.text.IDocumentInformationMapping}
+ * for the projection mapping between a master and a slave document.
+ *
+ * @since 3.0
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ProjectionMapping : IDocumentInformationMapping , IDocumentInformationMappingExtension, IDocumentInformationMappingExtension2, IMinimalMapping {
+
+    private static const int LEFT=  -1;
+    private static const int NONE=   0;
+    private static const int RIGHT= +1;
+
+    /** The master document */
+    private IDocument fMasterDocument;
+    /** The position category used to manage the projection fragments inside the master document */
+    private String fFragmentsCategory;
+    /** The projection document */
+    private IDocument fSlaveDocument;
+    /** The position category to manage the projection segments inside the slave document. */
+    private String fSegmentsCategory;
+    /** Cached segments */
+    private Position[] fCachedSegments;
+    /** Cached fragments */
+    private Position[] fCachedFragments;
+
+    /**
+     * Creates a new mapping between the given parent document and the given projection document.
+     *
+     * @param masterDocument the master document
+     * @param fragmentsCategory the position category of the parent document used to manage the projected regions
+     * @param slaveDocument the slave document
+     * @param segmentsCategory the position category of the projection document used to manage the fragments
+     */
+    public this(IDocument masterDocument, String fragmentsCategory, IDocument slaveDocument, String segmentsCategory) {
+        fMasterDocument= masterDocument;
+        fFragmentsCategory= fragmentsCategory;
+        fSlaveDocument= slaveDocument;
+        fSegmentsCategory= segmentsCategory;
+    }
+
+    /**
+     * Notifies this projection mapping that there was a projection change.
+     */
+    public void projectionChanged() {
+        fCachedSegments= null;
+        fCachedFragments= null;
+    }
+
+    private Position[] getSegments() {
+        if (fCachedSegments is null) {
+            try {
+                fCachedSegments= fSlaveDocument.getPositions(fSegmentsCategory);
+            } catch (BadPositionCategoryException e) {
+                return new Position[0];
+            }
+        }
+        return fCachedSegments;
+    }
+
+    private Position[] getFragments() {
+        if (fCachedFragments is null) {
+            try {
+                fCachedFragments= fMasterDocument.getPositions(fFragmentsCategory);
+            } catch (BadPositionCategoryException e) {
+                return new Position[0];
+            }
+        }
+        return fCachedFragments;
+    }
+
+    private int findSegmentIndex(int offset)  {
+        Position[] segments= getSegments();
+        if (segments.length is 0) {
+            if (offset > 0)
+                throw new BadLocationException();
+            return -1;
+        }
+
+        try {
+            int index= fSlaveDocument.computeIndexInCategory(fSegmentsCategory, offset);
+            if (index is segments.length && offset > exclusiveEnd(segments[index-1]))
+                throw new BadLocationException();
+
+            if (index < segments.length && offset is segments[index].offset)
+                return index;
+
+            if (index > 0)
+                index--;
+
+            return index;
+
+        } catch (BadPositionCategoryException e) {
+            throw new IllegalStateException();
+        }
+    }
+
+    private Segment findSegment(int offset)  {
+
+        checkImageOffset(offset);
+
+        int index= findSegmentIndex(offset);
+        if (index is -1) {
+
+            Segment s= new Segment(0, 0);
+            Fragment f= new Fragment(0, 0);
+            s.fragment= f;
+            f.segment= s;
+            return s;
+        }
+
+        Position[] segments= getSegments();
+        return cast(Segment) segments[index];
+    }
+
+    /**
+     * Computes the fragment index given an origin offset. Returns the index of
+     * the fragment that contains <code>offset</code>, or <code>-1</code>
+     * if no fragment contains <code>offset</code>.
+     * <p>
+     * If <code>extensionDirection</code> is set to <code>RIGHT</code> or
+     * <code>LEFT</code>, the next fragment in that direction is returned if
+     * there is no fragment containing <code>offset</code>. Note that if
+     * <code>offset</code> occurs before any fragment and
+     * <code>extensionDirection</code> is <code>LEFT</code>,
+     * <code>-1</code> is also returned. The same applies for an offset after
+     * the last fragment and <code>extensionDirection</code> set to
+     * <code>RIGHT</code>.
+     * </p>
+     *
+     * @param offset an origin offset
+     * @param extensionDirection the direction in which to extend the search, or
+     *        <code>NONE</code>
+     * @return the index of the fragment containing <code>offset</code>, or
+     *         <code>-1</code>
+     * @throws BadLocationException if the index is not valid on the master
+     *         document
+     */
+    private int findFragmentIndex(int offset, int extensionDirection)  {
+        try {
+
+            Position[] fragments= getFragments();
+            if (fragments.length is 0)
+                return -1;
+
+            int index= fMasterDocument.computeIndexInCategory(fFragmentsCategory, offset);
+
+            if (index < fragments.length && offset is fragments[index].offset)
+                return index;
+
+            if (0 < index && index <= fragments.length && fragments[index - 1].includes(offset))
+                return index - 1;
+
+            switch (extensionDirection) {
+                case LEFT:
+                    return index - 1;
+                case RIGHT:
+                    if (index < fragments.length)
+                        return index;
+                default:
+            }
+
+            return -1;
+
+        } catch (BadPositionCategoryException e) {
+            throw new IllegalStateException();
+        }
+    }
+
+    private Fragment findFragment(int offset)  {
+        checkOriginOffset(offset);
+
+        int index= findFragmentIndex(offset, NONE);
+        Position[] fragments= getFragments();
+        if (index is -1) {
+            if (fragments.length > 0) {
+                Fragment last= cast(Fragment) fragments[fragments.length - 1];
+                if (exclusiveEnd(last) is offset)
+                    return last;
+            }
+            return null;
+        }
+        return cast(Fragment) fragments[index];
+    }
+
+    /**
+     * Returns the image region for <code>originRegion</code>.
+     *
+     * @param originRegion the region to get the image for
+     * @param exact if <code>true</code>, the begin and end offsets of
+     *        <code>originRegion</code> must be projected, otherwise
+     *        <code>null</code> is returned. If <code>false</code>, the
+     *        begin and end range that is not visible is simply clipped.
+     * @param takeClosestImage if <code>false</code>, <code>null</code> is
+     *        returned if <code>originRegion</code> is completely invisible.
+     *        If <code>true</code>, the zero-length region is returned that
+     *        "covers" the hidden origin region
+     * @return the image region of <code>originRegion</code>
+     * @throws BadLocationException if the region is not a valid origin region
+     */
+    private IRegion toImageRegion(IRegion originRegion, bool exact, bool takeClosestImage)  {
+        if (originRegion.getLength() is 0 && !takeClosestImage) {
+            int imageOffset= toImageOffset(originRegion.getOffset());
+            return imageOffset is -1 ? null : new Region(imageOffset, 0);
+        }
+
+        Fragment[] fragments= findFragments(originRegion, exact, takeClosestImage);
+        if (fragments is null) {
+            if (takeClosestImage) {
+                // originRegion may before the first or after the last fragment
+                Position[] allFragments= getFragments();
+                if (allFragments.length > 0) {
+                    // before the first
+                    if (exclusiveEnd(originRegion) <= allFragments[0].getOffset())
+                        return new Region(0, 0);
+                    // after last
+                    Position last= allFragments[allFragments.length - 1];
+                    if (originRegion.getOffset() >= exclusiveEnd(last))
+                        return new Region(exclusiveEnd((cast(Fragment) last).segment), 0);
+                }
+                return new Region(0, 0);
+            }
+            return null;
+        }
+
+        int imageOffset, exclusiveImageEndOffset;
+
+        // translate start offset
+        int relative= originRegion.getOffset() - fragments[0].getOffset();
+        if (relative < 0) {
+            Assert.isTrue(!exact);
+            relative= 0;
+        }
+        imageOffset= fragments[0].segment.getOffset() + relative;
+
+        // translate end offset
+        relative= exclusiveEnd(originRegion) - fragments[1].getOffset();
+        if (relative > fragments[1].getLength()) {
+            Assert.isTrue(!exact);
+            relative= fragments[1].getLength();
+        }
+        exclusiveImageEndOffset= fragments[1].segment.getOffset() + relative;
+
+        return new Region(imageOffset, exclusiveImageEndOffset - imageOffset);
+    }
+
+    /**
+     * Returns the two fragments containing the begin and end offsets of
+     * <code>originRegion</code>.
+     *
+     * @param originRegion the region to get the fragments for
+     * @param exact if <code>true</code>, only the fragments that contain the
+     *        begin and end offsets are returned; if <code>false</code>, the
+     *        first fragment after the begin offset and the last fragment before
+     *        the end offset are returned if the offsets are not projected
+     * @param takeClosestImage if <code>true</code>, the method will return
+     *        fragments also if <code>originRegion</code> completely lies in
+     *        an unprojected region.
+     * @return the two fragments containing the begin and end offset of
+     *         <code>originRegion</code>, or <code>null</code> if these do
+     *         not exist
+     * @throws BadLocationException if the region is not a valid origin region
+     */
+    private Fragment[] findFragments(IRegion originRegion, bool exact, bool takeClosestImage)  {
+        Position[] fragments= getFragments();
+        if (fragments.length is 0)
+            return null;
+
+        checkOriginRegion(originRegion);
+
+        int startFragmentIdx= findFragmentIndex(originRegion.getOffset(), exact ? NONE : RIGHT);
+        if (startFragmentIdx is -1)
+            return null;
+
+        int endFragmentIdx= findFragmentIndex(inclusiveEnd(originRegion), exact ? NONE : LEFT);
+        if (!takeClosestImage && startFragmentIdx > endFragmentIdx || endFragmentIdx is -1)
+            return null;
+
+        Fragment[] result= [cast(Fragment) fragments[startFragmentIdx], cast(Fragment) fragments[endFragmentIdx]];
+        return result;
+    }
+
+    private IRegion createOriginStartRegion(Segment image, int offsetShift) {
+        return new Region(image.fragment.getOffset() + offsetShift, image.fragment.getLength() - offsetShift);
+    }
+
+    private IRegion createOriginRegion(Segment image) {
+        return new Region(image.fragment.getOffset(), image.fragment.getLength());
+    }
+
+    private IRegion createOriginEndRegion(Segment image, int lengthReduction) {
+        return new Region(image.fragment.getOffset(), image.fragment.getLength() - lengthReduction);
+    }
+
+    private IRegion createImageStartRegion(Fragment origin, int offsetShift) {
+        int shift= offsetShift > 0 ? offsetShift : 0;
+        return new Region(origin.segment.getOffset() + shift, origin.segment.getLength() - shift);
+    }
+
+    private IRegion createImageRegion(Fragment origin) {
+        return new Region(origin.segment.getOffset(), origin.segment.getLength());
+    }
+
+    private IRegion createImageEndRegion(Fragment origin, int lengthReduction) {
+        int reduction= lengthReduction > 0 ? lengthReduction : 0;
+        return new Region(origin.segment.getOffset(), origin.segment.getLength() - reduction);
+    }
+
+    private IRegion createOriginStartRegion(Fragment origin, int offsetShift) {
+        int shift= offsetShift > 0 ? offsetShift : 0;
+        return new Region(origin.getOffset() + shift, origin.getLength() - shift);
+    }
+
+    private IRegion createOriginRegion(Fragment origin) {
+        return new Region(origin.getOffset(), origin.getLength());
+    }
+
+    private IRegion createOriginEndRegion(Fragment origin, int lengthReduction) {
+        int reduction= lengthReduction > 0 ? lengthReduction : 0;
+        return new Region(origin.getOffset(), origin.getLength() - reduction);
+    }
+
+    private IRegion getIntersectingRegion(IRegion left, IRegion right) {
+        int offset= Math.max(left.getOffset(), right.getOffset());
+        int exclusiveEndOffset= Math.min(exclusiveEnd(left), exclusiveEnd(right));
+        if (exclusiveEndOffset < offset)
+            return null;
+        return new Region(offset, exclusiveEndOffset - offset);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMapping#getCoverage()
+     */
+    public IRegion getCoverage() {
+        Position[] fragments= getFragments();
+        if (fragments !is null && fragments.length > 0) {
+            Position first=fragments[0];
+            Position last= fragments[fragments.length -1];
+            return  new Region(first.offset, exclusiveEnd(last) - first.offset);
+        }
+        return new Region(0, 0);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginOffset(int)
+     */
+    public int toOriginOffset(int imageOffset)  {
+        Segment segment= findSegment(imageOffset);
+        int relative= imageOffset - segment.offset;
+        return segment.fragment.offset + relative;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginRegion(org.eclipse.jface.text.IRegion)
+     */
+    public IRegion toOriginRegion(IRegion imageRegion)  {
+        int imageOffset= imageRegion.getOffset();
+        int imageLength= imageRegion.getLength();
+
+        if (imageLength is 0) {
+            if (imageOffset is 0) {
+                Position[] fragments= getFragments();
+                if (fragments.length is 0 || (fragments.length is 1 && fragments[0].getOffset() is 0 && fragments[0].getLength() is 0))
+                    return new Region(0, fMasterDocument.getLength());
+            }
+            return new Region(toOriginOffset(imageOffset), 0);
+        }
+
+        int originOffset= toOriginOffset(imageOffset);
+        int inclusiveImageEndOffset= imageOffset + imageLength -1;
+        int inclusiveOriginEndOffset= toOriginOffset(inclusiveImageEndOffset);
+
+        return new Region(originOffset, (inclusiveOriginEndOffset + 1) - originOffset);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginLines(int)
+     */
+    public IRegion toOriginLines(int imageLine)  {
+        IRegion imageRegion= fSlaveDocument.getLineInformation(imageLine);
+        IRegion originRegion= toOriginRegion(imageRegion);
+
+        int originStartLine= fMasterDocument.getLineOfOffset(originRegion.getOffset());
+        if (originRegion.getLength() is 0)
+            return new Region(originStartLine, 1);
+
+        int originEndLine= fMasterDocument.getLineOfOffset(inclusiveEnd(originRegion));
+        return new Region(originStartLine, (originEndLine + 1) - originStartLine);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginLine(int)
+     */
+    public int toOriginLine(int imageLine)  {
+        IRegion lines= toOriginLines(imageLine);
+        return (lines.getLength() > 1 ? -1 : lines.getOffset());
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMapping#toImageOffset(int)
+     */
+    public int toImageOffset(int originOffset)  {
+        Fragment fragment= findFragment(originOffset);
+        if (fragment !is null) {
+            int relative= originOffset - fragment.offset;
+            return fragment.segment.offset + relative;
+        }
+        return -1;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMappingExtension#toExactImageRegion(org.eclipse.jface.text.IRegion)
+     */
+    public IRegion toExactImageRegion(IRegion originRegion)  {
+        return toImageRegion(originRegion, true, false);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMapping#toImageRegion(org.eclipse.jface.text.IRegion)
+     */
+    public IRegion toImageRegion(IRegion originRegion)  {
+        return toImageRegion(originRegion, false, false);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMappingExtension2#toClosestImageRegion(org.eclipse.jface.text.IRegion)
+     * @since 3.1
+     */
+    public IRegion toClosestImageRegion(IRegion originRegion)  {
+        return toImageRegion(originRegion, false, true);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMapping#toImageLine(int)
+     */
+    public int toImageLine(int originLine)  {
+        IRegion originRegion= fMasterDocument.getLineInformation(originLine);
+        IRegion imageRegion= toImageRegion(originRegion);
+        if (imageRegion is null) {
+            int imageOffset= toImageOffset(originRegion.getOffset());
+            if (imageOffset > -1)
+                imageRegion= new Region(imageOffset, 0);
+            else
+                return -1;
+        }
+
+        int startLine= fSlaveDocument.getLineOfOffset(imageRegion.getOffset());
+        if (imageRegion.getLength() is 0)
+            return startLine;
+
+        int endLine= fSlaveDocument.getLineOfOffset(imageRegion.getOffset() + imageRegion.getLength());
+        if (endLine !is startLine)
+            throw new IllegalStateException();
+
+        return startLine;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMapping#toClosestImageLine(int)
+     */
+    public int toClosestImageLine(int originLine)  {
+        try {
+
+            int imageLine= toImageLine(originLine);
+            if (imageLine > -1)
+                return imageLine;
+
+            Position[] fragments= getFragments();
+            if (fragments.length is 0)
+                return -1;
+
+            IRegion originLineRegion= fMasterDocument.getLineInformation(originLine);
+            int index= fMasterDocument.computeIndexInCategory(fFragmentsCategory, originLineRegion.getOffset());
+
+            if (0 < index && index < fragments.length) {
+                Fragment left= cast(Fragment) fragments[index - 1];
+                int leftDistance= originLineRegion.getOffset() - (exclusiveEnd(left));
+                Fragment right= cast(Fragment) fragments[index];
+                int rightDistance= right.getOffset() - (exclusiveEnd(originLineRegion));
+
+                if (leftDistance <= rightDistance)
+                    originLine= fMasterDocument.getLineOfOffset(left.getOffset() + Math.max(left.getLength() - 1, 0));
+                else
+                    originLine= fMasterDocument.getLineOfOffset(right.getOffset());
+
+            } else if (index is 0) {
+                Fragment right= cast(Fragment) fragments[index];
+                originLine= fMasterDocument.getLineOfOffset(right.getOffset());
+            } else if (index is fragments.length) {
+                Fragment left= cast(Fragment) fragments[index - 1];
+                originLine= fMasterDocument.getLineOfOffset(exclusiveEnd(left));
+            }
+
+            return toImageLine(originLine);
+
+        } catch (BadPositionCategoryException x) {
+        }
+
+        return -1;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMappingExtension#toExactOriginRegions(org.eclipse.jface.text.IRegion)
+     */
+    public IRegion[] toExactOriginRegions(IRegion imageRegion)  {
+
+        if (imageRegion.getLength() is 0)
+            return [ new Region(toOriginOffset(imageRegion.getOffset()), 0) ];
+
+        int endOffset= exclusiveEnd(imageRegion);
+        Position[] segments= getSegments();
+        int firstIndex= findSegmentIndex(imageRegion.getOffset());
+        int lastIndex= findSegmentIndex(endOffset - 1);
+
+        int resultLength= lastIndex - firstIndex + 1;
+        IRegion[] result= new IRegion[resultLength];
+
+        // first
+        result[0]= createOriginStartRegion(cast(Segment) segments[firstIndex], imageRegion.getOffset() - segments[firstIndex].getOffset());
+        // middles
+        for (int i= 1; i < resultLength - 1; i++)
+            result[i]= createOriginRegion(cast(Segment) segments[firstIndex + i]);
+        // last
+        Segment last= cast(Segment) segments[lastIndex];
+        int segmentEndOffset= exclusiveEnd(last);
+        IRegion lastRegion= createOriginEndRegion(last, segmentEndOffset - endOffset);
+        if (resultLength > 1) {
+            // first !is last
+            result[resultLength - 1]= lastRegion;
+        } else {
+            // merge first and last
+            IRegion intersection= getIntersectingRegion(result[0], lastRegion);
+            if (intersection is null)
+                result= new IRegion[0];
+            else
+                result[0]= intersection;
+        }
+
+        return result;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMappingExtension#getImageLength()
+     */
+    public int getImageLength() {
+        Position[] segments= getSegments();
+        int length= 0;
+        for (int i= 0; i < segments.length; i++)
+            length += segments[i].length;
+        return length;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMappingExtension#toExactImageRegions(org.eclipse.jface.text.IRegion)
+     */
+    public IRegion[] toExactImageRegions(IRegion originRegion)  {
+
+        int offset= originRegion.getOffset();
+        if (originRegion.getLength() is 0) {
+            int imageOffset= toImageOffset(offset);
+            return imageOffset > -1 ? [ cast(IRegion) new Region(imageOffset, 0) ] : null;
+        }
+
+        int endOffset= exclusiveEnd(originRegion);
+        Position[] fragments= getFragments();
+        int firstIndex= findFragmentIndex(offset, RIGHT);
+        int lastIndex= findFragmentIndex(endOffset - 1, LEFT);
+
+        if (firstIndex is -1 || firstIndex > lastIndex)
+            return null;
+
+        int resultLength= lastIndex - firstIndex + 1;
+        IRegion[] result= new IRegion[resultLength];
+
+        // first
+        result[0]= createImageStartRegion(cast(Fragment) fragments[firstIndex], offset - fragments[firstIndex].getOffset());
+        // middles
+        for (int i= 1; i < resultLength - 1; i++)
+            result[i]= createImageRegion(cast(Fragment) fragments[firstIndex + i]);
+        // last
+        Fragment last= cast(Fragment) fragments[lastIndex];
+        int fragmentEndOffset= exclusiveEnd(last);
+        IRegion lastRegion= createImageEndRegion(last, fragmentEndOffset - endOffset);
+        if (resultLength > 1) {
+            // first !is last
+            result[resultLength - 1]= lastRegion;
+        } else {
+            // merge first and last
+            IRegion intersection= getIntersectingRegion(result[0], lastRegion);
+            if (intersection is null)
+                return null;
+            result[0]= intersection;
+        }
+
+        return result;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentInformationMappingExtension#getExactCoverage(org.eclipse.jface.text.IRegion)
+     */
+    public IRegion[] getExactCoverage(IRegion originRegion)  {
+
+        int originOffset= originRegion.getOffset();
+        int originLength= originRegion.getLength();
+
+        if (originLength is 0) {
+            int imageOffset= toImageOffset(originOffset);
+            return imageOffset > -1 ? [ cast(IRegion)new Region(originOffset, 0) ] : null;
+        }
+
+        int endOffset= originOffset + originLength;
+        Position[] fragments= getFragments();
+        int firstIndex= findFragmentIndex(originOffset, RIGHT);
+        int lastIndex= findFragmentIndex(endOffset - 1, LEFT);
+
+        if (firstIndex is -1 || firstIndex > lastIndex)
+            return null;
+
+        int resultLength= lastIndex - firstIndex + 1;
+        IRegion[] result= new IRegion[resultLength];
+
+        // first
+        result[0]= createOriginStartRegion(cast(Fragment) fragments[firstIndex], originOffset - fragments[firstIndex].getOffset());
+        // middles
+        for (int i= 1; i < resultLength - 1; i++)
+            result[i]= createOriginRegion(cast(Fragment) fragments[firstIndex + i]);
+        // last
+        Fragment last= cast(Fragment) fragments[lastIndex];
+        int fragmentEndOffset= exclusiveEnd(last);
+        IRegion lastRegion= createOriginEndRegion(last, fragmentEndOffset - endOffset);
+        if (resultLength > 1) {
+            // first !is last
+            result[resultLength - 1]= lastRegion;
+        } else {
+            // merge first and last
+            IRegion intersection= getIntersectingRegion(result[0], lastRegion);
+            if (intersection is null)
+                return null;
+            result[0]= intersection;
+        }
+
+        return result;
+    }
+
+    private final void checkOriginRegion(IRegion originRegion)  {
+        int offset= originRegion.getOffset();
+        int endOffset= inclusiveEnd(originRegion);
+        int max= fMasterDocument.getLength();
+        if (offset < 0 || offset > max || endOffset < 0 || endOffset > max)
+            throw new BadLocationException();
+    }
+
+    private final void checkOriginOffset(int originOffset)  {
+        if (originOffset < 0 || originOffset > fMasterDocument.getLength())
+            throw new BadLocationException();
+    }
+
+    private final void checkImageOffset(int imageOffset)  {
+        if (imageOffset < 0 || imageOffset > getImageLength())
+            throw new BadLocationException();
+    }
+
+    private final int exclusiveEnd(Position position) {
+        return position.offset + position.length;
+    }
+
+    private final int exclusiveEnd(IRegion region) {
+        return region.getOffset() + region.getLength();
+    }
+
+    private final int inclusiveEnd(IRegion region) {
+        int length= region.getLength();
+        if (length is 0)
+            return region.getOffset();
+        return region.getOffset() + length - 1;
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/projection/ProjectionTextStore.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.projection.ProjectionTextStore;
+
+import org.eclipse.jface.text.projection.ProjectionMapping; // packageimport
+import org.eclipse.jface.text.projection.ChildDocumentManager; // packageimport
+import org.eclipse.jface.text.projection.SegmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.Segment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocument; // packageimport
+import org.eclipse.jface.text.projection.FragmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentEvent; // packageimport
+import org.eclipse.jface.text.projection.ChildDocument; // packageimport
+import org.eclipse.jface.text.projection.IMinimalMapping; // packageimport
+import org.eclipse.jface.text.projection.Fragment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextStore;
+import org.eclipse.jface.text.Region;
+
+
+/**
+ * A text store representing the projection defined by the given document
+ * information mapping.
+ *
+ * @since 3.0
+ */
+class ProjectionTextStore : ITextStore {
+
+    /**
+     * Implementation of {@link IRegion} that can be reused
+     * by setting the offset and the length.
+     */
+    private static class ReusableRegion : IRegion {
+
+        private int fOffset;
+        private int fLength;
+
+        /*
+         * @see org.eclipse.jface.text.IRegion#getLength()
+         */
+        public int getLength() {
+            return fLength;
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IRegion#getOffset()
+         */
+        public int getOffset() {
+            return fOffset;
+        }
+
+        /**
+         * Updates this region.
+         *
+         * @param offset the new offset
+         * @param length the new length
+         */
+        public void update(int offset, int length) {
+            fOffset= offset;
+            fLength= length;
+        }
+    }
+
+    /** The master document */
+    private IDocument fMasterDocument;
+    /** The document information mapping */
+    private IMinimalMapping fMapping;
+    /** Internal region used for querying the mapping. */
+    private ReusableRegion fReusableRegion;
+
+
+    /**
+     * Creates a new projection text store for the given master document and
+     * the given document information mapping.
+     *
+     * @param masterDocument the master document
+     * @param mapping the document information mapping
+     */
+    public this(IDocument masterDocument, IMinimalMapping mapping) {
+        fReusableRegion= new ReusableRegion();
+        fMasterDocument= masterDocument;
+        fMapping= mapping;
+    }
+
+    private void internalError() {
+        throw new IllegalStateException();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#set(java.lang.String)
+     */
+    public void set(String contents) {
+
+        IRegion masterRegion= fMapping.getCoverage();
+        if (masterRegion is null)
+            internalError();
+
+        try {
+            fMasterDocument.replace(masterRegion.getOffset(), masterRegion.getLength(), contents);
+        } catch (BadLocationException e) {
+            internalError();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#replace(int, int, java.lang.String)
+     */
+    public void replace(int offset, int length, String text) {
+        fReusableRegion.update(offset, length);
+        try {
+            IRegion masterRegion= fMapping.toOriginRegion(fReusableRegion);
+            fMasterDocument.replace(masterRegion.getOffset(), masterRegion.getLength(), text);
+        } catch (BadLocationException e) {
+            internalError();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#getLength()
+     */
+    public int getLength() {
+        return fMapping.getImageLength();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ITextStore#get(int)
+     */
+    public char get(int offset) {
+        try {
+            int originOffset= fMapping.toOriginOffset(offset);
+            return fMasterDocument.getChar(originOffset);
+        } catch (BadLocationException e) {
+            internalError();
+        }
+
+        // unreachable
+        return cast(wchar) 0;
+    }
+
+    /*
+     * @see ITextStore#get(int, int)
+     */
+    public String get(int offset, int length) {
+        try {
+            IRegion[] fragments= fMapping.toExactOriginRegions(new Region(offset, length));
+            StringBuffer buffer= new StringBuffer();
+            for (int i= 0; i < fragments.length; i++) {
+                IRegion fragment= fragments[i];
+                buffer.append(fMasterDocument.get(fragment.getOffset(), fragment.getLength()));
+            }
+            return buffer.toString();
+        } catch (BadLocationException e) {
+            internalError();
+        }
+
+        // unreachable
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/projection/Segment.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.projection.Segment;
+
+import org.eclipse.jface.text.projection.ProjectionMapping; // packageimport
+import org.eclipse.jface.text.projection.ChildDocumentManager; // packageimport
+import org.eclipse.jface.text.projection.SegmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocument; // packageimport
+import org.eclipse.jface.text.projection.FragmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentEvent; // packageimport
+import org.eclipse.jface.text.projection.ChildDocument; // packageimport
+import org.eclipse.jface.text.projection.IMinimalMapping; // packageimport
+import org.eclipse.jface.text.projection.Fragment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionTextStore; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.Position;
+
+
+/**
+ * Internal class. Do not use. Only public for testing purposes.
+ * <p>
+ * A segment is the image of a master document fragment in a projection
+ * document.
+ *
+ * @since 3.0
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class Segment : Position {
+
+    /** The corresponding fragment for this segment. */
+    public Fragment fragment;
+    /** A flag indicating that the segment updater should stretch this segment when a change happens at its boundaries. */
+    public bool isMarkedForStretch_;
+    /** A flag indicating that the segment updater should shift this segment when a change happens at its boundaries. */
+    public bool isMarkedForShift_;
+
+    /**
+     * Creates a new segment covering the given range.
+     *
+     * @param offset the offset of the segment
+     * @param length the length of the segment
+     */
+    public this(int offset, int length) {
+        super(offset, length);
+    }
+
+    /**
+     * Sets the stretching flag.
+     */
+    public void markForStretch() {
+        isMarkedForStretch_= true;
+    }
+
+    /**
+     * Returns <code>true</code> if the stretching flag is set, <code>false</code> otherwise.
+     * @return <code>true</code> if the stretching flag is set, <code>false</code> otherwise
+     */
+    public bool isMarkedForStretch() {
+        return isMarkedForStretch_;
+    }
+    public bool isMarkedForStretch(bool v) {
+        isMarkedForStretch_ = v;
+        return isMarkedForStretch();
+    }
+
+    /**
+     * Sets the shifting flag.
+     */
+    public void markForShift() {
+        isMarkedForShift_= true;
+    }
+
+    /**
+     * Returns <code>true</code> if the shifting flag is set, <code>false</code> otherwise.
+     * @return <code>true</code> if the shifting flag is set, <code>false</code> otherwise
+     */
+    public bool isMarkedForShift() {
+        return isMarkedForShift_;
+    }
+    public bool isMarkedForShift(bool v) {
+        isMarkedForShift_ = v;
+        return isMarkedForShift();
+    }
+
+    /**
+     * Clears the shifting and the stretching flag.
+     */
+    public void clearMark() {
+        isMarkedForStretch_= false;
+        isMarkedForShift_= false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/projection/SegmentUpdater.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.projection.SegmentUpdater;
+
+import org.eclipse.jface.text.projection.ProjectionMapping; // packageimport
+import org.eclipse.jface.text.projection.ChildDocumentManager; // packageimport
+import org.eclipse.jface.text.projection.Segment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocument; // packageimport
+import org.eclipse.jface.text.projection.FragmentUpdater; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentEvent; // packageimport
+import org.eclipse.jface.text.projection.ChildDocument; // packageimport
+import org.eclipse.jface.text.projection.IMinimalMapping; // packageimport
+import org.eclipse.jface.text.projection.Fragment; // packageimport
+import org.eclipse.jface.text.projection.ProjectionTextStore; // packageimport
+import org.eclipse.jface.text.projection.ProjectionDocumentManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DefaultPositionUpdater;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.Position;
+
+
+/**
+ * The position updater used to adapt the segments of a projection document to
+ * changes of the master document. Depending on the flags set on a segment, a
+ * segment is either extended to shifted if an insertion happens at a segment's
+ * offset. The last segment is extended if an insert operation happens at the
+ * end of the segment.
+ *
+ * @since 3.0
+ */
+class SegmentUpdater : DefaultPositionUpdater {
+
+    private Segment fNextSegment= null;
+    private bool fIsProjectionChange= false;
+
+    /**
+     * Creates the segment updater for the given category.
+     *
+     * @param segmentCategory the position category used for managing the segments of a projection document
+     */
+    /+protected+/ this(String segmentCategory) {
+        super(segmentCategory);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent)
+     */
+    public void update(DocumentEvent event) {
+
+        Assert.isTrue( null !is cast(ProjectionDocumentEvent)event );
+        fIsProjectionChange= (cast(ProjectionDocumentEvent) event).getChangeType() is ProjectionDocumentEvent.PROJECTION_CHANGE;
+
+        try {
+
+            Position[] category= event.getDocument().getPositions(getCategory());
+
+            fOffset= event.getOffset();
+            fLength= event.getLength();
+            fReplaceLength= (event.getText() is null ? 0 : event.getText().length());
+            fDocument= event.getDocument();
+
+            for (int i= 0; i < category.length; i++) {
+
+                fPosition= category[i];
+                Assert.isTrue( null !is cast(Segment)fPosition );
+
+                if (i < category.length - 1)
+                    fNextSegment= cast(Segment) category[i + 1];
+                else
+                    fNextSegment= null;
+
+                fOriginalPosition.offset= fPosition.offset;
+                fOriginalPosition.length= fPosition.length;
+
+                if (notDeleted())
+                    adaptToReplace();
+
+            }
+
+        } catch (BadPositionCategoryException x) {
+            // do nothing
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.DefaultPositionUpdater#adaptToInsert()
+     */
+    protected void adaptToInsert() {
+
+        Segment segment= cast(Segment) fPosition;
+        int myStart= segment.offset;
+        int myEnd= segment.offset + segment.length - (segment.isMarkedForStretch || fNextSegment is null || isAffectingReplace() ? 0 : 1);
+        myEnd= Math.max(myStart, myEnd);
+        int yoursStart= fOffset;
+
+        try {
+
+            if (myEnd < yoursStart)
+                return;
+
+            if (segment.isMarkedForStretch) {
+                Assert.isTrue(fIsProjectionChange);
+                segment.isMarkedForShift= false;
+                if (fNextSegment !is null) {
+                    fNextSegment.isMarkedForShift= true;
+                    fNextSegment.isMarkedForStretch= false;
+                }
+            }
+
+            if (fLength <= 0) {
+
+                if (myStart < (yoursStart + (segment.isMarkedForShift ? 0 : 1)))
+                    fPosition.length += fReplaceLength;
+                else
+                    fPosition.offset += fReplaceLength;
+
+            } else {
+
+                if (myStart <= yoursStart && fOriginalPosition.offset <= yoursStart)
+                    fPosition.length += fReplaceLength;
+                else
+                    fPosition.offset += fReplaceLength;
+            }
+
+        } finally {
+            segment.clearMark();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/source/Annotation.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,226 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.Annotation;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * Annotation managed by an
+ * {@link org.eclipse.jface.text.source.IAnnotationModel}.
+ * <p>
+ * Annotations are typed, can have an associated text and can be marked as persistent and
+ * deleted. Annotations which are not explicitly initialized with an annotation
+ * type are of type <code>"org.eclipse.text.annotation.unknown"</code>.
+ */
+public class Annotation {
+
+    /**
+     * Constant for unknown annotation types.<p>
+     * Value: <code>"org.eclipse.text.annotation.unknown"</code>
+     * @since 3.0
+     */
+    public const static String TYPE_UNKNOWN= "org.eclipse.text.annotation.unknown";  //$NON-NLS-1$
+
+
+    /**
+     * The type of this annotation.
+     * @since 3.0
+     */
+    private String fType;
+    /**
+     * Indicates whether this annotation is persistent or not.
+     * @since 3.0
+     */
+    private bool fIsPersistent= false;
+    /**
+     * Indicates whether this annotation is marked as deleted or not.
+     * @since 3.0
+     */
+    private bool fMarkedAsDeleted= false;
+    /**
+     * The text associated with this annotation.
+     * @since 3.0
+     */
+    private String fText;
+
+
+    /**
+     * Creates a new annotation that is not persistent and type less.
+     */
+    protected this() {
+        this(null, false, null);
+    }
+
+    /**
+     * Creates a new annotation with the given properties.
+     *
+     * @param type the unique name of this annotation type
+     * @param isPersistent <code>true</code> if this annotation is
+     *            persistent, <code>false</code> otherwise
+     * @param text the text associated with this annotation
+     * @since 3.0
+     */
+    public this(String type, bool isPersistent, String text) {
+        fType= type;
+        fIsPersistent= isPersistent;
+        fText= text;
+    }
+
+    /**
+     * Creates a new annotation with the given persistence state.
+     *
+     * @param isPersistent <code>true</code> if persistent, <code>false</code> otherwise
+     * @since 3.0
+     */
+    public this(bool isPersistent) {
+        this(null, isPersistent, null);
+    }
+
+    /**
+     * Returns whether this annotation is persistent.
+     *
+     * @return <code>true</code> if this annotation is persistent, <code>false</code>
+     *         otherwise
+     * @since 3.0
+     */
+    public bool isPersistent() {
+        return fIsPersistent;
+    }
+
+    /**
+     * Sets the type of this annotation.
+     *
+     * @param type the annotation type
+     * @since 3.0
+     */
+    public void setType(String type) {
+        fType= type;
+    }
+
+    /**
+     * Returns the type of the annotation.
+     *
+     * @return the type of the annotation
+     * @since 3.0
+     */
+    public String getType() {
+        return fType is null ? TYPE_UNKNOWN : fType;
+    }
+
+    /**
+     * Marks this annotation deleted according to the value of the
+     * <code>deleted</code> parameter.
+     *
+     * @param deleted <code>true</code> if annotation should be marked as deleted
+     * @since 3.0
+     */
+    public void markDeleted(bool deleted) {
+        fMarkedAsDeleted= deleted;
+    }
+
+    /**
+     * Returns whether this annotation is marked as deleted.
+     *
+     * @return <code>true</code> if annotation is marked as deleted, <code>false</code>
+     *         otherwise
+     * @since 3.0
+     */
+    public bool isMarkedDeleted() {
+        return fMarkedAsDeleted;
+    }
+
+    /**
+     * Sets the text associated with this annotation.
+     *
+     * @param text the text associated with this annotation
+     * @since 3.0
+     */
+    public void setText(String text) {
+        fText= text;
+    }
+
+    /**
+     * Returns the text associated with this annotation.
+     *
+     * @return the text associated with this annotation or <code>null</code>
+     * @since 3.0
+     */
+    public String getText() {
+        return fText;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/source/AnnotationMap.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,296 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.source.AnnotationMap;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+
+
+
+
+
+
+
+
+/**
+ * Internal implementation of {@link org.eclipse.jface.text.source.IAnnotationMap}.
+ *
+ * @since 3.0
+ */
+class AnnotationMap : IAnnotationMap {
+
+    /**
+     * The lock object used to synchronize the operations explicitly defined by
+     * <code>IAnnotationMap</code>
+     */
+    private Object fLockObject;
+    /**
+     * The internal lock object used if <code>fLockObject</code> is <code>null</code>.
+     * @since 3.2
+     */
+    private const Object fInternalLockObject;
+
+    /** The map holding the annotations */
+    private Map fInternalMap;
+
+    /**
+     * Creates a new annotation map with the given capacity.
+     *
+     * @param capacity the capacity
+     */
+    public this(int capacity) {
+        fInternalLockObject= new Object();
+        fInternalMap= new HashMap(capacity);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.ISynchronizable#setLockObject(java.lang.Object)
+     */
+    public synchronized void setLockObject(Object lockObject) {
+        fLockObject= lockObject;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.ISynchronizable#getLockObject()
+     */
+    public synchronized Object getLockObject() {
+        if (fLockObject is null)
+            return fInternalLockObject;
+        return fLockObject;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationMap#valuesIterator()
+     */
+    public Iterator valuesIterator() {
+        synchronized (getLockObject()) {
+            return (new ArrayList(fInternalMap.values())).iterator();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationMap#keySetIterator()
+     */
+    public Iterator keySetIterator() {
+        synchronized (getLockObject()) {
+            return (new ArrayList(fInternalMap.keySet())).iterator();
+        }
+    }
+
+    /*
+     * @see java.util.Map#containsKey(java.lang.Object)
+     */
+    public bool containsKey(Object annotation) {
+        synchronized (getLockObject()) {
+            return fInternalMap.containsKey(annotation);
+        }
+    }
+
+    /*
+     * @see java.util.Map#put(java.lang.Object, java.lang.Object)
+     */
+    public Object put(Object annotation, Object position) {
+        synchronized (getLockObject()) {
+            return fInternalMap.put(annotation, position);
+        }
+    }
+
+    /*
+     * @see java.util.Map#get(java.lang.Object)
+     */
+    public Object get(Object annotation) {
+        synchronized (getLockObject()) {
+            return fInternalMap.get(annotation);
+        }
+    }
+
+    /*
+     * @see java.util.Map#clear()
+     */
+    public void clear() {
+        synchronized (getLockObject()) {
+            fInternalMap.clear();
+        }
+    }
+
+    /*
+     * @see java.util.Map#remove(java.lang.Object)
+     */
+    public Object remove(Object annotation) {
+        synchronized (getLockObject()) {
+            return fInternalMap.remove(annotation);
+        }
+    }
+
+    /*
+     * @see java.util.Map#size()
+     */
+    public int size() {
+        synchronized (getLockObject()) {
+            return fInternalMap.size();
+        }
+    }
+
+    /*
+     * @see java.util.Map#isEmpty()
+     */
+    public bool isEmpty() {
+        synchronized (getLockObject()) {
+            return fInternalMap.isEmpty();
+        }
+    }
+
+    /*
+     * @see java.util.Map#containsValue(java.lang.Object)
+     */
+    public bool containsValue(Object value) {
+        synchronized(getLockObject()) {
+            return fInternalMap.containsValue(value);
+        }
+    }
+
+    /*
+     * @see java.util.Map#putAll(java.util.Map)
+     */
+    public void putAll(Map map) {
+        synchronized (getLockObject()) {
+            fInternalMap.putAll(map);
+        }
+    }
+
+    /*
+     * @see IAnnotationMap#entrySet()
+     */
+    public Set entrySet() {
+        synchronized (getLockObject()) {
+            return fInternalMap.entrySet();
+        }
+    }
+
+    /*
+     * @see IAnnotationMap#keySet()
+     */
+    public Set keySet() {
+        synchronized (getLockObject()) {
+            return fInternalMap.keySet();
+        }
+    }
+
+    /*
+     * @see IAnnotationMap#values()
+     */
+    public Collection values() {
+        synchronized (getLockObject()) {
+            return fInternalMap.values();
+        }
+    }
+
+    /// SWT extension of Collection interfaces
+
+    public bool containsKey(String key) {
+        return containsKey(stringcast(key));
+    }
+    public Object get(String key) {
+        return get(stringcast(key));
+    }
+    public Object put(String key, String value) {
+        return put(stringcast(key), stringcast(value));
+    }
+    public Object put(Object key, String value) {
+        return put(key, stringcast(value));
+    }
+    public Object put(String key, Object value) {
+        return put(stringcast(key), value);
+    }
+    public Object remove(String key) {
+        return remove(stringcast(key));
+    }
+    public int opApply (int delegate(ref Object value) dg){
+        implMissing(__FILE__,__LINE__);
+        return 0;
+    }
+    public int opApply (int delegate(ref Object key, ref Object value) dg){
+        implMissing(__FILE__,__LINE__);
+        return 0;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/source/AnnotationModel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1064 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.AnnotationModel;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import tango.core.Exception;
+import java.lang.JThread;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.AbstractDocument;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.ISynchronizable;
+import org.eclipse.jface.text.Position;
+
+
+/**
+ * Standard implementation of {@link IAnnotationModel} and its extension
+ * interfaces. This class can directly be used by clients. Subclasses may adapt
+ * this annotation model to other existing annotation mechanisms. This class
+ * also implements {@link org.eclipse.jface.text.ISynchronizable}. All
+ * modifications of the model's internal annotation map are synchronized using
+ * the model's lock object.
+ */
+public class AnnotationModel : IAnnotationModel, IAnnotationModelExtension, IAnnotationModelExtension2, ISynchronizable {
+
+
+    /**
+     * Iterator that returns the annotations for a given region.
+     *
+     * @since 3.4
+     * @see AnnotationModel.RegionIterator#RegionIterator(Iterator, IAnnotationModel, int, int, bool, bool)
+     */
+    private static final class RegionIterator : Iterator {
+
+        private const Iterator fParentIterator;
+        private const bool fCanEndAfter;
+        private const bool fCanStartBefore;
+        private const IAnnotationModel fModel;
+        private Object fNext;
+        private Position fRegion;
+
+        /**
+         * Iterator that returns all annotations from the parent iterator which
+         * have a position in the given model inside the given region.
+         * <p>
+         * See {@link IAnnotationModelExtension2} for a definition of inside.
+         * </p>
+         *
+         * @param parentIterator iterator containing all annotations
+         * @param model the model to use to retrieve positions from for each
+         *            annotation
+         * @param offset start position of the region
+         * @param length length of the region
+         * @param canStartBefore include annotations starting before region
+         * @param canEndAfter include annotations ending after region
+         * @see IAnnotationModelExtension2
+         */
+        public this(Iterator parentIterator, IAnnotationModel model, int offset, int length, bool canStartBefore, bool canEndAfter) {
+            fParentIterator= parentIterator;
+            fModel= model;
+            fRegion= new Position(offset, length);
+            fCanEndAfter= canEndAfter;
+            fCanStartBefore= canStartBefore;
+            fNext= findNext();
+        }
+
+        /*
+         * @see java.util.Iterator#hasNext()
+         */
+        public bool hasNext() {
+            return fNext !is null;
+        }
+
+        /*
+         * @see java.util.Iterator#next()
+         */
+        public Object next() {
+            if (!hasNext())
+                throw new NoSuchElementException(null);
+
+            Object result= fNext;
+            fNext= findNext();
+            return result;
+        }
+
+        /*
+         * @see java.util.Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        private Object findNext() {
+            while (fParentIterator.hasNext()) {
+                Annotation next= cast(Annotation) fParentIterator.next();
+                Position position= fModel.getPosition(next);
+                if (position !is null) {
+                    int offset= position.getOffset();
+                    if (isWithinRegion(offset, position.getLength()))
+                        return next;
+                }
+            }
+            return null;
+        }
+
+        private bool isWithinRegion(int start, int length) {
+            if (fCanStartBefore && fCanEndAfter)
+                return fRegion.overlapsWith(start, length);
+            else if (fCanStartBefore)
+                return fRegion.includes(start + length - 1);
+            else if (fCanEndAfter)
+                return fRegion.includes(start);
+            else
+                return fRegion.includes(start) && fRegion.includes(start + length - 1);
+        }
+    }
+
+    /**
+     * An iterator iteration over a Positions and mapping positions to
+     * annotations using a provided map if the provided map contains the element.
+     *
+     * @since 3.4
+     */
+    private static final class AnnotationsInterator : Iterator {
+
+        private Object fNext;
+        private const Position[] fPositions;
+        private int fIndex;
+        private const Map fMap;
+
+        /**
+         * @param positions positions to iterate over
+         * @param map a map to map positions to annotations
+         */
+        public this(Position[] positions, Map map) {
+            fPositions= positions;
+            fIndex= 0;
+            fMap= map;
+            fNext= findNext();
+        }
+
+        /* (non-Javadoc)
+         * @see java.util.Iterator#hasNext()
+         */
+        public bool hasNext() {
+            return fNext !is null;
+        }
+
+        /* (non-Javadoc)
+         * @see java.util.Iterator#next()
+         */
+        public Object next() {
+            Object result= fNext;
+            fNext= findNext();
+            return result;
+        }
+
+        /* (non-Javadoc)
+         * @see java.util.Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        private Object findNext() {
+            while (fIndex < fPositions.length) {
+                Position position= fPositions[fIndex];
+                fIndex++;
+                if (fMap.containsKey(position))
+                    return fMap.get(position);
+            }
+
+            return null;
+        }
+    }
+
+    /**
+     * A single iterator builds its behavior based on a sequence of iterators.
+     *
+     * @since 3.1
+     */
+    private static class MetaIterator : Iterator {
+
+        /** The iterator over a list of iterators. */
+        private Iterator fSuperIterator;
+        /** The current iterator. */
+        private Iterator fCurrent;
+        /** The current element. */
+        private Object fCurrentElement;
+
+
+        public this(Iterator iterator) {
+            fSuperIterator= iterator;
+            fCurrent= cast(Iterator) fSuperIterator.next(); // there is at least one.
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        public bool hasNext() {
+            if (fCurrentElement !is null)
+                return true;
+
+            if (fCurrent.hasNext()) {
+                fCurrentElement= fCurrent.next();
+                return true;
+            } else if (fSuperIterator.hasNext()) {
+                fCurrent= cast(Iterator) fSuperIterator.next();
+                return hasNext();
+            } else
+                return false;
+        }
+
+        public Object next() {
+            if (!hasNext())
+                throw new NoSuchElementException(null);
+
+            Object element= fCurrentElement;
+            fCurrentElement= null;
+            return element;
+        }
+    }
+
+    /**
+     * Internal annotation model listener for forwarding annotation model changes from the attached models to the
+     * registered listeners of the outer most annotation model.
+     *
+     * @since 3.0
+     */
+    private class InternalModelListener : IAnnotationModelListener, IAnnotationModelListenerExtension {
+
+        /*
+         * @see org.eclipse.jface.text.source.IAnnotationModelListener#modelChanged(org.eclipse.jface.text.source.IAnnotationModel)
+         */
+        public void modelChanged(IAnnotationModel model) {
+            this.outer.fireModelChanged(new AnnotationModelEvent(model, true));
+        }
+
+        /*
+         * @see org.eclipse.jface.text.source.IAnnotationModelListenerExtension#modelChanged(org.eclipse.jface.text.source.AnnotationModelEvent)
+         */
+        public void modelChanged(AnnotationModelEvent event) {
+            this.outer.fireModelChanged(event);
+        }
+    }
+
+    /**
+     * The list of managed annotations
+     * @deprecated since 3.0 use <code>getAnnotationMap</code> instead
+     */
+    protected Map fAnnotations;
+    /**
+     * The map which maps {@link Position} to {@link Annotation}.
+     * @since 3.4
+     **/
+    private IdentityHashMap fPositions;
+    /** The list of annotation model listeners */
+    protected ArrayList fAnnotationModelListeners;
+    /** The document connected with this model */
+    protected IDocument fDocument;
+    /** The number of open connections to the same document */
+    private int fOpenConnections= 0;
+    /** The document listener for tracking whether document positions might have been changed. */
+    private IDocumentListener fDocumentListener;
+    /** The flag indicating whether the document positions might have been changed. */
+    private bool fDocumentChanged= true;
+    /**
+     * The model's attachment.
+     * @since 3.0
+     */
+    private Map fAttachments;
+    /**
+     * The annotation model listener on attached sub-models.
+     * @since 3.0
+     */
+    private IAnnotationModelListener fModelListener;
+    /**
+     * The current annotation model event.
+     * @since 3.0
+     */
+    private AnnotationModelEvent fModelEvent;
+    /**
+     * The modification stamp.
+     * @since 3.0
+     */
+    private Object fModificationStamp;
+    /**
+     * Creates a new annotation model. The annotation is empty, i.e. does not
+     * manage any annotations and is not connected to any document.
+     */
+    public this() {
+        fAttachments= new HashMap();
+        fModelListener= new InternalModelListener();
+        fModificationStamp= new Object();
+        fAnnotations= new AnnotationMap(10);
+        fPositions= new IdentityHashMap(10);
+        fAnnotationModelListeners= new ArrayList(2);
+
+        fDocumentListener= new class()  IDocumentListener {
+
+            public void documentAboutToBeChanged(DocumentEvent event) {
+            }
+
+            public void documentChanged(DocumentEvent event) {
+                fDocumentChanged= true;
+            }
+        };
+    }
+
+    /**
+     * Returns the annotation map internally used by this annotation model.
+     *
+     * @return the annotation map internally used by this annotation model
+     * @since 3.0
+     */
+    protected IAnnotationMap getAnnotationMap() {
+        return cast(IAnnotationMap) fAnnotations;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ISynchronizable#getLockObject()
+     * @since 3.0
+     */
+    public Object getLockObject() {
+        return getAnnotationMap().getLockObject();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ISynchronizable#setLockObject(java.lang.Object)
+     * @since 3.0
+     */
+    public void setLockObject(Object lockObject) {
+        getAnnotationMap().setLockObject(lockObject);
+    }
+
+    /**
+     * Returns the current annotation model event. This is the event that will be sent out
+     * when calling <code>fireModelChanged</code>.
+     *
+     * @return the current annotation model event
+     * @since 3.0
+     */
+    protected final AnnotationModelEvent getAnnotationModelEvent() {
+        synchronized (getLockObject()) {
+            if (fModelEvent is null) {
+                fModelEvent= createAnnotationModelEvent();
+                fModelEvent.markWorldChange(false);
+                fModificationStamp= fModelEvent;
+            }
+            return fModelEvent;
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModel#addAnnotation(org.eclipse.jface.text.source.Annotation, org.eclipse.jface.text.Position)
+     */
+    public void addAnnotation(Annotation annotation, Position position) {
+        try {
+            addAnnotation(annotation, position, true);
+        } catch (BadLocationException e) {
+            // ignore invalid position
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelExtension#replaceAnnotations(org.eclipse.jface.text.source.Annotation[], java.util.Map)
+     * @since 3.0
+     */
+    public void replaceAnnotations(Annotation[] annotationsToRemove, Map annotationsToAdd) {
+        try {
+            replaceAnnotations(annotationsToRemove, annotationsToAdd, true);
+        } catch (BadLocationException x) {
+        }
+    }
+
+    /**
+     * Replaces the given annotations in this model and if advised fires a
+     * model change event.
+     *
+     * @param annotationsToRemove the annotations to be removed
+     * @param annotationsToAdd the annotations to be added
+     * @param fireModelChanged <code>true</code> if a model change event
+     *            should be fired, <code>false</code> otherwise
+     * @throws BadLocationException in case an annotation should be added at an
+     *             invalid position
+     * @since 3.0
+     */
+    protected void replaceAnnotations(Annotation[] annotationsToRemove, Map annotationsToAdd, bool fireModelChanged_)  {
+
+        if (annotationsToRemove !is null) {
+            for (int i= 0, length= annotationsToRemove.length; i < length; i++)
+                removeAnnotation(annotationsToRemove[i], false);
+        }
+
+        if (annotationsToAdd !is null) {
+            Iterator iter= annotationsToAdd.entrySet().iterator();
+            while (iter.hasNext()) {
+                Map.Entry mapEntry= cast(Map.Entry) iter.next();
+                Annotation annotation= cast(Annotation) mapEntry.getKey();
+                Position position= cast(Position) mapEntry.getValue();
+                addAnnotation(annotation, position, false);
+            }
+        }
+
+        if (fireModelChanged_)
+            fireModelChanged();
+    }
+
+    /**
+     * Adds the given annotation to this model. Associates the
+     * annotation with the given position. If requested, all annotation
+     * model listeners are informed about this model change. If the annotation
+     * is already managed by this model nothing happens.
+     *
+     * @param annotation the annotation to add
+     * @param position the associate position
+     * @param fireModelChanged indicates whether to notify all model listeners
+     * @throws BadLocationException if the position is not a valid document position
+     */
+    protected void addAnnotation(Annotation annotation, Position position, bool fireModelChanged_)  {
+        if (!fAnnotations.containsKey(annotation)) {
+
+            addPosition(fDocument, position);
+            fAnnotations.put(annotation, position);
+            fPositions.put(position, annotation);
+            synchronized (getLockObject()) {
+                getAnnotationModelEvent().annotationAdded(annotation);
+            }
+
+            if (fireModelChanged_)
+                fireModelChanged();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModel#addAnnotationModelListener(org.eclipse.jface.text.source.IAnnotationModelListener)
+     */
+    public void addAnnotationModelListener(IAnnotationModelListener listener) {
+        if (!fAnnotationModelListeners.contains(cast(Object)listener)) {
+            fAnnotationModelListeners.add(cast(Object)listener);
+            if ( cast(IAnnotationModelListenerExtension)listener ) {
+                IAnnotationModelListenerExtension extension= cast(IAnnotationModelListenerExtension) listener;
+                AnnotationModelEvent event= createAnnotationModelEvent();
+                event.markSealed();
+                extension.modelChanged(event);
+            } else
+                listener.modelChanged(this);
+        }
+    }
+
+    /**
+     * Adds the given position to the default position category of the
+     * given document.
+     *
+     * @param document the document to which to add the position
+     * @param position the position to add
+     * @throws BadLocationException if the position is not a valid document position
+     */
+    protected void addPosition(IDocument document, Position position)  {
+        if (document !is null)
+            document.addPosition(position);
+    }
+
+    /**
+     * Removes the given position from the default position category of the
+     * given document.
+     *
+     * @param document the document to which to add the position
+     * @param position the position to add
+     *
+     * @since 3.0
+     */
+    protected void removePosition(IDocument document, Position position) {
+        if (document !is null)
+            document.removePosition(position);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModel#connect(org.eclipse.jface.text.IDocument)
+     */
+    public void connect(IDocument document) {
+        Assert.isTrue(fDocument is null || fDocument is document);
+
+        if (fDocument is null) {
+            fDocument= document;
+            Iterator e= getAnnotationMap().valuesIterator();
+            while (e.hasNext())
+                try {
+                    addPosition(fDocument, cast(Position) e.next());
+                } catch (BadLocationException x) {
+                    // ignore invalid position
+                }
+        }
+
+        ++ fOpenConnections;
+        if (fOpenConnections is 1) {
+            fDocument.addDocumentListener(fDocumentListener);
+            connected();
+        }
+
+        for (Iterator it= fAttachments.keySet().iterator(); it.hasNext();) {
+            IAnnotationModel model= cast(IAnnotationModel) fAttachments.get(it.next());
+            model.connect(document);
+        }
+    }
+
+    /**
+     * Hook method. Is called as soon as this model becomes connected to a document.
+     * Subclasses may re-implement.
+     */
+    protected void connected() {
+    }
+
+    /**
+     * Hook method. Is called as soon as this model becomes disconnected from its document.
+     * Subclasses may re-implement.
+     */
+    protected void disconnected() {
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModel#disconnect(org.eclipse.jface.text.IDocument)
+     */
+    public void disconnect(IDocument document) {
+
+        Assert.isTrue(fDocument is document);
+
+        for (Iterator it= fAttachments.keySet().iterator(); it.hasNext();) {
+            IAnnotationModel model= cast(IAnnotationModel) fAttachments.get(it.next());
+            model.disconnect(document);
+        }
+
+        -- fOpenConnections;
+        if (fOpenConnections is 0) {
+
+            disconnected();
+            fDocument.removeDocumentListener(fDocumentListener);
+
+            if (fDocument !is null) {
+                Iterator e= getAnnotationMap().valuesIterator();
+                while (e.hasNext()) {
+                    Position p= cast(Position) e.next();
+                    removePosition(fDocument, p);
+                }
+                fDocument= null;
+            }
+        }
+    }
+
+    /**
+     * Informs all annotation model listeners that this model has been changed.
+     */
+    protected void fireModelChanged() {
+        AnnotationModelEvent modelEvent= null;
+
+        synchronized(getLockObject()) {
+            if (fModelEvent !is null) {
+                modelEvent= fModelEvent;
+                fModelEvent= null;
+            }
+        }
+
+        if (modelEvent !is null)
+            fireModelChanged(modelEvent);
+    }
+
+    /**
+     * Creates and returns a new annotation model event. Subclasses may override.
+     *
+     * @return a new and empty annotation model event
+     * @since 3.0
+     */
+    protected AnnotationModelEvent createAnnotationModelEvent() {
+        return new AnnotationModelEvent(this);
+    }
+
+    /**
+     * Informs all annotation model listeners that this model has been changed
+     * as described in the annotation model event. The event is sent out
+     * to all listeners implementing <code>IAnnotationModelListenerExtension</code>.
+     * All other listeners are notified by just calling <code>modelChanged(IAnnotationModel)</code>.
+     *
+     * @param event the event to be sent out to the listeners
+     * @since 2.0
+     */
+    protected void fireModelChanged(AnnotationModelEvent event) {
+
+        event.markSealed();
+
+        if (event.isEmpty())
+            return;
+
+        ArrayList v= new ArrayList(fAnnotationModelListeners);
+        Iterator e= v.iterator();
+        while (e.hasNext()) {
+            IAnnotationModelListener l= cast(IAnnotationModelListener) e.next();
+            if ( cast(IAnnotationModelListenerExtension)l )
+                (cast(IAnnotationModelListenerExtension) l).modelChanged(event);
+            else if (l !is null)
+                l.modelChanged(this);
+        }
+    }
+
+    /**
+     * Removes the given annotations from this model. If requested all
+     * annotation model listeners will be informed about this change.
+     * <code>modelInitiated</code> indicates whether the deletion has
+     * been initiated by this model or by one of its clients.
+     *
+     * @param annotations the annotations to be removed
+     * @param fireModelChanged indicates whether to notify all model listeners
+     * @param modelInitiated indicates whether this changes has been initiated by this model
+     */
+    protected void removeAnnotations(List annotations, bool fireModelChanged_, bool modelInitiated) {
+        if (annotations.size() > 0) {
+            Iterator e= annotations.iterator();
+            while (e.hasNext())
+                removeAnnotation(cast(Annotation) e.next(), false);
+
+            if (fireModelChanged_)
+                fireModelChanged();
+        }
+    }
+
+    /**
+     * Removes all annotations from the model whose associated positions have been
+     * deleted. If requested inform all model listeners about the change.
+     *
+     * @param fireModelChanged indicates whether to notify all model listeners
+     */
+    protected void cleanup(bool fireModelChanged_) {
+        cleanup(fireModelChanged_, true);
+    }
+
+    /**
+     * Removes all annotations from the model whose associated positions have been
+     * deleted. If requested inform all model listeners about the change. If requested
+     * a new thread is created for the notification of the model listeners.
+     *
+     * @param fireModelChanged indicates whether to notify all model listeners
+     * @param forkNotification <code>true</code> iff notification should be done in a new thread
+     * @since 3.0
+     */
+    private void cleanup(bool fireModelChanged_, bool forkNotification) {
+        if (fDocumentChanged) {
+            fDocumentChanged= false;
+
+            ArrayList deleted= new ArrayList();
+            Iterator e= getAnnotationMap().keySetIterator();
+            while (e.hasNext()) {
+                Annotation a= cast(Annotation) e.next();
+                Position p= cast(Position) fAnnotations.get(a);
+                if (p is null || p.isDeleted())
+                    deleted.add(a);
+            }
+
+            if (fireModelChanged_ && forkNotification) {
+                removeAnnotations(deleted, false, false);
+                synchronized (getLockObject()) {
+                    if (fModelEvent !is null)
+                        (new JThread ( &fireModelChanged )).start();
+                }
+            } else
+                removeAnnotations(deleted, fireModelChanged_, false);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModel#getAnnotationIterator()
+     */
+    public Iterator getAnnotationIterator() {
+        return getAnnotationIterator(true, true);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 3.4
+     */
+    public Iterator getAnnotationIterator(int offset, int length, bool canStartBefore, bool canEndAfter) {
+        Iterator regionIterator= getRegionAnnotationIterator(offset, length, canStartBefore, canEndAfter);
+
+        if (fAttachments.isEmpty())
+            return regionIterator;
+
+        List iterators= new ArrayList(fAttachments.size() + 1);
+        iterators.add(cast(Object)regionIterator);
+        Iterator it= fAttachments.keySet().iterator();
+        while (it.hasNext()) {
+            IAnnotationModel attachment= cast(IAnnotationModel) fAttachments.get(it.next());
+            if ( cast(IAnnotationModelExtension2)attachment )
+                iterators.add(cast(Object)(cast(IAnnotationModelExtension2) attachment).getAnnotationIterator(offset, length, canStartBefore, canEndAfter));
+            else
+                iterators.add(new RegionIterator(attachment.getAnnotationIterator(), attachment, offset, length, canStartBefore, canEndAfter));
+        }
+
+        return new MetaIterator(iterators.iterator());
+    }
+
+    /**
+     * Returns an iterator as specified in {@link IAnnotationModelExtension2#getAnnotationIterator(int, int, bool, bool)}
+     *
+     * @param offset region start
+     * @param length region length
+     * @param canStartBefore position can start before region
+     * @param canEndAfter position can end after region
+     * @return an iterator to iterate over annotations in region
+     * @see IAnnotationModelExtension2#getAnnotationIterator(int, int, bool, bool)
+     * @since 3.4
+     */
+    private Iterator getRegionAnnotationIterator(int offset, int length, bool canStartBefore, bool canEndAfter) {
+        if (!( cast(AbstractDocument)fDocument ))
+            return new RegionIterator(getAnnotationIterator(true), this, offset, length, canStartBefore, canEndAfter);
+
+        AbstractDocument document= cast(AbstractDocument) fDocument;
+        cleanup(true);
+
+        try {
+            Position[] positions= document.getPositions(IDocument.DEFAULT_CATEGORY, offset, length, canStartBefore, canEndAfter);
+            return new AnnotationsInterator(positions, fPositions);
+        } catch (BadPositionCategoryException e) {
+            //can not happen
+            Assert.isTrue(false);
+            return null;
+        }
+    }
+
+    /**
+     * Returns all annotations managed by this model. <code>cleanup</code>
+     * indicates whether all annotations whose associated positions are
+     * deleted should previously be removed from the model. <code>recurse</code> indicates
+     * whether annotations of attached sub-models should also be returned.
+     *
+     * @param cleanup indicates whether annotations with deleted associated positions are removed
+     * @param recurse whether to return annotations managed by sub-models.
+     * @return all annotations managed by this model
+     * @since 3.0
+     */
+    private Iterator getAnnotationIterator(bool cleanup, bool recurse) {
+        Iterator iter= getAnnotationIterator(cleanup);
+        if (!recurse || fAttachments.isEmpty())
+            return iter;
+
+        List iterators= new ArrayList(fAttachments.size() + 1);
+        iterators.add(cast(Object)iter);
+        Iterator it= fAttachments.keySet().iterator();
+        while (it.hasNext())
+            iterators.add(cast(Object)(cast(IAnnotationModel) fAttachments.get(it.next())).getAnnotationIterator());
+
+        return new MetaIterator(iterators.iterator());
+    }
+
+    /**
+     * Returns all annotations managed by this model. <code>cleanup</code>
+     * indicates whether all annotations whose associated positions are
+     * deleted should previously be removed from the model.
+     *
+     * @param cleanup indicates whether annotations with deleted associated positions are removed
+     * @return all annotations managed by this model
+     */
+    protected Iterator getAnnotationIterator(bool cleanup_) {
+        if (cleanup_)
+            cleanup(true);
+
+        return getAnnotationMap().keySetIterator();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModel#getPosition(org.eclipse.jface.text.source.Annotation)
+     */
+    public Position getPosition(Annotation annotation) {
+        Position position= cast(Position) fAnnotations.get(annotation);
+        if (position !is null)
+            return position;
+
+        Iterator it= fAttachments.values().iterator();
+        while (position is null && it.hasNext())
+            position= (cast(IAnnotationModel) it.next()).getPosition(annotation);
+        return position;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelExtension#removeAllAnnotations()
+     * @since 3.0
+     */
+    public void removeAllAnnotations() {
+        removeAllAnnotations(true);
+    }
+
+    /**
+     * Removes all annotations from the annotation model. If requested
+     * inform all model change listeners about this change.
+     *
+     * @param fireModelChanged indicates whether to notify all model listeners
+     */
+    protected void removeAllAnnotations(bool fireModelChanged_) {
+
+        if (fDocument !is null) {
+            Iterator e= getAnnotationMap().keySetIterator();
+            while (e.hasNext()) {
+                Annotation a= cast(Annotation) e.next();
+                Position p= cast(Position) fAnnotations.get(a);
+                removePosition(fDocument, p);
+//              p.delete_();
+                synchronized (getLockObject()) {
+                    getAnnotationModelEvent().annotationRemoved(a, p);
+                }
+            }
+        }
+
+        fAnnotations.clear();
+        fPositions.clear();
+
+        if (fireModelChanged_)
+            fireModelChanged();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModel#removeAnnotation(org.eclipse.jface.text.source.Annotation)
+     */
+    public void removeAnnotation(Annotation annotation) {
+        removeAnnotation(annotation, true);
+    }
+
+    /**
+     * Removes the given annotation from the annotation model.
+     * If requested inform all model change listeners about this change.
+     *
+     * @param annotation the annotation to be removed
+     * @param fireModelChanged indicates whether to notify all model listeners
+     */
+    protected void removeAnnotation(Annotation annotation, bool fireModelChanged_) {
+        if (fAnnotations.containsKey(annotation)) {
+
+            Position p= null;
+            p= cast(Position) fAnnotations.get(annotation);
+            if (fDocument !is null) {
+                removePosition(fDocument, p);
+//              p.delete_();
+            }
+
+            fAnnotations.remove(annotation);
+            fPositions.remove(p);
+            synchronized (getLockObject()) {
+                getAnnotationModelEvent().annotationRemoved(annotation, p);
+            }
+
+            if (fireModelChanged_)
+                fireModelChanged();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelExtension#modifyAnnotationPosition(org.eclipse.jface.text.source.Annotation, org.eclipse.jface.text.Position)
+     * @since 3.0
+     */
+    public void modifyAnnotationPosition(Annotation annotation, Position position) {
+        modifyAnnotationPosition(annotation, position, true);
+    }
+
+    /**
+     * Modifies the associated position of the given annotation to the given
+     * position. If the annotation is not yet managed by this annotation model,
+     * the annotation is added. When the position is <code>null</code>, the
+     * annotation is removed from the model.
+     * <p>
+     * If requested, all annotation model change listeners will be informed
+     * about the change.
+     *
+     * @param annotation the annotation whose associated position should be
+     *            modified
+     * @param position the position to whose values the associated position
+     *            should be changed
+     * @param fireModelChanged indicates whether to notify all model listeners
+     * @since 3.0
+     */
+    protected void modifyAnnotationPosition(Annotation annotation, Position position, bool fireModelChanged_) {
+        if (position is null) {
+            removeAnnotation(annotation, fireModelChanged_);
+        } else {
+            Position p= cast(Position) fAnnotations.get(annotation);
+            if (p !is null) {
+
+                if (position.getOffset() !is p.getOffset() || position.getLength() !is p.getLength()) {
+                    fDocument.removePosition(p);
+                    p.setOffset(position.getOffset());
+                    p.setLength(position.getLength());
+                    try {
+                        fDocument.addPosition(p);
+                    } catch (BadLocationException e) {
+                        // ignore invalid position
+                    }
+                }
+                synchronized (getLockObject()) {
+                    getAnnotationModelEvent().annotationChanged(annotation);
+                }
+                if (fireModelChanged_)
+                    fireModelChanged();
+
+            } else {
+                try {
+                    addAnnotation(annotation, position, fireModelChanged_);
+                } catch (BadLocationException x) {
+                    // ignore invalid position
+                }
+            }
+        }
+    }
+
+    /**
+     * Modifies the given annotation if the annotation is managed by this
+     * annotation model.
+     * <p>
+     * If requested, all annotation model change listeners will be informed
+     * about the change.
+     *
+     * @param annotation the annotation to be modified
+     * @param fireModelChanged indicates whether to notify all model listeners
+     * @since 3.0
+     */
+    protected void modifyAnnotation(Annotation annotation, bool fireModelChanged_) {
+        if (fAnnotations.containsKey(annotation)) {
+            synchronized (getLockObject()) {
+                getAnnotationModelEvent().annotationChanged(annotation);
+            }
+            if (fireModelChanged_)
+                fireModelChanged();
+        }
+    }
+
+    /*
+     * @see IAnnotationModel#removeAnnotationModelListener(IAnnotationModelListener)
+     */
+    public void removeAnnotationModelListener(IAnnotationModelListener listener) {
+        fAnnotationModelListeners.remove(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelExtension#attach(java.lang.Object, java.lang.Object)
+     * @since 3.0
+     */
+    public void addAnnotationModel(Object key, IAnnotationModel attachment) {
+        Assert.isNotNull(cast(Object)attachment);
+        if (!fAttachments.containsValue(cast(Object)attachment)) {
+            fAttachments.put(key, cast(Object)attachment);
+            for (int i= 0; i < fOpenConnections; i++)
+                attachment.connect(fDocument);
+            attachment.addAnnotationModelListener(fModelListener);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelExtension#get(java.lang.Object)
+     * @since 3.0
+     */
+    public IAnnotationModel getAnnotationModel(Object key) {
+        return cast(IAnnotationModel) fAttachments.get(key);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelExtension#detach(java.lang.Object)
+     * @since 3.0
+     */
+    public IAnnotationModel removeAnnotationModel(Object key) {
+        IAnnotationModel ret= cast(IAnnotationModel) fAttachments.remove(key);
+        if (ret !is null) {
+            for (int i= 0; i < fOpenConnections; i++)
+                ret.disconnect(fDocument);
+            ret.removeAnnotationModelListener(fModelListener);
+        }
+        return ret;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelExtension#getModificationStamp()
+     * @since 3.0
+     */
+    public Object getModificationStamp() {
+        return fModificationStamp;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/source/AnnotationModelEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,333 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.AnnotationModelEvent;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+
+
+
+
+
+
+import org.eclipse.jface.text.Position;
+
+
+/**
+ * Specification of changes applied to annotation models. The event carries the
+ * changed annotation model as well as added, removed, and modified annotations.
+ * <p>
+ * An event can be sealed. Afterwards it can not be modified. Thus, the normal
+ * process is that an empty event is created, filled with the changed
+ * information, and before it is sent to the listeners, the event is sealed.
+ *
+ * @see org.eclipse.jface.text.source.IAnnotationModel
+ * @see org.eclipse.jface.text.source.IAnnotationModelListenerExtension
+ * @since 2.0
+ */
+public class AnnotationModelEvent {
+
+    /** The model this event refers to. */
+    private IAnnotationModel fAnnotationModel;
+    /**
+     * The added annotations.
+     * @since 3.0
+     */
+    private Set fAddedAnnotations;
+    /**
+     * The removed annotations.
+     * @since 3.0
+     */
+    private Map fRemovedAnnotations;
+    /**
+     * The changed annotations.
+     * @since 3.0
+     */
+    private Set fChangedAnnotations;
+    /**
+     * Indicates that this event does not contain detailed information.
+     * @since 3.0
+     */
+    private bool fIsWorldChange;
+    /**
+     * The modification stamp.
+     * @since 3.0
+     */
+    private Object fModificationStamp;
+
+    /**
+     * Creates a new annotation model event for the given model.
+     *
+     * @param model the model
+     */
+    public this(IAnnotationModel model) {
+        this(model, true);
+    }
+
+    /**
+     * Creates a new annotation model event for the given model.
+     *
+     * @param model the model
+     * @param isWorldChange <code>true</code> if world change
+     * @since 3.0
+     */
+    public this(IAnnotationModel model, bool isWorldChange) {
+        fAddedAnnotations= new HashSet();
+        fRemovedAnnotations= new HashMap();
+        fChangedAnnotations= new HashSet();
+
+        fAnnotationModel= model;
+        fIsWorldChange= isWorldChange;
+    }
+
+    /**
+     * Returns the model this event refers to.
+     *
+     * @return the model this events belongs to
+     */
+    public IAnnotationModel getAnnotationModel() {
+        return fAnnotationModel;
+    }
+
+    /**
+     * Adds the given annotation to the set of annotations that are reported as
+     * being added from the model. If this event is considered a world change,
+     * it is no longer so after this method has successfully finished.
+     *
+     * @param annotation the added annotation
+     * @since 3.0
+     */
+    public void annotationAdded(Annotation annotation) {
+        fAddedAnnotations.add(annotation);
+        fIsWorldChange= false;
+    }
+
+    /**
+     * Returns the added annotations.
+     *
+     * @return the added annotations
+     * @since 3.0
+     */
+    public Annotation[] getAddedAnnotations() {
+        int size= fAddedAnnotations.size();
+        Annotation[] added= new Annotation[size];
+        fAddedAnnotations.toArray(added);
+        return added;
+    }
+
+    /**
+     * Adds the given annotation to the set of annotations that are reported as
+     * being removed from the model. If this event is considered a world
+     * change, it is no longer so after this method has successfully finished.
+     *
+     * @param annotation the removed annotation
+     * @since 3.0
+     */
+    public void annotationRemoved(Annotation annotation) {
+        annotationRemoved(annotation, null);
+    }
+
+    /**
+     * Adds the given annotation to the set of annotations that are reported as
+     * being removed from the model. If this event is considered a world
+     * change, it is no longer so after this method has successfully finished.
+     *
+     * @param annotation the removed annotation
+     * @param position the position of the removed annotation
+     * @since 3.0
+     */
+    public void annotationRemoved(Annotation annotation, Position position) {
+        fRemovedAnnotations.put(annotation, position);
+        fIsWorldChange= false;
+    }
+
+    /**
+     * Returns the removed annotations.
+     *
+     * @return the removed annotations
+     * @since 3.0
+     */
+    public Annotation[] getRemovedAnnotations() {
+        int size= fRemovedAnnotations.size();
+        Annotation[] removed= new Annotation[size];
+        fRemovedAnnotations.keySet().toArray(removed);
+        return removed;
+    }
+
+    /**
+     * Returns the position of the removed annotation at that point in time
+     * when the annotation has been removed.
+     *
+     * @param annotation the removed annotation
+     * @return the position of the removed annotation or <code>null</code>
+     * @since 3.0
+     */
+    public Position getPositionOfRemovedAnnotation(Annotation annotation) {
+        return cast(Position) fRemovedAnnotations.get(annotation);
+    }
+
+    /**
+     * Adds the given annotation to the set of annotations that are reported as
+     * being changed from the model. If this event is considered a world
+     * change, it is no longer so after this method has successfully finished.
+     *
+     * @param annotation the changed annotation
+     * @since 3.0
+     */
+    public void annotationChanged(Annotation annotation) {
+        fChangedAnnotations.add(annotation);
+        fIsWorldChange= false;
+    }
+
+    /**
+     * Returns the changed annotations.
+     *
+     * @return the changed annotations
+     * @since 3.0
+     */
+    public Annotation[] getChangedAnnotations() {
+        int size= fChangedAnnotations.size();
+        Annotation[] changed= new Annotation[size];
+        fChangedAnnotations.toArray(changed);
+        return changed;
+    }
+
+    /**
+     * Returns whether this annotation model event is empty or not. If this
+     * event represents a world change, this method returns <code>false</code>
+     * although the event does not carry any added, removed, or changed
+     * annotations.
+     *
+     * @return <code>true</code> if this event is empty
+     * @since 3.0
+     */
+    public bool isEmpty() {
+        return !fIsWorldChange && fAddedAnnotations.isEmpty() && fRemovedAnnotations.isEmpty() && fChangedAnnotations.isEmpty();
+    }
+
+    /**
+     * Returns whether this annotation model events contains detailed
+     * information about the modifications applied to the event annotation
+     * model or whether it represents a world change. I.e. everything in the
+     * model might have changed.
+     *
+     * @return <code>true</code> if world change, <code>false</code> otherwise
+     * @since 3.0
+     */
+    public bool isWorldChange() {
+        return fIsWorldChange;
+    }
+
+    /**
+     * Marks this event as world change according to the given flag.
+     *
+     * @param isWorldChange <code>true</code> if this event is a world change, <code>false</code> otherwise
+     * @since 3.0
+     */
+    void markWorldChange(bool isWorldChange) {
+        fIsWorldChange= isWorldChange;
+    }
+
+    /**
+     * Returns whether this annotation model event is still valid.
+     *
+     * @return <code>true</code> if this event is still valid, <code>false</code> otherwise
+     * @since 3.0
+     */
+    public bool isValid() {
+        if (fModificationStamp !is null && cast(IAnnotationModelExtension)fAnnotationModel ) {
+            IAnnotationModelExtension extension= cast(IAnnotationModelExtension) fAnnotationModel;
+            return fModificationStamp is extension.getModificationStamp();
+        }
+        return true;
+    }
+
+    /**
+     * Seals this event. Any direct modification to the annotation model after the event has been sealed
+     * invalidates this event.
+     *
+     * @since 3.0
+     */
+    public void markSealed() {
+        if ( cast(IAnnotationModelExtension)fAnnotationModel ) {
+            IAnnotationModelExtension extension= cast(IAnnotationModelExtension) fAnnotationModel;
+            fModificationStamp= extension.getModificationStamp();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/source/IAnnotationMap.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.IAnnotationMap;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+
+
+
+
+
+import org.eclipse.jface.text.ISynchronizable;
+
+
+/**
+ * An annotation map is a map specialized for the requirements of an annotation
+ * model. The annotation map supports a customizable lock object which is used
+ * to synchronize concurrent operations on the map (see
+ * {@link org.eclipse.jface.text.ISynchronizable}. The map supports two
+ * iterator methods, one for the values and one for the keys of the map. The
+ * returned iterators are robust, i.e. they work on a copy of the values and
+ * keys set that is made at the point in time the iterator methods are called.
+ * <p>
+ * The returned collections of the methods <code>values</code>,
+ * <code>entrySet</code>, and <code>keySet</code> are not synchronized on
+ * the annotation map's lock object.
+ * <p>
+ *
+ * @see org.eclipse.jface.text.source.IAnnotationModel
+ * @since 3.0
+ */
+public interface IAnnotationMap : Map, ISynchronizable {
+
+    /**
+     * Returns an iterator for a copy of this annotation map's values.
+     *
+     * @return an iterator for a copy of this map's values
+     */
+    Iterator valuesIterator();
+
+    /**
+     * Returns an iterator for a copy of this map's key set.
+     *
+     * @return an iterator for a copy of this map's key set
+     */
+    Iterator keySetIterator();
+
+    /**
+     * {@inheritDoc}
+     *
+     * The returned set is not synchronized on this annotation map's lock object.
+     */
+    Set entrySet();
+
+    /**
+     * {@inheritDoc}
+     *
+     * The returned set is not synchronized on this annotation map's lock object.
+     */
+    Set keySet();
+
+    /**
+     * {@inheritDoc}
+     *
+     * The returned collection is not synchronized on this annotation map's lock object.
+     */
+    Collection values();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/source/IAnnotationModel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,216 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.IAnnotationModel;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.Position;
+
+
+/**
+ * This interface defines the model for managing annotations attached to a document.
+ * The model maintains a set of annotations for a given document and notifies registered annotation
+ * model listeners about annotation model changes. It also provides methods
+ * for querying the current position of an annotation managed
+ * by this model.
+ * <p>
+ * In order to provide backward compatibility for clients of <code>IAnnotationModel</code>, extension
+ * interfaces are used to provide a means of evolution. The following extension interfaces
+ * exist:
+ * <ul>
+ * <li> {@link org.eclipse.jface.text.source.IAnnotationModelExtension} since version 3.0 introducing the concept
+ *      of model piggybacking annotation models, modification time stamps, and enhanced manipulation methods.
+ * </li>
+ * <li> {@link org.eclipse.jface.text.source.IAnnotationModelExtension2} since version 3.4 allows to retrieve
+ *      annotations within a given region.
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * Clients may implement this interface or use the default implementation provided
+ * by <code>AnnotationModel</code>.
+ *
+ * @see org.eclipse.jface.text.source.IAnnotationModelExtension
+ * @see org.eclipse.jface.text.source.IAnnotationModelExtension2
+ * @see org.eclipse.jface.text.source.Annotation
+ * @see org.eclipse.jface.text.source.IAnnotationModelListener
+ */
+public interface IAnnotationModel {
+
+    /**
+     * Registers the annotation model listener with this annotation model.
+     * After registration listener is informed about each change of this model.
+     * If the listener is already registered nothing happens.
+     *
+     * @param listener the listener to be registered, may not be <code>null</code>
+     */
+    void addAnnotationModelListener(IAnnotationModelListener listener);
+
+    /**
+     * Removes the listener from the model's list of annotation model listeners.
+     * If the listener is not registered with the model nothing happens.
+     *
+     * @param listener the listener to be removed, may not be <code>null</code>
+     */
+    void removeAnnotationModelListener(IAnnotationModelListener listener);
+
+    /**
+     * Connects the annotation model to a document. The annotations managed
+     * by this model must subsequently update according to the changes applied
+     * to the document. Once an annotation model is connected to a document,
+     * all further <code>connect</code> calls must mention the document the
+     * model is already connected to. An annotation model primarily uses
+     * <code>connect</code> and <code>disconnect</code> for reference counting
+     * the document. Reference counting frees the clients from keeping tracker
+     * whether a model has already been connected to a document.
+     *
+     * @param document the document the model gets connected to,
+     *      may not be <code>null</code>
+     *
+     * @see #disconnect(IDocument)
+     */
+    void connect(IDocument document);
+
+    /**
+     * Disconnects this model from a document. After that, document changes no longer matter.
+     * An annotation model may only be disconnected from a document to which it has been
+     * connected before. If the model reference counts the connections to a document,
+     * the connection to the document may only be terminated if the reference count does
+     * down to 0.
+     *
+     * @param document the document the model gets disconnected from,
+     *      may not be <code>null</code>
+     *
+     * @see #connect(IDocument) for further specification details
+     */
+    void disconnect(IDocument document);
+
+    /**
+     * Adds a annotation to this annotation model. The annotation is associated with
+     * with the given position which describes the range covered by the annotation.
+     * All registered annotation model listeners are informed about the change.
+     * If the model is connected to a document, the position is automatically
+     * updated on document changes. If the annotation is already managed by
+     * this annotation model or is not a valid position in the connected document
+     * nothing happens.
+     * <p>
+     * <strong>Performance hint:</strong> Use {@link IAnnotationModelExtension#replaceAnnotations(Annotation[], java.util.Map)}
+     * if several annotations are added and/or removed.
+     * </p>
+     *
+     * @param annotation the annotation to add, may not be <code>null</code>
+     * @param position the position describing the range covered by this annotation,
+     *      may not be <code>null</code>
+     */
+    void addAnnotation(Annotation annotation, Position position);
+
+    /**
+     * Removes the given annotation from the model. I.e. the annotation is no
+     * longer managed by this model. The position associated with the annotation
+     * is no longer updated on document changes. If the annotation is not
+     * managed by this model, nothing happens.
+     * <p>
+     * <strong>Performance hint:</strong> Use {@link IAnnotationModelExtension#replaceAnnotations(Annotation[], java.util.Map)}
+     * if several annotations are removed and/or added.
+     * </p>
+     *
+     * @param annotation the annotation to be removed from this model,
+     *      may not be <code>null</code>
+     */
+    void removeAnnotation(Annotation annotation);
+
+    /**
+     * Returns all annotations managed by this model.
+     *
+     * @return all annotations managed by this model (element type: {@link Annotation})
+     */
+    Iterator getAnnotationIterator();
+
+    /**
+     * Returns the position associated with the given annotation.
+     *
+     * @param annotation the annotation whose position should be returned
+     * @return the position of the given annotation or <code>null</code> if no
+     *      associated annotation exists
+     */
+    Position getPosition(Annotation annotation);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/source/IAnnotationModelExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.source.IAnnotationModelExtension;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Map;
+
+
+
+import org.eclipse.jface.text.Position;
+
+
+/**
+ * Extends {@link org.eclipse.jface.text.source.IAnnotationModel}with the
+ * ability piggyback other annotation models. It also introduces the concept of
+ * modification time stamps and adds methods for richer manipulation methods.
+ *
+ * @since 3.0
+ */
+public interface IAnnotationModelExtension {
+
+    /**
+     * Attaches <code>attachment</code> to the receiver. Connects
+     * <code>attachment</code> to the currently connected document. If
+     * <code>attachment</code> is already attached (even) under a different
+     * key), it is not attached again.
+     *
+     * @param key the key through which the attachment is identified.
+     * @param attachment the attached <code>IAnnotationModel</code>
+     */
+    void addAnnotationModel(Object key, IAnnotationModel attachment);
+
+    /**
+     * Returns the attached <code>IAnnotationModel</code> for <code>key</code>,
+     * or <code>null</code> if none is attached for <code>key</code>.
+     *
+     * @param key the key through which the attachment is identified.
+     * @return an <code>IAnnotationModel</code> attached under
+     *         <code>key</code>, or <code>null</code>
+     */
+    IAnnotationModel getAnnotationModel(Object key);
+
+    /**
+     * Removes and returns the attached <code>IAnnotationModel</code> for
+     * <code>key</code>.
+     *
+     * @param key the key through which the attachment is identified.
+     * @return an <code>IAnnotationModel</code> attached under
+     *         <code>key</code>, or <code>null</code>
+     */
+    IAnnotationModel removeAnnotationModel(Object key);
+
+    /**
+     * Adds and removes annotations to/from this annotation model in a single
+     * step. The annotations to remove are given in an array. The annotations to
+     * add are provided in a map associating the annotations with the positions
+     * at which they should be added. All registered annotation model listeners
+     * are informed about the change. If the model is connected to a document,
+     * the positions are automatically updated on document changes. Annotations
+     * that are already managed by this annotation model or are not associated
+     * with a valid position in the connected document have no effect.
+     *
+     * @param annotationsToRemove the annotations to be removed, may be
+     *            <code>null</code>
+     * @param annotationsToAdd the annotations which will be added, may be
+     *            <code>null</code> each map entry has an
+     *            <code>Annotation</code> as key and a <code>Position</code>
+     *            as value
+     * @throws ClassCastException if one of the map key or values has a wrong
+     *             type
+     */
+    void replaceAnnotations(Annotation[] annotationsToRemove, Map annotationsToAdd) ;
+
+    /**
+     * Modifies the position associated with the given annotation to equal the
+     * given position. If the annotation is not yet managed by this annotation
+     * model, the annotation is added. If the given position is
+     * <code>null</code> the annotation is removed from the model. All
+     * annotation model change listeners will be informed about the change.
+     *
+     * @param annotation the annotation whose associated position should be
+     *            modified
+     * @param position the position to whose values the associated position
+     *            should be changed
+     */
+    void modifyAnnotationPosition(Annotation annotation, Position position);
+
+    /**
+     * Removes all annotations from this annotation model.
+     */
+    void removeAllAnnotations();
+
+    /**
+     * Returns the modification stamp of this annotation model.
+     *
+     * @return the modification stamp of this annotation model
+     */
+    Object getModificationStamp();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/source/IAnnotationModelExtension2.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.IAnnotationModelExtension2;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Set;
+
+
+
+/**
+ * Extends {@link org.eclipse.jface.text.source.IAnnotationModel} with the
+ * ability to retrieve a set of annotations within a given region.
+ *
+ * @since 3.4
+ */
+public interface IAnnotationModelExtension2 {
+
+    /**
+     * Returns an iterator over all annotations managed by this model that are
+     * inside the given region.
+     * 
+     * @param offset the start position of the region, must be >= 0
+     * @param length the length of the region, must be >= 0
+     * @param canStartBefore if <code>true</code> then annotations are included
+     *            which start before the region if they end at or after the region's start
+     * @param canEndAfter if <code>true</code> then annotations are included
+     *            which end after the region if they start at or before the region's end
+     * @return all annotations inside the region managed by this model (element type: {@link Annotation})
+     */
+    Iterator getAnnotationIterator(int offset, int length, bool canStartBefore, bool canEndAfter);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/source/IAnnotationModelListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.IAnnotationModelListener;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Interface for objects interested in getting informed about annotation model
+ * changes. Changes are the addition or removal of annotations managed by the
+ * model. Clients may implement this interface.
+ *
+ * In order to provided backward compatibility for clients of
+ * <code>IAnnotationModelListener</code>, extension interfaces are used to
+ * provide a means of evolution. The following extension interfaces exist:
+ * <ul>
+ * <li>{@link org.eclipse.jface.text.source.IAnnotationModelListenerExtension}
+ *     since version 2.0 replacing the change notification mechanisms.</li>
+ * </ul>
+ *
+ * @see org.eclipse.jface.text.source.IAnnotationModel
+ */
+public interface IAnnotationModelListener {
+
+    /**
+     * Called if a model change occurred on the given model.<p>
+     * Replaced by {@link IAnnotationModelListenerExtension#modelChanged(AnnotationModelEvent)}.
+     *
+     * @param model the changed annotation model
+     */
+    void modelChanged(IAnnotationModel model);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/source/IAnnotationModelListenerExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.source.IAnnotationModelListenerExtension;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * Extension interface for {@link IAnnotationModelListener}. Introduces a
+ * notification mechanism that notifies the user by means of <code>AnnotationModelEvent</code>s.
+ * Thus, more detailed information can be sent to the listener. This mechanism replaces the original notification
+ * mechanism of <code>IAnnotationModelListener</code>.
+ *
+ * @since 2.0
+ */
+public interface IAnnotationModelListenerExtension {
+
+    /**
+     * Called if a model change occurred on the given model.
+     *
+     * @param event the event to be sent out
+     */
+    void modelChanged(AnnotationModelEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/templates/DocumentTemplateContext.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,214 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.templates.DocumentTemplateContext;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.Position;
+
+/**
+ * Instances of this class describe the context of a template as a region of
+ * a document. That region may be either specified by its offset and length, or
+ * by a <code>Position</code> which may or may not be registered with the
+ * document.
+ * <p>
+ * Clients may instantiate and extend this class.
+ * </p>
+ *
+ * @since 3.0
+ */
+public class DocumentTemplateContext : TemplateContext {
+
+    /** The text of the document. */
+    private const IDocument fDocument;
+    /**
+     * The region of the document described by this context. We store a
+     * position since clients may specify the document region as (updateable)
+     * Positions.
+     */
+    private const Position fPosition;
+    /**
+     * The original offset of this context. Will only be updated by the setter
+     * method.
+     */
+    private int fOriginalOffset;
+    /**
+     * The original length of this context. Will only be updated by the setter
+     * method.
+     */
+    private int fOriginalLength;
+
+    /**
+     * Creates a document template context.
+     *
+     * @param type the context type
+     * @param document the document this context applies to
+     * @param offset the offset of the document region
+     * @param length the length of the document region
+     */
+    public this(TemplateContextType type, IDocument document, int offset, int length) {
+        this(type, document, new Position(offset, length));
+    }
+
+    /**
+     * Creates a document template context. The supplied <code>Position</code>
+     * will be queried to compute the <code>getStart</code> and
+     * <code>getEnd</code> methods, which will therefore answer updated
+     * position data if it is registered with the document.
+     *
+     * @param type the context type
+     * @param document the document this context applies to
+     * @param position the position describing the area of the document which
+     *        forms the template context
+     * @since 3.1
+     */
+    public this(TemplateContextType type, IDocument document, Position position) {
+        super(type);
+
+        Assert.isNotNull(cast(Object)document);
+        Assert.isNotNull(position);
+        Assert.isTrue(position.getOffset() <= document.getLength());
+
+        fDocument= document;
+        fPosition= position;
+        fOriginalOffset= fPosition.getOffset();
+        fOriginalLength= fPosition.getLength();
+    }
+
+    /**
+     * Returns the document.
+     *
+     * @return the document
+     */
+    public IDocument getDocument() {
+        return fDocument;
+    }
+
+    /**
+     * Returns the completion offset within the string of the context.
+     *
+     * @return the completion offset within the string of the context
+     */
+    public int getCompletionOffset() {
+        return fOriginalOffset;
+    }
+
+    /**
+     * Sets the completion offset.
+     *
+     * @param newOffset the new completion offset
+     */
+    protected void setCompletionOffset(int newOffset) {
+        fOriginalOffset= newOffset;
+        fPosition.setOffset(newOffset);
+    }
+
+    /**
+     * Returns the completion length within the string of the context.
+     *
+     * @return the completion length within the string of the context
+     */
+    public int getCompletionLength() {
+        return fOriginalLength;
+    }
+
+    /**
+     * Sets the completion length.
+     *
+     * @param newLength the new completion length
+     */
+    protected void setCompletionLength(int newLength) {
+        fOriginalLength= newLength;
+        fPosition.setLength(newLength);
+    }
+
+    /**
+     * Returns the keyword which triggered template insertion.
+     *
+     * @return the keyword which triggered template insertion
+     */
+    public String getKey() {
+        int offset= getStart();
+        int length= getEnd() - offset;
+        try {
+            return fDocument.get(offset, length);
+        } catch (BadLocationException e) {
+            return ""; //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Returns the beginning offset of the keyword.
+     *
+     * @return the beginning offset of the keyword
+     */
+    public int getStart() {
+        return fPosition.getOffset();
+    }
+
+    /**
+     * Returns the end offset of the keyword.
+     *
+     * @return the end offset of the keyword
+     */
+    public int getEnd() {
+        return fPosition.getOffset() + fPosition.getLength();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.templates.TemplateContext#canEvaluate(org.eclipse.jface.text.templates.Template)
+     */
+    public bool canEvaluate(Template template_) {
+        return true;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.templates.TemplateContext#evaluate(org.eclipse.jface.text.templates.Template)
+     */
+    public TemplateBuffer evaluate(Template template_)  {
+        if (!canEvaluate(template_))
+            return null;
+
+        TemplateTranslator translator= new TemplateTranslator();
+        TemplateBuffer buffer= translator.translate(template_);
+
+        getContextType().resolve(buffer, this);
+
+        return buffer;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/templates/GlobalTemplateVariables.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,204 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Sebastian Davids: sdavids@gmx.de - see bug 25376
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.templates.GlobalTemplateVariables;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+// import com.ibm.icu.text.DateFormat;
+// import com.ibm.icu.util.Calendar;
+
+/**
+ * Global variables which are available in any context.
+ * <p>
+ * Clients may instantiate the classes contained within this class.
+ * </p>
+ *
+ * @since 3.0
+ */
+public class GlobalTemplateVariables {
+
+    /** The type of the selection variables. */
+    public static const String SELECTION= "selection"; //$NON-NLS-1$
+
+    /**
+     * The cursor variable determines the cursor placement after template edition.
+     */
+    public static class Cursor : SimpleTemplateVariableResolver {
+
+        /** Name of the cursor variable, value= {@value} */
+        public static const String NAME= "cursor"; //$NON-NLS-1$
+
+        /**
+         * Creates a new cursor variable
+         */
+        public this() {
+            super(NAME, TextTemplateMessages.getString("GlobalVariables.variable.description.cursor")); //$NON-NLS-1$
+            setEvaluationString(""); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * The word selection variable determines templates that work on a full
+     * lines selection.
+     */
+    public static class WordSelection : SimpleTemplateVariableResolver {
+
+        /** Name of the word selection variable, value= {@value} */
+        public static const String NAME= "word_selection"; //$NON-NLS-1$
+
+        /**
+         * Creates a new word selection variable
+         */
+        public this() {
+            super(NAME, TextTemplateMessages.getString("GlobalVariables.variable.description.selectedWord")); //$NON-NLS-1$
+        }
+        protected String resolve(TemplateContext context) {
+            String selection= context.getVariable(SELECTION);
+            if (selection is null)
+                return ""; //$NON-NLS-1$
+            return selection;
+        }
+    }
+
+    /**
+     * The line selection variable determines templates that work on selected
+     * lines.
+     */
+    public static class LineSelection : SimpleTemplateVariableResolver {
+
+        /** Name of the line selection variable, value= {@value} */
+        public static const String NAME= "line_selection"; //$NON-NLS-1$
+
+        /**
+         * Creates a new line selection variable
+         */
+        public this() {
+            super(NAME, TextTemplateMessages.getString("GlobalVariables.variable.description.selectedLines")); //$NON-NLS-1$
+        }
+        protected String resolve(TemplateContext context) {
+            String selection= context.getVariable(SELECTION);
+            if (selection is null)
+                return ""; //$NON-NLS-1$
+            return selection;
+        }
+    }
+
+    /**
+     * The dollar variable inserts an escaped dollar symbol.
+     */
+    public static class Dollar : SimpleTemplateVariableResolver {
+        /**
+         * Creates a new dollar variable
+         */
+        public this() {
+            super("dollar", TextTemplateMessages.getString("GlobalVariables.variable.description.dollar")); //$NON-NLS-1$ //$NON-NLS-2$
+            setEvaluationString("$"); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * The date variable evaluates to the current date.
+     */
+    public static class Date : SimpleTemplateVariableResolver {
+        /**
+         * Creates a new date variable
+         */
+        public this() {
+            super("date", TextTemplateMessages.getString("GlobalVariables.variable.description.date")); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+        protected String resolve(TemplateContext context) {
+            implMissing(__FILE__,__LINE__);
+            return null;
+            //return DateFormat.getDateInstance().format(new java.util.Date());
+        }
+    }
+
+    /**
+     * The year variable evaluates to the current year.
+     */
+    public static class Year : SimpleTemplateVariableResolver {
+        /**
+         * Creates a new year variable
+         */
+        public this() {
+            super("year", TextTemplateMessages.getString("GlobalVariables.variable.description.year")); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+        protected String resolve(TemplateContext context) {
+            implMissing(__FILE__,__LINE__);
+            return null;
+            //return Integer.toString(Calendar.getInstance().get(Calendar.YEAR));
+        }
+    }
+
+    /**
+     * The time variable evaluates to the current time.
+     */
+    public static class Time : SimpleTemplateVariableResolver {
+        /**
+         * Creates a new time variable
+         */
+        public this() {
+            super("time", TextTemplateMessages.getString("GlobalVariables.variable.description.time")); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        protected String resolve(TemplateContext context) {
+            implMissing(__FILE__,__LINE__);
+            return null;
+            //return DateFormat.getTimeInstance().format(new java.util.Date());
+        }
+    }
+
+    /**
+     * The user variable evaluates to the current user.
+     */
+    public static class User : SimpleTemplateVariableResolver {
+        /**
+         * Creates a new user name variable
+         */
+        public this() {
+            super("user", TextTemplateMessages.getString("GlobalVariables.variable.description.user")); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        protected String resolve(TemplateContext context) {
+            return System.getProperty("user.name"); //$NON-NLS-1$
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/templates/SimpleTemplateVariableResolver.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.templates.SimpleTemplateVariableResolver;
+
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+/**
+ * A simple template variable resolver, which always evaluates to a defined string.
+ * <p>
+ * Clients may instantiate and extend this class.
+ * </p>
+ *
+ * @since 3.0
+ */
+public class SimpleTemplateVariableResolver : TemplateVariableResolver {
+
+    /** The string to which this variable evaluates. */
+    private String fEvaluationString;
+
+    /*
+     * @see TemplateVariableResolver#TemplateVariableResolver(String, String)
+     */
+    protected this(String type, String description) {
+        super(type, description);
+    }
+
+    /**
+     * Sets the string to which this variable evaluates.
+     *
+     * @param evaluationString the evaluation string, may be <code>null</code>.
+     */
+    public final void setEvaluationString(String evaluationString) {
+        fEvaluationString= evaluationString;
+    }
+
+    /*
+     * @see TemplateVariableResolver#evaluate(TemplateContext)
+     */
+    protected String resolve(TemplateContext context) {
+        return fEvaluationString;
+    }
+
+    /**
+     * Returns always <code>true</code>, since simple variables are normally
+     * unambiguous.
+     *
+     * @param context {@inheritDoc}
+     * @return <code>true</code>
+     */
+    protected bool isUnambiguous(TemplateContext context) {
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/templates/Template.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.templates.Template;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * A template consisting of a name and a pattern.
+ * <p>
+ * Clients may instantiate this class. May become final in the future.
+ * </p>
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class Template {
+
+    private alias .toHash toHash;
+    private alias .equals equals;
+
+    /** The name of this template */
+    private /*final*/ String fName;
+    /** A description of this template */
+    private /*final*/ String fDescription;
+    /** The name of the context type of this template */
+    private /*final*/ String fContextTypeId;
+    /** The template pattern. */
+    private /*final*/ String fPattern;
+    /**
+     * The auto insertable property.
+     * @since 3.1
+     */
+    private const bool fIsAutoInsertable;
+
+    /**
+     * Creates an empty template.
+     */
+    public this() {
+        this("", "", "", "", true); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+    }
+
+    /**
+     * Creates a copy of a template.
+     *
+     * @param template the template to copy
+     */
+    public this(Template template_) {
+        this(template_.getName(), template_.getDescription(), template_.getContextTypeId(), template_.getPattern(), template_.isAutoInsertable());
+    }
+
+    /**
+     * Creates a template.
+     *
+     * @param name the name of the template
+     * @param description the description of the template
+     * @param contextTypeId the id of the context type in which the template can be applied
+     * @param pattern the template pattern
+     * @deprecated as of 3.1 replaced by {@link #Template(String, String, String, String, bool)}
+     */
+    public this(String name, String description, String contextTypeId, String pattern) {
+        this(name, description, contextTypeId, pattern, true); // templates are auto insertable per default
+    }
+
+    /**
+     * Creates a template.
+     *
+     * @param name the name of the template
+     * @param description the description of the template
+     * @param contextTypeId the id of the context type in which the template can be applied
+     * @param pattern the template pattern
+     * @param isAutoInsertable the auto insertable property of the template
+     * @since 3.1
+     */
+    public this(String name, String description, String contextTypeId, String pattern, bool isAutoInsertable) {
+        Assert.isNotNull(description);
+        fDescription= description;
+        fName= name;
+        Assert.isNotNull(contextTypeId);
+        fContextTypeId= contextTypeId;
+        fPattern= pattern;
+        fIsAutoInsertable= isAutoInsertable;
+    }
+
+    /*
+     * @see Object#hashCode()
+     */
+    public override hash_t toHash() {
+        return fName.toHash() ^ fPattern.toHash() ^ fContextTypeId.toHash();
+    }
+
+    /**
+     * Sets the description of the template.
+     *
+     * @param description the new description
+     * @deprecated Templates should never be modified
+     */
+    public void setDescription(String description) {
+        Assert.isNotNull(description);
+        fDescription= description;
+    }
+
+    /**
+     * Returns the description of the template.
+     *
+     * @return the description of the template
+     */
+    public String getDescription() {
+        return fDescription;
+    }
+
+    /**
+     * Sets the name of the context type in which the template can be applied.
+     *
+     * @param contextTypeId the new context type name
+     * @deprecated Templates should never be modified
+     */
+    public void setContextTypeId(String contextTypeId) {
+        Assert.isNotNull(contextTypeId);
+        fContextTypeId= contextTypeId;
+    }
+
+    /**
+     * Returns the id of the context type in which the template can be applied.
+     *
+     * @return the id of the context type in which the template can be applied
+     */
+    public String getContextTypeId() {
+        return fContextTypeId;
+    }
+
+    /**
+     * Sets the name of the template.
+     *
+     * @param name the name of the template
+     * @deprecated Templates should never be modified
+     */
+    public void setName(String name) {
+        fName= name;
+    }
+
+    /**
+     * Returns the name of the template.
+     *
+     * @return the name of the template
+     */
+    public String getName() {
+        return fName;
+    }
+
+    /**
+     * Sets the pattern of the template.
+     *
+     * @param pattern the new pattern of the template
+     * @deprecated Templates should never be modified
+     */
+    public void setPattern(String pattern) {
+        fPattern= pattern;
+    }
+
+    /**
+     * Returns the template pattern.
+     *
+     * @return the template pattern
+     */
+    public String getPattern() {
+        return fPattern;
+    }
+
+    /**
+     * Returns <code>true</code> if template is enabled and matches the context,
+     * <code>false</code> otherwise.
+     *
+     * @param prefix the prefix (e.g. inside a document) to match
+     * @param contextTypeId the context type id to match
+     * @return <code>true</code> if template is enabled and matches the context,
+     * <code>false</code> otherwise
+     */
+    public bool matches(String prefix, String contextTypeId) {
+        return fContextTypeId.equals(contextTypeId);
+    }
+
+    /*
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public bool equals(Object o) {
+        if (!( cast(Template)o ))
+            return false;
+
+        Template t= cast(Template) o;
+        if (t is this)
+            return true;
+
+        return t.fName.equals(fName)
+                && t.fPattern.equals(fPattern)
+                && t.fContextTypeId.equals(fContextTypeId)
+                && t.fDescription.equals(fDescription)
+                && t.fIsAutoInsertable is fIsAutoInsertable;
+    }
+
+    /**
+     * Returns the auto insertable property of the template.
+     *
+     * @return the auto insertable property of the template
+     * @since 3.1
+     */
+    public bool isAutoInsertable() {
+        return fIsAutoInsertable;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateBuffer.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.templates.TemplateBuffer;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * A template buffer is a container for a string and variables.
+ * <p>
+ * Clients may instantiate this class.
+ * </p>
+ *
+ * @since 3.0
+ */
+public final class TemplateBuffer {
+
+    /** The string of the template buffer */
+    private String fString;
+    /** The variable positions of the template buffer */
+    private TemplateVariable[] fVariables;
+
+    /**
+     * Creates a template buffer.
+     *
+     * @param string the string
+     * @param variables the variable positions
+     */
+    public this(String string, TemplateVariable[] variables) {
+        setContent(string, variables);
+    }
+
+    /**
+     * Sets the content of the template buffer.
+     *
+     * @param string the string
+     * @param variables the variable positions
+     */
+    public final void setContent(String string, TemplateVariable[] variables) {
+        Assert.isNotNull(string);
+//         Assert.isNotNull(variables);
+
+        // XXX assert non-overlapping variable properties
+
+        fString= string;
+        fVariables= copy(variables);
+    }
+
+    /**
+     * Returns a copy of the given array.
+     *
+     * @param array the array to be copied
+     * @return a copy of the given array or <code>null</code> when <code>array</code> is <code>null</code>
+     * @since 3.1
+     */
+    private static TemplateVariable[] copy(TemplateVariable[] array) {
+        if (array !is null) {
+            TemplateVariable[] copy= new TemplateVariable[array.length];
+            System.arraycopy(array, 0, copy, 0, array.length);
+            return copy;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the string of the template buffer.
+     *
+     * @return the string representation of the template buffer
+     */
+    public final String getString() {
+        return fString;
+    }
+
+    /**
+     * Returns the variable positions of the template buffer. The returned array is
+     * owned by this variable and must not be modified.
+     *
+     * @return the variable positions of the template buffer
+     */
+    public final TemplateVariable[] getVariables() {
+        return fVariables;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateContext.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.templates.TemplateContext;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+
+
+import org.eclipse.jface.text.BadLocationException;
+
+/**
+ * Provides the context for a <code>Template</code> being resolved. Keeps track
+ * of resolved variables.
+ * <p>
+ * Clients may extend this class.
+ * </p>
+ *
+ * @since 3.0
+ */
+public abstract class TemplateContext {
+
+    /** The context type of this context */
+    private const TemplateContextType fContextType;
+    /** Additional variables. */
+    private const Map fVariables;
+    /** A flag to indicate that the context should not be modified. */
+    private bool fReadOnly;
+
+    /**
+     * Creates a template context of a particular context type.
+     *
+     * @param contextType the context type of this context
+     */
+    protected this(TemplateContextType contextType) {
+        fVariables= new HashMap();
+        fContextType= contextType;
+        fReadOnly= true;
+    }
+
+    /**
+     * Returns the context type of this context.
+     *
+     * @return the context type of this context
+     */
+    public TemplateContextType getContextType() {
+        return fContextType;
+    }
+
+    /**
+     * Sets or clears the read-only flag.
+     *
+     * @param readOnly the new read-only state
+     */
+    public void setReadOnly(bool readOnly) {
+        fReadOnly= readOnly;
+    }
+
+    /**
+     * Returns <code>true</code> if the receiver is read-only, <code>false</code> otherwise.
+     *
+     * @return <code>true</code> if the receiver is read-only, <code>false</code> otherwise
+     */
+    public bool isReadOnly() {
+        return fReadOnly;
+    }
+
+    /**
+     * Defines the value of a variable.
+     *
+     * @param name the name of the variable
+     * @param value the value of the variable, <code>null</code> to un-define a variable
+     */
+    public void setVariable(String name, String value) {
+        fVariables.put(name, value);
+    }
+
+    /**
+     * Returns the value of a defined variable.
+     *
+     * @param name the name of the variable
+     * @return returns the value of the variable, <code>null</code> if the variable was not defined
+     */
+    public String getVariable(String name) {
+        return stringcast( fVariables.get(name));
+    }
+
+    /**
+     * Evaluates the template in this context and returns a template buffer.
+     * <p>
+     * Evaluation means translating the template into a <code>TemplateBuffer</code>,
+     * resolving the defined variables in this context and possibly formatting
+     * the resolved buffer.</p>
+     *
+     * @param template the template to evaluate
+     * @return returns the buffer with the evaluated template or <code>null</code> if the buffer could not be created
+     * @throws BadLocationException if evaluation fails due to concurrently changed documents etc.
+     * @throws TemplateException if the template specification is not valid
+     */
+    public abstract TemplateBuffer evaluate(Template template_);
+
+    /**
+     * Tests if the specified template can be evaluated in this context.
+     * <p>Examples are templates defined for a different context (e.g. a javadoc
+     * template cannot be evaluated in Java context).</p>
+     *
+     * @param template the <code>Template</code> to check
+     * @return <code>true</code> if <code>template</code> can be evaluated
+     *         in this context, <code>false</code> otherwise
+     */
+    public abstract bool canEvaluate(Template template_);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateContextType.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,331 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.templates.TemplateContextType;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+
+import java.lang.all;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+
+
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.RangeMarker;
+import org.eclipse.text.edits.ReplaceEdit;
+import org.eclipse.text.edits.TextEdit;
+
+
+/**
+ * A context type defines a context within which templates are resolved. It
+ * stores a number of <code>TemplateVariableResolver</code>s. A
+ * <code>TemplateBuffer</code> can be resolved in a
+ * <code>TemplateContext</code> using the
+ * {@link #resolve(TemplateBuffer, TemplateContext)} method.
+ * <p>
+ * Clients may extend this class.
+ * </p>
+ *
+ * @since 3.0
+ */
+public class TemplateContextType {
+
+    /** The id of the context type. */
+    private /* final */ String fId= null;
+
+    /** Variable resolvers used by this content type. */
+    private const Map fResolvers;
+
+    /** The name of the context type. */
+    private String fName= null;
+
+    /**
+     * Creates a context type with an identifier. The identifier must be unique,
+     * a qualified name is suggested. The id is also used as name.
+     *
+     * @param id the unique identifier of the context type
+     */
+    public this(String id) {
+        this(id, id);
+    }
+
+    /**
+     * Creates a context type with an identifier. The identifier must be unique, a qualified name is suggested.
+     *
+     * @param id the unique identifier of the context type
+     * @param name the name of the context type
+     */
+    public this(String id, String name) {
+        fResolvers= new HashMap();
+
+        Assert.isNotNull(id);
+        Assert.isNotNull(name);
+        fId= id;
+        fName= name;
+    }
+
+    /**
+     * Returns the id of the context type.
+     *
+     * @return the id of the receiver
+     */
+    public String getId() {
+        return fId;
+    }
+
+
+    /**
+     * Returns the name of the context type.
+     *
+     * @return the name of the context type
+     */
+    public String getName() {
+        return fName;
+    }
+
+    /**
+     * Creates a context type with a <code>null</code> identifier.
+     * <p>
+     * This is a framework-only constructor that exists only so that context
+     * types can be contributed via an extension point and that should not be
+     * called in client code except for subclass constructors; use
+     * {@link #TemplateContextType(String)} instead.
+     * </p>
+     */
+    public this() {
+        fResolvers= new HashMap();
+    }
+
+    /**
+     * Sets the id of this context.
+     * <p>
+     * This is a framework-only method that exists solely so that context types
+     * can be contributed via an extension point and that should not be called
+     * in client code; use {@link #TemplateContextType(String)} instead.
+     * </p>
+     *
+     * @param id the identifier of this context
+     * @throws RuntimeException an unspecified exception if the id has already
+     *         been set on this context type
+     */
+    public final void setId(String id)  {
+        Assert.isNotNull(id);
+        Assert.isTrue(fId is null); // may only be called once when the context is instantiated
+        fId= id;
+    }
+
+    /**
+     * Sets the name of the context type.
+     *
+     * <p>
+     * This is a framework-only method that exists solely so that context types
+     * can be contributed via an extension point and that should not be called
+     * in client code; use {@link #TemplateContextType(String, String)} instead.
+     * </p>
+     *
+     * @param name the name of the context type
+     */
+    public final void setName(String name) {
+        Assert.isTrue(fName is null); // only initialized by extension code
+        fName= name;
+    }
+
+    /**
+     * Adds a variable resolver to the context type. If there already is a resolver
+     * for the same type, the previous one gets replaced by <code>resolver</code>.
+     *
+     * @param resolver the resolver to be added under its name
+     */
+    public void addResolver(TemplateVariableResolver resolver) {
+        Assert.isNotNull(resolver);
+        fResolvers.put(resolver.getType(), resolver);
+    }
+
+    /**
+     * Removes a template variable from the context type.
+     *
+     * @param resolver the variable to be removed
+     */
+    public void removeResolver(TemplateVariableResolver resolver) {
+        Assert.isNotNull(resolver);
+        fResolvers.remove(resolver.getType());
+    }
+
+    /**
+     * Removes all template variables from the context type.
+     */
+    public void removeAllResolvers() {
+        fResolvers.clear();
+    }
+
+    /**
+     * Returns an iterator for the variables known to the context type.
+     *
+     * @return an iterator over the variables in this context type
+     */
+    public Iterator resolvers() {
+        return Collections.unmodifiableMap(fResolvers).values().iterator();
+    }
+
+    /**
+     * Returns the resolver for the given type.
+     *
+     * @param type the type for which a resolver is needed
+     * @return a resolver for the given type, or <code>null</code> if none is registered
+     */
+    protected TemplateVariableResolver getResolver(String type) {
+        return cast(TemplateVariableResolver) fResolvers.get(type);
+    }
+
+    /**
+     * Validates a pattern, a <code>TemplateException</code> is thrown if
+     * validation fails.
+     *
+     * @param pattern the template pattern to validate
+     * @throws TemplateException if the pattern is invalid
+     */
+    public void validate(String pattern)  {
+        TemplateTranslator translator= new TemplateTranslator();
+        TemplateBuffer buffer= translator.translate(pattern);
+        validateVariables(buffer.getVariables());
+    }
+
+    /**
+     * Validates the variables in this context type. If a variable is not valid,
+     * e.g. if its type is not known in this context type, a
+     * <code>TemplateException</code> is thrown.
+     * <p>
+     * The default implementation does nothing.
+     * </p>
+     *
+     * @param variables the variables to validate
+     * @throws TemplateException if one of the variables is not valid in this
+     *         context type
+     */
+    protected void validateVariables(TemplateVariable[] variables)  {
+    }
+
+    /**
+     * Resolves the variables in <code>buffer</code> within <code>context</code>
+     * and edits the template buffer to reflect the resolved variables.
+     *
+     * @param buffer the template buffer
+     * @param context the template context
+     * @throws MalformedTreeException if the positions in the buffer overlap
+     * @throws BadLocationException if the buffer cannot be successfully modified
+     */
+    public void resolve(TemplateBuffer buffer, TemplateContext context)  {
+        Assert.isNotNull(context);
+        TemplateVariable[] variables= buffer.getVariables();
+
+        List positions= variablesToPositions(variables);
+        List edits= new ArrayList(5);
+
+        // iterate over all variables and try to resolve them
+        for (int i= 0; i !is variables.length; i++) {
+            TemplateVariable variable= variables[i];
+
+            if (!variable.isResolved())
+                resolve(variable, context);
+
+            String value= variable.getDefaultValue();
+            int[] offsets= variable.getOffsets();
+            // update buffer to reflect new value
+            for (int k= 0; k !is offsets.length; k++)
+                edits.add(new ReplaceEdit(offsets[k], variable.getInitialLength(), value));
+
+        }
+
+        IDocument document= new Document(buffer.getString());
+        MultiTextEdit edit= new MultiTextEdit(0, document.getLength());
+        edit.addChildren(arraycast!(TextEdit)( positions.toArray()));
+        edit.addChildren(arraycast!(TextEdit)( edits.toArray()));
+        edit.apply(document, TextEdit.UPDATE_REGIONS);
+
+        positionsToVariables(positions, variables);
+
+        buffer.setContent(document.get(), variables);
+    }
+
+    /**
+     * Resolves a single variable in a context. Resolving is delegated to the registered resolver.
+     *
+     * @param variable the variable to resolve
+     * @param context the context in which to resolve the variable
+     * @since 3.3
+     */
+    public void resolve(TemplateVariable variable, TemplateContext context) {
+        String type= variable.getType();
+        TemplateVariableResolver resolver= cast(TemplateVariableResolver) fResolvers.get(type);
+        if (resolver is null)
+            resolver= new TemplateVariableResolver(type, ""); //$NON-NLS-1$
+        resolver.resolve(variable, context);
+    }
+
+    private static List variablesToPositions(TemplateVariable[] variables) {
+        List positions= new ArrayList(5);
+        for (int i= 0; i !is variables.length; i++) {
+            int[] offsets= variables[i].getOffsets();
+            for (int j= 0; j !is offsets.length; j++)
+                positions.add(new RangeMarker(offsets[j], 0));
+        }
+
+        return positions;
+    }
+
+    private static void positionsToVariables(List positions, TemplateVariable[] variables) {
+        Iterator iterator= positions.iterator();
+
+        for (int i= 0; i !is variables.length; i++) {
+            TemplateVariable variable= variables[i];
+
+            int[] offsets= new int[variable.getOffsets().length];
+            for (int j= 0; j !is offsets.length; j++)
+                offsets[j]= (cast(TextEdit) iterator.next()).getOffset();
+
+            variable.setOffsets(offsets);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.jface.text.templates.TemplateException;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+import java.lang.all;
+
+
+/**
+ * Thrown when a template cannot be validated.
+ * <p>
+ * Clients may instantiate this class.
+ * </p>
+ * <p>
+ * This class is not intended to be serialized.
+ * </p>
+ *
+ * @since 3.0
+ */
+public class TemplateException : Exception {
+
+    /**
+     * Serial version UID for this class.
+     * <p>
+     * Note: This class is not intended to be serialized.
+     * </p>
+     * @since 3.1
+     */
+    private static const long serialVersionUID= 3906362710416699442L;
+
+    /**
+     * Creates a new template exception.
+     */
+    public this() {
+        super(null);
+    }
+
+    /**
+     * Creates a new template exception.
+     *
+     * @param message the message describing the problem that arose
+     */
+    public this(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new template exception.
+     *
+     * @param message the message describing the problem that arose
+     * @param cause the original exception
+     */
+    public this(String message, Exception cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Creates a new template exception.
+     *
+     * @param cause the original exception
+     */
+    public this(Exception cause) {
+        super(null, cause);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateTranslator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,353 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.templates.TemplateTranslator;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import org.eclipse.dwtxhelper.regex;
+
+/**
+ * The template translator translates a string into a template buffer. Regions marked as variables
+ * are translated into <code>TemplateVariable</code>s.
+ * <p>
+ * The EBNF grammar of a valid string is as follows:
+ * </p>
+ * <p>
+ * template := (text | escape)*. <br />
+ * text := character - dollar. <br />
+ * escape := dollar ('{' variable '}' | dollar). <br />
+ * dollar := '$'. <br />
+ * variable := identifier | identifier ':' type. <br />
+ * type := qualifiedname | qualifiedname '(' arguments ')'. <br />
+ * arguments := (argument ',')* argument. <br />
+ * argument := qualifiedname | argumenttext. <br />
+ * qualifiedname := (identifier '.')* identifier. <br />
+ * argumenttext := "'" (character - "'" | "'" "'")* "'". <br />
+ * </p>
+ * <p>
+ * Clients may only replace the <code>createVariable</code> method of this class.
+ * </p>
+ *
+ * @since 3.0
+ */
+public class TemplateTranslator {
+    /**
+     * Regex pattern for qualifiedname
+     * @since 3.4
+     */
+    private static const String QUALIFIED_NAME= "(?:\\w++\\.)*\\w++"; //$NON-NLS-1$
+
+    /**
+     * Regex pattern for argumenttext
+     * @since 3.4
+     */
+    private static const String ARGUMENT_TEXT= "'(?:(?:'')|(?:[^']))*'"; //$NON-NLS-1$
+
+    /**
+     * Regex pattern for argument
+     * @since 3.4
+     */
+    private static const String ARGUMENT= "(?:" ~ QUALIFIED_NAME ~ ")|(?:" ~ ARGUMENT_TEXT ~ ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+    /**
+     * Precompiled regex pattern for qualified names.
+     * @since 3.3
+     */
+    private static Pattern PARAM_PATTERN_;
+    private static Pattern PARAM_PATTERN(){
+        if( PARAM_PATTERN_ is null ){
+            synchronized( TemplateTranslator.classinfo ){
+                if( PARAM_PATTERN_ is null ){
+                    PARAM_PATTERN_ = Pattern.compile(ARGUMENT);
+                }
+            }
+        }
+        return PARAM_PATTERN_;
+    }
+    /**
+     * Precompiled regex pattern for valid dollar escapes (dollar literals and variables) and
+     * (invalid) single dollars.
+     * @since 3.3
+     */
+    private static Pattern ESCAPE_PATTERN_;
+    private static Pattern ESCAPE_PATTERN(){
+        if( ESCAPE_PATTERN_ is null ){
+            synchronized( TemplateTranslator.classinfo ){
+                if( ESCAPE_PATTERN_ is null ){
+                    ESCAPE_PATTERN_ = Pattern.compile(
+                        "\\$\\$|\\$\\{\\s*+" ~ // $$|${                                                         //$NON-NLS-1$
+                        "(\\w*+)" ~ // variable id group (1)                                                    //$NON-NLS-1$
+                        "\\s*+(?::\\s*+" ~ // :                                                                 //$NON-NLS-1$
+                        "(" ~ QUALIFIED_NAME ~ ")" ~ // variable type group (2)                                 //$NON-NLS-1$ //$NON-NLS-2$
+                        "\\s*+(?:\\(\\s*+" ~ // (                                                               //$NON-NLS-1$
+                        "((?:(?:" ~ ARGUMENT ~ ")\\s*+,\\s*+)*(?:" ~ ARGUMENT ~ "))" ~ // arguments group (3)   //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                        "\\s*+\\))?\\s*+)?\\}|\\$"); // )}|$                                                    //$NON-NLS-1$
+                }
+            }
+        }
+        return ESCAPE_PATTERN_;
+    }
+    /**
+     * @since 3.3
+     */
+    private final class VariableDescription {
+        const List fOffsets;
+        const String fName;
+        TemplateVariableType fType;
+
+        this(String name, TemplateVariableType type) {
+            fOffsets= new ArrayList(5);
+            fName= name;
+            fType= type;
+        }
+
+        void mergeType(TemplateVariableType type)  {
+            if (type is null)
+                return;
+            if (fType is null)
+                fType= type;
+            if (!type.opEquals(fType))
+                fail(TextTemplateMessages.getFormattedString("TemplateTranslator.error.incompatible.type", stringcast(fName))); //$NON-NLS-1$
+        }
+    }
+
+    /** Last translation error. */
+    private String fErrorMessage;
+    /**
+     * Used to ensure compatibility with subclasses overriding
+     * {@link #createVariable(String, String, int[])}.
+     * @since 3.3
+     */
+    private TemplateVariableType fCurrentType;
+
+    /**
+     * Returns an error message if an error occurred for the last translation, <code>null</code>
+     * otherwise.
+     *
+     * @return the error message if an error occurred during the most recent translation,
+     *         <code>null</code> otherwise
+     */
+    public String getErrorMessage() {
+        return fErrorMessage;
+    }
+
+    /**
+     * Translates a template to a <code>TemplateBuffer</code>. <code>null</code> is returned if
+     * there was an error. <code>getErrorMessage()</code> retrieves the associated error message.
+     *
+     * @param template the template to translate.
+     * @return returns the template buffer corresponding to the string
+     * @see #getErrorMessage()
+     * @throws TemplateException if translation failed
+     */
+    public TemplateBuffer translate(Template template_)  {
+        return parse(template_.getPattern());
+    }
+
+    /**
+     * Translates a template string to <code>TemplateBuffer</code>. <code>null</code> is
+     * returned if there was an error. <code>getErrorMessage()</code> retrieves the associated
+     * error message.
+     *
+     * @param string the string to translate.
+     * @return returns the template buffer corresponding to the string
+     * @see #getErrorMessage()
+     * @throws TemplateException if translation failed
+     */
+    public TemplateBuffer translate(String string)  {
+        return parse(string);
+    }
+
+    /**
+     * Internal parser.
+     *
+     * @param string the string to parse
+     * @return the parsed <code>TemplateBuffer</code>
+     * @throws TemplateException if the string does not conform to the template format
+     */
+    private TemplateBuffer parse(String string)  {
+
+        fErrorMessage= null;
+        final StringBuffer buffer= new StringBuffer(string.length());
+        final Matcher matcher= ESCAPE_PATTERN.matcher(string);
+        final Map variables= new LinkedHashMap();
+
+        int complete= 0;
+        while (matcher.find()) {
+            // append any verbatim text
+            buffer.append(string.substring(complete, matcher.start()));
+
+            // check the escaped sequence
+            if ("$".equals(matcher.group())) { //$NON-NLS-1$
+                fail(TextTemplateMessages.getString("TemplateTranslator.error.incomplete.variable")); //$NON-NLS-1$
+            } else if ("$$".equals(matcher.group())) { //$NON-NLS-1$
+                // escaped $
+                buffer.append('$');
+            } else {
+                // parse variable
+                String name= matcher.group(1);
+                String typeName= matcher.group(2);
+                String params= matcher.group(3);
+                TemplateVariableType type= createType(typeName, params);
+
+                updateOrCreateVariable(variables, name, type, buffer.length());
+
+                buffer.append(name);
+            }
+            complete= matcher.end();
+        }
+        // append remaining verbatim text
+        buffer.append(string.substring(complete));
+
+        TemplateVariable[] vars= createVariables(variables);
+        return new TemplateBuffer(buffer.toString(), vars);
+    }
+
+    private TemplateVariableType createType(String typeName, String paramString) {
+        if (typeName is null)
+            return null;
+
+        if (paramString is null)
+            return new TemplateVariableType(typeName);
+
+        final Matcher matcher= PARAM_PATTERN.matcher(paramString);
+        List params= new ArrayList(5);
+        while (matcher.find()) {
+            String argument= matcher.group();
+            if (argument.charAt(0) is '\'') {
+                // argumentText
+                argument= argument.substring(1, argument.length() - 1).replaceAll("''", "'"); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+
+            params.add(argument);
+        }
+
+        return new TemplateVariableType(typeName, stringcast( params.toArray()));
+    }
+
+    private void fail(String message)  {
+        fErrorMessage= message;
+        throw new TemplateException(message);
+    }
+
+    /**
+     * If there is no variable named <code>name</code>, a new variable with the given type, name
+     * and offset is created. If one exists, the offset is added to the variable and the type is
+     * merged with the existing type.
+     *
+     * @param variables the variables by variable name
+     * @param name the name of the variable
+     * @param type the variable type, <code>null</code> for not defined
+     * @param offset the buffer offset of the variable
+     * @throws TemplateException if merging the type fails
+     * @since 3.3
+     */
+    private void updateOrCreateVariable(Map variables, String name, TemplateVariableType type, int offset)  {
+        VariableDescription varDesc= cast(VariableDescription) variables.get(name);
+        if (varDesc is null) {
+            varDesc= new VariableDescription(name, type);
+            variables.put(name, varDesc);
+        } else {
+            varDesc.mergeType(type);
+        }
+        varDesc.fOffsets.add(new Integer(offset));
+    }
+
+    /**
+     * Creates proper {@link TemplateVariable}s from the variable descriptions.
+     *
+     * @param variables the variable descriptions by variable name
+     * @return the corresponding variables
+     * @since 3.3
+     */
+    private TemplateVariable[] createVariables(Map variables) {
+        TemplateVariable[] result= new TemplateVariable[variables.size()];
+        int idx= 0;
+        for (Iterator it= variables.values().iterator(); it.hasNext(); idx++) {
+            VariableDescription desc= cast(VariableDescription) it.next();
+            TemplateVariableType type= desc.fType is null ? new TemplateVariableType(desc.fName) : desc.fType;
+            int[] offsets= new int[desc.fOffsets.size()];
+            int i= 0;
+            for (Iterator intIt= desc.fOffsets.iterator(); intIt.hasNext(); i++) {
+                Integer offset= cast(Integer) intIt.next();
+                offsets[i]= offset.intValue();
+            }
+            fCurrentType= type;
+            /*
+             * Call the deprecated version of createVariable. When not overridden, it will delegate
+             * to the new version using fCurrentType.
+             */
+            TemplateVariable var= createVariable(type.getName(), desc.fName, offsets);
+            result[idx]= var;
+        }
+        fCurrentType= null; // avoid dangling reference
+        return result;
+    }
+
+    /**
+     * Hook method to create new variables. Subclasses may override to supply their custom variable
+     * type.
+     * <p>
+     * Clients may replace this method.
+     * </p>
+     *
+     * @param type the type of the new variable.
+     * @param name the name of the new variable.
+     * @param offsets the offsets where the variable occurs in the template
+     * @return a new instance of <code>TemplateVariable</code>
+     * @deprecated as of 3.3 use {@link #createVariable(TemplateVariableType, String, int[])} instead
+     */
+    protected TemplateVariable createVariable(String type, String name, int[] offsets) {
+        return createVariable(fCurrentType, name, offsets);
+    }
+
+    /**
+     * Hook method to create new variables. Subclasses may override to supply their custom variable
+     * type.
+     * <p>
+     * Clients may replace this method.
+     * </p>
+     *
+     * @param type the type of the new variable.
+     * @param name the name of the new variable.
+     * @param offsets the offsets where the variable occurs in the template
+     * @return a new instance of <code>TemplateVariable</code>
+     * @since 3.3
+     */
+    protected TemplateVariable createVariable(TemplateVariableType type, String name, int[] offsets) {
+        return new TemplateVariable(type, name, name, offsets);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateVariable.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,310 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.templates.TemplateVariable;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.TextUtilities;
+
+/**
+ * A <code>TemplateVariable</code> represents a set of positions into a
+ * <code>TemplateBuffer</code> with identical content each. <code>TemplateVariableResolver</code>s
+ * can be used to resolve a template variable to a symbol available from the
+ * <code>TemplateContext</code>. A resolved variable may have one or more possible
+ * {@link #getValues() values} which may be presented to the user as choices. If there is no user
+ * interaction the {@link #getDefaultValue() default value} is chosen as textual representation of
+ * the variable.
+ * <p>
+ * Clients may instantiate and extend this class.
+ * </p>
+ *
+ * @see TemplateVariableResolver
+ * @see TemplateBuffer
+ * @since 3.0
+ */
+public class TemplateVariable {
+
+    /** The type name of the variable */
+    private const TemplateVariableType fType;
+    /** The name of the variable. */
+    private const String fName;
+    /** The initial length in the template pattern. */
+    private const int fInitialLength;
+    /** The offsets of the variable. */
+    private int[] fOffsets;
+    /** Flag indicating if the variable has been resolved unambiguously. */
+    private bool fIsUnambiguous;
+    /** Flag indicating if the variable has been resolved by a resolver. */
+    private bool fIsResolved;
+    /**
+     * The proposal strings available for this variable. The first string is
+     * the default value.
+     */
+    private String[] fValues;
+
+    /**
+     * Creates a template variable. The type is used as the name of the
+     * variable.
+     *
+     * @param type the type of the variable
+     * @param defaultValue the default value of the variable
+     * @param offsets the array of offsets of the variable
+     */
+    public this(String type, String defaultValue, int[] offsets) {
+        this(type, [ defaultValue ], offsets);
+    }
+
+    /**
+     * Creates a template variable.
+     *
+     * @param type the type of the variable
+     * @param name the name of the variable
+     * @param defaultValue the default value of the variable
+     * @param offsets the array of offsets of the variable
+     */
+    public this(String type, String name, String defaultValue, int[] offsets) {
+        this(type, name, [ defaultValue ], offsets);
+    }
+
+    /**
+     * Creates a template variable.
+     *
+     * @param type the type of the variable
+     * @param name the name of the variable
+     * @param defaultValue the default value of the variable
+     * @param offsets the array of offsets of the variable
+     * @since 3.3
+     */
+    public this(TemplateVariableType type, String name, String defaultValue, int[] offsets) {
+        this(type, name, [ defaultValue ], offsets);
+    }
+
+    /**
+     * Creates a template variable with multiple possible values. The type is
+     * used as the name of the template.
+     *
+     * @param type the type of the template variable
+     * @param values the values available at this variable, non-empty
+     * @param offsets the array of offsets of the variable
+     */
+    public this(String type, String[] values, int[] offsets) {
+        this(type, type, values, offsets);
+    }
+
+    /**
+     * Creates a template variable with multiple possible values.
+     *
+     * @param type the type of the variable
+     * @param name the name of the variable
+     * @param values the values available at this variable, non-empty
+     * @param offsets the array of offsets of the variable
+     */
+    public this(String type, String name, String[] values, int[] offsets) {
+        this(new TemplateVariableType(type), name, values, offsets);
+    }
+
+    /**
+     * Creates a template variable with multiple possible values.
+     *
+     * @param type the type of the variable
+     * @param name the name of the variable
+     * @param values the values available at this variable, non-empty
+     * @param offsets the array of offsets of the variable
+     * @since 3.3
+     */
+    this(TemplateVariableType type, String name, String[] values, int[] offsets) {
+        Assert.isNotNull(type);
+        Assert.isNotNull(name);
+        fType= type;
+        fName= name;
+        setValues(values);
+        setOffsets(offsets);
+        setUnambiguous(false);
+        setResolved(false);
+        fInitialLength= values[0].length();
+    }
+
+    /**
+     * Returns the type name of the variable.
+     *
+     * @return the type name of the variable
+     */
+    public String getType() {
+        return fType.getName();
+    }
+
+    /**
+     * Returns the type of the variable.
+     *
+     * @return the type of the variable
+     * @since 3.3
+     */
+    public TemplateVariableType getVariableType() {
+        return fType;
+    }
+
+    /**
+     * Returns the name of the variable.
+     *
+     * @return the name of the variable
+     */
+    public String getName() {
+        return fName;
+    }
+
+    /**
+     * Returns the default value of the variable. Typically, this is the first of
+     * the possible values (see {@link #getValues()}.
+     *
+     * @return the default value of the variable
+     */
+    public String getDefaultValue() {
+        return getValues()[0];
+    }
+
+    /**
+     * Returns the possible values for this variable. The returned array is owned by this variable
+     * and must not be modified. The array is not empty.
+     *
+     * @return the possible values for this variable
+     */
+    public String[] getValues() {
+        return fValues;
+    }
+
+    /**
+     * Returns the length of the variable's default value.
+     *
+     * @return the length of the variable
+     */
+    public int getLength() {
+        return getDefaultValue().length();
+    }
+
+    /**
+     * Returns the initial length of the variable. The initial length is the lenght as it occurred
+     * in the template pattern and is used when resolving a template to update the pattern with the
+     * resolved values of the variable.
+     *
+     * @return the initial length of the variable
+     * @since 3.3
+     */
+    final int getInitialLength() {
+        return fInitialLength;
+    }
+
+    /**
+     * Sets the offsets of the variable.
+     *
+     * @param offsets the new offsets of the variable
+     */
+    public void setOffsets(int[] offsets) {
+        fOffsets= TextUtilities.copy(offsets);
+    }
+
+    /**
+     * Returns the offsets of the variable. The returned array is
+     * owned by this variable and must not be modified.
+     *
+     * @return the length of the variable
+     */
+    public int[] getOffsets() {
+        return fOffsets;
+    }
+
+    /**
+     * Resolves the variable to a single value. This is a shortcut for
+     * <code>setValues(new String[] { value })</code>.
+     *
+     * @param value the new default value
+     */
+    public final void setValue(String value) {
+        setValues([ value ]);
+    }
+
+    /**
+     * Resolves the variable to several possible values for this variable, with the first being the
+     * default value.
+     *
+     * @param values a non-empty array of values
+     */
+    public void setValues(String[] values) {
+        Assert.isTrue(values.length > 0);
+        fValues= TextUtilities.copy(values);
+        setResolved(true);
+    }
+
+    /**
+     * Sets the <em>isUnambiguous</em> flag of the variable.
+     *
+     * @param unambiguous the new unambiguous state of the variable
+     */
+    public void setUnambiguous(bool unambiguous) {
+        fIsUnambiguous= unambiguous;
+        if (unambiguous)
+            setResolved(true);
+    }
+
+    /**
+     * Returns <code>true</code> if the variable is unambiguously resolved, <code>false</code> otherwise.
+     *
+     * @return <code>true</code> if the variable is unambiguously resolved, <code>false</code> otherwise
+     */
+    public bool isUnambiguous() {
+        return fIsUnambiguous;
+    }
+
+    /**
+     * Sets the <em>resolved</em> flag of the variable.
+     *
+     * @param resolved the new <em>resolved</em> state
+     * @since 3.3
+     */
+    public void setResolved(bool resolved) {
+        fIsResolved= resolved;
+    }
+
+    /**
+     * Returns <code>true</code> if the variable has been resolved, <code>false</code>
+     * otherwise.
+     *
+     * @return <code>true</code> if the variable has been resolved, <code>false</code> otherwise
+     * @since 3.3
+     */
+    public bool isResolved() {
+        return fIsResolved;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateVariableResolver.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,197 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.templates.TemplateVariableResolver;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableType; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * A <code>TemplateVariableResolver</code> resolves <code>TemplateVariables</code>
+ * of a certain type inside a <code>TemplateContext</code>.
+ * <p>
+ * Clients may instantiate and extend this class.
+ * </p>
+ *
+ * @see TemplateVariable
+ * @since 3.0
+ */
+public class TemplateVariableResolver {
+
+    /** Type of this resolver. */
+    private String fType= null;
+
+    /** Description of the type resolved by this resolver. */
+    private String fDescription= null;
+
+    /**
+     * Creates an instance of <code>TemplateVariableResolver</code>.
+     *
+     * @param type the name of the type
+     * @param description the description for the type
+     */
+    /+protected+/ this(String type, String description) {
+        setType(type);
+        setDescription(description);
+    }
+
+    /**
+     * Creates an empty instance.
+     * <p>
+     * This is a framework-only constructor that exists only so that resolvers
+     * can be contributed via an extension point and that should not be called
+     * in client code except for subclass constructors; use
+     * {@link #TemplateVariableResolver(String, String)} instead.
+     * </p>
+     */
+    public this() {
+    }
+
+    /**
+     * Returns the type of this resolver.
+     *
+     * @return the type
+     */
+    public String getType() {
+        return fType;
+    }
+
+    /**
+     * Returns the description for the resolver.
+     *
+     * @return the description for the resolver
+     */
+    public String getDescription() {
+        return fDescription;
+    }
+
+    /**
+     * Returns an instance of the type resolved by the receiver available in <code>context</code>.
+     * To resolve means to provide a binding to a concrete text object (a
+     * <code>String</code>) in the given context.
+     * <p>
+     * The default implementation looks up the type in the context.</p>
+     *
+     * @param context the context in which to resolve the type
+     * @return the name of the text object of this type, or <code>null</code> if it cannot be determined
+     */
+    protected String resolve(TemplateContext context) {
+        return context.getVariable(getType());
+    }
+
+    /**
+     * Returns all possible bindings available in <code>context</code>. The default
+     * implementation simply returns an array which contains the result of
+     * {@link #resolve(TemplateContext)}, or an empty array if that call returns
+     * <code>null</code>.
+     *
+     * @param context the context in which to resolve the type
+     * @return an array of possible bindings of this type in <code>context</code>
+     */
+    protected String[] resolveAll(TemplateContext context) {
+        String binding= resolve(context);
+        if (binding is null)
+            return new String[0];
+        return [ binding ];
+    }
+
+    /**
+     * Resolves <code>variable</code> in <code>context</code>. To resolve
+     * means to find a valid binding of the receiver's type in the given <code>TemplateContext</code>.
+     * If the variable can be successfully resolved, its value is set using
+     * {@link TemplateVariable#setValues(String[])}.
+     *
+     * @param context the context in which variable is resolved
+     * @param variable the variable to resolve
+     */
+    public void resolve(TemplateVariable variable, TemplateContext context) {
+        String[] bindings= resolveAll(context);
+        if (bindings.length !is 0)
+            variable.setValues(bindings);
+        if (bindings.length > 1)
+            variable.setUnambiguous(false);
+        else
+            variable.setUnambiguous(isUnambiguous(context));
+        variable.setResolved(true);
+    }
+
+    /**
+     * Returns whether this resolver is able to resolve unambiguously. When
+     * resolving a <code>TemplateVariable</code>, its <code>isUmambiguous</code>
+     * state is set to the one of this resolver. By default, this method
+     * returns <code>false</code>. Clients can overwrite this method to give
+     * a hint about whether there should be e.g. prompting for input values for
+     * ambiguous variables.
+     *
+     * @param context the context in which the resolved check should be
+     *        evaluated
+     * @return <code>true</code> if the receiver is unambiguously resolvable
+     *         in <code>context</code>, <code>false</code> otherwise
+     */
+    protected bool isUnambiguous(TemplateContext context) {
+        return false;
+    }
+
+    /**
+     * Sets the description.
+     * <p>
+     * This is a framework-only method that exists only so that resolvers
+     * can be contributed via an extension point and that should not be called
+     * in client code; use {@link #TemplateVariableResolver(String, String)} instead.
+     * </p>
+     *
+     * @param description the description of this resolver
+     */
+    public final void setDescription(String description) {
+        Assert.isNotNull(description);
+        Assert.isTrue(fDescription is null); // may only be called once when initialized
+        fDescription= description;
+    }
+
+    /**
+     * Sets the type name.
+     * <p>
+     * This is a framework-only method that exists only so that resolvers
+     * can be contributed via an extension point and that should not be called
+     * in client code; use {@link #TemplateVariableResolver(String, String)} instead.
+     * </p>
+     *
+     * @param type the type name of this resolver
+     */
+    public final void setType(String type) {
+        Assert.isNotNull(type);
+        Assert.isTrue(fType is null); // may only be called once when initialized
+        fType= type;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/templates/TemplateVariableType.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.jface.text.templates.TemplateVariableType;
+
+import org.eclipse.jface.text.templates.SimpleTemplateVariableResolver; // packageimport
+import org.eclipse.jface.text.templates.TemplateBuffer; // packageimport
+import org.eclipse.jface.text.templates.TemplateContext; // packageimport
+import org.eclipse.jface.text.templates.TemplateContextType; // packageimport
+import org.eclipse.jface.text.templates.Template; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariable; // packageimport
+import org.eclipse.jface.text.templates.PositionBasedCompletionProposal; // packageimport
+import org.eclipse.jface.text.templates.TemplateException; // packageimport
+import org.eclipse.jface.text.templates.TemplateTranslator; // packageimport
+import org.eclipse.jface.text.templates.DocumentTemplateContext; // packageimport
+import org.eclipse.jface.text.templates.GlobalTemplateVariables; // packageimport
+import org.eclipse.jface.text.templates.InclusivePositionUpdater; // packageimport
+import org.eclipse.jface.text.templates.TemplateProposal; // packageimport
+import org.eclipse.jface.text.templates.ContextTypeRegistry; // packageimport
+import org.eclipse.jface.text.templates.JFaceTextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor; // packageimport
+import org.eclipse.jface.text.templates.TextTemplateMessages; // packageimport
+import org.eclipse.jface.text.templates.TemplateVariableResolver; // packageimport
+
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * Value object that represents the type of a template variable. A type is defined by its name and
+ * may have parameters.
+ *
+ * @since 3.3
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public final class TemplateVariableType {
+
+    /** The name of the type. */
+    private const String fName;
+    /** The parameter list. */
+    private const List fParams;
+
+    this(String name) {
+        this(name, new String[0]);
+    }
+
+    this(String name, String[] params) {
+        Assert.isLegal(name !is null);
+        Assert.isLegal(params !is null);
+        fName= name;
+        fParams= Collections.unmodifiableList(new ArrayList(Arrays.asList(stringcast(params))));
+    }
+
+    /**
+     * Returns the type name of this variable type.
+     *
+     * @return the type name of this variable type
+     */
+    public String getName() {
+        return fName;
+    }
+
+    /**
+     * Returns the unmodifiable and possibly empty list of parameters (element type: {@link String})
+     *
+     * @return the list of parameters
+     */
+    public List getParams() {
+        return fParams;
+    }
+
+    /*
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public override int opEquals(Object obj) {
+        if ( cast(TemplateVariableType)obj ) {
+            TemplateVariableType other= cast(TemplateVariableType) obj;
+            return other.fName.equals(fName) && other.fParams.opEquals(cast(Object)fParams);
+        }
+        return false;
+    }
+
+    /*
+     * @see java.lang.Object#hashCode()
+     */
+    public override hash_t toHash() {
+        alias .toHash toHash;
+        return fName.toHash() + fParams.toHash();
+    }
+    /*
+     * @see java.lang.Object#toString()
+     * @since 3.3
+     */
+    public override String toString() {
+        return fName ~ (cast(Object)fParams).toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/templates/TextTemplateMessages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.jface.text.templates.TextTemplateMessages;
+
+import java.lang.all;
+
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+import java.text.MessageFormat;
+
+/*
+ * @since 3.0
+ */
+class TextTemplateMessages {
+
+//     private static const String RESOURCE_BUNDLE= TextTemplateMessages.classinfo.getName();
+    private static ResourceBundle fgResourceBundle_;//= ResourceBundle.getBundle(RESOURCE_BUNDLE);
+    private static ResourceBundle fgResourceBundle(){
+        if( fgResourceBundle_ is null ){
+            synchronized(TextTemplateMessages.classinfo ){
+                if( fgResourceBundle_ is null ){
+                    fgResourceBundle_ = ResourceBundle.getBundle(
+                        getImportData!("org.eclipse.jface.text.templates.TextTemplateMessages.properties"));
+                }
+            }
+        }
+        return fgResourceBundle_;
+    }
+
+    private this() {
+    }
+
+    public static String getString(String key) {
+        try {
+            return fgResourceBundle.getString(key);
+        } catch (MissingResourceException e) {
+            return '!' ~ key ~ '!';
+        }
+    }
+
+    public static String getFormattedString(String key, Object[] args... ) {
+        return MessageFormat.format(getString(key), args);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/CopySourceEdit.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,338 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.CopySourceEdit;
+
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+/**
+ * A copy source edit denotes the source of a copy operation. Copy
+ * source edits are only valid inside an edit tree if they have a
+ * corresponding target edit. Furthermore the corresponding
+ * target edit can't be a direct or indirect child of the source
+ * edit. Violating one of two requirements will result in a <code>
+ * MalformedTreeException</code> when executing the edit tree.
+ * <p>
+ * A copy source edit can manage an optional source modifier. A
+ * source modifier can provide a set of replace edits which will
+ * to applied to the source before it gets inserted at the target
+ * position.
+ *
+ * @see org.eclipse.text.edits.CopyTargetEdit
+ *
+ * @since 3.0
+ */
+public final class CopySourceEdit : TextEdit {
+
+    private CopyTargetEdit fTarget;
+    private ISourceModifier fModifier;
+
+    private String fSourceContent;
+    private TextEdit fSourceRoot;
+
+    private static class PartialCopier : TextEditVisitor {
+        TextEdit fResult;
+        List fParents;
+        TextEdit fCurrentParent;
+        public this(){
+            fParents= new ArrayList();
+        }
+        public static TextEdit perform(TextEdit source) {
+            PartialCopier copier= new PartialCopier();
+            source.accept(copier);
+            return copier.fResult;
+        }
+        private void manageCopy(TextEdit copy) {
+            if (fResult is null)
+                fResult= copy;
+            if (fCurrentParent !is null) {
+                fCurrentParent.addChild(copy);
+            }
+            fParents.add(fCurrentParent);
+            fCurrentParent= copy;
+        }
+        public void postVisit(TextEdit edit) {
+            fCurrentParent= cast(TextEdit)fParents.remove(fParents.size() - 1);
+        }
+        public bool visitNode(TextEdit edit) {
+            manageCopy(edit.doCopy_package());
+            return true;
+        }
+        public bool visit(CopySourceEdit edit) {
+            manageCopy(new RangeMarker(edit.getOffset(), edit.getLength()));
+            return true;
+        }
+        public bool visit(CopyTargetEdit edit) {
+            manageCopy(new InsertEdit(edit.getOffset(), edit.getSourceEdit().getContent()));
+            return true;
+        }
+        public bool visit(MoveSourceEdit edit) {
+            manageCopy(new DeleteEdit(edit.getOffset(), edit.getLength()));
+            return true;
+        }
+        public bool visit(MoveTargetEdit edit) {
+            manageCopy(new InsertEdit(edit.getOffset(), edit.getSourceEdit().getContent()));
+            return true;
+        }
+    }
+
+    /**
+     * Constructs a new copy source edit.
+     *
+     * @param offset the edit's offset
+     * @param length the edit's length
+     */
+    public this(int offset, int length) {
+        super(offset, length);
+    }
+
+    /**
+     * Constructs a new copy source edit.
+     *
+     * @param offset the edit's offset
+     * @param length the edit's length
+     * @param target the edit's target
+     */
+    public this(int offset, int length, CopyTargetEdit target) {
+        this(offset, length);
+        setTargetEdit(target);
+    }
+
+    /*
+     * Copy Constructor
+     */
+    private this(CopySourceEdit other) {
+        super(other);
+        if (other.fModifier !is null)
+            fModifier= other.fModifier.copy();
+    }
+
+    /**
+     * Returns the associated target edit or <code>null</code>
+     * if no target edit is associated yet.
+     *
+     * @return the target edit or <code>null</code>
+     */
+    public CopyTargetEdit getTargetEdit() {
+        return fTarget;
+    }
+
+    /**
+     * Sets the target edit.
+     *
+     * @param edit the new target edit.
+     *
+     * @exception MalformedTreeException is thrown if the target edit
+     *  is a direct or indirect child of the source edit
+     */
+    public void setTargetEdit(CopyTargetEdit edit)  {
+        Assert.isNotNull(edit);
+        if (fTarget !is edit) {
+            fTarget= edit;
+            fTarget.setSourceEdit(this);
+        }
+    }
+
+    /**
+     * Returns the current source modifier or <code>null</code>
+     * if no source modifier is set.
+     *
+     * @return the source modifier
+     */
+    public ISourceModifier getSourceModifier() {
+        return fModifier;
+    }
+
+    /**
+     * Sets the optional source modifier.
+     *
+     * @param modifier the source modifier or <code>null</code>
+     *  if no source modification is need.
+     */
+    public void setSourceModifier(ISourceModifier modifier) {
+        fModifier= modifier;
+    }
+
+    /*
+     * @see TextEdit#doCopy
+     */
+    protected TextEdit doCopy() {
+        return new CopySourceEdit(this);
+    }
+
+    /*
+     * @see TextEdit#accept0
+     */
+    protected void accept0(TextEditVisitor visitor) {
+        bool visitChildren= visitor.visit(this);
+        if (visitChildren) {
+            acceptChildren(visitor);
+        }
+    }
+
+    //---- API for CopyTargetEdit ------------------------------------------------
+
+    String getContent() {
+        // The source content can be null if the edit wasn't executed
+        // due to an exclusion list of the text edit processor. Return
+        // the empty string which can be moved without any harm.
+        if (fSourceContent is null)
+            return ""; //$NON-NLS-1$
+        return fSourceContent;
+    }
+
+    void clearContent() {
+        fSourceContent= null;
+    }
+
+    /*
+     * @see TextEdit#postProcessCopy
+     */
+    protected void postProcessCopy(TextEditCopier copier) {
+        if (fTarget !is null) {
+            CopySourceEdit source= cast(CopySourceEdit)copier.getCopy(this);
+            CopyTargetEdit target= cast(CopyTargetEdit)copier.getCopy(fTarget);
+            if (source !is null && target !is null)
+                source.setTargetEdit(target);
+        }
+    }
+
+    //---- consistency check ----------------------------------------------------
+
+    int traverseConsistencyCheck(TextEditProcessor processor, IDocument document, List sourceEdits) {
+        int result= super.traverseConsistencyCheck(processor, document, sourceEdits);
+        // Since source computation takes place in a recursive fashion (see
+        // performSourceComputation) we only do something if we don't have a
+        // computed source already.
+        if (fSourceContent is null) {
+            if (sourceEdits.size() <= result) {
+                List list= new ArrayList();
+                list.add(this);
+                for (int i= sourceEdits.size(); i < result; i++)
+                    sourceEdits.add(cast(Object)null);
+                sourceEdits.add(cast(Object)list);
+            } else {
+                List list= cast(List)sourceEdits.get(result);
+                if (list is null) {
+                    list= new ArrayList();
+                    sourceEdits.add(result, cast(Object)list);
+                }
+                list.add(this);
+            }
+        }
+        return result;
+    }
+
+    void performConsistencyCheck(TextEditProcessor processor, IDocument document)  {
+        if (fTarget is null)
+            throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("CopySourceEdit.no_target")); //$NON-NLS-1$
+        if (fTarget.getSourceEdit() !is this)
+            throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("CopySourceEdit.different_source")); //$NON-NLS-1$
+        /* causes ASTRewrite to fail
+        if (getRoot() !is fTarget.getRoot())
+            throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("CopySourceEdit.different_tree")); //$NON-NLS-1$
+        */
+    }
+
+    //---- source computation -------------------------------------------------------
+
+    void traverseSourceComputation(TextEditProcessor processor, IDocument document) {
+        // always perform source computation independent of processor.considerEdit
+        // The target might need the source and the source is computed in a
+        // temporary buffer.
+        performSourceComputation(processor, document);
+    }
+
+    void performSourceComputation(TextEditProcessor processor, IDocument document) {
+        try {
+            MultiTextEdit root= new MultiTextEdit(getOffset(), getLength());
+            root.internalSetChildren(internalGetChildren());
+            fSourceContent= document.get(getOffset(), getLength());
+            fSourceRoot= PartialCopier.perform(root);
+            fSourceRoot.internalMoveTree(-getOffset());
+            if (fSourceRoot.hasChildren()) {
+                EditDocument subDocument= new EditDocument(fSourceContent);
+                TextEditProcessor subProcessor= TextEditProcessor.createSourceComputationProcessor(subDocument, fSourceRoot, TextEdit.NONE);
+                subProcessor.performEdits();
+                if (needsTransformation())
+                    applyTransformation(subDocument);
+                fSourceContent= subDocument.get();
+                fSourceRoot= null;
+            } else {
+                if (needsTransformation()) {
+                    EditDocument subDocument= new EditDocument(fSourceContent);
+                    applyTransformation(subDocument);
+                    fSourceContent= subDocument.get();
+                }
+            }
+        } catch (BadLocationException cannotHappen) {
+            Assert.isTrue(false);
+        }
+    }
+
+    private bool needsTransformation() {
+        return fModifier !is null;
+    }
+
+    private void applyTransformation(IDocument document)  {
+        TextEdit newEdit= new MultiTextEdit(0, document.getLength());
+        ReplaceEdit[] replaces= fModifier.getModifications(document.get());
+        for (int i= 0; i < replaces.length; i++) {
+            newEdit.addChild(replaces[i]);
+        }
+        try {
+            newEdit.apply(document, TextEdit.NONE);
+        } catch (BadLocationException cannotHappen) {
+            Assert.isTrue(false);
+        }
+    }
+
+    //---- document updating ----------------------------------------------------------------
+
+    int performDocumentUpdating(IDocument document)  {
+        fDelta= 0;
+        return fDelta;
+    }
+
+    //---- region updating ----------------------------------------------------------------
+
+    /*
+     * @see TextEdit#deleteChildren
+     */
+    bool deleteChildren() {
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/CopyTargetEdit.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,189 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.CopyTargetEdit;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+/**
+ * A copy target edit denotes the target of a copy operation. Copy
+ * target edits are only valid inside an edit tree if they have a
+ * corresponding source edit. Furthermore a target edit can't
+ * can't be a direct or indirect child of the associated source edit.
+ * Violating one of two requirements will result in a <code>
+ * MalformedTreeException</code> when executing the edit tree.
+ * <p>
+ * Copy target edits can't be used as a parent for other edits.
+ * Trying to add an edit to a copy target edit results in a <code>
+ * MalformedTreeException</code> as well.
+ *
+ * @see org.eclipse.text.edits.CopySourceEdit
+ *
+ * @since 3.0
+ */
+public final class CopyTargetEdit : TextEdit {
+
+    private CopySourceEdit fSource;
+
+    /**
+     * Constructs a new copy target edit
+     *
+     * @param offset the edit's offset
+     */
+    public this(int offset) {
+        super(offset, 0);
+    }
+
+    /**
+     * Constructs an new copy target edit
+     *
+     * @param offset the edit's offset
+     * @param source the corresponding source edit
+     */
+    public this(int offset, CopySourceEdit source) {
+        this(offset);
+        setSourceEdit(source);
+    }
+
+    /*
+     * Copy constructor
+     */
+    private this(CopyTargetEdit other) {
+        super(other);
+    }
+
+    /**
+     * Returns the associated source edit or <code>null</code>
+     * if no source edit is associated yet.
+     *
+     * @return the source edit or <code>null</code>
+     */
+    public CopySourceEdit getSourceEdit() {
+        return fSource;
+    }
+
+    /**
+     * Sets the source edit.
+     *
+     * @param edit the source edit
+     *
+     * @exception MalformedTreeException is thrown if the target edit
+     *  is a direct or indirect child of the source edit
+     */
+    public void setSourceEdit(CopySourceEdit edit)  {
+        Assert.isNotNull(edit);
+        if (fSource !is edit) {
+            fSource= edit;
+            fSource.setTargetEdit(this);
+            TextEdit parent= getParent();
+            while (parent !is null) {
+                if (parent is fSource)
+                    throw new MalformedTreeException(parent, this, TextEditMessages.getString("CopyTargetEdit.wrong_parent")); //$NON-NLS-1$
+                parent= parent.getParent();
+            }
+        }
+    }
+
+    /*
+     * @see TextEdit#doCopy
+     */
+    protected TextEdit doCopy() {
+        return new CopyTargetEdit(this);
+    }
+
+    /*
+     * @see TextEdit#postProcessCopy
+     */
+    protected void postProcessCopy(TextEditCopier copier) {
+        if (fSource !is null) {
+            CopyTargetEdit target= cast(CopyTargetEdit)copier.getCopy(this);
+            CopySourceEdit source= cast(CopySourceEdit)copier.getCopy(fSource);
+            if (target !is null && source !is null)
+                target.setSourceEdit(source);
+        }
+    }
+
+    /*
+     * @see TextEdit#accept0
+     */
+    protected void accept0(TextEditVisitor visitor) {
+        bool visitChildren= visitor.visit(this);
+        if (visitChildren) {
+            acceptChildren(visitor);
+        }
+    }
+
+    /*
+     * @see TextEdit#traverseConsistencyCheck
+     */
+    int traverseConsistencyCheck(TextEditProcessor processor, IDocument document, List sourceEdits) {
+        return super.traverseConsistencyCheck(processor, document, sourceEdits) + 1;
+    }
+
+    /*
+     * @see TextEdit#performConsistencyCheck
+     */
+    void performConsistencyCheck(TextEditProcessor processor, IDocument document)  {
+        if (fSource is null)
+            throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("CopyTargetEdit.no_source")); //$NON-NLS-1$
+        if (fSource.getTargetEdit() !is this)
+            throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("CopyTargetEdit.different_target")); //$NON-NLS-1$
+    }
+
+    /*
+     * @see TextEdit#performDocumentUpdating
+     */
+    int performDocumentUpdating(IDocument document)  {
+        String source= fSource.getContent();
+        document.replace(getOffset(), getLength(), source);
+        fDelta= source.length() - getLength();
+        fSource.clearContent();
+        return fDelta;
+    }
+
+    /*
+     * @see TextEdit#deleteChildren
+     */
+    bool deleteChildren() {
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/CopyingRangeMarker.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.CopyingRangeMarker;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+/**
+ * A <code>CopyingRangeMarker</code> can be used to track positions when executing
+ * text edits. Additionally a copying range marker stores a local copy of the
+ * text it captures when it gets executed.
+ *
+ * @since 3.0
+ */
+public final class CopyingRangeMarker : TextEdit {
+
+    private String fText;
+
+    /**
+     * Creates a new <tt>CopyRangeMarker</tt> for the given
+     * offset and length.
+     *
+     * @param offset the marker's offset
+     * @param length the marker's length
+     */
+    public this(int offset, int length) {
+        super(offset, length);
+    }
+
+    /*
+     * Copy constructor
+     */
+    private this(CopyingRangeMarker other) {
+        super(other);
+        fText= other.fText;
+    }
+
+    /*
+     * @see TextEdit#doCopy
+     */
+    protected TextEdit doCopy() {
+        return new CopyingRangeMarker(this);
+    }
+
+    /*
+     * @see TextEdit#accept0
+     */
+    protected void accept0(TextEditVisitor visitor) {
+        bool visitChildren= visitor.visit(this);
+        if (visitChildren) {
+            acceptChildren(visitor);
+        }
+    }
+
+    /*
+     * @see TextEdit#performDocumentUpdating
+     */
+    int performDocumentUpdating(IDocument document)  {
+        fText= document.get(getOffset(), getLength());
+        fDelta= 0;
+        return fDelta;
+    }
+
+    /*
+     * @see TextEdit#deleteChildren
+     */
+    bool deleteChildren() {
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/DeleteEdit.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.DeleteEdit;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+/**
+ * Text edit to delete a range in a document.
+ * <p>
+ * A delete edit is equivalent to <code>ReplaceEdit(
+ * offset, length, "")</code>.
+ *
+ * @since 3.0
+ */
+public final class DeleteEdit : TextEdit {
+
+    /**
+     * Constructs a new delete edit.
+     *
+     * @param offset the offset of the range to replace
+     * @param length the length of the range to replace
+     */
+    public this(int offset, int length) {
+        super(offset, length);
+    }
+
+    /*
+     * Copy constructor
+     */
+    private this(DeleteEdit other) {
+        super(other);
+    }
+
+    /*
+     * @see TextEdit#doCopy
+     */
+    protected TextEdit doCopy() {
+        return new DeleteEdit(this);
+    }
+
+    /*
+     * @see TextEdit#accept0
+     */
+    protected void accept0(TextEditVisitor visitor) {
+        bool visitChildren= visitor.visit(this);
+        if (visitChildren) {
+            acceptChildren(visitor);
+        }
+    }
+
+    /*
+     * @see TextEdit#performDocumentUpdating
+     */
+    int performDocumentUpdating(IDocument document)  {
+        document.replace(getOffset(), getLength(), ""); //$NON-NLS-1$
+        fDelta= -getLength();
+        return fDelta;
+    }
+
+    /*
+     * @see TextEdit#deleteChildren
+     */
+    bool deleteChildren() {
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/EditDocument.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.EditDocument;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IDocumentPartitioner;
+import org.eclipse.jface.text.IDocumentPartitioningListener;
+import org.eclipse.jface.text.IPositionUpdater;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Position;
+
+class EditDocument : IDocument {
+
+    private StringBuffer fBuffer;
+
+    public this(String content) {
+        fBuffer= new StringBuffer(content);
+    }
+
+    public void addDocumentListener(IDocumentListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void addDocumentPartitioningListener(IDocumentPartitioningListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void addPosition(Position position)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public void addPosition(String category, Position position)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public void addPositionCategory(String category) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void addPositionUpdater(IPositionUpdater updater) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void addPrenotifiedDocumentListener(IDocumentListener documentAdapter) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int computeIndexInCategory(String category, int offset)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public int computeNumberOfLines(String text) {
+        throw new UnsupportedOperationException();
+    }
+
+    public ITypedRegion[] computePartitioning(int offset, int length)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public bool containsPosition(String category, int offset, int length) {
+        throw new UnsupportedOperationException();
+    }
+
+    public bool containsPositionCategory(String category) {
+        throw new UnsupportedOperationException();
+    }
+
+    public String get() {
+        return fBuffer.toString();
+    }
+
+    public String get(int offset, int length_)  {
+        return fBuffer.slice()[offset .. offset + length_ ];
+    }
+
+    public char getChar(int offset)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getContentType(int offset)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public IDocumentPartitioner getDocumentPartitioner() {
+        throw new UnsupportedOperationException();
+    }
+
+    public String[] getLegalContentTypes() {
+        throw new UnsupportedOperationException();
+    }
+
+    public String[] getLegalLineDelimiters() {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getLength() {
+        return fBuffer.length();
+    }
+
+    public String getLineDelimiter(int line)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public IRegion getLineInformation(int line)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public IRegion getLineInformationOfOffset(int offset)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getLineLength(int line)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getLineOffset(int line)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getLineOfOffset(int offset)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getNumberOfLines() {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getNumberOfLines(int offset, int length)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public ITypedRegion getPartition(int offset)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public String[] getPositionCategories() {
+        throw new UnsupportedOperationException();
+    }
+
+    public Position[] getPositions(String category)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public IPositionUpdater[] getPositionUpdaters() {
+        throw new UnsupportedOperationException();
+    }
+
+    public void insertPositionUpdater(IPositionUpdater updater, int index) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void removeDocumentListener(IDocumentListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void removeDocumentPartitioningListener(IDocumentPartitioningListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void removePosition(Position position) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void removePosition(String category, Position position)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public void removePositionCategory(String category)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public void removePositionUpdater(IPositionUpdater updater) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void removePrenotifiedDocumentListener(IDocumentListener documentAdapter) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void replace(int offset, int length, String text)  {
+        fBuffer.select(offset, length );
+        fBuffer.replace(text);
+    }
+
+    public int search(int startOffset, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord)  {
+        throw new UnsupportedOperationException();
+    }
+
+    public void set(String text) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void setDocumentPartitioner(IDocumentPartitioner partitioner) {
+        throw new UnsupportedOperationException();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/ISourceModifier.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.text.edits.ISourceModifier;
+
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A source modifier can be used to modify the source of
+ * a move or copy edit before it gets inserted at the target
+ * position. This is useful if the text to be  copied has to
+ * be modified before it is inserted without changing the
+ * original source.
+ *
+ * @since 3.0
+ */
+public interface ISourceModifier {
+    /**
+     * Returns the modification to be done to the passed
+     * string in form of replace edits. The set of returned
+     * replace edits must modify disjoint text regions.
+     * Violating this requirement will result in a <code>
+     * BadLocationException</code> while executing the
+     * associated move or copy edit.
+     * <p>
+     * The caller of this method is responsible to apply
+     * the returned edits to the passed source.
+     *
+     * @param source the source to be copied or moved
+     * @return an array of <code>ReplaceEdits</code>
+     *  describing the modifications.
+     */
+    public ReplaceEdit[] getModifications(String source);
+
+    /**
+     * Creates a copy of this source modifier object. The copy will
+     * be used in a different text edit object. So it should be
+     * created in a way that is doesn't conflict with other text edits
+     * referring to this source modifier.
+     *
+     * @return the copy of the source modifier
+     */
+    public ISourceModifier copy();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/InsertEdit.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.InsertEdit;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+/**
+ * Text edit to insert a text at a given position in a
+ * document.
+ * <p>
+ * An insert edit is equivalent to <code>ReplaceEdit(offset, 0, text)
+ * </code>
+ *
+ * @since 3.0
+ */
+public final class InsertEdit : TextEdit {
+
+    private String fText;
+
+    /**
+     * Constructs a new insert edit.
+     *
+     * @param offset the insertion offset
+     * @param text the text to insert
+     */
+    public this(int offset, String text) {
+        super(offset, 0);
+        Assert.isNotNull(text);
+        fText= text;
+    }
+
+    /*
+     * Copy constructor
+     */
+    private this(InsertEdit other) {
+        super(other);
+        fText= other.fText;
+    }
+
+    /**
+     * Returns the text to be inserted.
+     *
+     * @return the edit's text.
+     */
+    public String getText() {
+        return fText;
+    }
+
+    /*
+     * @see TextEdit#doCopy
+     */
+    protected TextEdit doCopy() {
+        return new InsertEdit(this);
+    }
+
+    /*
+     * @see TextEdit#accept0
+     */
+    protected void accept0(TextEditVisitor visitor) {
+        bool visitChildren= visitor.visit(this);
+        if (visitChildren) {
+            acceptChildren(visitor);
+        }
+    }
+
+    /*
+     * @see TextEdit#performDocumentUpdating
+     */
+    int performDocumentUpdating(IDocument document)  {
+        document.replace(getOffset(), getLength(), fText);
+        fDelta= fText.length() - getLength();
+        return fDelta;
+    }
+
+    /*
+     * @see TextEdit#deleteChildren
+     */
+    bool deleteChildren() {
+        return false;
+    }
+
+    /*
+     * @see org.eclipse.text.edits.TextEdit#internalToString(java.lang.StringBuffer, int)
+     * @since 3.3
+     */
+    void internalToString(StringBuffer buffer, int indent) {
+        super.internalToString(buffer, indent);
+        buffer.append(" <<").append(fText); //$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/MalformedTreeException.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.MalformedTreeException;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Thrown to indicate that an edit got added to a parent edit
+ * but the child edit somehow conflicts with the parent or
+ * one of it siblings.
+ * <p>
+ * This class is not intended to be serialized.
+ * </p>
+ *
+ * @see TextEdit#addChild(TextEdit)
+ * @see TextEdit#addChildren(TextEdit[])
+ *
+ * @since 3.0
+ */
+public class MalformedTreeException : RuntimeException {
+
+    // Not intended to be serialized
+    private static const long serialVersionUID= 1L;
+
+    private TextEdit fParent;
+    private TextEdit fChild;
+
+    /**
+     * Constructs a new malformed tree exception.
+     *
+     * @param parent the parent edit
+     * @param child the child edit
+     * @param message the detail message
+     */
+    public this(TextEdit parent, TextEdit child, String message) {
+        super(message);
+        fParent= parent;
+        fChild= child;
+    }
+
+    /**
+     * Returns the parent edit that caused the exception.
+     *
+     * @return the parent edit
+     */
+    public TextEdit getParent() {
+        return fParent;
+    }
+
+    /**
+     * Returns the child edit that caused the exception.
+     *
+     * @return the child edit
+     */
+    public TextEdit getChild() {
+        return fChild;
+    }
+
+    void setParent(TextEdit parent) {
+        fParent= parent;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/Messages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,35 @@
+###############################################################################
+# 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
+###############################################################################
+
+TextEdit.range_outside= Range of child edit lies outside of parent edit
+TextEdit.overlapping= Overlapping text edits
+TextEdit.deleted_edit= Cannot add deleted edit
+
+CopySourceEdit.no_target= No target edit provided.
+CopySourceEdit.different_source= Target edit has different source edit.
+CopySourceEdit.different_tree= The source and target edits belong to a different text edit tree.
+
+CopyTargetEdit.no_source= No source edit provided.
+CopyTargetEdit.different_target= Source edit has different target edit.
+CopyTargetEdit.wrong_parent=Source edit must not be the parent of the target.
+
+MoveSourceEdit.no_target= No target edit provided.
+MoveSourceEdit.different_source= Target edit has different source edit.
+MoveSourceEdit.different_tree= The source and target edits belong to a different text edit tree.
+
+MoveTargetEdit.no_source= No source edit provided.
+MoveTargetEdit.different_target= Source edit has different target edit.
+MoveTargetEdit.wrong_parent=Source edit must not be the parent of the target.
+
+TextEditProcessor.invalid_length=End position lies outside document range
+
+UndoEdit.no_children=Cannot add children to an undo edit
+UndoEdit.can_not_be_added=Cannot add an undo edit to another edit
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/MoveSourceEdit.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,457 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.MoveSourceEdit;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+
+
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
+
+/**
+ * A move source edit denotes the source of a move operation. Move
+ * source edits are only valid inside an edit tree if they have a
+ * corresponding target edit. Furthermore the corresponding target
+ * edit can't be a direct or indirect child of the source edit.
+ * Violating one of two requirements will result in a <code>
+ * MalformedTreeException</code> when executing the edit tree.
+ * <p>
+ * A move source edit can manage an optional source modifier. A
+ * source modifier can provide a set of replace edits which will
+ * to applied to the source before it gets inserted at the target
+ * position.
+ *
+ * @see org.eclipse.text.edits.MoveTargetEdit
+ * @see org.eclipse.text.edits.CopySourceEdit
+ *
+ * @since 3.0
+ */
+public final class MoveSourceEdit : TextEdit {
+
+    private MoveTargetEdit fTarget;
+    private ISourceModifier fModifier;
+
+    private String fSourceContent;
+    private MultiTextEdit fSourceRoot;
+
+    /**
+     * Constructs a new move source edit.
+     *
+     * @param offset the edit's offset
+     * @param length the edit's length
+     */
+    public this(int offset, int length) {
+        super(offset, length);
+    }
+
+    /**
+     * Constructs a new copy source edit.
+     *
+     * @param offset the edit's offset
+     * @param length the edit's length
+     * @param target the edit's target
+     */
+    public this(int offset, int length, MoveTargetEdit target) {
+        this(offset, length);
+        setTargetEdit(target);
+    }
+
+    /*
+     * Copy constructor
+     */
+    private this(MoveSourceEdit other) {
+        super(other);
+        if (other.fModifier !is null)
+            fModifier= other.fModifier.copy();
+    }
+
+    /**
+     * Returns the associated target edit or <code>null</code>
+     * if no target edit is associated yet.
+     *
+     * @return the target edit or <code>null</code>
+     */
+    public MoveTargetEdit getTargetEdit() {
+        return fTarget;
+    }
+
+    /**
+     * Sets the target edit.
+     *
+     * @param edit the new target edit.
+     *
+     * @exception MalformedTreeException is thrown if the target edit
+     *  is a direct or indirect child of the source edit
+     */
+    public void setTargetEdit(MoveTargetEdit edit) {
+        fTarget= edit;
+        fTarget.setSourceEdit(this);
+    }
+
+    /**
+     * Returns the current source modifier or <code>null</code>
+     * if no source modifier is set.
+     *
+     * @return the source modifier
+     */
+    public ISourceModifier getSourceModifier() {
+        return fModifier;
+    }
+
+    /**
+     * Sets the optional source modifier.
+     *
+     * @param modifier the source modifier or <code>null</code>
+     *  if no source modification is need.
+     */
+    public void setSourceModifier(ISourceModifier modifier) {
+        fModifier= modifier;
+    }
+
+    //---- API for MoveTargetEdit ---------------------------------------------
+
+    String getContent() {
+        // The source content can be null if the edit wasn't executed
+        // due to an exclusion list of the text edit processor. Return
+        // the empty string which can be moved without any harm.
+        if (fSourceContent is null)
+            return ""; //$NON-NLS-1$
+        return fSourceContent;
+    }
+
+    MultiTextEdit getSourceRoot() {
+        return fSourceRoot;
+    }
+
+    void clearContent() {
+        fSourceContent= null;
+        fSourceRoot= null;
+    }
+
+    //---- Copying -------------------------------------------------------------
+
+    /*
+     * @see TextEdit#doCopy
+     */
+    protected TextEdit doCopy() {
+        return new MoveSourceEdit(this);
+    }
+
+    /*
+     * @see TextEdit#postProcessCopy
+     */
+    protected void postProcessCopy(TextEditCopier copier) {
+        if (fTarget !is null) {
+            MoveSourceEdit source= cast(MoveSourceEdit)copier.getCopy(this);
+            MoveTargetEdit target= cast(MoveTargetEdit)copier.getCopy(fTarget);
+            if (source !is null && target !is null)
+                source.setTargetEdit(target);
+        }
+    }
+
+    //---- Visitor -------------------------------------------------------------
+
+    /*
+     * @see TextEdit#accept0
+     */
+    protected void accept0(TextEditVisitor visitor) {
+        bool visitChildren= visitor.visit(this);
+        if (visitChildren) {
+            acceptChildren(visitor);
+        }
+    }
+
+    //---- consistency check ----------------------------------------------------------------
+
+    int traverseConsistencyCheck(TextEditProcessor processor, IDocument document, List sourceEdits) {
+        int result= super.traverseConsistencyCheck(processor, document, sourceEdits);
+        // Since source computation takes place in a recursive fashion (see
+        // performSourceComputation) we only do something if we don't have a
+        // computed source already.
+        if (fSourceContent is null) {
+            if (sourceEdits.size() <= result) {
+                List list= new ArrayList();
+                list.add(this);
+                for (int i= sourceEdits.size(); i < result; i++)
+                    sourceEdits.add(cast(Object)null);
+                sourceEdits.add(cast(Object)list);
+            } else {
+                List list= cast(List)sourceEdits.get(result);
+                if (list is null) {
+                    list= new ArrayList();
+                    sourceEdits.add(result, cast(Object)list);
+                }
+                list.add(this);
+            }
+        }
+        return result;
+    }
+
+    void performConsistencyCheck(TextEditProcessor processor, IDocument document)  {
+        if (fTarget is null)
+            throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("MoveSourceEdit.no_target")); //$NON-NLS-1$
+        if (fTarget.getSourceEdit() !is this)
+            throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("MoveSourceEdit.different_source"));  //$NON-NLS-1$
+        /* Causes AST rewrite to fail
+        if (getRoot() !is fTarget.getRoot())
+            throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("MoveSourceEdit.different_tree")); //$NON-NLS-1$
+        */
+    }
+
+    //---- source computation --------------------------------------------------------------
+
+    void traverseSourceComputation(TextEditProcessor processor, IDocument document) {
+        // always perform source computation independent of processor.considerEdit
+        // The target might need the source and the source is computed in a
+        // temporary buffer.
+        performSourceComputation(processor, document);
+    }
+
+    void performSourceComputation(TextEditProcessor processor, IDocument document) {
+        try {
+            TextEdit[] children= removeChildren();
+            if (children.length > 0) {
+                String content= document.get(getOffset(), getLength());
+                EditDocument subDocument= new EditDocument(content);
+                fSourceRoot= new MultiTextEdit(getOffset(), getLength());
+                fSourceRoot.addChildren(children);
+                fSourceRoot.internalMoveTree(-getOffset());
+                int processingStyle= getStyle(processor);
+                TextEditProcessor subProcessor= TextEditProcessor.createSourceComputationProcessor(subDocument, fSourceRoot, processingStyle);
+                subProcessor.performEdits();
+                if (needsTransformation())
+                    applyTransformation(subDocument, processingStyle);
+                fSourceContent= subDocument.get();
+            } else {
+                fSourceContent= document.get(getOffset(), getLength());
+                if (needsTransformation()) {
+                    EditDocument subDocument= new EditDocument(fSourceContent);
+                    applyTransformation(subDocument, getStyle(processor));
+                    fSourceContent= subDocument.get();
+                }
+            }
+        } catch (BadLocationException cannotHappen) {
+            Assert.isTrue(false);
+        }
+    }
+
+    private int getStyle(TextEditProcessor processor) {
+        // we never need undo while performing local edits.
+        if ((processor.getStyle() & TextEdit.UPDATE_REGIONS) !is 0)
+            return TextEdit.UPDATE_REGIONS;
+        return TextEdit.NONE;
+    }
+
+    //---- document updating ----------------------------------------------------------------
+
+    int performDocumentUpdating(IDocument document)  {
+        document.replace(getOffset(), getLength(), ""); //$NON-NLS-1$
+        fDelta= -getLength();
+        return fDelta;
+    }
+
+    //---- region updating --------------------------------------------------------------
+
+    /*
+     * @see TextEdit#deleteChildren
+     */
+    bool deleteChildren() {
+        return false;
+    }
+
+    //---- content transformation --------------------------------------------------
+
+    private bool needsTransformation() {
+        return fModifier !is null;
+    }
+
+    private void applyTransformation(IDocument document, int style)  {
+        if ((style & TextEdit.UPDATE_REGIONS) !is 0 && fSourceRoot !is null) {
+            Map editMap= new HashMap();
+            TextEdit newEdit= createEdit(editMap);
+            List replaces= new ArrayList(Arrays.asList(fModifier.getModifications(document.get())));
+            insertEdits(newEdit, replaces);
+            try {
+                newEdit.apply(document, style);
+            } catch (BadLocationException cannotHappen) {
+                Assert.isTrue(false);
+            }
+            restorePositions(editMap);
+        } else {
+            MultiTextEdit newEdit= new MultiTextEdit(0, document.getLength());
+            TextEdit[] replaces= fModifier.getModifications(document.get());
+            for (int i= 0; i < replaces.length; i++) {
+                newEdit.addChild(replaces[i]);
+            }
+            try {
+                newEdit.apply(document, style);
+            } catch (BadLocationException cannotHappen) {
+                Assert.isTrue(false);
+            }
+        }
+    }
+
+    private TextEdit createEdit(Map editMap) {
+        MultiTextEdit result= new MultiTextEdit(0, fSourceRoot.getLength());
+        editMap.put(result, fSourceRoot);
+        createEdit(fSourceRoot, result, editMap);
+        return result;
+    }
+
+    private static void createEdit(TextEdit source, TextEdit target, Map editMap) {
+        TextEdit[] children= source.getChildren();
+        for (int i= 0; i < children.length; i++) {
+            TextEdit child= children[i];
+            // a deleted child remains deleted even if the temporary buffer
+            // gets modified.
+            if (child.isDeleted())
+                continue;
+            RangeMarker marker= new RangeMarker(child.getOffset(), child.getLength());
+            target.addChild(marker);
+            editMap.put(marker, child);
+            createEdit(child, marker, editMap);
+        }
+    }
+
+    private void insertEdits(TextEdit root, List edits) {
+        while(edits.size() > 0) {
+            ReplaceEdit edit= cast(ReplaceEdit)edits.remove(0);
+            insert(root, edit, edits);
+        }
+    }
+    private static void insert(TextEdit parent, ReplaceEdit edit, List edits) {
+        if (!parent.hasChildren()) {
+            parent.addChild(edit);
+            return;
+        }
+        TextEdit[] children= parent.getChildren();
+        // First dive down to find the right parent.
+        int removed= 0;
+        for (int i= 0; i < children.length; i++) {
+            TextEdit child= children[i];
+            if (child.covers(edit)) {
+                insert(child, edit, edits);
+                return;
+            } else if (edit.covers(child)) {
+                parent.removeChild(i - removed++);
+                edit.addChild(child);
+            } else {
+                IRegion intersect= intersect(edit, child);
+                if (intersect !is null) {
+                    ReplaceEdit[] splits= splitEdit(edit, intersect);
+                    insert(child, splits[0], edits);
+                    edits.add(splits[1]);
+                    return;
+                }
+            }
+        }
+        parent.addChild(edit);
+    }
+
+    public static IRegion intersect(TextEdit op1, TextEdit op2) {
+        int offset1= op1.getOffset();
+        int length1= op1.getLength();
+        int end1= offset1 + length1 - 1;
+        int offset2= op2.getOffset();
+        if (end1 < offset2)
+            return null;
+        int length2= op2.getLength();
+        int end2= offset2 + length2 - 1;
+        if (end2 < offset1)
+            return null;
+
+        int end= Math.min(end1, end2);
+        if (offset1 < offset2) {
+            return new Region(offset2, end - offset2 + 1);
+        }
+        return new Region(offset1, end - offset1 + 1);
+    }
+
+    private static ReplaceEdit[] splitEdit(ReplaceEdit edit, IRegion intersect) {
+        if (edit.getOffset() !is intersect.getOffset())
+            return splitIntersectRight(edit, intersect);
+        return splitIntersectLeft(edit, intersect);
+    }
+
+    private static ReplaceEdit[] splitIntersectRight(ReplaceEdit edit, IRegion intersect) {
+        ReplaceEdit[] result= new ReplaceEdit[2];
+        // this is the actual delete. We use replace to only deal with one type
+        result[0]= new ReplaceEdit(intersect.getOffset(), intersect.getLength(), ""); //$NON-NLS-1$
+        result[1]= new ReplaceEdit(
+                            edit.getOffset(),
+                            intersect.getOffset() - edit.getOffset(),
+                            edit.getText());
+        return result;
+    }
+
+    private static ReplaceEdit[] splitIntersectLeft(ReplaceEdit edit, IRegion intersect) {
+        ReplaceEdit[] result= new ReplaceEdit[2];
+        result[0]= new ReplaceEdit(intersect.getOffset(), intersect.getLength(), edit.getText());
+        result[1]= new ReplaceEdit( // this is the actual delete. We use replace to only deal with one type
+                            intersect.getOffset() + intersect.getLength(),
+                            edit.getLength() - intersect.getLength(),
+                            ""); //$NON-NLS-1$
+        return result;
+    }
+
+    private static void restorePositions(Map editMap) {
+        for (Iterator iter= editMap.keySet().iterator(); iter.hasNext();) {
+            TextEdit marker= cast(TextEdit)iter.next();
+            TextEdit edit= cast(TextEdit)editMap.get(marker);
+            if (marker.isDeleted()) {
+                edit.markAsDeleted();
+            } else {
+                edit.adjustOffset(marker.getOffset() - edit.getOffset());
+                edit.adjustLength(marker.getLength() - edit.getLength());
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/MoveTargetEdit.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,221 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.MoveTargetEdit;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+/**
+ * A move target edit denotes the target of a move operation. Move
+ * target edits are only valid inside an edit tree if they have a
+ * corresponding source edit. Furthermore a target edit can't
+ * can't be a direct or indirect child of its associated source edit.
+ * Violating one of two requirements will result in a <code>
+ * MalformedTreeException</code> when executing the edit tree.
+ * <p>
+ * Move target edits can't be used as a parent for other edits.
+ * Trying to add an edit to a move target edit results in a <code>
+ * MalformedTreeException</code> as well.
+ *
+ * @see org.eclipse.text.edits.MoveSourceEdit
+ * @see org.eclipse.text.edits.CopyTargetEdit
+ *
+ * @since 3.0
+ */
+public final class MoveTargetEdit : TextEdit {
+
+    private MoveSourceEdit fSource;
+
+    /**
+     * Constructs a new move target edit
+     *
+     * @param offset the edit's offset
+     */
+    public this(int offset) {
+        super(offset, 0);
+    }
+
+    /**
+     * Constructs an new move target edit
+     *
+     * @param offset the edit's offset
+     * @param source the corresponding source edit
+     */
+    public this(int offset, MoveSourceEdit source) {
+        this(offset);
+        setSourceEdit(source);
+    }
+
+    /*
+     * Copy constructor
+     */
+    private this(MoveTargetEdit other) {
+        super(other);
+    }
+
+    /**
+     * Returns the associated source edit or <code>null</code>
+     * if no source edit is associated yet.
+     *
+     * @return the source edit or <code>null</code>
+     */
+    public MoveSourceEdit getSourceEdit() {
+        return fSource;
+    }
+
+    /**
+     * Sets the source edit.
+     *
+     * @param edit the source edit
+     *
+     * @exception MalformedTreeException is thrown if the target edit
+     *  is a direct or indirect child of the source edit
+     */
+    public void setSourceEdit(MoveSourceEdit edit) {
+        if (fSource !is edit) {
+            fSource= edit;
+            fSource.setTargetEdit(this);
+            TextEdit parent= getParent();
+            while (parent !is null) {
+                if (parent is fSource)
+                    throw new MalformedTreeException(parent, this, TextEditMessages.getString("MoveTargetEdit.wrong_parent")); //$NON-NLS-1$
+                parent= parent.getParent();
+            }
+        }
+    }
+
+    /*
+     * @see TextEdit#doCopy
+     */
+    protected TextEdit doCopy() {
+        return new MoveTargetEdit(this);
+    }
+
+    /*
+     * @see TextEdit#postProcessCopy
+     */
+    protected void postProcessCopy(TextEditCopier copier) {
+        if (fSource !is null) {
+            MoveTargetEdit target= cast(MoveTargetEdit)copier.getCopy(this);
+            MoveSourceEdit source= cast(MoveSourceEdit)copier.getCopy(fSource);
+            if (target !is null && source !is null)
+                target.setSourceEdit(source);
+        }
+    }
+
+    /*
+     * @see TextEdit#accept0
+     */
+    protected void accept0(TextEditVisitor visitor) {
+        bool visitChildren= visitor.visit(this);
+        if (visitChildren) {
+            acceptChildren(visitor);
+        }
+    }
+
+    //---- consistency check ----------------------------------------------------------
+
+    /*
+     * @see TextEdit#traverseConsistencyCheck
+     */
+    int traverseConsistencyCheck(TextEditProcessor processor, IDocument document, List sourceEdits) {
+        return super.traverseConsistencyCheck(processor, document, sourceEdits) + 1;
+    }
+
+    /*
+     * @see TextEdit#performConsistencyCheck
+     */
+    void performConsistencyCheck(TextEditProcessor processor, IDocument document)  {
+        if (fSource is null)
+            throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("MoveTargetEdit.no_source")); //$NON-NLS-1$
+        if (fSource.getTargetEdit() !is this)
+            throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("MoveTargetEdit.different_target")); //$NON-NLS-1$
+    }
+
+    //---- document updating ----------------------------------------------------------------
+
+    /*
+     * @see TextEdit#performDocumentUpdating
+     */
+    int performDocumentUpdating(IDocument document)  {
+        String source= fSource.getContent();
+        document.replace(getOffset(), getLength(), source);
+        fDelta= source.length() - getLength();
+
+        MultiTextEdit sourceRoot= fSource.getSourceRoot();
+        if (sourceRoot !is null) {
+            sourceRoot.internalMoveTree(getOffset());
+            TextEdit[] sourceChildren= sourceRoot.removeChildren();
+            List children= new ArrayList(sourceChildren.length);
+            for (int i= 0; i < sourceChildren.length; i++) {
+                TextEdit child= sourceChildren[i];
+                child.internalSetParent(this);
+                children.add(child);
+            }
+            internalSetChildren(children);
+        }
+        fSource.clearContent();
+        return fDelta;
+    }
+
+    //---- region updating --------------------------------------------------------------
+
+    /*
+     * @see org.eclipse.text.edits.TextEdit#traversePassThree
+     */
+    int traverseRegionUpdating(TextEditProcessor processor, IDocument document, int accumulatedDelta, bool delete_) {
+        // the children got already updated / normalized while they got removed
+        // from the source edit. So we only have to adjust the offset computed to
+        // far.
+        if (delete_) {
+            deleteTree();
+        } else {
+            internalMoveTree(accumulatedDelta);
+        }
+        return accumulatedDelta + fDelta;
+    }
+
+    bool deleteChildren() {
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/MultiTextEdit.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.MultiTextEdit;
+
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+import java.lang.all;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+
+/**
+ * A multi-text edit can be used to aggregate several edits into
+ * one edit. The edit itself doesn't modify a document.
+ * <p>
+ * Clients are allowed to implement subclasses of a multi-text
+ * edit.Subclasses must implement <code>doCopy()</code> to ensure
+ * the a copy of the right type is created. Not implementing
+ * <code>doCopy()</code> in subclasses will result in an assertion
+ * failure during copying.
+ *
+ * @since 3.0
+ */
+public class MultiTextEdit : TextEdit {
+
+    private bool fDefined;
+
+    /**
+     * Creates a new <code>MultiTextEdit</code>. The range
+     * of the edit is determined by the range of its children.
+     *
+     * Adding this edit to a parent edit sets its range to the
+     * range covered by its children. If the edit doesn't have
+     * any children its offset is set to the parent's offset
+     * and its length is set to 0.
+     */
+    public this() {
+        super(0, Integer.MAX_VALUE);
+        fDefined= false;
+    }
+
+    /**
+     * Creates a new </code>MultiTextEdit</code> for the given
+     * range. Adding a child to this edit which isn't covered
+     * by the given range will result in an exception.
+     *
+     * @param offset the edit's offset
+     * @param length the edit's length.
+     * @see TextEdit#addChild(TextEdit)
+     * @see TextEdit#addChildren(TextEdit[])
+     */
+    public this(int offset, int length) {
+        super(offset, length);
+        fDefined= true;
+    }
+
+    /*
+     * Copy constructor.
+     */
+    protected this(MultiTextEdit other) {
+        super(other);
+    }
+
+    /**
+     * Checks the edit's integrity.
+     * <p>
+     * Note that this method <b>should only be called</b> by the edit
+     * framework and not by normal clients.</p>
+     *<p>
+     * This default implementation does nothing. Subclasses may override
+     * if needed.</p>
+     *
+     * @exception MalformedTreeException if the edit isn't in a valid state
+     *  and can therefore not be executed
+     */
+    protected void checkIntegrity()  {
+        // does nothing
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    final bool isDefined() {
+        if (fDefined)
+            return true;
+        return hasChildren();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public final int getOffset() {
+        if (fDefined)
+            return super.getOffset();
+
+        List/*<TextEdit>*/ children= internalGetChildren();
+        if (children is null || children.size() is 0)
+            return 0;
+        // the children are already sorted
+        return (cast(TextEdit)children.get(0)).getOffset();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public final int getLength() {
+        if (fDefined)
+            return super.getLength();
+
+        List/*<TextEdit>*/ children= internalGetChildren();
+        if (children is null || children.size() is 0)
+            return 0;
+        // the children are already sorted
+        TextEdit first= cast(TextEdit)children.get(0);
+        TextEdit last= cast(TextEdit)children.get(children.size() - 1);
+        return last.getOffset() - first.getOffset() + last.getLength();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public final bool covers(TextEdit other) {
+        if (fDefined)
+            return super.covers(other);
+        // an undefined multiple text edit covers everything
+        return true;
+    }
+
+    /*
+     * @see org.eclipse.text.edits.TextEdit#canZeroLengthCover()
+     */
+    protected bool canZeroLengthCover() {
+        return true;
+    }
+
+    /*
+     * @see TextEdit#copy
+     */
+    protected TextEdit doCopy() {
+        Assert.isTrue(MultiTextEdit.classinfo is this.classinfo, "Subclasses must reimplement copy0"); //$NON-NLS-1$
+        return new MultiTextEdit(this);
+    }
+
+    /*
+     * @see TextEdit#accept0
+     */
+    protected void accept0(TextEditVisitor visitor) {
+        bool visitChildren= visitor.visit(this);
+        if (visitChildren) {
+            acceptChildren(visitor);
+        }
+    }
+
+    /*
+     * @see org.eclipse.text.edits.TextEdit#adjustOffset(int)
+     * @since 3.1
+     */
+    void adjustOffset(int delta) {
+        if (fDefined)
+            super.adjustOffset(delta);
+    }
+
+    /*
+     * @see org.eclipse.text.edits.TextEdit#adjustLength(int)
+     * @since 3.1
+     */
+    void adjustLength(int delta) {
+        if (fDefined)
+            super.adjustLength(delta);
+    }
+
+    /*
+     * @see TextEdit#performConsistencyCheck
+     */
+    void performConsistencyCheck(TextEditProcessor processor, IDocument document)  {
+        checkIntegrity();
+    }
+
+    /*
+     * @see TextEdit#performDocumentUpdating
+     */
+    int performDocumentUpdating(IDocument document)  {
+        fDelta= 0;
+        return fDelta;
+    }
+
+    /*
+     * @see TextEdit#deleteChildren
+     */
+    bool deleteChildren() {
+        return false;
+    }
+
+    void aboutToBeAdded(TextEdit parent) {
+        defineRegion(parent.getOffset());
+    }
+
+    void defineRegion(int parentOffset) {
+        if (fDefined)
+            return;
+        if (hasChildren()) {
+            IRegion region= getCoverage(getChildren());
+            internalSetOffset(region.getOffset());
+            internalSetLength(region.getLength());
+        } else {
+            internalSetOffset(parentOffset);
+            internalSetLength(0);
+        }
+        fDefined= true;
+    }
+
+    /*
+     * @see org.eclipse.text.edits.TextEdit#internalToString(java.lang.StringBuffer, int)
+     * @since 3.3
+     */
+    void internalToString(StringBuffer buffer, int indent) {
+        super.internalToString(buffer, indent);
+        if (! fDefined)
+            buffer.append(" [undefined]"); //$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/RangeMarker.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.RangeMarker;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+/**
+ * A range marker can be used to track positions when executing
+ * text edits.
+ *
+ * @since 3.0
+ */
+public final class RangeMarker : TextEdit {
+
+    /**
+     * Creates a new range marker for the given offset and length.
+     *
+     * @param offset the marker's offset
+     * @param length the marker's length
+     */
+    public this(int offset, int length) {
+        super(offset, length);
+    }
+
+    /*
+     * Copy constructor
+     */
+    private this(RangeMarker other) {
+        super(other);
+    }
+
+    /*
+     * @see TextEdit#copy
+     */
+    protected TextEdit doCopy() {
+        return new RangeMarker(this);
+    }
+
+    /*
+     * @see TextEdit#accept0
+     */
+    protected void accept0(TextEditVisitor visitor) {
+        bool visitChildren= visitor.visit(this);
+        if (visitChildren) {
+            acceptChildren(visitor);
+        }
+    }
+
+    /*
+     * @see TextEdit#performDocumentUpdating
+     */
+    int performDocumentUpdating(IDocument document)  {
+        fDelta= 0;
+        return fDelta;
+    }
+
+    /*
+     * @see TextEdit#deleteChildren
+     */
+    bool deleteChildren() {
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/ReplaceEdit.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.ReplaceEdit;
+
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+/**
+ * Text edit to replace a range in a document with a different
+ * string.
+ *
+ * @since 3.0
+ */
+public final class ReplaceEdit : TextEdit {
+
+    private String fText;
+
+    /**
+     * Constructs a new replace edit.
+     *
+     * @param offset the offset of the range to replace
+     * @param length the length of the range to replace
+     * @param text the new text
+     */
+    public this(int offset, int length, String text) {
+        super(offset, length);
+        Assert.isNotNull(text);
+        fText= text;
+    }
+
+    /*
+     * Copy constructor
+     *
+     * @param other the edit to copy from
+     */
+    private this(ReplaceEdit other) {
+        super(other);
+        fText= other.fText;
+    }
+
+    /**
+     * Returns the new text replacing the text denoted
+     * by the edit.
+     *
+     * @return the edit's text.
+     */
+    public String getText() {
+        return fText;
+    }
+
+    /*
+     * @see TextEdit#doCopy
+     */
+    protected TextEdit doCopy() {
+        return new ReplaceEdit(this);
+    }
+
+    /*
+     * @see TextEdit#accept0
+     */
+    protected void accept0(TextEditVisitor visitor) {
+        bool visitChildren= visitor.visit(this);
+        if (visitChildren) {
+            acceptChildren(visitor);
+        }
+    }
+
+    /*
+     * @see TextEdit#performDocumentUpdating
+     */
+    int performDocumentUpdating(IDocument document)  {
+        document.replace(getOffset(), getLength(), fText);
+        fDelta= fText.length() - getLength();
+        return fDelta;
+    }
+
+    /*
+     * @see TextEdit#deleteChildren
+     */
+    bool deleteChildren() {
+        return true;
+    }
+
+    /*
+     * @see org.eclipse.text.edits.TextEdit#internalToString(java.lang.StringBuffer, int)
+     * @since 3.3
+     */
+    void internalToString(StringBuffer buffer, int indent) {
+        super.internalToString(buffer, indent);
+        buffer.append(" <<").append(fText); //$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/TextEdit.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,995 @@
+/*******************************************************************************
+ * 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 org.eclipse.text.edits.TextEdit;
+
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+
+import java.lang.all;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import tango.text.convert.Format;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
+
+
+/**
+ * A text edit describes an elementary text manipulation operation. Edits are
+ * executed by applying them to a document (e.g. an instance of <code>IDocument
+ * </code>).
+ * <p>
+ * Text edits form a tree. Clients can navigate the tree upwards, from child to
+ * parent, as well as downwards. Newly created edits are un-parented. New edits
+ * are added to the tree by calling one of the <code>add</code> methods on a parent
+ * edit.
+ * </p>
+ * <p>
+ * An edit tree is well formed in the following sense:
+ * <ul>
+ *   <li>a parent edit covers all its children</li>
+ *   <li>children don't overlap</li>
+ *   <li>an edit with length 0 can't have any children</li>
+ * </ul>
+ * Any manipulation of the tree that violates one of the above requirements results
+ * in a <code>MalformedTreeException</code>.
+ * </p>
+ * <p>
+ * Insert edits are represented by an edit of length 0. If more than one insert
+ * edit exists at the same offset then the edits are executed in the order in which
+ * they have been added to a parent. The following code example:
+ * <pre>
+ *    IDocument document= new Document("org");
+ *    MultiTextEdit edit= new MultiTextEdit();
+ *    edit.addChild(new InsertEdit(0, "www."));
+ *    edit.addChild(new InsertEdit(0, "eclipse."));
+ *    edit.apply(document);
+ * </pre>
+ * therefore results in string: "www.eclipse.org".
+ * </p>
+ * <p>
+ * Text edits can be executed in a mode where the edit's region is updated to
+ * reflect the edit's position in the changed document. Region updating is enabled
+ * by default or can be requested by passing <code>UPDATE_REGIONS</code> to the
+ * {@link #apply(IDocument, int) apply(IDocument, int)} method. In the above example
+ * the region of the <code>InsertEdit(0, "eclipse.")</code> edit after executing
+ * the root edit is <code>[3, 8]</code>. If the region of an edit got deleted during
+ * change execution the region is set to <code>[-1, -1]</code> and the method {@link
+ * #isDeleted() isDeleted} returns <code>true</code>.
+ * </p>
+ * This class isn't intended to be subclassed outside of the edit framework. Clients
+ * are only allowed to subclass <code>MultiTextEdit</code>.
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public abstract class TextEdit {
+
+    /**
+     * Flags indicating that neither <code>CREATE_UNDO</code> nor
+     * <code>UPDATE_REGIONS</code> is set.
+     */
+    public static const int NONE= 0;
+
+    /**
+     * Flags indicating that applying an edit tree to a document
+     * is supposed to create a corresponding undo edit. If not
+     * specified <code>null</code> is returned from method <code>
+     * apply</code>.
+     */
+    public static const int CREATE_UNDO= 1 << 0;
+
+    /**
+     * Flag indicating that the edit's region will be updated to
+     * reflect its position in the changed document. If not specified
+     * when applying an edit tree to a document the edit's region will
+     * be arbitrary. It is even not guaranteed that the tree is still
+     * well formed.
+     */
+    public static const int UPDATE_REGIONS= 1 << 1;
+
+    private static class InsertionComparator : Comparator {
+        public int compare(Object o1, Object o2)  {
+            TextEdit edit1= cast(TextEdit)o1;
+            TextEdit edit2= cast(TextEdit)o2;
+
+            int offset1= edit1.getOffset();
+            int length1= edit1.getLength();
+
+            int offset2= edit2.getOffset();
+            int length2= edit2.getLength();
+
+            if (offset1 is offset2 && length1 is 0 && length2 is 0) {
+                return 0;
+            }
+            if (offset1 + length1 <= offset2) {
+                return -1;
+            }
+            if (offset2 + length2 <= offset1) {
+                return 1;
+            }
+            throw new MalformedTreeException(
+                    null, edit1,
+                    TextEditMessages.getString("TextEdit.overlapping")); //$NON-NLS-1$
+        }
+    }
+
+    private static const TextEdit[] EMPTY_ARRAY;
+    private static /+const+/ InsertionComparator INSERTION_COMPARATOR;
+
+    private static const int DELETED_VALUE= -1;
+
+    private int fOffset;
+    private int fLength;
+
+    private TextEdit fParent;
+    private List fChildren;
+
+    int fDelta;
+
+    /**
+     * Create a new text edit. Parent is initialized to <code>
+     * null<code> and the edit doesn't have any children.
+     *
+     * @param offset the edit's offset
+     * @param length the edit's length
+     */
+    protected this(int offset, int length) {
+        if( INSERTION_COMPARATOR is null ) INSERTION_COMPARATOR= new InsertionComparator();
+        Assert.isTrue(offset >= 0 && length >= 0);
+        fOffset= offset;
+        fLength= length;
+        fDelta= 0;
+    }
+
+    /**
+     * Copy constructor
+     *
+     * @param source the source to copy form
+     */
+    protected this(TextEdit source) {
+        if( INSERTION_COMPARATOR is null ) INSERTION_COMPARATOR= new InsertionComparator();
+        fOffset= source.fOffset;
+        fLength= source.fLength;
+        fDelta= 0;
+    }
+
+    //---- Region management -----------------------------------------------
+
+    /**
+     * Returns the range that this edit is manipulating. The returned
+     * <code>IRegion</code> contains the edit's offset and length at
+     * the point in time when this call is made. Any subsequent changes
+     * to the edit's offset and length aren't reflected in the returned
+     * region object.
+     * <p>
+     * Creating a region for a deleted edit will result in an assertion
+     * failure.
+     *
+     * @return the manipulated region
+     */
+    public final IRegion getRegion() {
+        return new Region(getOffset(), getLength());
+    }
+
+    /**
+     * Returns the offset of the edit. An offset is a 0-based
+     * character index. Returns <code>-1</code> if the edit
+     * is marked as deleted.
+     *
+     * @return the offset of the edit
+     */
+    public int getOffset() {
+        return fOffset;
+    }
+
+    /**
+     * Returns the length of the edit. Returns <code>-1</code>
+     * if the edit is marked as deleted.
+     *
+     * @return the length of the edit
+     */
+    public int getLength() {
+        return fLength;
+    }
+
+    /**
+     * Returns the inclusive end position of this edit. The inclusive end
+     * position denotes the last character of the region manipulated by
+     * this edit. The returned value is the result of the following
+     * calculation:
+     * <pre>
+     *   getOffset() + getLength() - 1;
+     * <pre>
+     *
+     * @return the inclusive end position
+     */
+    public final int getInclusiveEnd() {
+        return getOffset() + getLength() - 1;
+    }
+
+    /**
+     * Returns the exclusive end position of this edit. The exclusive end
+     * position denotes the next character of the region manipulated by
+     * this edit. The returned value is the result of the following
+     * calculation:
+     * <pre>
+     *   getOffset() + getLength();
+     * </pre>
+     *
+     * @return the exclusive end position
+     */
+    public final int getExclusiveEnd() {
+        return getOffset() + getLength();
+    }
+
+    /**
+     * Returns whether this edit has been deleted or not.
+     *
+     * @return <code>true</code> if the edit has been
+     *  deleted; otherwise <code>false</code> is returned.
+     */
+    public final bool isDeleted() {
+        return fOffset is DELETED_VALUE && fLength is DELETED_VALUE;
+    }
+
+    /**
+     * Move all offsets in the tree by the given delta. This node must be a
+     * root node. The resulting offsets must be greater or equal to zero.
+     *
+     * @param delta the delta
+     * @since 3.1
+     */
+    public final void moveTree(int delta) {
+        Assert.isTrue(fParent is null);
+        Assert.isTrue(getOffset() + delta >= 0);
+        internalMoveTree(delta);
+    }
+
+    /**
+     * Returns <code>true</code> if the edit covers the given edit
+     * <code>other</code>. It is up to the concrete text edit to
+     * decide if a edit of length zero can cover another edit.
+     *
+     * @param other the other edit
+     * @return <code>true<code> if the edit covers the other edit;
+     *  otherwise <code>false</code> is returned.
+     */
+    public bool covers(TextEdit other) {
+        if (getLength() is 0 && !canZeroLengthCover())
+            return false;
+
+        if (!other.isDefined())
+            return true;
+
+        int thisOffset= getOffset();
+        int otherOffset= other.getOffset();
+        return thisOffset <= otherOffset && otherOffset + other.getLength() <= thisOffset + getLength();
+    }
+
+    /**
+     * Returns <code>true</code> if an edit with length zero can cover
+     * another edit. Returns <code>false</code> otherwise.
+     *
+     * @return whether an edit of length zero can cover another edit
+     */
+    protected bool canZeroLengthCover() {
+        return false;
+    }
+
+    /**
+     * Returns whether the region of this edit is defined or not.
+     *
+     * @return whether the region of this edit is defined or not
+     *
+     * @since 3.1
+     */
+    bool isDefined() {
+        return true;
+    }
+
+    //---- parent and children management -----------------------------
+
+    /**
+     * Returns the edit's parent. The method returns <code>null</code>
+     * if this edit hasn't been add to another edit.
+     *
+     * @return the edit's parent
+     */
+    public final TextEdit getParent() {
+        return fParent;
+    }
+
+    /**
+     * Returns the root edit of the edit tree.
+     *
+     * @return the root edit of the edit tree
+     * @since 3.1
+     */
+    public final TextEdit getRoot() {
+        TextEdit result= this;
+        while (result.fParent !is null) {
+            result= result.fParent;
+        }
+        return result;
+    }
+
+    /**
+     * Adds the given edit <code>child</code> to this edit.
+     *
+     * @param child the child edit to add
+     * @exception MalformedTreeException is thrown if the child
+     *  edit can't be added to this edit. This is the case if the child
+     *  overlaps with one of its siblings or if the child edit's region
+     *  isn't fully covered by this edit.
+     */
+    public final void addChild(TextEdit child)  {
+        internalAdd(child);
+    }
+
+    /**
+     * Adds all edits in <code>edits</code> to this edit.
+     *
+     * @param edits the text edits to add
+     * @exception MalformedTreeException is thrown if one of
+     *  the given edits can't be added to this edit.
+     *
+     * @see #addChild(TextEdit)
+     */
+    public final void addChildren(TextEdit[] edits)  {
+        for (int i= 0; i < edits.length; i++) {
+            internalAdd(edits[i]);
+        }
+    }
+
+    /**
+     * Removes the edit specified by the given index from the list
+     * of children. Returns the child edit that was removed from
+     * the list of children. The parent of the returned edit is
+     * set to <code>null</code>.
+     *
+     * @param index the index of the edit to remove
+     * @return the removed edit
+     * @exception IndexOutOfBoundsException if the index
+     *  is out of range
+     */
+    public final TextEdit removeChild(int index) {
+        if (fChildren is null)
+            throw new IndexOutOfBoundsException(Format("Index: {} Size: 0", index ));  //$NON-NLS-1$//$NON-NLS-2$
+        TextEdit result= cast(TextEdit)fChildren.remove(index);
+        result.internalSetParent(null);
+        if (fChildren.isEmpty())
+            fChildren= null;
+        return result;
+    }
+
+    /**
+     * Removes the first occurrence of the given child from the list
+     * of children.
+     *
+     * @param child the child to be removed
+     * @return <code>true</code> if the edit contained the given
+     *  child; otherwise <code>false</code> is returned
+     */
+    public final bool removeChild(TextEdit child) {
+        Assert.isNotNull(child);
+        if (fChildren is null)
+            return false;
+        bool result= fChildren.remove(child);
+        if (result) {
+            child.internalSetParent(null);
+            if (fChildren.isEmpty())
+                fChildren= null;
+        }
+        return result;
+    }
+
+    /**
+     * Removes all child edits from and returns them. The parent
+     * of the removed edits is set to <code>null</code>.
+     *
+     * @return an array of the removed edits
+     */
+    public final TextEdit[] removeChildren() {
+        if (fChildren is null)
+            return EMPTY_ARRAY;
+        int size= fChildren.size();
+        TextEdit[] result= new TextEdit[size];
+        for (int i= 0; i < size; i++) {
+            result[i]= cast(TextEdit)fChildren.get(i);
+            result[i].internalSetParent(null);
+        }
+        fChildren= null;
+        return result;
+    }
+
+    /**
+     * Returns <code>true</code> if this edit has children. Otherwise
+     * <code>false</code> is returned.
+     *
+     * @return <code>true</code> if this edit has children; otherwise
+     *  <code>false</code> is returned
+     */
+    public final bool hasChildren() {
+        return fChildren !is null && ! fChildren.isEmpty();
+    }
+
+    /**
+     * Returns the edit's children. If the edit doesn't have any
+     * children an empty array is returned.
+     *
+     * @return the edit's children
+     */
+    public final TextEdit[] getChildren() {
+        if (fChildren is null)
+            return EMPTY_ARRAY;
+        return arraycast!(TextEdit)(fChildren.toArray());
+    }
+
+    /**
+     * Returns the size of the managed children.
+     *
+     * @return the size of the children
+     */
+    public final int getChildrenSize() {
+        if (fChildren is null)
+            return 0;
+        return fChildren.size();
+    }
+
+    /**
+     * Returns the text range spawned by the given array of text edits.
+     * The method requires that the given array contains at least one
+     * edit. If all edits passed are deleted the method returns <code>
+     * null</code>.
+     *
+     * @param edits an array of edits
+     * @return the text range spawned by the given array of edits or
+     *  <code>null</code> if all edits are marked as deleted
+     */
+    public static IRegion getCoverage(TextEdit[] edits) {
+        Assert.isTrue(edits !is null && edits.length > 0);
+
+        int offset= Integer.MAX_VALUE;
+        int end= Integer.MIN_VALUE;
+        int deleted= 0;
+        for (int i= 0; i < edits.length; i++) {
+            TextEdit edit= edits[i];
+            if (edit.isDeleted()) {
+                deleted++;
+            } else {
+                offset= Math.min(offset, edit.getOffset());
+                end= Math.max(end, edit.getExclusiveEnd());
+            }
+        }
+        if (edits.length is deleted)
+            return null;
+
+        return new Region(offset, end - offset);
+    }
+
+    /*
+     * Hook called before this edit gets added to the passed
+     * parent.
+     */
+    void aboutToBeAdded(TextEdit parent) {
+    }
+
+    //---- Object methods ------------------------------------------------------
+
+    /**
+     * The <code>Edit</code> implementation of this <code>Object</code>
+     * method uses object identity (is).
+     *
+     * @param obj the other object
+     * @return <code>true</code> iff <code>this is obj</code>; otherwise
+     *  <code>false</code> is returned
+     *
+     * @see Object#equals(java.lang.Object)
+     */
+    public final bool equals(Object obj) {
+        return this is obj; // equivalent to Object.equals
+    }
+
+    /**
+     * The <code>Edit</code> implementation of this <code>Object</code>
+     * method calls uses <code>Object#hashCode()</code> to compute its
+     * hash code.
+     *
+     * @return the object's hash code value
+     *
+     * @see Object#hashCode()
+     */
+    public final override hash_t toHash() {
+        return super.toHash();
+    }
+
+    /*
+     * @see java.lang.Object#toString()
+     */
+    public override String toString() {
+        StringBuffer buffer= new StringBuffer();
+        toStringWithChildren(buffer, 0);
+        return buffer.toString();
+    }
+
+    /**
+     * Adds the string representation of this text edit without
+     * children information to the given string buffer.
+     *
+     * @param buffer    the string buffer
+     * @param indent    the indent level in number of spaces
+     * @since 3.3
+     */
+    void internalToString(StringBuffer buffer, int indent) {
+        for (int i= indent; i > 0; i--) {
+            buffer.append("  "); //$NON-NLS-1$
+        }
+        buffer.append("{"); //$NON-NLS-1$
+        String name= this.classinfo.name;
+        int index= name.lastIndexOf('.');
+        if (index !is -1) {
+            buffer.append(name.substring(index + 1));
+        } else {
+            buffer.append(name);
+        }
+        buffer.append("} "); //$NON-NLS-1$
+        if (isDeleted()) {
+            buffer.append("[deleted]"); //$NON-NLS-1$
+        } else {
+            buffer.append("["); //$NON-NLS-1$
+            buffer.append(getOffset());
+            buffer.append(","); //$NON-NLS-1$
+            buffer.append(getLength());
+            buffer.append("]"); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Adds the string representation for this text edit
+     * and its children to the given string buffer.
+     *
+     * @param buffer    the string buffer
+     * @param indent    the indent level in number of spaces
+     * @since 3.3
+     */
+    private void toStringWithChildren(StringBuffer buffer, int indent) {
+        internalToString(buffer, indent);
+        if (fChildren !is null) {
+            for (Iterator iterator= fChildren.iterator(); iterator.hasNext();) {
+                TextEdit child= cast(TextEdit) iterator.next();
+                buffer.append('\n');
+                child.toStringWithChildren(buffer, indent + 1);
+            }
+        }
+    }
+
+    //---- Copying -------------------------------------------------------------
+
+    /**
+     * Creates a deep copy of the edit tree rooted at this
+     * edit.
+     *
+     * @return a deep copy of the edit tree
+     * @see #doCopy()
+     */
+    public final TextEdit copy() {
+        TextEditCopier copier= new TextEditCopier(this);
+        return copier.perform();
+    }
+
+    /**
+     * Creates and returns a copy of this edit. The copy method should be
+     * implemented in a way so that the copy can executed without causing
+     * any harm to the original edit. Implementors of this method are
+     * responsible for creating deep or shallow copies of referenced
+     * object to fulfill this requirement.
+     * <p>
+     * Implementers of this method should use the copy constructor <code>
+     * Edit#Edit(Edit source) to initialize the edit part of the copy.
+     * Implementors aren't responsible to actually copy the children or
+     * to set the right parent.
+     * <p>
+     * This method <b>should not be called</b> from outside the framework.
+     * Please use <code>copy</code> to create a copy of a edit tree.
+     *
+     * @return a copy of this edit.
+     * @see #copy()
+     * @see #postProcessCopy(TextEditCopier)
+     * @see TextEditCopier
+     */
+    protected abstract TextEdit doCopy();
+    package TextEdit doCopy_package(){
+        return doCopy();
+    }
+
+    /**
+     * This method is called on every edit of the copied tree to do some
+     * post-processing like connected an edit to a different edit in the tree.
+     * <p>
+     * This default implementation does nothing
+     *
+     * @param copier the copier that manages a map between original and
+     *  copied edit.
+     * @see TextEditCopier
+     */
+    protected void postProcessCopy(TextEditCopier copier) {
+    }
+    package void postProcessCopy_package(TextEditCopier copier) {
+        postProcessCopy(copier);
+    }
+
+    //---- Visitor support -------------------------------------------------
+
+    /**
+     * Accepts the given visitor on a visit of the current edit.
+     *
+     * @param visitor the visitor object
+     * @exception IllegalArgumentException if the visitor is null
+     */
+    public final void accept(TextEditVisitor visitor) {
+        Assert.isNotNull(visitor);
+        // begin with the generic pre-visit
+        visitor.preVisit(this);
+        // dynamic dispatch to internal method for type-specific visit/endVisit
+        accept0(visitor);
+        // end with the generic post-visit
+        visitor.postVisit(this);
+    }
+
+    /**
+     * Accepts the given visitor on a type-specific visit of the current edit.
+     * This method must be implemented in all concrete text edits.
+     * <p>
+     * General template for implementation on each concrete TextEdit class:
+     * <pre>
+     * <code>
+     * bool visitChildren= visitor.visit(this);
+     * if (visitChildren) {
+     *    acceptChildren(visitor);
+     * }
+     * </code>
+     * </pre>
+     * Note that the caller (<code>accept</code>) takes care of invoking
+     * <code>visitor.preVisit(this)</code> and <code>visitor.postVisit(this)</code>.
+     * </p>
+     *
+     * @param visitor the visitor object
+     */
+    protected abstract void accept0(TextEditVisitor visitor);
+
+
+    /**
+     * Accepts the given visitor on the edits children.
+     * <p>
+     * This method must be used by the concrete implementations of
+     * <code>accept</code> to traverse list-values properties; it
+     * encapsulates the proper handling of on-the-fly changes to the list.
+     * </p>
+     *
+     * @param visitor the visitor object
+     */
+    protected final void acceptChildren(TextEditVisitor visitor) {
+        if (fChildren is null)
+            return;
+        Iterator iterator= fChildren.iterator();
+        while (iterator.hasNext()) {
+            TextEdit curr= cast(TextEdit) iterator.next();
+            curr.accept(visitor);
+        }
+    }
+
+    //---- Execution -------------------------------------------------------
+
+    /**
+     * Applies the edit tree rooted by this edit to the given document. To check
+     * if the edit tree can be applied to the document either catch <code>
+     * MalformedTreeException</code> or use <code>TextEditProcessor</code> to
+     * execute an edit tree.
+     *
+     * @param document the document to be manipulated
+     * @param style flags controlling the execution of the edit tree. Valid
+     *  flags are: <code>CREATE_UNDO</code> and </code>UPDATE_REGIONS</code>.
+     * @return a undo edit, if <code>CREATE_UNDO</code> is specified. Otherwise
+     *  <code>null</code> is returned.
+     *
+     * @exception MalformedTreeException is thrown if the tree isn't
+     *  in a valid state. This exception is thrown before any edit
+     *  is executed. So the document is still in its original state.
+     * @exception BadLocationException is thrown if one of the edits
+     *  in the tree can't be executed. The state of the document is
+     *  undefined if this exception is thrown.
+     *
+     * @see TextEditProcessor#performEdits()
+     */
+    public final UndoEdit apply(IDocument document, int style)  {
+        try {
+            TextEditProcessor processor= new TextEditProcessor(document, this, style);
+            return processor.performEdits();
+        } finally {
+            // disconnect from text edit processor
+            fParent= null;
+        }
+    }
+
+    /**
+     * Applies the edit tree rooted by this edit to the given document. This
+     * method is a convenience method for <code>apply(document, CREATE_UNDO | UPDATE_REGIONS)
+     * </code>
+     *
+     * @param document the document to which to apply this edit
+     * @return a undo edit, if <code>CREATE_UNDO</code> is specified. Otherwise
+     *  <code>null</code> is returned.
+     * @exception MalformedTreeException is thrown if the tree isn't
+     *  in a valid state. This exception is thrown before any edit
+     *  is executed. So the document is still in its original state.
+     * @exception BadLocationException is thrown if one of the edits
+     *  in the tree can't be executed. The state of the document is
+     *  undefined if this exception is thrown.
+     * @see #apply(IDocument, int)
+     */
+    public final UndoEdit apply(IDocument document)  {
+        return apply(document, CREATE_UNDO | UPDATE_REGIONS);
+    }
+
+    UndoEdit dispatchPerformEdits(TextEditProcessor processor)  {
+        return processor.executeDo();
+    }
+
+    void dispatchCheckIntegrity(TextEditProcessor processor)  {
+        processor.checkIntegrityDo();
+    }
+
+    //---- internal state accessors ----------------------------------------------------------
+
+    void internalSetParent(TextEdit parent) {
+        if (parent !is null)
+            Assert.isTrue(fParent is null);
+        fParent= parent;
+    }
+
+    void internalSetOffset(int offset) {
+        Assert.isTrue(offset >= 0);
+        fOffset= offset;
+    }
+
+    void internalSetLength(int length) {
+        Assert.isTrue(length >= 0);
+        fLength= length;
+    }
+
+    List internalGetChildren() {
+        return fChildren;
+    }
+
+    void internalSetChildren(List children) {
+        fChildren= children;
+    }
+
+    void internalAdd(TextEdit child)  {
+        child.aboutToBeAdded(this);
+        if (child.isDeleted())
+            throw new MalformedTreeException(this, child, TextEditMessages.getString("TextEdit.deleted_edit")); //$NON-NLS-1$
+        if (!covers(child))
+            throw new MalformedTreeException(this, child, TextEditMessages.getString("TextEdit.range_outside")); //$NON-NLS-1$
+        if (fChildren is null) {
+            fChildren= new ArrayList(2);
+        }
+        int index= computeInsertionIndex(child);
+        fChildren.add(index, child);
+        child.internalSetParent(this);
+    }
+
+    private int computeInsertionIndex(TextEdit edit)  {
+        int size= fChildren.size();
+        if (size is 0)
+            return 0;
+        int lastIndex= size - 1;
+        TextEdit last= cast(TextEdit)fChildren.get(lastIndex);
+        if (last.getExclusiveEnd() <= edit.getOffset())
+            return size;
+        try {
+
+            int index= Collections.binarySearch(fChildren, edit, INSERTION_COMPARATOR);
+
+            if (index < 0)
+                // edit is not in fChildren
+                return -index - 1;
+
+            // edit is already in fChildren
+            // make sure that multiple insertion points at the same offset are inserted last.
+            while (index < lastIndex && INSERTION_COMPARATOR.compare(fChildren.get(index), fChildren.get(index + 1)) is 0)
+                index++;
+
+            return index + 1;
+
+        } catch(MalformedTreeException e) {
+            e.setParent(this);
+            throw e;
+        }
+    }
+
+    //---- Offset & Length updating -------------------------------------------------
+
+    /**
+     * Adjusts the edits offset according to the given
+     * delta. This method doesn't update any children.
+     *
+     * @param delta the delta of the text replace operation
+     */
+    void adjustOffset(int delta) {
+        if (isDeleted())
+            return;
+        fOffset+= delta;
+        Assert.isTrue(fOffset >= 0);
+    }
+
+    /**
+     * Adjusts the edits length according to the given
+     * delta. This method doesn't update any children.
+     *
+     * @param delta the delta of the text replace operation
+     */
+    void adjustLength(int delta) {
+        if (isDeleted())
+            return;
+        fLength+= delta;
+        Assert.isTrue(fLength >= 0);
+    }
+
+    /**
+     * Marks the edit as deleted. This method doesn't update
+     * any children.
+     */
+    void markAsDeleted() {
+        fOffset= DELETED_VALUE;
+        fLength= DELETED_VALUE;
+    }
+
+    //---- Edit processing ----------------------------------------------
+
+    /**
+     * Traverses the edit tree to perform the consistency check.
+     *
+     * @param processor the text edit processor
+     * @param document the document to be manipulated
+     * @param sourceEdits the list of source edits to be performed before
+     *  the actual tree is applied to the document
+     *
+     * @return the number of indirect move or copy target edit children
+     */
+    int traverseConsistencyCheck(TextEditProcessor processor, IDocument document, List sourceEdits) {
+        int result= 0;
+        if (fChildren !is null) {
+            for (int i= fChildren.size() - 1; i >= 0; i--) {
+                TextEdit child= cast(TextEdit)fChildren.get(i);
+                result= Math.max(result, child.traverseConsistencyCheck(processor, document, sourceEdits));
+            }
+        }
+        if (processor.considerEdit_package(this)) {
+            performConsistencyCheck(processor, document);
+        }
+        return result;
+    }
+
+    void performConsistencyCheck(TextEditProcessor processor, IDocument document) {
+    }
+
+    void traverseSourceComputation(TextEditProcessor processor, IDocument document) {
+    }
+
+    void performSourceComputation(TextEditProcessor processor, IDocument document) {
+    }
+
+    int traverseDocumentUpdating(TextEditProcessor processor, IDocument document)  {
+        int delta= 0;
+        if (fChildren !is null) {
+            for (int i= fChildren.size() - 1; i >= 0; i--) {
+                TextEdit child= cast(TextEdit)fChildren.get(i);
+                delta+= child.traverseDocumentUpdating(processor, document);
+                childDocumentUpdated();
+            }
+        }
+        if (processor.considerEdit_package(this)) {
+            if (delta !is 0)
+                adjustLength(delta);
+            int r= performDocumentUpdating(document);
+            if (r !is 0)
+                adjustLength(r);
+            delta+= r;
+        }
+        return delta;
+    }
+
+    /**
+     * Hook method called when the document updating of a child edit has been
+     * completed. When a client calls {@link #apply(IDocument)} or
+     * {@link #apply(IDocument, int)} this method is called
+     * {@link #getChildrenSize()} times.
+     * <p>
+     * May be overridden by subclasses of {@link MultiTextEdit}.
+     *
+     * @since 3.1
+     */
+    protected void childDocumentUpdated() {
+    }
+
+    abstract int performDocumentUpdating(IDocument document) ;
+
+    int traverseRegionUpdating(TextEditProcessor processor, IDocument document, int accumulatedDelta, bool delete_) {
+        performRegionUpdating(accumulatedDelta, delete_);
+        if (fChildren !is null) {
+            bool childDelete= delete_ || deleteChildren();
+            for (Iterator iter= fChildren.iterator(); iter.hasNext();) {
+                TextEdit child= cast(TextEdit)iter.next();
+                accumulatedDelta= child.traverseRegionUpdating(processor, document, accumulatedDelta, childDelete);
+                childRegionUpdated();
+            }
+        }
+        return accumulatedDelta + fDelta;
+    }
+
+    /**
+     * Hook method called when the region updating of a child edit has been
+     * completed. When a client calls {@link #apply(IDocument)} this method is
+     * called {@link #getChildrenSize()} times. When calling
+     * {@link #apply(IDocument, int)} this method is called
+     * {@link #getChildrenSize()} times, when the style parameter contains the
+     * {@link #UPDATE_REGIONS} flag.
+     * <p>
+     * May be overridden by subclasses of {@link MultiTextEdit}.
+     *
+     * @since 3.1
+     */
+    protected void childRegionUpdated() {
+    }
+
+    void performRegionUpdating(int accumulatedDelta, bool delete_) {
+        if (delete_)
+            markAsDeleted();
+        else
+            adjustOffset(accumulatedDelta);
+    }
+
+    abstract bool deleteChildren();
+
+    void internalMoveTree(int delta) {
+        adjustOffset(delta);
+        if (fChildren !is null) {
+            for (Iterator iter= fChildren.iterator(); iter.hasNext();) {
+                (cast(TextEdit)iter.next()).internalMoveTree(delta);
+            }
+        }
+    }
+
+    void deleteTree() {
+        markAsDeleted();
+        if (fChildren !is null) {
+            for (Iterator iter= fChildren.iterator(); iter.hasNext();) {
+                TextEdit child= cast(TextEdit)iter.next();
+                child.deleteTree();
+            }
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/TextEditCopier.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.TextEditCopier;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+
+
+/**
+ * Copies a tree of text edits. A text edit copier keeps a map
+ * between original and new text edits. It can be used to map
+ * a copy back to its original edit.
+ *
+ * @since 3.0
+ */
+public final class TextEditCopier {
+
+    private TextEdit fEdit;
+    private Map fCopies;
+
+    /**
+     * Constructs a new <code>TextEditCopier</code> for the
+     * given edit. The actual copy is done by calling <code>
+     * perform</code>.
+     *
+     * @param edit the edit to copy
+     *
+     * @see #perform()
+     */
+    public this(TextEdit edit) {
+//         super();
+        Assert.isNotNull(edit);
+        fEdit= edit;
+        fCopies= new HashMap();
+    }
+
+    /**
+     * Performs the actual copying.
+     *
+     * @return the copy
+     */
+    public TextEdit perform() {
+        TextEdit result= doCopy(fEdit);
+        if (result !is null) {
+            for (Iterator iter= fCopies.keySet().iterator(); iter.hasNext();) {
+                TextEdit edit= cast(TextEdit)iter.next();
+                edit.postProcessCopy_package(this);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns the copy for the original text edit.
+     *
+     * @param original the original for which the copy
+     *  is requested
+     * @return the copy of the original edit or <code>null</code>
+     *  if the original isn't managed by this copier
+     */
+    public TextEdit getCopy(TextEdit original) {
+        Assert.isNotNull(original);
+        return cast(TextEdit)fCopies.get(original);
+    }
+
+    //---- helper methods --------------------------------------------
+
+    private TextEdit doCopy(TextEdit edit) {
+        TextEdit result= edit.doCopy_package();
+        List children= edit.internalGetChildren();
+        if (children !is null) {
+            List newChildren= new ArrayList(children.size());
+            for (Iterator iter= children.iterator(); iter.hasNext();) {
+                TextEdit childCopy= doCopy(cast(TextEdit)iter.next());
+                childCopy.internalSetParent(result);
+                newChildren.add(childCopy);
+            }
+            result.internalSetChildren(newChildren);
+        }
+        addCopy(edit, result);
+        return result;
+    }
+
+    private void addCopy(TextEdit original, TextEdit copy) {
+        fCopies.put(original, copy);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/TextEditGroup.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,192 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.IRegion;
+
+
+/**
+ * A text edit group combines a list of {@link TextEdit}s
+ * and a name into a single object. The name must be a human
+ * readable string use to present the text edit group in the
+ * user interface.
+ * <p>
+ * Clients may extend this class to add extra information to
+ * a text edit group.
+ * </p>
+ *
+ * @since 3.0
+ */
+public class TextEditGroup {
+
+    private String fDescription;
+    private List fEdits;
+
+    /**
+     * Creates a new text edit group with the given name.
+     *
+     * @param name the name of the text edit group. Must be
+     *  a human readable string
+     */
+    public this(String name) {
+//         super();
+        Assert.isNotNull(name);
+        fDescription= name;
+        fEdits= new ArrayList(3);
+    }
+
+    /**
+     * Creates a new text edit group with a name and a single
+     * {@link TextEdit}.
+     *
+     * @param name the name of the text edit group. Must be
+     *  a human readable string
+     * @param edit the edit to manage
+     */
+    public this(String name, TextEdit edit) {
+        Assert.isNotNull(name);
+        Assert.isNotNull(edit);
+        fDescription= name;
+        fEdits= new ArrayList(1);
+        fEdits.add(edit);
+    }
+
+    /**
+     * Creates a new text edit group with the given name and
+     * array of edits.
+     *
+     * @param name the name of the text edit group. Must be
+     *  a human readable string
+     * @param edits the array of edits
+     */
+    public this(String name, TextEdit[] edits) {
+//         super();
+        Assert.isNotNull(name);
+        Assert.isTrue(edits !is null);
+        fDescription= name;
+        fEdits= new ArrayList(Arrays.asList(edits));
+    }
+
+    /**
+     * Returns the edit group's name.
+     *
+     * @return the edit group's name
+     */
+    public String getName() {
+        return fDescription;
+    }
+
+    /**
+     * Adds the given {@link TextEdit} to this group.
+     *
+     * @param edit the edit to add
+     */
+    public void addTextEdit(TextEdit edit) {
+        fEdits.add(edit);
+    }
+
+    /**
+     * Removes the given {@link TextEdit} from this group.
+     *
+     * @param edit the edit to remove
+     * @return <code>true</code> if this group contained the specified edit.
+     * @since 3.3
+     */
+    public bool removeTextEdit(TextEdit edit) {
+      return fEdits.remove(edit);
+    }
+
+    /**
+     * Removes all text edits from this group.
+     *
+     * @since 3.3
+     */
+    public void clearTextEdits() {
+      fEdits.clear();
+    }
+
+
+
+    /**
+     * Returns <code>true</code> if the list of managed
+     * {@link TextEdit}s is empty; otherwise <code>false
+     * </code> is returned.
+     *
+     * @return whether the list of managed text edits is
+     *  empty or not
+     */
+    public bool isEmpty() {
+        return fEdits.isEmpty();
+    }
+
+    /**
+     * Returns an array of {@link TextEdit}s containing
+     * the edits managed by this group.
+     *
+     * @return the managed text edits
+     */
+    public TextEdit[] getTextEdits() {
+        return arraycast!(TextEdit)( fEdits.toArray());
+    }
+
+    /**
+     * Returns the text region covered by the edits managed via this
+     * edit group. If the group doesn't manage any edits <code>null
+     * </code> is returned.
+     *
+     * @return the text region covered by this edit group or <code>
+     *  null</code> if no edits are managed
+     */
+    public IRegion getRegion() {
+        int size= fEdits.size();
+        if (size is 0) {
+            return null;
+        } else if (size is 1) {
+            return (cast(TextEdit)fEdits.get(0)).getRegion();
+        } else {
+            return TextEdit.getCoverage(arraycast!(TextEdit)(fEdits.toArray()));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/TextEditMessages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.TextEditMessages;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+import java.text.MessageFormat;
+
+class TextEditMessages {
+
+//     private static const String BUNDLE_NAME= "org.eclipse.text.edits.Messages"; //$NON-NLS-1$
+
+    private static const ResourceBundle RESOURCE_BUNDLE;//= ResourceBundle.getBundle(BUNDLE_NAME);
+
+    static this() {
+        RESOURCE_BUNDLE = ResourceBundle.getBundle(
+            getImportData!("org.eclipse.text.edits.TextEditMessages.properties"));
+    }
+
+    private this() {
+    }
+
+    public static String getString(String key) {
+        try {
+            return RESOURCE_BUNDLE.getString(key);
+        } catch (MissingResourceException e) {
+            return '!' ~ key ~ '!';
+        }
+    }
+
+    public static String getFormattedString(String key, Object[] args...) {
+        return MessageFormat.format(getString(key), args);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/TextEditProcessor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,265 @@
+/*******************************************************************************
+ * 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 org.eclipse.text.edits.TextEditProcessor;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * A <code>TextEditProcessor</code> manages a set of edits and applies
+ * them as a whole to an <code>IDocument</code>.
+ * <p>
+ * This class isn't intended to be subclassed.</p>
+ *
+ * @see org.eclipse.text.edits.TextEdit#apply(IDocument)
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class TextEditProcessor {
+
+    private IDocument fDocument;
+    private TextEdit fRoot;
+    private int fStyle;
+
+    private bool fChecked;
+    private MalformedTreeException fException;
+
+    private List fSourceEdits;
+
+    /**
+     * Constructs a new edit processor for the given
+     * document.
+     *
+     * @param document the document to manipulate
+     * @param root the root of the text edit tree describing
+     *  the modifications. By passing a text edit a a text edit
+     *  processor the ownership of the edit is transfered to the
+     *  text edit processors. Clients must not modify the edit
+     *  (e.g adding new children) any longer.
+     *
+     * @param style {@link TextEdit#NONE}, {@link TextEdit#CREATE_UNDO} or {@link TextEdit#UPDATE_REGIONS})
+     */
+    public this(IDocument document, TextEdit root, int style) {
+        this(document, root, style, false);
+    }
+
+    private this(IDocument document, TextEdit root, int style, bool secondary) {
+        Assert.isNotNull(cast(Object)document);
+        Assert.isNotNull(root);
+        fDocument= document;
+        fRoot= root;
+        if ( auto mte = cast(MultiTextEdit)fRoot )
+            mte.defineRegion(0);
+        fStyle= style;
+        if (secondary) {
+            fChecked= true;
+            fSourceEdits= new ArrayList();
+        }
+    }
+
+    /**
+     * Creates a special internal processor used to during source computation inside
+     * move source and copy source edits
+     *
+     * @param document the document to be manipulated
+     * @param root the edit tree
+     * @param style {@link TextEdit#NONE}, {@link TextEdit#CREATE_UNDO} or {@link TextEdit#UPDATE_REGIONS})
+     * @return a secondary text edit processor
+     * @since 3.1
+     */
+    static TextEditProcessor createSourceComputationProcessor(IDocument document, TextEdit root, int style) {
+        return new TextEditProcessor(document, root, style, true);
+    }
+
+    /**
+     * Returns the document to be manipulated.
+     *
+     * @return the document
+     */
+    public IDocument getDocument() {
+        return fDocument;
+    }
+
+    /**
+     * Returns the edit processor's root edit.
+     *
+     * @return the processor's root edit
+     */
+    public TextEdit getRoot() {
+        return fRoot;
+    }
+
+    /**
+     * Returns the style bits of the text edit processor
+     *
+     * @return the style bits
+     * @see TextEdit#CREATE_UNDO
+     * @see TextEdit#UPDATE_REGIONS
+     */
+    public int getStyle() {
+        return fStyle;
+    }
+
+    /**
+     * Checks if the processor can execute all its edits.
+     *
+     * @return <code>true</code> if the edits can be executed. Return  <code>false
+     *  </code>otherwise. One major reason why edits cannot be executed are wrong
+     *  offset or length values of edits. Calling perform in this case will very
+     *  likely end in a <code>BadLocationException</code>.
+     */
+    public bool canPerformEdits() {
+        try {
+            fRoot.dispatchCheckIntegrity(this);
+            fChecked= true;
+        } catch (MalformedTreeException e) {
+            fException= e;
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Executes the text edits.
+     *
+     * @return an object representing the undo of the executed edits
+     * @exception MalformedTreeException is thrown if the edit tree isn't
+     *  in a valid state. This exception is thrown before any edit is executed.
+     *  So the document is still in its original state.
+     * @exception BadLocationException is thrown if one of the edits in the
+     *  tree can't be executed. The state of the document is undefined if this
+     *  exception is thrown.
+     */
+    public UndoEdit performEdits()  {
+        if (!fChecked) {
+            fRoot.dispatchCheckIntegrity(this);
+        } else {
+            if (fException !is null)
+                throw fException;
+        }
+        return fRoot.dispatchPerformEdits(this);
+    }
+
+    /*
+     * Class isn't intended to be sub-lcassed
+     */
+    protected bool considerEdit(TextEdit edit) {
+        return true;
+    }
+    package bool considerEdit_package(TextEdit edit) {
+        return considerEdit(edit);
+    }
+
+
+    //---- checking --------------------------------------------------------------------
+
+    void checkIntegrityDo()  {
+        fSourceEdits= new ArrayList();
+        fRoot.traverseConsistencyCheck(this, fDocument, fSourceEdits);
+        if (fRoot.getExclusiveEnd() > fDocument.getLength())
+            throw new MalformedTreeException(null, fRoot, TextEditMessages.getString("TextEditProcessor.invalid_length")); //$NON-NLS-1$
+    }
+
+    void checkIntegrityUndo() {
+        if (fRoot.getExclusiveEnd() > fDocument.getLength())
+            throw new MalformedTreeException(null, fRoot, TextEditMessages.getString("TextEditProcessor.invalid_length")); //$NON-NLS-1$
+    }
+
+    //---- execution --------------------------------------------------------------------
+
+    UndoEdit executeDo()  {
+        UndoCollector collector= new UndoCollector(fRoot);
+        try {
+            if (createUndo())
+                collector.connect(fDocument);
+            computeSources();
+            fRoot.traverseDocumentUpdating(this, fDocument);
+            if (updateRegions()) {
+                fRoot.traverseRegionUpdating(this, fDocument, 0, false);
+            }
+        } finally {
+            collector.disconnect(fDocument);
+        }
+        return collector.undo_package;
+    }
+
+    private void computeSources() {
+        for (Iterator iter= fSourceEdits.iterator(); iter.hasNext();) {
+            List list= cast(List)iter.next();
+            if (list !is null) {
+                for (Iterator edits= list.iterator(); edits.hasNext();) {
+                    TextEdit edit= cast(TextEdit)edits.next();
+                    edit.traverseSourceComputation(this, fDocument);
+                }
+            }
+        }
+    }
+
+    UndoEdit executeUndo()  {
+        UndoCollector collector= new UndoCollector(fRoot);
+        try {
+            if (createUndo())
+                collector.connect(fDocument);
+            TextEdit[] edits= fRoot.getChildren();
+            for (int i= edits.length - 1; i >= 0; i--) {
+                edits[i].performDocumentUpdating(fDocument);
+            }
+        } finally {
+            collector.disconnect(fDocument);
+        }
+        return collector.undo_package;
+    }
+
+    private bool createUndo() {
+        return (fStyle & TextEdit.CREATE_UNDO) !is 0;
+    }
+
+    private bool updateRegions() {
+        return (fStyle & TextEdit.UPDATE_REGIONS) !is 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/TextEditVisitor.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,252 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.TextEditVisitor;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+
+/**
+ * A visitor for text edits.
+ * <p>
+ * For each different concrete text edit type <it>T</it> there is a method:
+ * <ul>
+ *   <li><code>public bool visit(<it>T</it> node)</code> - Visits the given edit to
+ *   perform some arbitrary operation. If <code>true </code> is returned, the given edit's
+ *   child edits will be visited next; however, if <code>false</code> is returned, the
+ *   given edit's child edits will not be visited. The default implementation provided by
+ *   this class calls a generic method <code>visitNode(<it>TextEdit</it> node)</code>.
+ *   Subclasses may reimplement these method as needed.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * In addition, there are methods for visiting text edits in the
+ * abstract, regardless of node type:
+ * <ul>
+ *   <li><code>public void preVisit(TextEdit edit)</code> - Visits
+ *   the given edit to perform some arbitrary operation.
+ *   This method is invoked prior to the appropriate type-specific
+ *   <code>visit</code> method.
+ *   The default implementation of this method does nothing.
+ *   Subclasses may reimplement this method as needed.</li>
+ *
+ *   <li><code>public void postVisit(TextEdit edit)</code> - Visits
+ *   the given edit to perform some arbitrary operation.
+ *   This method is invoked after the appropriate type-specific
+ *   <code>endVisit</code> method.
+ *   The default implementation of this method does nothing.
+ *   Subclasses may reimplement this method as needed.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * For edits with children, the child nodes are visited in increasing order.
+ * </p>
+ *
+ * @see TextEdit#accept(TextEditVisitor)
+ * @since 3.0
+ */
+public class TextEditVisitor {
+
+    /**
+     * Visits the given text edit prior to the type-specific visit.
+     * (before <code>visit</code>).
+     * <p>
+     * The default implementation does nothing. Subclasses may reimplement.
+     * </p>
+     *
+     * @param edit the node to visit
+     */
+    public void preVisit(TextEdit edit) {
+        // default implementation: do nothing
+    }
+
+    /**
+     * Visits the given text edit following the type-specific visit
+     * (after <code>endVisit</code>).
+     * <p>
+     * The default implementation does nothing. Subclasses may reimplement.
+     * </p>
+     *
+     * @param edit the node to visit
+     */
+    public void postVisit(TextEdit edit) {
+        // default implementation: do nothing
+    }
+
+    /**
+     * Visits the given text edit. This method is called by default from
+     * type-specific visits. It is not called by an edit's accept method.
+     * The default implementation returns <code>true</code>.
+     *
+     * @param edit the node to visit
+     * @return If <code>true</code> is returned, the given node's child
+     *  nodes will be visited next; however, if <code>false</code> is
+     *  returned, the given node's child nodes will not be visited.
+     */
+    public bool visitNode(TextEdit edit) {
+        return true;
+    }
+
+    /**
+     * Visits a <code>CopySourceEdit</code> instance.
+     *
+     * @param edit the node to visit
+     * @return If <code>true</code> is returned, the given node's child
+     *  nodes will be visited next; however, if <code>false</code> is
+     *  returned, the given node's child nodes will not be visited.
+     */
+    public bool visit(CopySourceEdit edit) {
+        return visitNode(edit);
+    }
+
+    /**
+     * Visits a <code>CopyTargetEdit</code> instance.
+     *
+     * @param edit the node to visit
+     * @return If <code>true</code> is returned, the given node's child
+     *  nodes will be visited next; however, if <code>false</code> is
+     *  returned, the given node's child nodes will not be visited.
+     */
+    public bool visit(CopyTargetEdit edit) {
+        return visitNode(edit);
+    }
+
+    /**
+     * Visits a <code>MoveSourceEdit</code> instance.
+     *
+     * @param edit the node to visit
+     * @return If <code>true</code> is returned, the given node's child
+     *  nodes will be visited next; however, if <code>false</code> is
+     *  returned, the given node's child nodes will not be visited.
+     */
+    public bool visit(MoveSourceEdit edit) {
+        return visitNode(edit);
+    }
+
+    /**
+     * Visits a <code>MoveTargetEdit</code> instance.
+     *
+     * @param edit the node to visit
+     * @return If <code>true</code> is returned, the given node's child
+     *  nodes will be visited next; however, if <code>false</code> is
+     *  returned, the given node's child nodes will not be visited.
+     */
+    public bool visit(MoveTargetEdit edit) {
+        return visitNode(edit);
+    }
+
+    /**
+     * Visits a <code>RangeMarker</code> instance.
+     *
+     * @param edit the node to visit
+     * @return If <code>true</code> is returned, the given node's child
+     *  nodes will be visited next; however, if <code>false</code> is
+     *  returned, the given node's child nodes will not be visited.
+     */
+    public bool visit(RangeMarker edit) {
+        return visitNode(edit);
+    }
+
+    /**
+     * Visits a <code>CopyingRangeMarker</code> instance.
+     *
+     * @param edit the node to visit
+     * @return If <code>true</code> is returned, the given node's child
+     *  nodes will be visited next; however, if <code>false</code> is
+     *  returned, the given node's child nodes will not be visited.
+     */
+    public bool visit(CopyingRangeMarker edit) {
+        return visitNode(edit);
+    }
+
+    /**
+     * Visits a <code>DeleteEdit</code> instance.
+     *
+     * @param edit the node to visit
+     * @return If <code>true</code> is returned, the given node's child
+     *  nodes will be visited next; however, if <code>false</code> is
+     *  returned, the given node's child nodes will not be visited.
+     */
+    public bool visit(DeleteEdit edit) {
+        return visitNode(edit);
+    }
+
+    /**
+     * Visits a <code>InsertEdit</code> instance.
+     *
+     * @param edit the node to visit
+     * @return If <code>true</code> is returned, the given node's child
+     *  nodes will be visited next; however, if <code>false</code> is
+     *  returned, the given node's child nodes will not be visited.
+     */
+    public bool visit(InsertEdit edit) {
+        return visitNode(edit);
+    }
+
+    /**
+     * Visits a <code>ReplaceEdit</code> instance.
+     *
+     * @param edit the node to visit
+     * @return If <code>true</code> is returned, the given node's child
+     *  nodes will be visited next; however, if <code>false</code> is
+     *  returned, the given node's child nodes will not be visited.
+     */
+    public bool visit(ReplaceEdit edit) {
+        return visitNode(edit);
+    }
+
+    /**
+     * Visits a <code>UndoEdit</code> instance.
+     *
+     * @param edit the node to visit
+     * @return If <code>true</code> is returned, the given node's child
+     *  nodes will be visited next; however, if <code>false</code> is
+     *  returned, the given node's child nodes will not be visited.
+     */
+    public bool visit(UndoEdit edit) {
+        return visitNode(edit);
+    }
+
+    /**
+     * Visits a <code>MultiTextEdit</code> instance.
+     *
+     * @param edit the node to visit
+     * @return If <code>true</code> is returned, the given node's child
+     *  nodes will be visited next; however, if <code>false</code> is
+     *  returned, the given node's child nodes will not be visited.
+     */
+    public bool visit(MultiTextEdit edit) {
+        return visitNode(edit);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/TreeIterationInfo.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.TreeIterationInfo;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+
+
+class TreeIterationInfo {
+
+    interface Visitor {
+        void visit(TextEdit edit);
+    }
+
+    private int fMark= -1;
+    private TextEdit[][] fEditStack;
+    private int[] fIndexStack;
+
+    public this(){
+        fEditStack= new TextEdit[][](10);
+        fIndexStack= new int[10];
+    }
+
+    public int getSize() {
+        return fMark + 1;
+    }
+    public void push(TextEdit[] edits) {
+        if (++fMark is fEditStack.length) {
+            TextEdit[][] t1= new TextEdit[][](fEditStack.length * 2);
+            SimpleType!(TextEdit[]).arraycopy(fEditStack, 0, t1, 0, fEditStack.length);
+            fEditStack= t1;
+            int[] t2= new int[fEditStack.length];
+            System.arraycopy(fIndexStack, 0, t2, 0, fIndexStack.length);
+            fIndexStack= t2;
+        }
+        fEditStack[fMark]= edits;
+        fIndexStack[fMark]= -1;
+    }
+    public void setIndex(int index) {
+        fIndexStack[fMark]= index;
+    }
+    public void pop() {
+        fEditStack[fMark]= null;
+        fIndexStack[fMark]= -1;
+        fMark--;
+    }
+    public void accept(Visitor visitor) {
+        for (int i= fMark; i >= 0; i--) {
+            Assert.isTrue(fIndexStack[i] >= 0);
+            int start= fIndexStack[i] + 1;
+            TextEdit[] edits= fEditStack[i];
+            for (int s= start; s < edits.length; s++) {
+                visitor.visit(edits[s]);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/UndoCollector.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.text.edits.UndoCollector;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.UndoEdit; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
+
+
+class UndoCollector : IDocumentListener {
+
+    protected UndoEdit undo;
+
+    package UndoEdit undo_package(){
+        return undo;
+    }
+
+    private int fOffset;
+    private int fLength;
+
+    /**
+     * @since 3.1
+     */
+    private String fLastCurrentText;
+
+    public this(TextEdit root) {
+        fOffset= root.getOffset();
+        fLength= root.getLength();
+    }
+
+    public void connect(IDocument document) {
+        document.addDocumentListener(this);
+        undo= new UndoEdit();
+    }
+
+    public void disconnect(IDocument document) {
+        if (undo !is null) {
+            document.removeDocumentListener(this);
+            undo.defineRegion(fOffset, fLength);
+        }
+    }
+
+    public void documentChanged(DocumentEvent event) {
+        fLength+= getDelta(event);
+    }
+
+    private static int getDelta(DocumentEvent event) {
+        String text= event.getText();
+        return text is null ? -event.getLength() : (text.length() - event.getLength());
+    }
+
+    public void documentAboutToBeChanged(DocumentEvent event) {
+        int offset= event.getOffset();
+        int currentLength= event.getLength();
+        String currentText= null;
+        try {
+            currentText= event.getDocument().get(offset, currentLength);
+        } catch (BadLocationException cannotHappen) {
+            Assert.isTrue(false, "Can't happen"); //$NON-NLS-1$
+        }
+
+        /*
+         * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=93634
+         * If the same string is replaced on many documents (e.g. rename
+         * package), the size of the undo can be reduced by using the same
+         * String instance in all edits, instead of using the unique String
+         * returned from IDocument.get(int, int).
+         */
+        if (fLastCurrentText !is null && fLastCurrentText.equals(currentText))
+            currentText= fLastCurrentText;
+        else
+            fLastCurrentText= currentText;
+
+        String newText= event.getText();
+        undo.add(new ReplaceEdit(offset, newText !is null ? newText.length() : 0, currentText));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/edits/UndoEdit.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * 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 org.eclipse.text.edits.UndoEdit;
+
+import org.eclipse.text.edits.MultiTextEdit; // packageimport
+import org.eclipse.text.edits.CopySourceEdit; // packageimport
+import org.eclipse.text.edits.MoveSourceEdit; // packageimport
+import org.eclipse.text.edits.CopyingRangeMarker; // packageimport
+import org.eclipse.text.edits.ReplaceEdit; // packageimport
+import org.eclipse.text.edits.EditDocument; // packageimport
+import org.eclipse.text.edits.UndoCollector; // packageimport
+import org.eclipse.text.edits.DeleteEdit; // packageimport
+import org.eclipse.text.edits.MoveTargetEdit; // packageimport
+import org.eclipse.text.edits.CopyTargetEdit; // packageimport
+import org.eclipse.text.edits.TextEditCopier; // packageimport
+import org.eclipse.text.edits.ISourceModifier; // packageimport
+import org.eclipse.text.edits.TextEditMessages; // packageimport
+import org.eclipse.text.edits.TextEditProcessor; // packageimport
+import org.eclipse.text.edits.MalformedTreeException; // packageimport
+import org.eclipse.text.edits.TreeIterationInfo; // packageimport
+import org.eclipse.text.edits.TextEditVisitor; // packageimport
+import org.eclipse.text.edits.TextEditGroup; // packageimport
+import org.eclipse.text.edits.TextEdit; // packageimport
+import org.eclipse.text.edits.RangeMarker; // packageimport
+import org.eclipse.text.edits.InsertEdit; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * This class encapsulates the reverse changes of an executed text
+ * edit tree. To apply an undo memento to a document use method
+ * <code>apply(IDocument)</code>.
+ * <p>
+ * Clients can't add additional children to an undo edit nor can they
+ * add an undo edit as a child to another edit. Doing so results in
+ * both cases in a <code>MalformedTreeException<code>.
+ *
+ * @since 3.0
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public final class UndoEdit : TextEdit {
+
+    this() {
+        super(0, Integer.MAX_VALUE);
+    }
+
+    private this(UndoEdit other) {
+        super(other);
+    }
+
+    /*
+     * @see org.eclipse.text.edits.TextEdit#internalAdd(org.eclipse.text.edits.TextEdit)
+     */
+    void internalAdd(TextEdit child)  {
+        throw new MalformedTreeException(null, this, TextEditMessages.getString("UndoEdit.no_children")); //$NON-NLS-1$
+    }
+
+    /*
+     * @see org.eclipse.text.edits.MultiTextEdit#aboutToBeAdded(org.eclipse.text.edits.TextEdit)
+     */
+    void aboutToBeAdded(TextEdit parent) {
+        throw new MalformedTreeException(parent, this, TextEditMessages.getString("UndoEdit.can_not_be_added")); //$NON-NLS-1$
+    }
+
+    UndoEdit dispatchPerformEdits(TextEditProcessor processor)  {
+        return processor.executeUndo();
+    }
+
+    void dispatchCheckIntegrity(TextEditProcessor processor)  {
+        processor.checkIntegrityUndo();
+    }
+
+    /*
+     * @see org.eclipse.text.edits.TextEdit#doCopy()
+     */
+    protected TextEdit doCopy() {
+        return new UndoEdit(this);
+    }
+
+    /*
+     * @see TextEdit#accept0
+     */
+    protected void accept0(TextEditVisitor visitor) {
+        bool visitChildren= visitor.visit(this);
+        if (visitChildren) {
+            acceptChildren(visitor);
+        }
+    }
+
+    /*
+     * @see TextEdit#performDocumentUpdating
+     */
+    int performDocumentUpdating(IDocument document)  {
+        fDelta= 0;
+        return fDelta;
+    }
+
+    void add(ReplaceEdit edit) {
+        List children= internalGetChildren();
+        if (children is null) {
+            children= new ArrayList(2);
+            internalSetChildren(children);
+        }
+        children.add(edit);
+    }
+
+    void defineRegion(int offset, int length) {
+        internalSetOffset(offset);
+        internalSetLength(length);
+    }
+
+    bool deleteChildren() {
+        return false;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.text.undo.DocumentUndoEvent;
+
+// import org.eclipse.text.undo.DocumentUndoManager; // packageimport
+// import org.eclipse.text.undo.DocumentUndoManagerRegistry; // packageimport
+// import org.eclipse.text.undo.IDocumentUndoListener; // packageimport
+// import org.eclipse.text.undo.UndoMessages; // packageimport
+// import org.eclipse.text.undo.IDocumentUndoManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * Describes document changes initiated by undo or redo.
+ * <p>
+ * Clients are not supposed to subclass or create instances of this class.
+ * </p>
+ *
+ * @see IDocumentUndoManager
+ * @see IDocumentUndoListener
+ * @since 3.2
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class DocumentUndoEvent {
+
+    /**
+     * Indicates that the described document event is about to be
+     * undone.
+     */
+    public static const int ABOUT_TO_UNDO= 1 << 0;
+
+    /**
+     * Indicates that the described document event is about to be
+     * redone.
+     */
+    public static const int ABOUT_TO_REDO= 1 << 1;
+
+    /**
+     * Indicates that the described document event has been undone.
+     */
+    public static const int UNDONE= 1 << 2;
+
+    /**
+     * Indicates that the described document event has been redone.
+     */
+    public static const int REDONE= 1 << 3;
+
+    /**
+     * Indicates that the described document event is a compound undo
+     * or redo event.
+     */
+    public static const int COMPOUND= 1 << 4;
+
+    /** The changed document. */
+    private IDocument fDocument;
+
+    /** The document offset where the change begins. */
+    private int fOffset;
+
+    /** Text inserted into the document. */
+    private String fText;
+
+    /** Text replaced in the document. */
+    private String fPreservedText;
+
+    /** Bit mask of event types describing the event */
+    private int fEventType;
+
+    /** The source that triggered this event or <code>null</code> if unknown. */
+    private Object fSource;
+
+    /**
+     * Creates a new document event.
+     *
+     * @param doc the changed document
+     * @param offset the offset of the replaced text
+     * @param text the substitution text
+     * @param preservedText the replaced text
+     * @param eventType a bit mask describing the type(s) of event
+     * @param source the source that triggered this event or <code>null</code> if unknown
+     */
+    this(IDocument doc, int offset, String text, String preservedText, int eventType, Object source) {
+
+        Assert.isNotNull(cast(Object)doc);
+        Assert.isTrue(offset >= 0);
+
+        fDocument= doc;
+        fOffset= offset;
+        fText= text;
+        fPreservedText= preservedText;
+        fEventType= eventType;
+        fSource= source;
+    }
+
+    /**
+     * Returns the changed document.
+     *
+     * @return the changed document
+     */
+    public IDocument getDocument() {
+        return fDocument;
+    }
+
+    /**
+     * Returns the offset of the change.
+     *
+     * @return the offset of the change
+     */
+    public int getOffset() {
+        return fOffset;
+    }
+
+    /**
+     * Returns the text that has been inserted.
+     *
+     * @return the text that has been inserted
+     */
+    public String getText() {
+        return fText;
+    }
+
+    /**
+     * Returns the text that has been replaced.
+     *
+     * @return the text that has been replaced
+     */
+    public String getPreservedText() {
+        return fPreservedText;
+    }
+
+    /**
+     * Returns the type of event that is occurring.
+     *
+     * @return the bit mask that indicates the type (or types) of the event
+     */
+    public int getEventType() {
+        return fEventType;
+    }
+
+    /**
+     * Returns the source that triggered this event.
+     *
+     * @return the source that triggered this event.
+     */
+    public Object getSource() {
+        return fSource;
+    }
+
+    /**
+     * Returns whether the change was a compound change or not.
+     *
+     * @return  <code>true</code> if the undo or redo change is a
+     *          compound change, <code>false</code> if it is not
+     */
+    public bool isCompound() {
+        return (fEventType & COMPOUND) !is 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1300 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.text.undo.DocumentUndoManager;
+
+import org.eclipse.text.undo.DocumentUndoManagerRegistry; // packageimport
+import org.eclipse.text.undo.DocumentUndoEvent; // packageimport
+import org.eclipse.text.undo.IDocumentUndoListener; // packageimport
+import org.eclipse.text.undo.UndoMessages; // packageimport
+import org.eclipse.text.undo.IDocumentUndoManager; // packageimport
+
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.operations.AbstractOperation;
+import org.eclipse.core.commands.operations.IContextReplacingOperation;
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.commands.operations.IOperationHistoryListener;
+import org.eclipse.core.commands.operations.IUndoContext;
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.commands.operations.ObjectUndoContext;
+import org.eclipse.core.commands.operations.OperationHistoryEvent;
+import org.eclipse.core.commands.operations.OperationHistoryFactory;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension4;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.TextUtilities;
+
+/**
+ * A standard implementation of a document-based undo manager that
+ * creates an undo history based on changes to its document.
+ * <p>
+ * Based on the 3.1 implementation of DefaultUndoManager, it was implemented
+ * using the document-related manipulations defined in the original
+ * DefaultUndoManager, by separating the document manipulations from the
+ * viewer-specific processing.</p>
+ * <p>
+ * The classes representing individual text edits (formerly text commands)
+ * were promoted from inner types to their own classes in order to support
+ * reassignment to a different undo manager.<p>
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ *
+ * @see IDocumentUndoManager
+ * @see DocumentUndoManagerRegistry
+ * @see IDocumentUndoListener
+ * @see org.eclipse.jface.text.IDocument
+ * @since 3.2
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class DocumentUndoManager : IDocumentUndoManager {
+
+
+    /**
+     * Represents an undo-able text change, described as the
+     * replacement of some preserved text with new text.
+     * <p>
+     * Based on the DefaultUndoManager.TextCommand from R3.1.
+     * </p>
+     */
+    private static class UndoableTextChange : AbstractOperation {
+
+        /** The start index of the replaced text. */
+        protected int fStart= -1;
+
+        /** The end index of the replaced text. */
+        protected int fEnd= -1;
+
+        /** The newly inserted text. */
+        protected String fText;
+
+        /** The replaced text. */
+        protected String fPreservedText;
+
+        /** The undo modification stamp. */
+        protected long fUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+
+        /** The redo modification stamp. */
+        protected long fRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+
+        /** The undo manager that generated the change. */
+        protected DocumentUndoManager fDocumentUndoManager;
+
+        /**
+         * Creates a new text change.
+         *
+         * @param manager the undo manager for this change
+         */
+        this(DocumentUndoManager manager) {
+            super(UndoMessages.getString("DocumentUndoManager.operationLabel")); //$NON-NLS-1$
+            this.fDocumentUndoManager= manager;
+            addContext(manager.getUndoContext());
+        }
+
+        /**
+         * Re-initializes this text change.
+         */
+        protected void reinitialize() {
+            fStart= fEnd= -1;
+            fText= fPreservedText= null;
+            fUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+            fRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+        }
+
+        /**
+         * Sets the start and the end index of this change.
+         *
+         * @param start the start index
+         * @param end the end index
+         */
+        protected void set(int start, int end) {
+            fStart= start;
+            fEnd= end;
+            fText= null;
+            fPreservedText= null;
+        }
+
+        /*
+         * @see org.eclipse.core.commands.operations.IUndoableOperation#dispose()
+         */
+        public void dispose() {
+            reinitialize();
+        }
+
+        /**
+         * Undo the change described by this change.
+         */
+        protected void undoTextChange() {
+            try {
+                if (auto de4 = cast(IDocumentExtension4)fDocumentUndoManager.fDocument )
+                    de4.replace(fStart, fText
+                            .length(), fPreservedText, fUndoModificationStamp);
+                else
+                    fDocumentUndoManager.fDocument.replace(fStart, fText.length(),
+                            fPreservedText);
+            } catch (BadLocationException x) {
+            }
+        }
+
+        /*
+         * @see org.eclipse.core.commands.operations.IUndoableOperation#canUndo()
+         */
+        public bool canUndo() {
+            if (isValid()) {
+                if (cast(IDocumentExtension4)fDocumentUndoManager.fDocument) {
+                    long docStamp= (cast(IDocumentExtension4) fDocumentUndoManager.fDocument)
+                            .getModificationStamp();
+
+                    // Normal case: an undo is valid if its redo will restore
+                    // document to its current modification stamp
+                    bool canUndo= docStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP
+                            || docStamp is getRedoModificationStamp();
+
+                    /*
+                     * Special case to check if the answer is false. If the last
+                     * document change was empty, then the document's modification
+                     * stamp was incremented but nothing was committed. The
+                     * operation being queried has an older stamp. In this case
+                     * only, the comparison is different. A sequence of document
+                     * changes that include an empty change is handled correctly
+                     * when a valid commit follows the empty change, but when
+                     * #canUndo() is queried just after an empty change, we must
+                     * special case the check. The check is very specific to prevent
+                     * false positives. see
+                     * https://bugs.eclipse.org/bugs/show_bug.cgi?id=98245
+                     */
+                    if (!canUndo
+                            && this is fDocumentUndoManager.fHistory
+                                    .getUndoOperation(fDocumentUndoManager.fUndoContext)
+                                // this is the latest operation
+                            && this !is fDocumentUndoManager.fCurrent
+                                // there is a more current operation not on the stack
+                            && !fDocumentUndoManager.fCurrent.isValid()
+                            // the current operation is not a valid document
+                            // modification
+                            && fDocumentUndoManager.fCurrent.fUndoModificationStamp !is
+                            // the invalid current operation has a document stamp
+                            IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) {
+                        canUndo= fDocumentUndoManager.fCurrent.fRedoModificationStamp is docStamp;
+                    }
+                    /*
+                     * When the composite is the current operation, it may hold the
+                     * timestamp of a no-op change. We check this here rather than
+                     * in an override of canUndo() in UndoableCompoundTextChange simply to
+                     * keep all the special case checks in one place.
+                     */
+                    if (!canUndo
+                            && this is fDocumentUndoManager.fHistory
+                                    .getUndoOperation(fDocumentUndoManager.fUndoContext)
+                            && // this is the latest operation
+                            null !is cast(UndoableCompoundTextChange)this
+                            && this is fDocumentUndoManager.fCurrent
+                            && // this is the current operation
+                            this.fStart is -1
+                            && // the current operation text is not valid
+                            fDocumentUndoManager.fCurrent.fRedoModificationStamp !is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) {
+                            // but it has a redo stamp
+                        canUndo= fDocumentUndoManager.fCurrent.fRedoModificationStamp is docStamp;
+                    }
+                    return canUndo;
+
+                }
+                // if there is no timestamp to check, simply return true per the
+                // 3.0.1 behavior
+                return true;
+            }
+            return false;
+        }
+
+        /*
+         * @see org.eclipse.core.commands.operations.IUndoableOperation#canRedo()
+         */
+        public bool canRedo() {
+            if (isValid()) {
+                if (cast(IDocumentExtension4)fDocumentUndoManager.fDocument ) {
+                    long docStamp= (cast(IDocumentExtension4) fDocumentUndoManager.fDocument)
+                            .getModificationStamp();
+                    return docStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP
+                            || docStamp is getUndoModificationStamp();
+                }
+                // if there is no timestamp to check, simply return true per the
+                // 3.0.1 behavior
+                return true;
+            }
+            return false;
+        }
+
+        /*
+         * @see org.eclipse.core.commands.operations.IUndoableOperation#canExecute()
+         */
+        public bool canExecute() {
+            return fDocumentUndoManager.isConnected();
+        }
+
+        /*
+         * @see org.eclipse.core.commands.operations.IUndoableOperation.IUndoableOperation#execute(IProgressMonitor, IAdaptable)
+         */
+        public IStatus execute(IProgressMonitor monitor, IAdaptable uiInfo) {
+            // Text changes execute as they are typed, so executing one has no
+            // effect.
+            return Status.OK_STATUS;
+        }
+
+        /**
+         * {@inheritDoc}
+         * Notifies clients about the undo.
+         */
+        public IStatus undo(IProgressMonitor monitor, IAdaptable uiInfo) {
+            if (isValid()) {
+                fDocumentUndoManager.fireDocumentUndo(fStart, fPreservedText, fText, cast(Object)uiInfo, DocumentUndoEvent.ABOUT_TO_UNDO, false);
+                undoTextChange();
+                fDocumentUndoManager.resetProcessChangeState();
+                fDocumentUndoManager.fireDocumentUndo(fStart, fPreservedText, fText, cast(Object)uiInfo, DocumentUndoEvent.UNDONE, false);
+                return Status.OK_STATUS;
+            }
+            return IOperationHistory.OPERATION_INVALID_STATUS;
+        }
+
+        /**
+         * Re-applies the change described by this change.
+         */
+        protected void redoTextChange() {
+            try {
+                if (cast(IDocumentExtension4)fDocumentUndoManager.fDocument)
+                    (cast(IDocumentExtension4) fDocumentUndoManager.fDocument).replace(fStart, fEnd - fStart, fText, fRedoModificationStamp);
+                else
+                    fDocumentUndoManager.fDocument.replace(fStart, fEnd - fStart, fText);
+            } catch (BadLocationException x) {
+            }
+        }
+
+        /**
+         * Re-applies the change described by this change that was previously
+         * undone. Also notifies clients about the redo.
+         *
+         * @param monitor the progress monitor to use if necessary
+         * @param uiInfo an adaptable that can provide UI info if needed
+         * @return the status
+         */
+        public IStatus redo(IProgressMonitor monitor, IAdaptable uiInfo) {
+            if (isValid()) {
+                fDocumentUndoManager.fireDocumentUndo(fStart, fText, fPreservedText, cast(Object)uiInfo, DocumentUndoEvent.ABOUT_TO_REDO, false);
+                redoTextChange();
+                fDocumentUndoManager.resetProcessChangeState();
+                fDocumentUndoManager.fireDocumentUndo(fStart, fText, fPreservedText, cast(Object)uiInfo, DocumentUndoEvent.REDONE, false);
+                return Status.OK_STATUS;
+            }
+            return IOperationHistory.OPERATION_INVALID_STATUS;
+        }
+
+        /**
+         * Update the change in response to a commit.
+         */
+
+        protected void updateTextChange() {
+            fText= fDocumentUndoManager.fTextBuffer.toString();
+            fDocumentUndoManager.fTextBuffer.clear();
+            fPreservedText= fDocumentUndoManager.fPreservedTextBuffer.toString();
+            fDocumentUndoManager.fPreservedTextBuffer.clear();
+        }
+
+        /**
+         * Creates a new uncommitted text change depending on whether a compound
+         * change is currently being executed.
+         *
+         * @return a new, uncommitted text change or a compound text change
+         */
+        protected UndoableTextChange createCurrent() {
+            if (fDocumentUndoManager.fFoldingIntoCompoundChange)
+                return new UndoableCompoundTextChange(fDocumentUndoManager);
+            return new UndoableTextChange(fDocumentUndoManager);
+        }
+
+        /**
+         * Commits the current change into this one.
+         */
+        protected void commit() {
+            if (fStart < 0) {
+                if (fDocumentUndoManager.fFoldingIntoCompoundChange) {
+                    fDocumentUndoManager.fCurrent= createCurrent();
+                } else {
+                    reinitialize();
+                }
+            } else {
+                updateTextChange();
+                fDocumentUndoManager.fCurrent= createCurrent();
+            }
+            fDocumentUndoManager.resetProcessChangeState();
+        }
+
+        /**
+         * Updates the text from the buffers without resetting the buffers or adding
+         * anything to the stack.
+         */
+        protected void pretendCommit() {
+            if (fStart > -1) {
+                fText= fDocumentUndoManager.fTextBuffer.toString();
+                fPreservedText= fDocumentUndoManager.fPreservedTextBuffer.toString();
+            }
+        }
+
+        /**
+         * Attempt a commit of this change and answer true if a new fCurrent was
+         * created as a result of the commit.
+         *
+         * @return <code>true</code> if the change was committed and created
+         *          a new <code>fCurrent</code>, <code>false</code> if not
+         */
+        protected bool attemptCommit() {
+            pretendCommit();
+            if (isValid()) {
+                fDocumentUndoManager.commit();
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Checks whether this text change is valid for undo or redo.
+         *
+         * @return <code>true</code> if the change is valid for undo or redo
+         */
+        protected bool isValid() {
+            return fStart > -1 && fEnd > -1 && fText !is null;
+        }
+
+        /*
+         * @see java.lang.Object#toString()
+         */
+        public override String toString() {
+            String delimiter= ", "; //$NON-NLS-1$
+            StringBuffer text= new StringBuffer(super.toString());
+            text.append("\n"); //$NON-NLS-1$
+            text.append(this.classinfo.name);
+            text.append(" undo modification stamp: "); //$NON-NLS-1$
+            text.append(fUndoModificationStamp);
+            text.append(" redo modification stamp: "); //$NON-NLS-1$
+            text.append(fRedoModificationStamp);
+            text.append(" start: "); //$NON-NLS-1$
+            text.append(fStart);
+            text.append(delimiter);
+            text.append("end: "); //$NON-NLS-1$
+            text.append(fEnd);
+            text.append(delimiter);
+            text.append("text: '"); //$NON-NLS-1$
+            text.append(fText);
+            text.append('\'');
+            text.append(delimiter);
+            text.append("preservedText: '"); //$NON-NLS-1$
+            text.append(fPreservedText);
+            text.append('\'');
+            return text.toString();
+        }
+
+        /**
+         * Return the undo modification stamp
+         *
+         * @return the undo modification stamp for this change
+         */
+        protected long getUndoModificationStamp() {
+            return fUndoModificationStamp;
+        }
+
+        /**
+         * Return the redo modification stamp
+         *
+         * @return the redo modification stamp for this change
+         */
+        protected long getRedoModificationStamp() {
+            return fRedoModificationStamp;
+        }
+    }
+
+
+    /**
+     * Represents an undo-able text change consisting of several individual
+     * changes.
+     */
+    private static class UndoableCompoundTextChange : UndoableTextChange {
+
+        /** The list of individual changes */
+        private List fChanges;
+
+        /**
+         * Creates a new compound text change.
+         *
+         * @param manager
+         *            the undo manager for this change
+         */
+        this(DocumentUndoManager manager) {
+            fChanges= new ArrayList();
+            super(manager);
+        }
+
+        /**
+         * Adds a new individual change to this compound change.
+         *
+         * @param change the change to be added
+         */
+        protected void add(UndoableTextChange change) {
+            fChanges.add(change);
+        }
+
+        /*
+         * @see org.eclipse.text.undo.UndoableTextChange#undo(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.core.runtime.IAdaptable)
+         */
+        public IStatus undo(IProgressMonitor monitor, IAdaptable uiInfo) {
+
+            int size= fChanges.size();
+            if (size > 0) {
+                UndoableTextChange c;
+
+                c= cast(UndoableTextChange) fChanges.get(0);
+                fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fPreservedText, c.fText, cast(Object)uiInfo, DocumentUndoEvent.ABOUT_TO_UNDO, true);
+
+                for (int i= size - 1; i >= 0; --i) {
+                    c= cast(UndoableTextChange) fChanges.get(i);
+                    c.undoTextChange();
+                }
+                fDocumentUndoManager.resetProcessChangeState();
+                fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fPreservedText, c.fText, cast(Object)uiInfo,
+                        DocumentUndoEvent.UNDONE, true);
+            }
+            return Status.OK_STATUS;
+        }
+
+        /*
+         * @see org.eclipse.text.undo.UndoableTextChange#redo(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.core.runtime.IAdaptable)
+         */
+        public IStatus redo(IProgressMonitor monitor, IAdaptable uiInfo) {
+
+            int size= fChanges.size();
+            if (size > 0) {
+
+                UndoableTextChange c;
+                c= cast(UndoableTextChange) fChanges.get(size - 1);
+                fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fText, c.fPreservedText, cast(Object)uiInfo, DocumentUndoEvent.ABOUT_TO_REDO, true);
+
+                for (int i= 0; i <= size - 1; ++i) {
+                    c= cast(UndoableTextChange) fChanges.get(i);
+                    c.redoTextChange();
+                }
+                fDocumentUndoManager.resetProcessChangeState();
+                fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fText, c.fPreservedText, cast(Object)uiInfo, DocumentUndoEvent.REDONE, true);
+            }
+
+            return Status.OK_STATUS;
+        }
+
+        /*
+         * @see org.eclipse.text.undo.UndoableTextChange#updateTextChange()
+         */
+        protected void updateTextChange() {
+            // first gather the data from the buffers
+            super.updateTextChange();
+
+            // the result of the update is stored as a child change
+            UndoableTextChange c= new UndoableTextChange(fDocumentUndoManager);
+            c.fStart= fStart;
+            c.fEnd= fEnd;
+            c.fText= fText;
+            c.fPreservedText= fPreservedText;
+            c.fUndoModificationStamp= fUndoModificationStamp;
+            c.fRedoModificationStamp= fRedoModificationStamp;
+            add(c);
+
+            // clear out all indexes now that the child is added
+            reinitialize();
+        }
+
+        /*
+         * @see org.eclipse.text.undo.UndoableTextChange#createCurrent()
+         */
+        protected UndoableTextChange createCurrent() {
+
+            if (!fDocumentUndoManager.fFoldingIntoCompoundChange)
+                return new UndoableTextChange(fDocumentUndoManager);
+
+            reinitialize();
+            return this;
+        }
+
+        /*
+         * @see org.eclipse.text.undo.UndoableTextChange#commit()
+         */
+        protected void commit() {
+            // if there is pending data, update the text change
+            if (fStart > -1)
+                updateTextChange();
+            fDocumentUndoManager.fCurrent= createCurrent();
+            fDocumentUndoManager.resetProcessChangeState();
+        }
+
+        /*
+         * @see org.eclipse.text.undo.UndoableTextChange#isValid()
+         */
+        protected bool isValid() {
+            return fStart > -1 || fChanges.size() > 0;
+        }
+
+        /*
+         * @see org.eclipse.text.undo.UndoableTextChange#getUndoModificationStamp()
+         */
+        protected long getUndoModificationStamp() {
+            if (fStart > -1)
+                return super.getUndoModificationStamp();
+            else if (fChanges.size() > 0)
+                return (cast(UndoableTextChange) fChanges.get(0))
+                        .getUndoModificationStamp();
+
+            return fUndoModificationStamp;
+        }
+
+        /*
+         * @see org.eclipse.text.undo.UndoableTextChange#getRedoModificationStamp()
+         */
+        protected long getRedoModificationStamp() {
+            if (fStart > -1)
+                return super.getRedoModificationStamp();
+            else if (fChanges.size() > 0)
+                return (cast(UndoableTextChange) fChanges.get(fChanges.size() - 1))
+                        .getRedoModificationStamp();
+
+            return fRedoModificationStamp;
+        }
+    }
+
+
+    /**
+     * Internal listener to document changes.
+     */
+    private class DocumentListener : IDocumentListener {
+
+        private String fReplacedText;
+
+        /*
+         * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
+         */
+        public void documentAboutToBeChanged(DocumentEvent event) {
+            try {
+                fReplacedText= event.getDocument().get(event.getOffset(),
+                        event.getLength());
+                fPreservedUndoModificationStamp= event.getModificationStamp();
+            } catch (BadLocationException x) {
+                fReplacedText= null;
+            }
+        }
+
+        /*
+         * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
+         */
+        public void documentChanged(DocumentEvent event) {
+            fPreservedRedoModificationStamp= event.getModificationStamp();
+
+            // record the current valid state for the top operation in case it
+            // remains the
+            // top operation but changes state.
+            IUndoableOperation op= fHistory.getUndoOperation(fUndoContext);
+            bool wasValid= false;
+            if (op !is null)
+                wasValid= op.canUndo();
+            // Process the change, providing the before and after timestamps
+            processChange(event.getOffset(), event.getOffset()
+                    + event.getLength(), event.getText(), fReplacedText,
+                    fPreservedUndoModificationStamp,
+                    fPreservedRedoModificationStamp);
+
+            // now update fCurrent with the latest buffers from the document
+            // change.
+            fCurrent.pretendCommit();
+
+            if (op is fCurrent) {
+                // if the document change did not cause a new fCurrent to be
+                // created, then we should
+                // notify the history that the current operation changed if its
+                // validity has changed.
+                if (wasValid !is fCurrent.isValid())
+                    fHistory.operationChanged(op);
+            } else {
+                // if the change created a new fCurrent that we did not yet add
+                // to the
+                // stack, do so if it's valid and we are not in the middle of a
+                // compound change.
+                if (fCurrent !is fLastAddedTextEdit && fCurrent.isValid()) {
+                    addToOperationHistory(fCurrent);
+                }
+            }
+        }
+    }
+
+    /*
+     * @see IOperationHistoryListener
+     */
+    private class HistoryListener : IOperationHistoryListener {
+
+        private IUndoableOperation fOperation;
+
+        public void historyNotification(OperationHistoryEvent event) {
+            final int type= event.getEventType();
+            switch (type) {
+            case OperationHistoryEvent.ABOUT_TO_UNDO:
+            case OperationHistoryEvent.ABOUT_TO_REDO:
+                // if this is one of our operations
+                if (event.getOperation().hasContext(fUndoContext)) {
+                    // if we are undoing/redoing an operation we generated, then
+                    // ignore
+                    // the document changes associated with this undo or redo.
+                    if (cast(UndoableTextChange)event.getOperation() ) {
+                        listenToTextChanges(false);
+
+                        // in the undo case only, make sure compounds are closed
+                        if (type is OperationHistoryEvent.ABOUT_TO_UNDO) {
+                            if (fFoldingIntoCompoundChange) {
+                                endCompoundChange();
+                            }
+                        }
+                    } else {
+                        // the undo or redo has our context, but it is not one
+                        // of our edits. We will listen to the changes, but will
+                        // reset the state that tracks the undo/redo history.
+                        commit();
+                        fLastAddedTextEdit= null;
+                    }
+                    fOperation= event.getOperation();
+                }
+                break;
+            case OperationHistoryEvent.UNDONE:
+            case OperationHistoryEvent.REDONE:
+            case OperationHistoryEvent.OPERATION_NOT_OK:
+                if (event.getOperation() is fOperation) {
+                    listenToTextChanges(true);
+                    fOperation= null;
+                }
+                break;
+            default:
+            }
+        }
+
+    }
+
+
+    /**
+     * The undo context for this document undo manager.
+     */
+    private ObjectUndoContext fUndoContext;
+
+    /**
+     * The document whose changes are being tracked.
+     */
+    private IDocument fDocument;
+
+    /**
+     * The currently constructed edit.
+     */
+    private UndoableTextChange fCurrent;
+
+    /**
+     * The internal document listener.
+     */
+    private DocumentListener fDocumentListener;
+
+    /**
+     * Indicates whether the current change belongs to a compound change.
+     */
+    private bool fFoldingIntoCompoundChange= false;
+
+    /**
+     * The operation history being used to store the undo history.
+     */
+    private IOperationHistory fHistory;
+
+    /**
+     * The operation history listener used for managing undo and redo before and
+     * after the individual edits are performed.
+     */
+    private IOperationHistoryListener fHistoryListener;
+
+    /**
+     * The text edit last added to the operation history. This must be tracked
+     * internally instead of asking the history, since outside parties may be
+     * placing items on our undo/redo history.
+     */
+    private UndoableTextChange fLastAddedTextEdit= null;
+
+    /**
+     * The document modification stamp for redo.
+     */
+    private long fPreservedRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+
+    /**
+     * Text buffer to collect viewer content which has been replaced
+     */
+    private StringBuffer fPreservedTextBuffer;
+
+    /**
+     * The document modification stamp for undo.
+     */
+    private long fPreservedUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+
+    /**
+     * The last delete text edit.
+     */
+    private UndoableTextChange fPreviousDelete;
+
+    /**
+     * Text buffer to collect text which is inserted into the viewer
+     */
+    private StringBuffer fTextBuffer;
+
+    /** Indicates inserting state. */
+    private bool fInserting= false;
+
+    /** Indicates overwriting state. */
+    private bool fOverwriting= false;
+
+    /** The registered document listeners. */
+    private ListenerList fDocumentUndoListeners;
+
+    /** The list of clients connected. */
+    private List fConnected;
+
+    /**
+     *
+     * Create a DocumentUndoManager for the given document.
+     *
+     * @param document the document whose undo history is being managed.
+     */
+    public this(IDocument document) {
+//         super();
+        Assert.isNotNull(cast(Object)document);
+        fDocument= document;
+        fHistory= OperationHistoryFactory.getOperationHistory();
+        fUndoContext= new ObjectUndoContext(cast(Object)fDocument);
+        fConnected= new ArrayList();
+        fDocumentUndoListeners= new ListenerList(ListenerList.IDENTITY);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentUndoManager#addDocumentUndoListener(org.eclipse.jface.text.IDocumentUndoListener)
+     */
+    public void addDocumentUndoListener(IDocumentUndoListener listener) {
+        fDocumentUndoListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentUndoManager#removeDocumentUndoListener(org.eclipse.jface.text.IDocumentUndoListener)
+     */
+    public void removeDocumentUndoListener(IDocumentUndoListener listener) {
+        fDocumentUndoListeners.remove(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentUndoManager#getUndoContext()
+     */
+    public IUndoContext getUndoContext() {
+        return fUndoContext;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentUndoManager#commit()
+     */
+    public void commit() {
+        // if fCurrent has never been placed on the history, do so now.
+        // this can happen when there are multiple programmatically commits in a
+        // single document change.
+        if (fLastAddedTextEdit !is fCurrent) {
+            fCurrent.pretendCommit();
+            if (fCurrent.isValid())
+                addToOperationHistory(fCurrent);
+        }
+        fCurrent.commit();
+    }
+
+    /*
+     * @see org.eclipse.text.undo.IDocumentUndoManager#reset()
+     */
+    public void reset() {
+        if (isConnected()) {
+            shutdown();
+            initialize();
+        }
+    }
+
+    /*
+     * @see org.eclipse.text.undo.IDocumentUndoManager#redoable()
+     */
+    public bool redoable() {
+        return OperationHistoryFactory.getOperationHistory().canRedo(fUndoContext);
+    }
+
+    /*
+     * @see org.eclipse.text.undo.IDocumentUndoManager#undoable()
+     */
+    public bool undoable() {
+        return OperationHistoryFactory.getOperationHistory().canUndo(fUndoContext);
+    }
+
+    /*
+     * @see org.eclipse.text.undo.IDocumentUndoManager#undo()
+     */
+    public void redo()  {
+        if (isConnected() && redoable())
+            OperationHistoryFactory.getOperationHistory().redo(getUndoContext(), null, null);
+    }
+
+    /*
+     * @see org.eclipse.text.undo.IDocumentUndoManager#undo()
+     */
+    public void undo()  {
+        if (undoable())
+            OperationHistoryFactory.getOperationHistory().undo(fUndoContext, null, null);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentUndoManager#connect(java.lang.Object)
+     */
+    public void connect(Object client) {
+        if (!isConnected()) {
+            initialize();
+        }
+        if (!fConnected.contains(client))
+            fConnected.add(client);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentUndoManager#disconnect(java.lang.Object)
+     */
+    public void disconnect(Object client) {
+        fConnected.remove(client);
+        if (!isConnected()) {
+            shutdown();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentUndoManager#beginCompoundChange()
+     */
+    public void beginCompoundChange() {
+        if (isConnected()) {
+            fFoldingIntoCompoundChange= true;
+            commit();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentUndoManager#endCompoundChange()
+     */
+    public void endCompoundChange() {
+        if (isConnected()) {
+            fFoldingIntoCompoundChange= false;
+            commit();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentUndoManager#setUndoLimit(int)
+     */
+    public void setMaximalUndoLevel(int undoLimit) {
+        fHistory.setLimit(fUndoContext, undoLimit);
+    }
+
+    /**
+     * Fires a document undo event to all registered document undo listeners.
+     * Uses a robust iterator.
+     *
+     * @param offset the document offset
+     * @param text the text that was inserted
+     * @param preservedText the text being replaced
+     * @param source the source which triggered the event
+     * @param eventType the type of event causing the change
+     * @param isCompound a flag indicating whether the change is a compound change
+     * @see IDocumentUndoListener
+     */
+    void fireDocumentUndo(int offset, String text, String preservedText, Object source, int eventType, bool isCompound) {
+        eventType= isCompound ? eventType | DocumentUndoEvent.COMPOUND : eventType;
+        DocumentUndoEvent event= new DocumentUndoEvent(fDocument, offset, text, preservedText, eventType, source);
+        Object[] listeners= fDocumentUndoListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++) {
+            (cast(IDocumentUndoListener)listeners[i]).documentUndoNotification(event);
+        }
+    }
+
+    /**
+     * Adds any listeners needed to track the document and the operations
+     * history.
+     */
+    private void addListeners() {
+        fHistoryListener= new HistoryListener();
+        fHistory.addOperationHistoryListener(fHistoryListener);
+        listenToTextChanges(true);
+    }
+
+    /**
+     * Removes any listeners that were installed by the document.
+     */
+    private void removeListeners() {
+        listenToTextChanges(false);
+        fHistory.removeOperationHistoryListener(fHistoryListener);
+        fHistoryListener= null;
+    }
+
+    /**
+     * Adds the given text edit to the operation history if it is not part of a
+     * compound change.
+     *
+     * @param edit
+     *            the edit to be added
+     */
+    private void addToOperationHistory(UndoableTextChange edit) {
+        if (!fFoldingIntoCompoundChange
+                || cast(UndoableCompoundTextChange)edit ) {
+            fHistory.add(edit);
+            fLastAddedTextEdit= edit;
+        }
+    }
+
+    /**
+     * Disposes the undo history.
+     */
+    private void disposeUndoHistory() {
+        fHistory.dispose(fUndoContext, true, true, true);
+    }
+
+    /**
+     * Initializes the undo history.
+     */
+    private void initializeUndoHistory() {
+        if (fHistory !is null && fUndoContext !is null)
+            fHistory.dispose(fUndoContext, true, true, false);
+
+    }
+
+    /**
+     * Checks whether the given text starts with a line delimiter and
+     * subsequently contains a white space only.
+     *
+     * @param text the text to check
+     * @return <code>true</code> if the text is a line delimiter followed by
+     *         whitespace, <code>false</code> otherwise
+     */
+    private bool isWhitespaceText(String text) {
+
+        if (text is null || text.length() is 0)
+            return false;
+
+        String[] delimiters= fDocument.getLegalLineDelimiters();
+        int index= TextUtilities.startsWith(delimiters, text);
+        if (index > -1) {
+            char c;
+            int length= text.length();
+            for (int i= delimiters[index].length; i < length; i++) {
+                c= text.charAt(i);
+                if (c !is ' ' && c !is '\t')
+                    return false;
+            }
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Switches the state of whether there is a text listener or not.
+     *
+     * @param listen the state which should be established
+     */
+    private void listenToTextChanges(bool listen) {
+        if (listen) {
+            if (fDocumentListener is null && fDocument !is null) {
+                fDocumentListener= new DocumentListener();
+                fDocument.addDocumentListener(fDocumentListener);
+            }
+        } else if (!listen) {
+            if (fDocumentListener !is null && fDocument !is null) {
+                fDocument.removeDocumentListener(fDocumentListener);
+                fDocumentListener= null;
+            }
+        }
+    }
+
+    private void processChange(int modelStart, int modelEnd,
+            String insertedText, String replacedText,
+            long beforeChangeModificationStamp,
+            long afterChangeModificationStamp) {
+
+        if (insertedText is null)
+            insertedText= ""; //$NON-NLS-1$
+
+        if (replacedText is null)
+            replacedText= ""; //$NON-NLS-1$
+
+        int length= insertedText.length();
+        int diff= modelEnd - modelStart;
+
+        if (fCurrent.fUndoModificationStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP)
+            fCurrent.fUndoModificationStamp= beforeChangeModificationStamp;
+
+        // normalize
+        if (diff < 0) {
+            int tmp= modelEnd;
+            modelEnd= modelStart;
+            modelStart= tmp;
+        }
+
+        if (modelStart is modelEnd) {
+            // text will be inserted
+            if ((length is 1) || isWhitespaceText(insertedText)) {
+                // by typing or whitespace
+                if (!fInserting
+                        || (modelStart !is fCurrent.fStart
+                                + fTextBuffer.length())) {
+                    fCurrent.fRedoModificationStamp= beforeChangeModificationStamp;
+                    if (fCurrent.attemptCommit())
+                        fCurrent.fUndoModificationStamp= beforeChangeModificationStamp;
+
+                    fInserting= true;
+                }
+                if (fCurrent.fStart < 0)
+                    fCurrent.fStart= fCurrent.fEnd= modelStart;
+                if (length > 0)
+                    fTextBuffer.append(insertedText);
+            } else if (length > 0) {
+                // by pasting or model manipulation
+                fCurrent.fRedoModificationStamp= beforeChangeModificationStamp;
+                if (fCurrent.attemptCommit())
+                    fCurrent.fUndoModificationStamp= beforeChangeModificationStamp;
+
+                fCurrent.fStart= fCurrent.fEnd= modelStart;
+                fTextBuffer.append(insertedText);
+                fCurrent.fRedoModificationStamp= afterChangeModificationStamp;
+                if (fCurrent.attemptCommit())
+                    fCurrent.fUndoModificationStamp= afterChangeModificationStamp;
+
+            }
+        } else {
+            if (length is 0) {
+                // text will be deleted by backspace or DEL key or empty
+                // clipboard
+                length= replacedText.length;
+                String[] delimiters= fDocument.getLegalLineDelimiters();
+
+                if ((length is 1)
+                        || TextUtilities.equals(delimiters, replacedText) > -1) {
+
+                    // whereby selection is empty
+
+                    if (fPreviousDelete.fStart is modelStart
+                            && fPreviousDelete.fEnd is modelEnd) {
+                        // repeated DEL
+
+                        // correct wrong settings of fCurrent
+                        if (fCurrent.fStart is modelEnd
+                                && fCurrent.fEnd is modelStart) {
+                            fCurrent.fStart= modelStart;
+                            fCurrent.fEnd= modelEnd;
+                        }
+                        // append to buffer && extend edit range
+                        fPreservedTextBuffer.append(replacedText);
+                        ++fCurrent.fEnd;
+
+                    } else if (fPreviousDelete.fStart is modelEnd) {
+                        // repeated backspace
+
+                        // insert in buffer and extend edit range
+                        fPreservedTextBuffer.select(0,0);
+                        fPreservedTextBuffer.replace(replacedText);
+                        fCurrent.fStart= modelStart;
+
+                    } else {
+                        // either DEL or backspace for the first time
+
+                        fCurrent.fRedoModificationStamp= beforeChangeModificationStamp;
+                        if (fCurrent.attemptCommit())
+                            fCurrent.fUndoModificationStamp= beforeChangeModificationStamp;
+
+                        // as we can not decide whether it was DEL or backspace
+                        // we initialize for backspace
+                        fPreservedTextBuffer.append(replacedText);
+                        fCurrent.fStart= modelStart;
+                        fCurrent.fEnd= modelEnd;
+                    }
+
+                    fPreviousDelete.set(modelStart, modelEnd);
+
+                } else if (length > 0) {
+                    // whereby selection is not empty
+                    fCurrent.fRedoModificationStamp= beforeChangeModificationStamp;
+                    if (fCurrent.attemptCommit())
+                        fCurrent.fUndoModificationStamp= beforeChangeModificationStamp;
+
+                    fCurrent.fStart= modelStart;
+                    fCurrent.fEnd= modelEnd;
+                    fPreservedTextBuffer.append(replacedText);
+                }
+            } else {
+                // text will be replaced
+
+                if (length is 1) {
+                    length= replacedText.length;
+                    String[] delimiters= fDocument.getLegalLineDelimiters();
+
+                    if ((length is 1)
+                            || TextUtilities.equals(delimiters, replacedText) > -1) {
+                        // because of overwrite mode or model manipulation
+                        if (!fOverwriting
+                                || (modelStart !is fCurrent.fStart
+                                        + fTextBuffer.length())) {
+                            fCurrent.fRedoModificationStamp= beforeChangeModificationStamp;
+                            if (fCurrent.attemptCommit())
+                                fCurrent.fUndoModificationStamp= beforeChangeModificationStamp;
+
+                            fOverwriting= true;
+                        }
+
+                        if (fCurrent.fStart < 0)
+                            fCurrent.fStart= modelStart;
+
+                        fCurrent.fEnd= modelEnd;
+                        fTextBuffer.append(insertedText);
+                        fPreservedTextBuffer.append(replacedText);
+                        fCurrent.fRedoModificationStamp= afterChangeModificationStamp;
+                        return;
+                    }
+                }
+                // because of typing or pasting whereby selection is not empty
+                fCurrent.fRedoModificationStamp= beforeChangeModificationStamp;
+                if (fCurrent.attemptCommit())
+                    fCurrent.fUndoModificationStamp= beforeChangeModificationStamp;
+
+                fCurrent.fStart= modelStart;
+                fCurrent.fEnd= modelEnd;
+                fTextBuffer.append(insertedText);
+                fPreservedTextBuffer.append(replacedText);
+            }
+        }
+        // in all cases, the redo modification stamp is updated on the open
+        // text edit
+        fCurrent.fRedoModificationStamp= afterChangeModificationStamp;
+    }
+
+    /**
+     * Initialize the receiver.
+     */
+    private void initialize() {
+        initializeUndoHistory();
+
+        // open up the current text edit
+        fCurrent= new UndoableTextChange(this);
+        fPreviousDelete= new UndoableTextChange(this);
+        fTextBuffer= new StringBuffer();
+        fPreservedTextBuffer= new StringBuffer();
+
+        addListeners();
+    }
+
+    /**
+     * Reset processChange state.
+     *
+     * @since 3.2
+     */
+    private void resetProcessChangeState() {
+        fInserting= false;
+        fOverwriting= false;
+        fPreviousDelete.reinitialize();
+    }
+
+    /**
+     * Shutdown the receiver.
+     */
+    private void shutdown() {
+        removeListeners();
+
+        fCurrent= null;
+        fPreviousDelete= null;
+        fTextBuffer.clear();
+        fPreservedTextBuffer.clear();
+
+        disposeUndoHistory();
+    }
+
+    /**
+     * Return whether or not any clients are connected to the receiver.
+     *
+     * @return <code>true</code> if the receiver is connected to
+     *          clients, <code>false</code> if it is not
+     */
+    bool isConnected() {
+        if (fConnected is null)
+            return false;
+        return !fConnected.isEmpty();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentUndoManager#transferUndoHistory(IDocumentUndoManager)
+     */
+    public void transferUndoHistory(IDocumentUndoManager manager) {
+        IUndoContext oldUndoContext= manager.getUndoContext();
+        // Get the history for the old undo context.
+        IUndoableOperation [] operations= OperationHistoryFactory.getOperationHistory().getUndoHistory(oldUndoContext);
+        for (int i= 0; i < operations.length; i++) {
+            // First replace the undo context
+            IUndoableOperation op= operations[i];
+            if (cast(IContextReplacingOperation)op ) {
+                (cast(IContextReplacingOperation)op).replaceContext(oldUndoContext, getUndoContext());
+            } else {
+                op.addContext(getUndoContext());
+                op.removeContext(oldUndoContext);
+            }
+            // Now update the manager that owns the text edit.
+            if (cast(UndoableTextChange)op ) {
+                (cast(UndoableTextChange)op).fDocumentUndoManager= this;
+            }
+        }
+
+        IUndoableOperation op= OperationHistoryFactory.getOperationHistory().getUndoOperation(getUndoContext());
+        if (op !is null && !(cast(UndoableTextChange)op ))
+            return;
+
+        // Record the transfer itself as an undoable change.
+        // If the transfer results from some open operation, recording this change will
+        // cause our undo context to be added to the outer operation.  If there is no
+        // outer operation, there will be a local change to signify the transfer.
+        // This also serves to synchronize the modification stamps with the documents.
+        UndoableTextChange cmd= new UndoableTextChange(this);
+        cmd.fStart= cmd.fEnd= 0;
+        cmd.fText= cmd.fPreservedText= ""; //$NON-NLS-1$
+        if (cast(IDocumentExtension4)fDocument ) {
+            cmd.fRedoModificationStamp= (cast(IDocumentExtension4)fDocument).getModificationStamp();
+            if (op !is null)
+                cmd.fUndoModificationStamp= (cast(UndoableTextChange)op).fRedoModificationStamp;
+        }
+        addToOperationHistory(cmd);
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoManagerRegistry.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * 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 org.eclipse.text.undo.DocumentUndoManagerRegistry;
+
+import org.eclipse.text.undo.DocumentUndoManager; // packageimport
+import org.eclipse.text.undo.DocumentUndoEvent; // packageimport
+import org.eclipse.text.undo.IDocumentUndoListener; // packageimport
+import org.eclipse.text.undo.UndoMessages; // packageimport
+import org.eclipse.text.undo.IDocumentUndoManager; // packageimport
+
+
+import java.lang.all;
+import java.util.Map;
+import java.util.HashMap;
+
+
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * This document undo manager registry provides access to a document's
+ * undo manager. In order to connect a document a document undo manager
+ * call <code>connect</code>. After that call has successfully completed
+ * undo manager can be obtained via <code>getDocumentUndoManager</code>.
+ * The undo manager is created on the first connect and disposed on the last
+ * disconnect, i.e. this registry keeps track of how often a undo manager is
+ * connected and returns the same undo manager to each client as long as the
+ * document is connected.
+ * <p>
+ * <em>The recoding of changes starts with the first {@link #connect(IDocument)}.</em></p>
+ *
+ * @since 3.2
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public final class DocumentUndoManagerRegistry {
+
+    private static final class Record {
+        public this(IDocument document) {
+            count= 0;
+            undoManager= new DocumentUndoManager(document);
+        }
+        private int count;
+        private IDocumentUndoManager undoManager;
+    }
+
+    private static Map fgFactory_;
+    private static Map fgFactory(){
+        if( fgFactory_ is null ) fgFactory_ = new HashMap();
+        return fgFactory_;
+    }
+
+    private this() {
+        //  Do not instantiate
+    }
+
+
+    /**
+     * Connects the file at the given location to this manager. After that call
+     * successfully completed it is guaranteed that each call to <code>getFileBuffer</code>
+     * returns the same file buffer until <code>disconnect</code> is called.
+     * <p>
+     * <em>The recoding of changes starts with the first {@link #connect(IDocument)}.</em></p>
+     *
+     * @param document the document to be connected
+     */
+    public static synchronized void connect(IDocument document) {
+        Assert.isNotNull(cast(Object)document);
+        Record record= cast(Record)fgFactory.get(cast(Object)document);
+        if (record is null) {
+            record= new Record(document);
+            fgFactory.put(cast(Object)document, record);
+        }
+        record.count++;
+    }
+
+    /**
+     * Disconnects the given document from this registry.
+     *
+     * @param document the document to be disconnected
+     */
+    public static synchronized void disconnect(IDocument document) {
+        Assert.isNotNull(cast(Object)document);
+        Record record= cast(Record)fgFactory.get(cast(Object)document);
+        record.count--;
+        if (record.count is 0)
+            fgFactory.remove(cast(Object)document);
+
+    }
+
+    /**
+     * Returns the file buffer managed for the given location or <code>null</code>
+     * if there is no such file buffer.
+     * <p>
+     * The provided location is either a full path of a workspace resource or
+     * an absolute path in the local file system. The file buffer manager does
+     * not resolve the location of workspace resources in the case of linked
+     * resources.
+     * </p>
+     *
+     * @param document the document for which to get its undo manager
+     * @return the document undo manager or <code>null</code>
+     */
+    public static synchronized IDocumentUndoManager getDocumentUndoManager(IDocument document) {
+        Assert.isNotNull(cast(Object)document);
+        Record record= cast(Record)fgFactory.get(cast(Object)document);
+        if (record is null)
+            return null;
+        return record.undoManager;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/undo/IDocumentUndoListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.text.undo.IDocumentUndoListener;
+
+import org.eclipse.text.undo.DocumentUndoEvent; // packageimport
+
+
+import java.lang.all;
+
+
+/**
+ * This interface is used to listen to notifications from a DocumentUndoManager.
+ * The supplied DocumentUndoEvent describes the particular notification.
+ * <p>
+ * Document undo listeners must be prepared to receive notifications from a
+ * background thread. Any UI access occurring inside the implementation must be
+ * properly synchronized using the techniques specified by the client's widget
+ * library.</p>
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface IDocumentUndoListener {
+
+    /**
+     * The document is involved in an undo-related change.  Notify listeners
+     * with an event describing the change.
+     *
+     * @param event the document undo event that describes the particular notification
+     */
+    void documentUndoNotification(DocumentUndoEvent event);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/undo/IDocumentUndoManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.text.undo.IDocumentUndoManager;
+
+// import org.eclipse.text.undo.DocumentUndoManager; // packageimport
+// import org.eclipse.text.undo.DocumentUndoManagerRegistry; // packageimport
+import org.eclipse.text.undo.DocumentUndoEvent; // packageimport
+import org.eclipse.text.undo.IDocumentUndoListener; // packageimport
+// import org.eclipse.text.undo.UndoMessages; // packageimport
+
+
+import java.lang.all;
+import java.util.Set;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.operations.IUndoContext;
+
+/**
+ * Interface for a document undo manager. Tracks changes in a document and
+ * builds a history of text commands that describe the undoable changes to the
+ * document.
+ * <p>
+ * Clients must explicitly connect to the undo manager to express their interest
+ * in the undo history. Clients should disconnect from the undo manager when
+ * they are no longer interested in tracking the undo history. If there are no
+ * clients connected to the undo manager, it will not track the document's
+ * changes and will dispose of any history that was previously kept.</p>
+ * <p>
+ * Clients may also listen to the undo manager for notifications before and
+ * after undo or redo events are performed. Clients must connect to the undo
+ * manager in addition to registering listeners.</p>
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @see DocumentUndoManagerRegistry
+ * @see IDocumentUndoListener
+ * @see org.eclipse.jface.text.IDocument
+ * @since 3.2
+ */
+public interface IDocumentUndoManager {
+
+    /**
+     * Adds the specified listener to the list of document undo listeners that
+     * are notified before and after changes are undone or redone in the
+     * document. This method has no effect if the instance being added is
+     * already in the list.
+     * <p>
+     * Notifications will not be received if there are no clients connected to
+     * the receiver. Registering a document undo listener does not implicitly
+     * connect the listener to the receiver.</p>
+     * <p>
+     * Document undo listeners must be prepared to receive notifications from a
+     * background thread. Any UI access occurring inside the implementation must
+     * be properly synchronized using the techniques specified by the client's
+     * widget library.</p>
+     *
+     * @param listener the document undo listener to be added as a listener
+     */
+    void addDocumentUndoListener(IDocumentUndoListener listener);
+
+    /**
+     * Removes the specified listener from the list of document undo listeners.
+     * <p>
+     * Removing a listener which is not registered has no effect
+     * </p>
+     *
+     * @param listener the document undo listener to be removed
+     */
+    void removeDocumentUndoListener(IDocumentUndoListener listener);
+
+    /**
+     * Returns the undo context registered for this document
+     *
+     * @return the undo context registered for this document
+     */
+    IUndoContext getUndoContext();
+
+    /**
+     * Closes the currently open text edit and open a new one.
+     */
+    void commit();
+
+    /**
+     * Connects to the undo manager. Used to signify that a client is monitoring
+     * the history kept by the undo manager. This message has no effect if the
+     * client is already connected.
+     *
+     * @param client the object connecting to the undo manager
+     */
+    void connect(Object client);
+
+    /**
+     * Disconnects from the undo manager. Used to signify that a client is no
+     * longer monitoring the history kept by the undo manager. If all clients
+     * have disconnected from the undo manager, the undo history will be
+     * deleted.
+     *
+     * @param client the object disconnecting from the undo manager
+     */
+    void disconnect(Object client);
+
+    /**
+     * Signals the undo manager that all subsequent changes until
+     * <code>endCompoundChange</code> is called are to be undone in one piece.
+     */
+    void beginCompoundChange();
+
+    /**
+     * Signals the undo manager that the sequence of changes which started with
+     * <code>beginCompoundChange</code> has been finished. All subsequent
+     * changes are considered to be individually undo-able.
+     */
+    void endCompoundChange();
+
+    /**
+     * Sets the limit of the undo history to the specified value. The provided
+     * limit will supersede any previously set limit.
+     *
+     * @param undoLimit the length of this undo manager's history
+     */
+    void setMaximalUndoLevel(int undoLimit);
+
+    /**
+     * Resets the history of the undo manager. After that call,
+     * there aren't any undo-able or redo-able text changes.
+     */
+    void reset();
+
+    /**
+     * Returns whether at least one text change can be rolled back.
+     *
+     * @return <code>true</code> if at least one text change can be rolled back
+     */
+    bool undoable();
+
+    /**
+     * Returns whether at least one text change can be repeated. A text change
+     * can be repeated only if it was executed and rolled back.
+     *
+     * @return <code>true</code> if at least on text change can be repeated
+     */
+    bool redoable();
+
+    /**
+     * Rolls back the most recently executed text change.
+     *
+     * @throws ExecutionException if an exception occurred during undo
+     */
+    void undo() ;
+
+    /**
+     * Repeats the most recently rolled back text change.
+     *
+     * @throws ExecutionException if an exception occurred during redo
+     */
+    void redo() ;
+
+    /**
+     * Transfers the undo history from the specified document undo manager to
+     * this undo manager.  This message should only be used when it is known
+     * that the content of the document of the original undo manager when the
+     * last undo operation was recorded is the same as this undo manager's
+     * current document content, since the undo history is based on document
+     * indexes.  It is the responsibility of the caller
+     * to ensure that this call is used correctly.
+     *
+     * @param manager the document undo manger whose history is to be transferred to the receiver
+     */
+    public void transferUndoHistory(IDocumentUndoManager manager);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/undo/UndoMessages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 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 org.eclipse.text.undo.UndoMessages;
+
+import org.eclipse.text.undo.DocumentUndoManager; // packageimport
+import org.eclipse.text.undo.DocumentUndoManagerRegistry; // packageimport
+import org.eclipse.text.undo.DocumentUndoEvent; // packageimport
+import org.eclipse.text.undo.IDocumentUndoListener; // packageimport
+import org.eclipse.text.undo.IDocumentUndoManager; // packageimport
+
+
+import java.lang.all;
+
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+import java.text.MessageFormat;
+
+/**
+ * Helper class to get NLSed messages.
+ *
+ * @since 3.2
+ */
+final class UndoMessages {
+
+//     private static const String BUNDLE_NAME= "org.eclipse.text.undo.UndoMessages"; //$NON-NLS-1$
+
+    private static const ResourceBundle RESOURCE_BUNDLE;//= ResourceBundle.getBundle(BUNDLE_NAME);
+
+    static this() {
+        RESOURCE_BUNDLE = ResourceBundle.getBundle(
+            getImportData!("org.eclipse.text.undo.UndoMessages.properties"));
+    }
+
+    private this() {
+    }
+
+    public static String getString(String key) {
+        try {
+            return RESOURCE_BUNDLE.getString(key);
+        } catch (MissingResourceException e) {
+            return '!' ~ key ~ '!';
+        }
+    }
+
+    public static String getFormattedString(String key, Object[] args...) {
+        return MessageFormat.format(getString(key), args);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/text/undo/UndoMessages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,12 @@
+###############################################################################
+# Copyright (c) 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
+###############################################################################
+
+DocumentUndoManager.operationLabel= Typing
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.tools/Sleak/org/eclipse/swt/tools/internal/Sleak.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2000, 2002 IBM Corp.  All rights reserved.
+ * This file is made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Port to the D programming language
+ *   Frank Benoit <benoit@tionex.de>
+ */
+module org.eclipse.sleak.Sleak;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.DeviceData;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Region;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.MessageBox;
+
+import java.lang.all;
+import tango.text.convert.Format;
+
+public class Sleak {
+    Display display;
+    Shell shell;
+    List list;
+    Canvas canvas;
+    Button start, stop, check;
+    Text text;
+    Label label;
+
+    Object [] oldObjects;
+    Exception [] oldErrors;
+    Object [] objects;
+    Exception [] errors;
+
+public void open () {
+    display = Display.getCurrent ();
+    shell = new Shell (display);
+    shell.setText ("S-Leak");
+    list = new List (shell, SWT.BORDER | SWT.V_SCROLL);
+    list.addListener (SWT.Selection, new class() Listener  {
+        public void handleEvent (Event event) {
+            refreshObject ();
+        }
+    });
+    text = new Text (shell, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
+    canvas = new Canvas (shell, SWT.BORDER);
+    canvas.addListener (SWT.Paint, new class() Listener  {
+        public void handleEvent (Event event) {
+            paintCanvas (event);
+        }
+    });
+    check = new Button (shell, SWT.CHECK);
+    check.setText ("Stack");
+    check.addListener (SWT.Selection, new class() Listener  {
+        public void handleEvent (Event e) {
+            toggleStackTrace ();
+        }
+    });
+    start = new Button (shell, SWT.PUSH);
+    start.setText ("Snap");
+    start.addListener (SWT.Selection, new class() Listener  {
+        public void handleEvent (Event event) {
+            refreshAll ();
+        }
+    });
+    stop = new Button (shell, SWT.PUSH);
+    stop.setText ("Diff");
+    stop.addListener (SWT.Selection, new class() Listener  {
+        public void handleEvent (Event event) {
+            refreshDifference ();
+        }
+    });
+    label = new Label (shell, SWT.BORDER);
+    label.setText ("0 object(s)");
+    shell.addListener (SWT.Resize, new class() Listener  {
+        public void handleEvent (Event e) {
+            layout ();
+        }
+    });
+    check.setSelection (false);
+    text.setVisible (false);
+    Point size = shell.getSize ();
+    shell.setSize (size.x / 2, size.y / 2);
+    shell.open ();
+}
+
+void refreshLabel () {
+    int colors = 0, cursors = 0, fonts = 0, gcs = 0, images = 0, regions = 0;
+    for (int i=0; i<objects.length; i++) {
+        Object object = objects [i];
+        if (cast(Color)object ) colors++;
+        if (cast(Cursor)object ) cursors++;
+        if (cast(Font)object ) fonts++;
+        if (cast(GC)object ) gcs++;
+        if (cast(Image)object ) images++;
+        if (cast(Region)object ) regions++;
+    }
+    char[] string = "";
+    if (colors !is 0) string ~= Format( "{} Color(s)\n", colors );
+    if (cursors !is 0) string ~= Format( "{} Cursor(s)\n", cursors );
+    if (fonts !is 0) string ~= Format( "{} Font(s)\n", fonts );
+    if (gcs !is 0) string ~= Format( "{} GC(s)\n", gcs );
+    if (images !is 0) string ~= Format( "{} Image(s)\n", images );
+    /* Currently regions are not counted. */
+//  if (regions !is 0) string += regions + " Region(s)\n";
+    if (string.length !is 0) {
+        string = string.substring (0, string.length - 1);
+    }
+    label.setText (string);
+}
+
+void refreshDifference () {
+    DeviceData info = display.getDeviceData ();
+    if (!info.tracking) {
+        MessageBox dialog = new MessageBox (shell, SWT.ICON_WARNING | SWT.OK);
+        dialog.setText (shell.getText ());
+        dialog.setMessage ("Warning: Device is not tracking resource allocation");
+        dialog.open ();
+    }
+    Object [] newObjects = info.objects;
+    Exception [] newErrors = info.errors;
+    Object [] diffObjects = new Object [newObjects.length];
+    Exception [] diffErrors = new Exception [newErrors.length];
+    int count = 0;
+    for (int i=0; i<newObjects.length; i++) {
+        int index = 0;
+        while (index < oldObjects.length) {
+            if (newObjects [i] is oldObjects [index]) break;
+            index++;
+        }
+        if (index is oldObjects.length) {
+            diffObjects [count] = newObjects [i];
+            diffErrors [count] = newErrors [i];
+            count++;
+        }
+    }
+    objects = new Object [count];
+    errors = new Exception [count];
+    System.arraycopy (diffObjects, 0, objects, 0, count);
+    System.arraycopy (diffErrors, 0, errors, 0, count);
+    list.removeAll ();
+    text.setText ("");
+    canvas.redraw ();
+    for (int i=0; i<objects.length; i++) {
+        list.add (objectName (objects [i]));
+    }
+    refreshLabel ();
+    layout ();
+}
+
+char[] objectName (Object object) {
+    char[] string = object.toString ();
+    int index = string.lastIndexOf ('.');
+    if (index is -1) return string;
+    return string.substring (index + 1, string.length);
+}
+
+void toggleStackTrace () {
+    refreshObject ();
+    layout ();
+}
+
+void paintCanvas (Event event) {
+    canvas.setCursor (null);
+    int index = list.getSelectionIndex ();
+    if (index is -1) return;
+    GC gc = event.gc;
+    Object object = objects [index];
+    if (cast(Color)object ) {
+        if ((cast(Color)object).isDisposed ()) return;
+        gc.setBackground (cast(Color) object);
+        gc.fillRectangle (canvas.getClientArea());
+        return;
+    }
+    if (cast(Cursor)object ) {
+        if ((cast(Cursor)object).isDisposed ()) return;
+        canvas.setCursor (cast(Cursor) object);
+        return;
+    }
+    if (cast(Font)object ) {
+        if ((cast(Font)object).isDisposed ()) return;
+        gc.setFont (cast(Font) object);
+        FontData [] array = gc.getFont ().getFontData ();
+        char[] string = "";
+        char[] lf = text.getLineDelimiter ();
+        for (int i=0; i<array.length; i++) {
+            FontData data = array [i];
+            char[] style = "NORMAL";
+            int bits = data.getStyle ();
+            if (bits !is 0) {
+                if ((bits & SWT.BOLD) !is 0) style = "BOLD ";
+                if ((bits & SWT.ITALIC) !is 0) style ~= "ITALIC";
+            }
+            string ~= Format( "{} {} {}{}", data.getName (), data.getHeight (), style, lf );
+        }
+        gc.drawString (string, 0, 0);
+        return;
+    }
+    //NOTHING TO DRAW FOR GC
+//  if (object instanceof GC) {
+//      return;
+//  }
+    if (cast(Image)object ) {
+        if ((cast(Image)object).isDisposed ()) return;
+        gc.drawImage (cast(Image) object, 0, 0);
+        return;
+    }
+    if (cast(Region)object ) {
+        if ((cast(Region)object).isDisposed ()) return;
+        char[] string = (cast(Region)object).getBounds().toString();
+        gc.drawString (string, 0, 0);
+        return;
+    }
+}
+
+void refreshObject () {
+    int index = list.getSelectionIndex ();
+    if (index is -1) return;
+    if (check.getSelection ()) {
+        char[] txt = "Stacktrace info (if missing you need stacktrace support for your program):\n";
+        foreach( line; errors[index].info ){
+            txt ~= line ~ \n;
+        }
+        text.setText (txt);
+        text.setVisible (true);
+        canvas.setVisible (false);
+    } else {
+        canvas.setVisible (true);
+        text.setVisible (false);
+        canvas.redraw ();
+    }
+}
+
+void refreshAll () {
+    oldObjects = null;
+    oldErrors = null;
+    refreshDifference ();
+    oldObjects = objects;
+    oldErrors = errors;
+}
+
+void layout () {
+    Rectangle rect = shell.getClientArea ();
+    char[] [] strings = new char[] [](objects.length);
+    int width = 0;
+    char[] [] items = list.getItems ();
+    GC gc = new GC (list);
+    for (int i=0; i<objects.length; i++) {
+        width = Math.max (width, gc.stringExtent (items [i]).x);
+    }
+    gc.dispose ();
+    Point size1 = start.computeSize (SWT.DEFAULT, SWT.DEFAULT);
+    Point size2 = stop.computeSize (SWT.DEFAULT, SWT.DEFAULT);
+    Point size3 = check.computeSize (SWT.DEFAULT, SWT.DEFAULT);
+    Point size4 = label.computeSize (SWT.DEFAULT, SWT.DEFAULT);
+    width = Math.max (size1.x, Math.max (size2.x, Math.max (size3.x, width)));
+    width = Math.max (64, Math.max (size4.x, list.computeSize (width, SWT.DEFAULT).x));
+    start.setBounds (0, 0, width, size1.y);
+    stop.setBounds (0, size1.y, width, size2.y);
+    check.setBounds (0, size1.y + size2.y, width, size3.y);
+    label.setBounds (0, rect.height - size4.y, width, size4.y);
+    int height = size1.y + size2.y + size3.y;
+    list.setBounds (0, height, width, rect.height - height - size4.y);
+    text.setBounds (width, 0, rect.width - width, rect.height);
+    canvas.setBounds (width, 0, rect.width - width, rect.height);
+}
+
+public static void main (char[] [] args) {
+    Display display = new Display ();
+    Sleak sleak = new Sleak ();
+    sleak.open ();
+    while (!sleak.shell.isDisposed ()) {
+        if (!display.readAndDispatch ()) display.sleep ();
+    }
+    display.dispose ();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/AbstractFormPart.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.AbstractFormPart;
+
+import org.eclipse.ui.forms.IFormPart;
+import org.eclipse.ui.forms.IManagedForm;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * AbstractFormPart implements IFormPart interface and can be used as a
+ * convenient base class for concrete form parts. If a method contains
+ * code that must be called, look for instructions to call 'super'
+ * when overriding.
+ *
+ * @see org.eclipse.ui.forms.widgets.Section
+ * @since 3.0
+ */
+public abstract class AbstractFormPart : IFormPart {
+    private IManagedForm managedForm;
+    private bool dirty = false;
+    private bool stale = true;
+    /**
+     * @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm)
+     */
+    public void initialize(IManagedForm form) {
+        this.managedForm = form;
+    }
+    /**
+     * Returns the form that manages this part.
+     *
+     * @return the managed form
+     */
+    public IManagedForm getManagedForm() {
+        return managedForm;
+    }
+    /**
+     * Disposes the part. Subclasses should override to release any system
+     * resources.
+     */
+    public void dispose() {
+    }
+    /**
+     * Commits the part. Subclasses should call 'super' when overriding.
+     *
+     * @param onSave
+     *            <code>true</code> if the request to commit has arrived as a
+     *            result of the 'save' action.
+     */
+    public void commit(bool onSave) {
+        dirty = false;
+    }
+    /**
+     * Sets the overall form input. Subclases may elect to override the method
+     * and adjust according to the form input.
+     *
+     * @param input
+     *            the form input object
+     * @return <code>false</code>
+     */
+    public bool setFormInput(Object input) {
+        return false;
+    }
+    /**
+     * Instructs the part to grab keyboard focus.
+     */
+    public void setFocus() {
+    }
+    /**
+     * Refreshes the section after becoming stale (falling behind data in the
+     * model). Subclasses must call 'super' when overriding this method.
+     */
+    public void refresh() {
+        stale = false;
+        // since we have refreshed, any changes we had in the
+        // part are gone and we are not dirty
+        dirty = false;
+    }
+    /**
+     * Marks the part dirty. Subclasses should call this method as a result of
+     * user interaction with the widgets in the section.
+     */
+    public void markDirty() {
+        dirty = true;
+        managedForm.dirtyStateChanged();
+    }
+    /**
+     * Tests whether the part is dirty i.e. its widgets have state that is
+     * newer than the data in the model.
+     *
+     * @return <code>true</code> if the part is dirty, <code>false</code>
+     *         otherwise.
+     */
+    public bool isDirty() {
+        return dirty;
+    }
+    /**
+     * Tests whether the part is stale i.e. its widgets have state that is
+     * older than the data in the model.
+     *
+     * @return <code>true</code> if the part is stale, <code>false</code>
+     *         otherwise.
+     */
+    public bool isStale() {
+        return stale;
+    }
+    /**
+     * Marks the part stale. Subclasses should call this method as a result of
+     * model notification that indicates that the content of the section is no
+     * longer in sync with the model.
+     */
+    public void markStale() {
+        stale = true;
+        managedForm.staleStateChanged();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/DetailsPart.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,341 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.ui.forms.DetailsPart;
+
+import org.eclipse.ui.forms.IFormPart;
+import org.eclipse.ui.forms.IPartSelectionListener;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.IDetailsPageProvider;
+import org.eclipse.ui.forms.IDetailsPage;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.forms.widgets.ScrolledPageBook;
+
+import java.lang.all;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * This managed form part handles the 'details' portion of the
+ * 'master/details' block. It has a page book that manages pages
+ * of details registered for the current selection.
+ * <p>By default, details part accepts any number of pages.
+ * If dynamic page provider is registered, this number may
+ * be excessive. To avoid running out of steam (by creating
+ * a large number of pages with widgets on each), maximum
+ * number of pages can be set to some reasonable value (e.g. 10).
+ * When this number is reached, old pages (those created first)
+ * will be removed and disposed as new ones are added. If
+ * the disposed pages are needed again after that, they
+ * will be created again.
+ *
+ * @since 3.0
+ */
+public final class DetailsPart : IFormPart, IPartSelectionListener {
+    private IManagedForm managedForm;
+    private ScrolledPageBook pageBook;
+    private IFormPart masterPart;
+    private IStructuredSelection currentSelection;
+    private Hashtable pages;
+    private IDetailsPageProvider pageProvider;
+    private int pageLimit=Integer.MAX_VALUE;
+
+    private static class PageBag {
+        private static int counter;
+        private int ticket;
+        private IDetailsPage page;
+        private bool fixed;
+
+        public this(IDetailsPage page, bool fixed) {
+            this.page= page;
+            this.fixed = fixed;
+            this.ticket = ++counter;
+        }
+        public int getTicket() {
+            return ticket;
+        }
+        public IDetailsPage getPage() {
+            return page;
+        }
+        public void dispose() {
+            page.dispose();
+            page=null;
+        }
+        public bool isDisposed() {
+            return page is null;
+        }
+        public bool isFixed() {
+            return fixed;
+        }
+        public static int getCurrentTicket() {
+            return counter;
+        }
+    }
+/**
+ * Creates a details part by wrapping the provided page book.
+ * @param mform the parent form
+ * @param pageBook the page book to wrap
+ */
+    public this(IManagedForm mform, ScrolledPageBook pageBook) {
+        this.pageBook = pageBook;
+        pages = new Hashtable();
+        initialize(mform);
+    }
+/**
+ * Creates a new details part in the provided form by creating
+ * the page book.
+ * @param mform the parent form
+ * @param parent the composite to create the page book in
+ * @param style the style for the page book
+ */
+    public this(IManagedForm mform, Composite parent, int style) {
+        this(mform, mform.getToolkit().createPageBook(parent, style|SWT.V_SCROLL|SWT.H_SCROLL));
+    }
+/**
+ * Registers the details page to be used for all the objects of
+ * the provided object class.
+ * @param objectClass an object of type 'java.lang.Class' to be used
+ * as a key for the provided page
+ * @param page the page to show for objects of the provided object class
+ */
+    public void registerPage(Object objectClass, IDetailsPage page) {
+        registerPage(objectClass, page, true);
+    }
+
+    private void registerPage(Object objectClass, IDetailsPage page, bool fixed) {
+        pages.put(objectClass, new PageBag(page, fixed));
+        page.initialize(managedForm);
+    }
+/**
+ * Sets the dynamic page provider. The dynamic provider can return
+ * different pages for objects of the same class based on their state.
+ * @param provider the provider to use
+ */
+    public void setPageProvider(IDetailsPageProvider provider) {
+        this.pageProvider = provider;
+    }
+/**
+ * Commits the part by committing the current page.
+ * @param onSave <code>true</code> if commit is requested as a result
+ * of the 'save' action, <code>false</code> otherwise.
+ */
+    public void commit(bool onSave) {
+        IDetailsPage page = getCurrentPage();
+        if (page !is null)
+            page.commit(onSave);
+    }
+/**
+ * Returns the current page visible in the part.
+ * @return the current page
+ */
+    public IDetailsPage getCurrentPage() {
+        Control control = pageBook.getCurrentPage();
+        if (control !is null) {
+            Object data = control.getData();
+            if (null !is cast(IDetailsPage)data )
+                return cast(IDetailsPage) data;
+        }
+        return null;
+    }
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IFormPart#dispose()
+     */
+    public void dispose() {
+        for (Enumeration enm = pages.elements(); enm.hasMoreElements();) {
+            PageBag pageBag = cast(PageBag) enm.nextElement();
+            pageBag.dispose();
+        }
+    }
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm)
+     */
+    public void initialize(IManagedForm form) {
+        this.managedForm = form;
+    }
+/**
+ * Tests if the currently visible page is dirty.
+ * @return <code>true</code> if the page is dirty, <code>false</code> otherwise.
+ */
+    public bool isDirty() {
+        IDetailsPage page = getCurrentPage();
+        if (page !is null)
+            return page.isDirty();
+        return false;
+    }
+/**
+ * Tests if the currently visible page is stale and needs refreshing.
+ * @return <code>true</code> if the page is stale, <code>false</code> otherwise.
+ */
+    public bool isStale() {
+        IDetailsPage page = getCurrentPage();
+        if (page !is null)
+            return page.isStale();
+        return false;
+    }
+
+/**
+ * Refreshes the current page.
+ */
+    public void refresh() {
+        IDetailsPage page = getCurrentPage();
+        if (page !is null)
+            page.refresh();
+    }
+/**
+ * Sets the focus to the currently visible page.
+ */
+    public void setFocus() {
+        IDetailsPage page = getCurrentPage();
+        if (page !is null)
+            page.setFocus();
+    }
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IFormPart#setFormInput(java.lang.Object)
+     */
+    public bool setFormInput(Object input) {
+        return false;
+    }
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IPartSelectionListener#selectionChanged(org.eclipse.ui.forms.IFormPart,
+     *      org.eclipse.jface.viewers.ISelection)
+     */
+    public void selectionChanged(IFormPart part, ISelection selection) {
+        this.masterPart = part;
+        if (currentSelection !is null) {
+        }
+        if (null !is cast(IStructuredSelection)selection )
+            currentSelection = cast(IStructuredSelection) selection;
+        else
+            currentSelection = null;
+        update();
+    }
+    private void update() {
+        Object key = null;
+        if (currentSelection !is null) {
+            for (Iterator iter = currentSelection.iterator(); iter.hasNext();) {
+                Object obj = iter.next();
+                if (key is null)
+                    key = getKey(obj);
+                else if (getKey(obj).opEquals(key) is false) {
+                    key = null;
+                    break;
+                }
+            }
+        }
+        showPage(key);
+    }
+    private Object getKey(Object object) {
+        if (pageProvider !is null) {
+            Object key = pageProvider.getPageKey(object);
+            if (key !is null)
+                return key;
+        }
+        return object.classinfo;
+    }
+    private void showPage( Object key) {
+        checkLimit();
+        final IDetailsPage oldPage = getCurrentPage();
+        if (key !is null) {
+            PageBag pageBag = cast(PageBag)pages.get(key);
+            IDetailsPage page = pageBag !is null?pageBag.getPage():null;
+            if (page is null) {
+                // try to get the page dynamically from the provider
+                if (pageProvider !is null) {
+                    page = pageProvider.getPage(key);
+                    if (page !is null) {
+                        registerPage(key, page, false);
+                    }
+                }
+            }
+            if (page !is null) {
+                BusyIndicator.showWhile(pageBook.getDisplay(), dgRunnable( (IDetailsPage fpage,Object key,IDetailsPage oldPage) {
+                    if (!pageBook.hasPage(key)) {
+                        Composite parent = pageBook.createPage(key);
+                        fpage.createContents(parent);
+                        parent.setData(cast(Object)fpage);
+                    }
+                    //commit the current page
+                    if (oldPage !is null && oldPage.isDirty())
+                        oldPage.commit(false);
+                    //refresh the new page
+                    if (fpage.isStale())
+                        fpage.refresh();
+                    fpage.selectionChanged(masterPart, currentSelection);
+                    pageBook.showPage(key);
+                }, page, key, oldPage));
+                return;
+            }
+        }
+        // If we are switching from an old page to nothing,
+        // don't loose data
+        if (oldPage !is null && oldPage.isDirty())
+            oldPage.commit(false);
+        pageBook.showEmptyPage();
+    }
+    private void checkLimit() {
+        if (pages.size() <= getPageLimit()) return;
+        // overflow
+        int currentTicket = PageBag.getCurrentTicket();
+        int cutoffTicket = currentTicket - getPageLimit();
+        for (Enumeration enm=pages.keys(); enm.hasMoreElements();) {
+            Object key = enm.nextElement();
+            PageBag pageBag = cast(PageBag)pages.get(key);
+            if (pageBag.getTicket()<=cutoffTicket) {
+                // candidate - see if it is active and not fixed
+                if (!pageBag.isFixed() && !(cast(Object)pageBag.getPage()).opEquals(cast(Object)getCurrentPage())) {
+                    // drop it
+                    pageBag.dispose();
+                    pages.remove(key);
+                    pageBook.removePage(key, false);
+                }
+            }
+        }
+    }
+    /**
+     * Returns the maximum number of pages that should be
+     * maintained in this part. When an attempt is made to
+     * add more pages, old pages are removed and disposed
+     * based on the order of creation (the oldest pages
+     * are removed). The exception is made for the
+     * page that should otherwise be disposed but is
+     * currently active.
+     * @return maximum number of pages for this part
+     */
+    public int getPageLimit() {
+        return pageLimit;
+    }
+    /**
+     * Sets the page limit for this part.
+     * @see #getPageLimit()
+     * @param pageLimit the maximum number of pages that
+     * should be maintained in this part.
+     */
+    public void setPageLimit(int pageLimit) {
+        this.pageLimit = pageLimit;
+        checkLimit();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/FormColors.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,740 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.FormColors;
+
+import org.eclipse.ui.forms.IFormColors;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+
+import java.lang.all;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+/**
+ * Manages colors that will be applied to forms and form widgets. The colors are
+ * chosen to make the widgets look correct in the editor area. If a different
+ * set of colors is needed, subclass this class and override 'initialize' and/or
+ * 'initializeColors'.
+ *
+ * @since 3.0
+ */
+public class FormColors {
+    /**
+     * Key for the form title foreground color.
+     *
+     * @deprecated use <code>IFormColors.TITLE</code>.
+     */
+    public static const String TITLE = IFormColors.TITLE;
+
+    /**
+     * Key for the tree/table border color.
+     *
+     * @deprecated use <code>IFormColors.BORDER</code>
+     */
+    public static const String BORDER = IFormColors.BORDER;
+
+    /**
+     * Key for the section separator color.
+     *
+     * @deprecated use <code>IFormColors.SEPARATOR</code>.
+     */
+    public static const String SEPARATOR = IFormColors.SEPARATOR;
+
+    /**
+     * Key for the section title bar background.
+     *
+     * @deprecated use <code>IFormColors.TB_BG
+     */
+    public static const String TB_BG = IFormColors.TB_BG;
+
+    /**
+     * Key for the section title bar foreground.
+     *
+     * @deprecated use <code>IFormColors.TB_FG</code>
+     */
+    public static const String TB_FG = IFormColors.TB_FG;
+
+    /**
+     * Key for the section title bar gradient.
+     *
+     * @deprecated use <code>IFormColors.TB_GBG</code>
+     */
+    public static const String TB_GBG = IFormColors.TB_GBG;
+
+    /**
+     * Key for the section title bar border.
+     *
+     * @deprecated use <code>IFormColors.TB_BORDER</code>.
+     */
+    public static const String TB_BORDER = IFormColors.TB_BORDER;
+
+    /**
+     * Key for the section toggle color. Since 3.1, this color is used for all
+     * section styles.
+     *
+     * @deprecated use <code>IFormColors.TB_TOGGLE</code>.
+     */
+    public static const String TB_TOGGLE = IFormColors.TB_TOGGLE;
+
+    /**
+     * Key for the section toggle hover color.
+     *
+     * @since 3.1
+     * @deprecated use <code>IFormColors.TB_TOGGLE_HOVER</code>.
+     */
+    public static const String TB_TOGGLE_HOVER = IFormColors.TB_TOGGLE_HOVER;
+
+    protected Map colorRegistry;
+
+    protected Color background;
+
+    protected Color foreground;
+
+    private bool shared;
+
+    protected Display display;
+
+    protected Color border;
+
+    /**
+     * Creates form colors using the provided display.
+     *
+     * @param display
+     *            the display to use
+     */
+    public this(Display display) {
+        colorRegistry = new HashMap(10);
+        this.display = display;
+        initialize();
+    }
+
+    /**
+     * Returns the display used to create colors.
+     *
+     * @return the display
+     */
+    public Display getDisplay() {
+        return display;
+    }
+
+    /**
+     * Initializes the colors. Subclasses can override this method to change the
+     * way colors are created. Alternatively, only the color table can be
+     * modified by overriding <code>initializeColorTable()</code>.
+     *
+     * @see #initializeColorTable
+     */
+    protected void initialize() {
+        background = display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
+        foreground = display.getSystemColor(SWT.COLOR_LIST_FOREGROUND);
+        initializeColorTable();
+        updateBorderColor();
+    }
+
+    /**
+     * Allocates colors for the following keys: BORDER, SEPARATOR and
+     * TITLE. Subclasses can override to allocate these colors differently.
+     */
+    protected void initializeColorTable() {
+        createTitleColor();
+        createColor(IFormColors.SEPARATOR, getColor(IFormColors.TITLE).getRGB());
+        RGB black = getSystemColor(SWT.COLOR_BLACK);
+        RGB borderRGB = getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT);
+        createColor(IFormColors.BORDER, blend(borderRGB, black, 80));
+    }
+
+    /**
+     * Allocates colors for the section tool bar (all the keys that start with
+     * TB). Since these colors are only needed when TITLE_BAR style is used with
+     * the Section widget, they are not needed all the time and are allocated on
+     * demand. Consequently, this method will do nothing if the colors have been
+     * already initialized. Call this method prior to using colors with the TB
+     * keys to ensure they are available.
+     */
+    public void initializeSectionToolBarColors() {
+        if (colorRegistry.containsKey(IFormColors.TB_BG))
+            return;
+        createTitleBarGradientColors();
+        createTitleBarOutlineColors();
+        createTwistieColors();
+    }
+
+    /**
+     * Allocates additional colors for the form header, namely background
+     * gradients, bottom separator keylines and DND highlights. Since these
+     * colors are only needed for clients that want to use these particular
+     * style of header rendering, they are not needed all the time and are
+     * allocated on demand. Consequently, this method will do nothing if the
+     * colors have been already initialized. Call this method prior to using
+     * color keys with the H_ prefix to ensure they are available.
+     *
+     * @since 3.3
+     */
+    protected void initializeFormHeaderColors() {
+        if (colorRegistry.containsKey(IFormColors.H_BOTTOM_KEYLINE2))
+            return;
+        createFormHeaderColors();
+    }
+
+    /**
+     * Returns the RGB value of the system color represented by the code
+     * argument, as defined in <code>SWT</code> class.
+     *
+     * @param code
+     *            the system color constant as defined in <code>SWT</code>
+     *            class.
+     * @return the RGB value of the system color
+     */
+    public RGB getSystemColor(int code) {
+        return getDisplay().getSystemColor(code).getRGB();
+    }
+
+    /**
+     * Creates the color for the specified key using the provided RGB object.
+     * The color object will be returned and also put into the registry. When
+     * the class is disposed, the color will be disposed with it.
+     *
+     * @param key
+     *            the unique color key
+     * @param rgb
+     *            the RGB object
+     * @return the allocated color object
+     */
+    public Color createColor(String key, RGB rgb) {
+        return createColor(key, rgb.red, rgb.green, rgb.blue);
+    }
+
+    /**
+     * Creates a color that can be used for areas of the form that is inactive.
+     * These areas can contain images, links, controls and other content but are
+     * considered auxilliary to the main content area.
+     *
+     * <p>
+     * The color should not be disposed because it is managed by this class.
+     *
+     * @return the inactive form color
+     * @since 3.1
+     */
+    public Color getInactiveBackground() {
+        String key = "__ncbg__"; //$NON-NLS-1$
+        Color color = getColor(key);
+        if (color is null) {
+            RGB sel = getSystemColor(SWT.COLOR_LIST_SELECTION);
+            // a blend of 95% white and 5% list selection system color
+            RGB ncbg = blend(sel, getSystemColor(SWT.COLOR_WHITE), 5);
+            color = createColor(key, ncbg);
+        }
+        return color;
+    }
+
+    /**
+     * Creates the color for the specified key using the provided RGB values.
+     * The color object will be returned and also put into the registry. If
+     * there is already another color object under the same key in the registry,
+     * the existing object will be disposed. When the class is disposed, the
+     * color will be disposed with it.
+     *
+     * @param key
+     *            the unique color key
+     * @param r
+     *            red value
+     * @param g
+     *            green value
+     * @param b
+     *            blue value
+     * @return the allocated color object
+     */
+    public Color createColor(String key, int r, int g, int b) {
+        Color c = new Color(display, r, g, b);
+        Color prevC = cast(Color) colorRegistry.get(key);
+        if (prevC !is null)
+            prevC.dispose();
+        colorRegistry.put(key, c);
+        return c;
+    }
+
+    /**
+     * Computes the border color relative to the background. Allocated border
+     * color is designed to work well with white. Otherwise, stanard widget
+     * background color will be used.
+     */
+    protected void updateBorderColor() {
+        if (isWhiteBackground())
+            border = getColor(IFormColors.BORDER);
+        else {
+            border = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
+            Color bg = getImpliedBackground();
+            if (border.getRed() is bg.getRed()
+                    && border.getGreen() is bg.getGreen()
+                    && border.getBlue() is bg.getBlue())
+                border = display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW);
+        }
+    }
+
+    /**
+     * Sets the background color. All the toolkits that use this class will
+     * share the same background.
+     *
+     * @param bg
+     *            background color
+     */
+    public void setBackground(Color bg) {
+        this.background = bg;
+        updateBorderColor();
+        updateFormHeaderColors();
+    }
+
+    /**
+     * Sets the foreground color. All the toolkits that use this class will
+     * share the same foreground.
+     *
+     * @param fg
+     *            foreground color
+     */
+    public void setForeground(Color fg) {
+        this.foreground = fg;
+    }
+
+    /**
+     * Returns the current background color.
+     *
+     * @return the background color
+     */
+    public Color getBackground() {
+        return background;
+    }
+
+    /**
+     * Returns the current foreground color.
+     *
+     * @return the foreground color
+     */
+    public Color getForeground() {
+        return foreground;
+    }
+
+    /**
+     * Returns the computed border color. Border color depends on the background
+     * and is recomputed whenever the background changes.
+     *
+     * @return the current border color
+     */
+    public Color getBorderColor() {
+        return border;
+    }
+
+    /**
+     * Tests if the background is white. White background has RGB value
+     * 255,255,255.
+     *
+     * @return <samp>true</samp> if background is white, <samp>false</samp>
+     *         otherwise.
+     */
+    public bool isWhiteBackground() {
+        Color bg = getImpliedBackground();
+        return bg.getRed() is 255 && bg.getGreen() is 255
+                && bg.getBlue() is 255;
+    }
+
+    /**
+     * Returns the color object for the provided key or <samp>null </samp> if
+     * not in the registry.
+     *
+     * @param key
+     *            the color key
+     * @return color object if found, or <samp>null </samp> if not.
+     */
+    public Color getColor(String key) {
+        if (key.startsWith(IFormColors.TB_PREFIX))
+            initializeSectionToolBarColors();
+        else if (key.startsWith(IFormColors.H_PREFIX))
+            initializeFormHeaderColors();
+        return cast(Color) colorRegistry.get(key);
+    }
+
+    /**
+     * Disposes all the colors in the registry.
+     */
+    public void dispose() {
+        Iterator e = colorRegistry.values().iterator();
+        while (e.hasNext())
+            (cast(Color) e.next()).dispose();
+        colorRegistry = null;
+    }
+
+    /**
+     * Marks the colors shared. This prevents toolkits that share this object
+     * from disposing it.
+     */
+    public void markShared() {
+        this.shared = true;
+    }
+
+    /**
+     * Tests if the colors are shared.
+     *
+     * @return <code>true</code> if shared, <code>false</code> otherwise.
+     */
+    public bool isShared() {
+        return shared;
+    }
+
+    /**
+     * Blends c1 and c2 based in the provided ratio.
+     *
+     * @param c1
+     *            first color
+     * @param c2
+     *            second color
+     * @param ratio
+     *            percentage of the first color in the blend (0-100)
+     * @return the RGB value of the blended color
+     * @since 3.1
+     */
+    public static RGB blend(RGB c1, RGB c2, int ratio) {
+        int r = blend(c1.red, c2.red, ratio);
+        int g = blend(c1.green, c2.green, ratio);
+        int b = blend(c1.blue, c2.blue, ratio);
+        return new RGB(r, g, b);
+    }
+
+    /**
+     * Tests the source RGB for range.
+     *
+     * @param rgb
+     *            the tested RGB
+     * @param from
+     *            range start (excluding the value itself)
+     * @param to
+     *            range end (excluding the value itself)
+     * @return <code>true</code> if at least one of the primary colors in the
+     *         source RGB are within the provided range, <code>false</code>
+     *         otherwise.
+     * @since 3.1
+     */
+    public static bool testAnyPrimaryColor(RGB rgb, int from, int to) {
+        if (testPrimaryColor(rgb.red, from, to))
+            return true;
+        if (testPrimaryColor(rgb.green, from, to))
+            return true;
+        if (testPrimaryColor(rgb.blue, from, to))
+            return true;
+        return false;
+    }
+
+    /**
+     * Tests the source RGB for range.
+     *
+     * @param rgb
+     *            the tested RGB
+     * @param from
+     *            range start (excluding the value itself)
+     * @param to
+     *            tange end (excluding the value itself)
+     * @return <code>true</code> if at least two of the primary colors in the
+     *         source RGB are within the provided range, <code>false</code>
+     *         otherwise.
+     * @since 3.1
+     */
+    public static bool testTwoPrimaryColors(RGB rgb, int from, int to) {
+        int total = 0;
+        if (testPrimaryColor(rgb.red, from, to))
+            total++;
+        if (testPrimaryColor(rgb.green, from, to))
+            total++;
+        if (testPrimaryColor(rgb.blue, from, to))
+            total++;
+        return total >= 2;
+    }
+
+    /**
+     * Blends two primary color components based on the provided ratio.
+     *
+     * @param v1
+     *            first component
+     * @param v2
+     *            second component
+     * @param ratio
+     *            percentage of the first component in the blend
+     * @return
+     */
+    private static int blend(int v1, int v2, int ratio) {
+        int b = (ratio * v1 + (100 - ratio) * v2) / 100;
+        return Math.min(255, b);
+    }
+
+    private Color getImpliedBackground() {
+        if (getBackground() !is null)
+            return getBackground();
+        return getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
+    }
+
+    private static bool testPrimaryColor(int value, int from, int to) {
+        return value > from && value < to;
+    }
+
+    private void createTitleColor() {
+        /*
+         * RGB rgb = getSystemColor(SWT.COLOR_LIST_SELECTION); // test too light
+         * if (testTwoPrimaryColors(rgb, 120, 151)) rgb = blend(rgb, BLACK, 80);
+         * else if (testTwoPrimaryColors(rgb, 150, 256)) rgb = blend(rgb, BLACK,
+         * 50); createColor(TITLE, rgb);
+         */
+        RGB bg = getImpliedBackground().getRGB();
+        RGB listSelection = getSystemColor(SWT.COLOR_LIST_SELECTION);
+        RGB listForeground = getSystemColor(SWT.COLOR_LIST_FOREGROUND);
+        RGB rgb = listSelection;
+
+        // Group 1
+        // Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or
+        // between 0 and 120, then use 100% LIST_SELECTION as it is (no
+        // additions)
+        // Examples: XP Default, Win Classic Standard, Win High Con White, Win
+        // Classic Marine
+        if (testTwoPrimaryColors(listSelection, -1, 121))
+            rgb = listSelection;
+        // Group 2
+        // When LIST_BACKGROUND = white (255, 255, 255) or not black, text
+        // colour = LIST_SELECTION @ 100% Opacity + 50% LIST_FOREGROUND over
+        // LIST_BACKGROUND
+        // Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or
+        // between 121 and 255, then add 50% LIST_FOREGROUND to LIST_SELECTION
+        // foreground colour
+        // Examples: Win Vista, XP Silver, XP Olive , Win Classic Plum, OSX
+        // Aqua, OSX Graphite, Linux GTK
+        else if (testTwoPrimaryColors(listSelection, 120, 256)
+                || (bg.red is 0 && bg.green is 0 && bg.blue is 0))
+            rgb = blend(listSelection, listForeground, 50);
+        // Group 3
+        // When LIST_BACKGROUND = black (0, 0, 0), text colour = LIST_SELECTION
+        // @ 100% Opacity + 50% LIST_FOREGROUND over LIST_BACKGROUND
+        // Rule: If LIST_BACKGROUND = 0, 0, 0, then add 50% LIST_FOREGROUND to
+        // LIST_SELECTION foreground colour
+        // Examples: Win High Con Black, Win High Con #1, Win High Con #2
+        // (covered in the second part of the OR clause above)
+        createColor(IFormColors.TITLE, rgb);
+    }
+
+    private void createTwistieColors() {
+        RGB rgb = getColor(IFormColors.TITLE).getRGB();
+        RGB white = getSystemColor(SWT.COLOR_WHITE);
+        createColor(TB_TOGGLE, rgb);
+        rgb = blend(rgb, white, 60);
+        createColor(TB_TOGGLE_HOVER, rgb);
+    }
+
+    private void createTitleBarGradientColors() {
+        RGB tbBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND);
+        RGB bg = getImpliedBackground().getRGB();
+
+        // Group 1
+        // Rule: If at least 2 of the RGB values are equal to or between 180 and
+        // 255, then apply specified opacity for Group 1
+        // Examples: Vista, XP Silver, Wn High Con #2
+        // Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND
+        // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
+        if (testTwoPrimaryColors(tbBg, 179, 256))
+            tbBg = blend(tbBg, bg, 30);
+
+        // Group 2
+        // Rule: If at least 2 of the RGB values are equal to or between 121 and
+        // 179, then apply specified opacity for Group 2
+        // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black
+        // Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND
+        // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
+        else if (testTwoPrimaryColors(tbBg, 120, 180))
+            tbBg = blend(tbBg, bg, 20);
+
+        // Group 3
+        // Rule: Everything else
+        // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX
+        // Aqua, Wn High Con White, Wn High Con #1
+        // Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND
+        // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
+        else {
+            tbBg = blend(tbBg, bg, 10);
+        }
+
+        createColor(IFormColors.TB_BG, tbBg);
+
+        // for backward compatibility
+        createColor(TB_GBG, tbBg);
+    }
+
+    private void createTitleBarOutlineColors() {
+        // title bar outline - border color
+        RGB tbBorder = getSystemColor(SWT.COLOR_TITLE_BACKGROUND);
+        RGB bg = getImpliedBackground().getRGB();
+        // Group 1
+        // Rule: If at least 2 of the RGB values are equal to or between 180 and
+        // 255, then apply specified opacity for Group 1
+        // Examples: Vista, XP Silver, Wn High Con #2
+        // Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND
+        if (testTwoPrimaryColors(tbBorder, 179, 256))
+            tbBorder = blend(tbBorder, bg, 70);
+
+        // Group 2
+        // Rule: If at least 2 of the RGB values are equal to or between 121 and
+        // 179, then apply specified opacity for Group 2
+        // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black
+
+        // Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND
+        else if (testTwoPrimaryColors(tbBorder, 120, 180))
+            tbBorder = blend(tbBorder, bg, 50);
+
+        // Group 3
+        // Rule: Everything else
+        // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX
+        // Aqua, Wn High Con White, Wn High Con #1
+
+        // Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND
+        else {
+            tbBorder = blend(tbBorder, bg, 30);
+        }
+        createColor(FormColors.TB_BORDER, tbBorder);
+    }
+
+    private void updateFormHeaderColors() {
+        if (colorRegistry.containsKey(IFormColors.H_GRADIENT_END)) {
+            disposeIfFound(IFormColors.H_GRADIENT_END);
+            disposeIfFound(IFormColors.H_GRADIENT_START);
+            disposeIfFound(IFormColors.H_BOTTOM_KEYLINE1);
+            disposeIfFound(IFormColors.H_BOTTOM_KEYLINE2);
+            disposeIfFound(IFormColors.H_HOVER_LIGHT);
+            disposeIfFound(IFormColors.H_HOVER_FULL);
+            initializeFormHeaderColors();
+        }
+    }
+
+    private void disposeIfFound(String key) {
+        Color color = getColor(key);
+        if (color !is null) {
+            colorRegistry.remove(key);
+            color.dispose();
+        }
+    }
+
+    private void createFormHeaderColors() {
+        createFormHeaderGradientColors();
+        createFormHeaderKeylineColors();
+        createFormHeaderDNDColors();
+    }
+
+    private void createFormHeaderGradientColors() {
+        RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND);
+        Color bgColor = getImpliedBackground();
+        RGB bg = bgColor.getRGB();
+        RGB bottom, top;
+        // Group 1
+        // Rule: If at least 2 of the RGB values are equal to or between 180 and
+        // 255, then apply specified opacity for Group 1
+        // Examples: Vista, XP Silver, Wn High Con #2
+        // Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND
+        // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
+        if (testTwoPrimaryColors(titleBg, 179, 256)) {
+            bottom = blend(titleBg, bg, 30);
+            top = bg;
+        }
+
+        // Group 2
+        // Rule: If at least 2 of the RGB values are equal to or between 121 and
+        // 179, then apply specified opacity for Group 2
+        // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black
+        // Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND
+        // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
+        else if (testTwoPrimaryColors(titleBg, 120, 180)) {
+            bottom = blend(titleBg, bg, 20);
+            top = bg;
+        }
+
+        // Group 3
+        // Rule: If at least 2 of the RGB values are equal to or between 0 and
+        // 120, then apply specified opacity for Group 3
+        // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX
+        // Aqua, Wn High Con White, Wn High Con #1
+        // Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND
+        // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
+        else {
+            bottom = blend(titleBg, bg, 10);
+            top = bg;
+        }
+        createColor(IFormColors.H_GRADIENT_END, top);
+        createColor(IFormColors.H_GRADIENT_START, bottom);
+    }
+
+    private void createFormHeaderKeylineColors() {
+        RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND);
+        Color bgColor = getImpliedBackground();
+        RGB bg = bgColor.getRGB();
+        RGB keyline2;
+        // H_BOTTOM_KEYLINE1
+        createColor(IFormColors.H_BOTTOM_KEYLINE1, new RGB(255, 255, 255));
+
+        // H_BOTTOM_KEYLINE2
+        // Group 1
+        // Rule: If at least 2 of the RGB values are equal to or between 180 and
+        // 255, then apply specified opacity for Group 1
+        // Examples: Vista, XP Silver, Wn High Con #2
+        // Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND
+        if (testTwoPrimaryColors(titleBg, 179, 256))
+            keyline2 = blend(titleBg, bg, 70);
+
+        // Group 2
+        // Rule: If at least 2 of the RGB values are equal to or between 121 and
+        // 179, then apply specified opacity for Group 2
+        // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black
+        // Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND
+        else if (testTwoPrimaryColors(titleBg, 120, 180))
+            keyline2 = blend(titleBg, bg, 50);
+
+        // Group 3
+        // Rule: If at least 2 of the RGB values are equal to or between 0 and
+        // 120, then apply specified opacity for Group 3
+        // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX
+        // Aqua, Wn High Con White, Wn High Con #1
+
+        // Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND
+        else
+            keyline2 = blend(titleBg, bg, 30);
+        // H_BOTTOM_KEYLINE2
+        createColor(IFormColors.H_BOTTOM_KEYLINE2, keyline2);
+    }
+
+    private void createFormHeaderDNDColors() {
+        RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND_GRADIENT);
+        Color bgColor = getImpliedBackground();
+        RGB bg = bgColor.getRGB();
+        RGB light, full;
+        // ALL Themes
+        //
+        // Light Highlight
+        // When *near* the 'hot' area
+        // Rule: If near the title in the 'hot' area, show background highlight
+        // TITLE_BACKGROUND_GRADIENT @ 40%
+        light = blend(titleBg, bg, 40);
+        // Full Highlight
+        // When *on* the title area (regions 1 and 2)
+        // Rule: If near the title in the 'hot' area, show background highlight
+        // TITLE_BACKGROUND_GRADIENT @ 60%
+        full = blend(titleBg, bg, 60);
+        // H_DND_LIGHT
+        // H_DND_FULL
+        createColor(IFormColors.H_HOVER_LIGHT, light);
+        createColor(IFormColors.H_HOVER_FULL, full);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/FormDialog.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.ui.forms.FormDialog;
+
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.ManagedForm;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.jface.dialogs.TrayDialog;
+import org.eclipse.jface.window.IShellProvider;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.internal.forms.Messages;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A general-purpose dialog that hosts a form. Clients should extend the class
+ * and override <code>createFormContent(IManagedForm)</code> protected method.
+ * <p>
+ * Since forms with wrapped text typically don't have a preferred size, it is
+ * important to set the initial dialog size upon creation:
+ * <p>
+ *
+ * <pre>
+ * MyFormDialog dialog = new MyFormDialog(shell);
+ * dialog.create();
+ * dialog.getShell().setSize(500, 500);
+ * dialog.open();
+ * </pre>
+ *
+ * <p>
+ * Otherwise, the dialog may open very wide.
+ * <p>
+ *
+ * @since 3.3
+ */
+
+public class FormDialog : TrayDialog {
+    private FormToolkit toolkit;
+
+    /**
+     * Creates a new form dialog for a provided parent shell.
+     *
+     * @param shell
+     *            the parent shell
+     */
+    public this(Shell shell) {
+        super(shell);
+        setShellStyle(getShellStyle() | SWT.RESIZE);
+    }
+
+    /**
+     * Creates a new form dialog for a provided parent shell provider.
+     *
+     * @param parentShellProvider
+     *            the parent shell provider
+     */
+    public this(IShellProvider parentShellProvider) {
+        super(parentShellProvider);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.TrayDialog#close()
+     */
+    public bool close() {
+        bool rcode = super.close();
+        toolkit.dispose();
+        return rcode;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+     */
+    protected Control createDialogArea(Composite parent) {
+        toolkit = new FormToolkit(parent.getDisplay());
+        ScrolledForm sform = toolkit.createScrolledForm(parent);
+        sform.setLayoutData(new GridData(GridData.FILL_BOTH));
+        ManagedForm mform = new ManagedForm(toolkit, sform);
+        createFormContent(mform);
+        applyDialogFont(sform.getBody());
+        return sform;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.TrayDialog#createButtonBar(org.eclipse.swt.widgets.Composite)
+     */
+    protected Control createButtonBar(Composite parent) {
+        GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+        //Composite sep = new Composite(parent, SWT.NULL);
+        //sep.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
+        //gd.heightHint = 1;
+        Label sep = new Label(parent, SWT.HORIZONTAL|SWT.SEPARATOR);
+        sep.setLayoutData(gd);
+        Control bar = super.createButtonBar(parent);
+        return bar;
+    }
+
+    /**
+     * Configures the dialog form and creates form content. Clients should
+     * override this method.
+     *
+     * @param mform
+     *            the dialog form
+     */
+    protected void createFormContent(IManagedForm mform) {
+        mform.getForm().setText(Messages.FormDialog_defaultTitle);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/HyperlinkGroup.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,250 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.HyperlinkGroup;
+
+import org.eclipse.ui.forms.HyperlinkSettings;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.events.IHyperlinkListener;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * Manages a group of hyperlinks. It tracks activation, updates normal and
+ * active colors and updates underline state depending on the underline
+ * preference. Hyperlink labels are added to the group after creation and are
+ * automatically removed from the group when they are disposed.
+ *
+ * @since 3.0
+ */
+
+public final class HyperlinkGroup : HyperlinkSettings {
+    private ArrayList links;
+    private Hyperlink lastActivated;
+    private Hyperlink lastEntered;
+    private GroupListener listener;
+    private bool isActiveBackgroundSet;
+    private bool isActiveForegroundSet;
+    private bool isBackgroundSet;
+    private bool isForegroundSet;
+
+    private class GroupListener : Listener, IHyperlinkListener {
+
+        private Color previousBackground;
+        private Color previousForeground;
+
+        public void handleEvent(Event e) {
+            switch (e.type) {
+                case SWT.MouseEnter :
+                    onMouseEnter(e);
+                    break;
+                case SWT.MouseExit :
+                    onMouseExit(e);
+                    break;
+                case SWT.MouseDown :
+                    onMouseDown(e);
+                    break;
+                case SWT.Dispose :
+                    unhook(cast(Hyperlink) e.widget);
+                    break;
+                default:
+            }
+        }
+        private void onMouseEnter(Event e) {
+            Hyperlink link = cast(Hyperlink) e.widget;
+            previousBackground = link.getBackground();
+            previousForeground = link.getForeground();
+            if (isActiveBackgroundSet)
+                link.setBackground(getActiveBackground());
+            if (isActiveForegroundSet)
+                link.setForeground(getActiveForeground());
+            if (getHyperlinkUnderlineMode() is UNDERLINE_HOVER)
+                link.setUnderlined(true);
+            link.setCursor(getHyperlinkCursor());
+        }
+        private void onMouseExit(Event e) {
+            Hyperlink link = cast(Hyperlink) e.widget;
+            if (isActiveBackgroundSet)
+                link.setBackground(previousBackground);
+            if (isActiveForegroundSet)
+                link.setForeground(previousForeground);
+            if (getHyperlinkUnderlineMode() is UNDERLINE_HOVER)
+                link.setUnderlined(false);
+        }
+        public void linkActivated(HyperlinkEvent e) {
+        }
+
+        public void linkEntered(HyperlinkEvent e) {
+            Hyperlink link = cast(Hyperlink) e.widget;
+            if (lastEntered !is null) {
+                linkExited(lastEntered);
+            }
+            lastEntered = link;
+        }
+
+        public void linkExited(HyperlinkEvent e) {
+            linkExited(cast(Hyperlink) e.widget);
+        }
+        private void linkExited(Hyperlink link) {
+            link.setCursor(null);
+            if (lastEntered is link)
+                lastEntered = null;
+        }
+    }
+
+    /**
+     * Creates a hyperlink group.
+     */
+
+    public this(Display display) {
+        links = new ArrayList();
+        super(display);
+        listener = new GroupListener();
+    }
+
+    /**
+     * Returns the link that has been active the last, or <code>null</code>
+     * if no link has been active yet or the last active link has been
+     * disposed.
+     *
+     * @return the last active link or <code>null</code>
+     */
+    public Hyperlink getLastActivated() {
+        return lastActivated;
+    }
+    /**
+     * Adds a hyperlink to the group to be jointly managed. Hyperlink will be
+     * managed until it is disposed. Settings like colors, cursors and modes
+     * will affect all managed hyperlinks.
+     *
+     * @param link
+     */
+
+    public void add(Hyperlink link) {
+        if (isBackgroundSet)
+            link.setBackground(getBackground());
+        if (isForegroundSet)
+            link.setForeground(getForeground());
+        if (getHyperlinkUnderlineMode() is UNDERLINE_ALWAYS)
+            link.setUnderlined(true);
+        hook(link);
+    }
+
+    /**
+     * Sets the new active hyperlink background for all the links.
+     *
+     * @param newActiveBackground
+     *            the new active background
+     */
+    public void setActiveBackground(Color newActiveBackground) {
+        super.setActiveBackground(newActiveBackground);
+        isActiveBackgroundSet = true;
+    }
+
+    /**
+     * Sets the new active hyperlink foreground for all the links.
+     *
+     * @param newActiveForeground
+     *            the new active foreground
+     */
+    public void setActiveForeground(Color newActiveForeground) {
+        super.setActiveForeground(newActiveForeground);
+        isActiveForegroundSet = true;
+    }
+
+    /**
+     * Sets the group background and also sets the background of all the
+     * currently managed links.
+     *
+     * @param bg
+     *            the new background
+     */
+    public void setBackground(Color bg) {
+        super.setBackground(bg);
+        isBackgroundSet = true;
+        if (links !is null) {
+            for (int i = 0; i < links.size(); i++) {
+                Hyperlink label = cast(Hyperlink) links.get(i);
+                label.setBackground(bg);
+            }
+        }
+    }
+    /**
+     * Sets the group foreground and also sets the background of all the
+     * currently managed links.
+     *
+     * @param fg
+     *            the new foreground
+     */
+    public void setForeground(Color fg) {
+        super.setForeground(fg);
+        isForegroundSet = true;
+        if (links !is null) {
+            for (int i = 0; i < links.size(); i++) {
+                Hyperlink label = cast(Hyperlink) links.get(i);
+                label.setForeground(fg);
+            }
+        }
+    }
+    /**
+     * Sets the hyperlink underline mode.
+     *
+     * @param mode
+     *            the new hyperlink underline mode
+     * @see HyperlinkSettings
+     */
+    public void setHyperlinkUnderlineMode(int mode) {
+        super.setHyperlinkUnderlineMode(mode);
+        if (links !is null) {
+            for (int i = 0; i < links.size(); i++) {
+                Hyperlink label = cast(Hyperlink) links.get(i);
+                label.setUnderlined(mode is UNDERLINE_ALWAYS);
+            }
+        }
+    }
+
+    private void hook(Hyperlink link) {
+        link.addListener(SWT.MouseDown, listener);
+        link.addHyperlinkListener(listener);
+        link.addListener(SWT.Dispose, listener);
+        link.addListener(SWT.MouseEnter, listener);
+        link.addListener(SWT.MouseExit, listener);
+        links.add(link);
+    }
+
+    private void unhook(Hyperlink link) {
+        link.removeListener(SWT.MouseDown, listener);
+        link.removeHyperlinkListener(listener);
+        link.removeListener(SWT.MouseEnter, listener);
+        link.removeListener(SWT.MouseExit, listener);
+        if (lastActivated is link)
+            lastActivated = null;
+        if (lastEntered is link)
+            lastEntered = null;
+        links.remove(link);
+    }
+
+    private void onMouseDown(Event e) {
+        if (e.button is 1)
+            return;
+        lastActivated = cast(Hyperlink) e.widget;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/HyperlinkSettings.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,191 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.HyperlinkSettings;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.jface.resource.JFaceColors;
+import org.eclipse.ui.internal.forms.widgets.FormsResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Manages color and underline mode settings for a group of hyperlinks. The
+ * class is extended by HyperlinkGroup but is otherwise not intended to be
+ * subclassed.
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class HyperlinkSettings {
+    /**
+     * Underline mode to be used when hyperlinks should not be underlined.
+     */
+    public static const int UNDERLINE_NEVER = 1;
+    /**
+     * Underline mode to be used when hyperlinks should only be underlined on
+     * mouse hover.
+     */
+    public static const int UNDERLINE_HOVER = 2;
+    /**
+     * Underline mode to be used when hyperlinks should always be underlined.
+     */
+    public static const int UNDERLINE_ALWAYS = 3;
+    private int hyperlinkUnderlineMode = UNDERLINE_ALWAYS;
+    private Color background;
+    private Color foreground;
+    private Color activeBackground;
+    private Color activeForeground;
+    /**
+     * The constructor.
+     *
+     * @param display
+     *            the display to use when creating colors.
+     */
+    public this(Display display) {
+        initializeDefaultForegrounds(display);
+    }
+    /**
+     * Initializes the hyperlink foregrounds from the JFace defaults set for the
+     * entire workbench.
+     *
+     * @see JFaceColors
+     * @param display
+     *            the display to use when creating colors
+     */
+    public void initializeDefaultForegrounds(Display display) {
+        Color fg = JFaceColors.getHyperlinkText(display);
+        Color afg = JFaceColors.getActiveHyperlinkText(display);
+        if (fg is null)
+            fg = display.getSystemColor(SWT.COLOR_BLUE);
+        setForeground(fg);
+        setActiveForeground(afg);
+    }
+    /**
+     * Returns the background to use for the active hyperlink.
+     *
+     * @return active hyperlink background
+     */
+    public Color getActiveBackground() {
+        return activeBackground;
+    }
+    /**
+     * Returns the foreground to use for the active hyperlink.
+     *
+     * @return active hyperlink foreground
+     */
+    public Color getActiveForeground() {
+        return activeForeground;
+    }
+    /**
+     * Returns the background to use for the normal hyperlink.
+     *
+     * @return normal hyperlink background
+     */
+    public Color getBackground() {
+        return background;
+    }
+    /**
+     * Returns the cursor to use when the hyperlink is active. This cursor will
+     * be shown before hyperlink listeners have been notified of hyperlink
+     * activation and hidden when the notification method returns.
+     *
+     * @return the busy cursor
+     */
+    public Cursor getBusyCursor() {
+        return FormsResources.getBusyCursor();
+    }
+    /**
+     * Returns the cursor to use when over text.
+     *
+     * @return the text cursor
+     */
+    public Cursor getTextCursor() {
+        return FormsResources.getTextCursor();
+    }
+    /**
+     * Returns the foreground to use for the normal hyperlink.
+     *
+     * @return the normal hyperlink foreground
+     */
+    public Color getForeground() {
+        return foreground;
+    }
+    /**
+     * Returns the cursor to use when hovering over the hyperlink.
+     *
+     * @return the hyperlink cursor
+     */
+    public Cursor getHyperlinkCursor() {
+        return FormsResources.getHandCursor();
+    }
+    /**
+     * Returns the underline mode to be used for all the hyperlinks in this
+     * group.
+     *
+     * @return one of UNDERLINE_NEVER, UNDERLINE_ALWAYS, UNDERLINE_HOVER
+     */
+    public int getHyperlinkUnderlineMode() {
+        return hyperlinkUnderlineMode;
+    }
+    /**
+     * Sets the new active hyperlink background for all the links.
+     *
+     * @param newActiveBackground
+     *            the new active background
+     */
+    public void setActiveBackground(Color newActiveBackground) {
+        activeBackground = newActiveBackground;
+    }
+    /**
+     * Sets the new active hyperlink foreground for all the links.
+     *
+     * @param newActiveForeground
+     *            the new active foreground
+     */
+    public void setActiveForeground(Color newActiveForeground) {
+        activeForeground = newActiveForeground;
+    }
+    /**
+     * Sets the new hyperlink background for all the links.
+     *
+     * @param newBackground
+     *            the new hyperlink background
+     */
+    public void setBackground(Color newBackground) {
+        background = newBackground;
+    }
+    /**
+     * Sets the new hyperlink foreground for all the links.
+     *
+     * @param newForeground
+     *            the new hyperlink foreground
+     */
+    public void setForeground(Color newForeground) {
+        foreground = newForeground;
+    }
+    /**
+     * Sets the new hyperlink underline mode for all the links in this group.
+     *
+     * @param mode
+     *            one of <code>UNDERLINE_NEVER</code>,
+     *            <code>UNDERLINE_HOVER</code> and
+     *            <code>UNDERLINE_ALWAYS</code>.
+     */
+    public void setHyperlinkUnderlineMode(int mode) {
+        hyperlinkUnderlineMode = mode;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/IDetailsPage.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.ui.forms.IDetailsPage;
+
+import java.lang.all;
+
+import org.eclipse.ui.forms.IFormPart;
+import org.eclipse.ui.forms.IPartSelectionListener;
+
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * This interface should be implemented by clients providing
+ * pages to handle object types in DetailsPart. Most of the
+ * life cycle is the same as for the IFormPart. The page is
+ * a part selection listener since selections in the master
+ * part will be passed to the currently visible page.
+ *
+ * @see DetailsPart
+ * @see MasterDetailsBlock
+ * @since 3.0
+ */
+public interface IDetailsPage : IFormPart, IPartSelectionListener {
+    /**
+     * Creates the contents of the page in the provided parent.
+     * @param parent the parent to create the page in
+     */
+    void createContents(Composite parent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/IDetailsPageProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.ui.forms.IDetailsPageProvider;
+
+import java.lang.all;
+
+import org.eclipse.ui.forms.IDetailsPage;
+
+/**
+ * The class that implements this interface provides for dynamic
+ * computation of page key and the page itself based on the
+ * input object. It should be used in situations where
+ * using the object class as a static key is not enough
+ * i.e. different pages may need to be loaded for objects
+ * of the same type depending on their state.
+ *
+ * @see DetailsPart
+ * @see MasterDetailsBlock
+ * @since 3.0
+ */
+public interface IDetailsPageProvider {
+/**
+ * Returns the page key for the provided object. The assumption is
+ * that the provider knows about various object types and
+ * is in position to cast the object into a type and call methods
+ * on it to determine the matching page key.
+ * @param object the input object
+ * @return the page key for the provided object
+ */
+    Object getPageKey(Object object);
+/**
+ * Returns the page for the provided key. This method is the dynamic
+ * alternative to registering pages with the details part directly.
+ * @param key the page key
+ * @return the matching page for the provided key
+ */
+    IDetailsPage getPage(Object key);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/IFormColors.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.IFormColors;
+
+import java.lang.all;
+
+/**
+ * A place to hold all the color constants used in the forms package.
+ *
+ * @since 3.3
+ */
+
+public interface IFormColors {
+    /**
+     * A prefix for all the keys.
+     */
+    static const String PREFIX = "org.eclipse.ui.forms."; //$NON-NLS-1$
+    /**
+     * Key for the form title foreground color.
+     */
+    static const String TITLE = PREFIX ~ "TITLE"; //$NON-NLS-1$
+
+    /**
+     * A prefix for the header color constants.
+     */
+    static const String H_PREFIX = PREFIX ~ "H_"; //$NON-NLS-1$
+    /*
+     * A prefix for the section title bar color constants.
+     */
+    static const String TB_PREFIX = PREFIX ~ "TB_"; //$NON-NLS-1$
+    /**
+     * Key for the form header background gradient ending color.
+     */
+    static const String H_GRADIENT_END = H_PREFIX ~ "GRADIENT_END"; //$NON-NLS-1$
+
+    /**
+     * Key for the form header background gradient starting color.
+     *
+     */
+    static const String H_GRADIENT_START = H_PREFIX ~ "GRADIENT_START"; //$NON-NLS-1$
+    /**
+     * Key for the form header bottom keyline 1 color.
+     *
+     */
+    static const String H_BOTTOM_KEYLINE1 = H_PREFIX ~ "BOTTOM_KEYLINE1"; //$NON-NLS-1$
+    /**
+     * Key for the form header bottom keyline 2 color.
+     *
+     */
+    static const String H_BOTTOM_KEYLINE2 = H_PREFIX ~ "BOTTOM_KEYLINE2"; //$NON-NLS-1$
+    /**
+     * Key for the form header light hover color.
+     *
+     */
+    static const String H_HOVER_LIGHT = H_PREFIX ~ "H_HOVER_LIGHT"; //$NON-NLS-1$
+    /**
+     * Key for the form header full hover color.
+     *
+     */
+    static const String H_HOVER_FULL = H_PREFIX ~ "H_HOVER_FULL"; //$NON-NLS-1$
+
+    /**
+     * Key for the tree/table border color.
+     */
+    static const String BORDER = PREFIX ~ "BORDER"; //$NON-NLS-1$
+
+    /**
+     * Key for the section separator color.
+     */
+    static const String SEPARATOR = PREFIX ~ "SEPARATOR"; //$NON-NLS-1$
+
+    /**
+     * Key for the section title bar background.
+     */
+    static const String TB_BG = TB_PREFIX ~ "BG"; //$NON-NLS-1$
+
+    /**
+     * Key for the section title bar foreground.
+     */
+    static const String TB_FG = TB_PREFIX ~ "FG"; //$NON-NLS-1$
+
+    /**
+     * Key for the section title bar gradient.
+     * @deprecated Since 3.3, this color is not used any more. The
+     * tool bar gradient is created starting from {@link #TB_BG} to
+     * the section background color.
+     */
+    static const String TB_GBG = TB_BG;
+
+    /**
+     * Key for the section title bar border.
+     */
+    static const String TB_BORDER = TB_PREFIX ~ "BORDER"; //$NON-NLS-1$
+
+    /**
+     * Key for the section toggle color. Since 3.1, this color is used for all
+     * section styles.
+     */
+    static const String TB_TOGGLE = TB_PREFIX ~ "TOGGLE"; //$NON-NLS-1$
+
+    /**
+     * Key for the section toggle hover color.
+     *
+     */
+    static const String TB_TOGGLE_HOVER = TB_PREFIX ~ "TOGGLE_HOVER"; //$NON-NLS-1$
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/IFormPart.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.IFormPart;
+
+import org.eclipse.ui.forms.IManagedForm;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Classes that implement this interface can be added to the managed form and
+ * take part in the form life cycle. The part is initialized with the form and
+ * will be asked to accept focus. The part can receive form input and can elect
+ * to do something according to it (for example, select an object that matches
+ * the input).
+ * <p>
+ * The form part has two 'out of sync' states in respect to the model(s) that
+ * feed the form: <b>dirty</b> and <b>stale</b>. When a part is dirty, it
+ * means that the user interacted with it and now its widgets contain state that
+ * is newer than the model. In order to sync up with the model, 'commit' needs
+ * to be called. In contrast, the model can change 'under' the form (as a result
+ * of some actions outside the form), resulting in data in the model being
+ * 'newer' than the content presented in the form. A 'stale' form part is
+ * brought in sync with the model by calling 'refresh'. The part is responsible
+ * for notifying the form when one of these states change in the part. The form
+ * reserves the right to handle this notification in the most appropriate way
+ * for the situation (for example, if the form is in a page of the multi-page
+ * editor, it may do nothing for stale parts if the page is currently not
+ * showing).
+ * <p>
+ * When the form is disposed, each registered part is disposed as well. Parts
+ * are responsible for releasing any system resources they created and for
+ * removing themselves as listeners from all event providers.
+ *
+ * @see IManagedForm
+ * @since 3.0
+ *
+ */
+public interface IFormPart {
+    /**
+     * Initializes the part.
+     *
+     * @param form
+     *            the managed form that manages the part
+     */
+    void initialize(IManagedForm form);
+
+    /**
+     * Disposes the part allowing it to release allocated resources.
+     */
+    void dispose();
+
+    /**
+     * Returns true if the part has been modified with respect to the data
+     * loaded from the model.
+     *
+     * @return true if the part has been modified with respect to the data
+     *         loaded from the model
+     */
+    bool isDirty();
+
+    /**
+     * If part is displaying information loaded from a model, this method
+     * instructs it to commit the new (modified) data back into the model.
+     *
+     * @param onSave
+     *            indicates if commit is called during 'save' operation or for
+     *            some other reason (for example, if form is contained in a
+     *            wizard or a multi-page editor and the user is about to leave
+     *            the page).
+     */
+    void commit(bool onSave);
+
+    /**
+     * Notifies the part that an object has been set as overall form's input.
+     * The part can elect to react by revealing or selecting the object, or do
+     * nothing if not applicable.
+     *
+     * @return <code>true</code> if the part has selected and revealed the
+     *         input object, <code>false</code> otherwise.
+     */
+    bool setFormInput(Object input);
+
+    /**
+     * Instructs form part to transfer focus to the widget that should has focus
+     * in that part. The method can do nothing (if it has no widgets capable of
+     * accepting focus).
+     */
+    void setFocus();
+
+    /**
+     * Tests whether the form part is stale and needs refreshing. Parts can
+     * receive notification from models that will make their content stale, but
+     * may need to delay refreshing to improve performance (for example, there
+     * is no need to immediately refresh a part on a form that is current on a
+     * hidden page).
+     * <p>
+     * It is important to differentiate 'stale' and 'dirty' states. Part is
+     * 'dirty' if user interacted with its editable widgets and changed the
+     * values. In contrast, part is 'stale' when the data it presents in the
+     * widgets has been changed in the model without direct user interaction.
+     *
+     * @return <code>true</code> if the part needs refreshing,
+     *         <code>false</code> otherwise.
+     */
+    bool isStale();
+
+    /**
+     * Refreshes the part completely from the information freshly obtained from
+     * the model. The method will not be called if the part is not stale.
+     * Otherwise, the part is responsible for clearing the 'stale' flag after
+     * refreshing itself.
+     */
+    void refresh();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/IManagedForm.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.IManagedForm;
+
+import org.eclipse.ui.forms.IFormPart;
+import org.eclipse.ui.forms.IMessageManager;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Managed form wraps a form widget and adds life cycle methods for form parts.
+ * A form part is a portion of the form that participates in form life cycle
+ * events.
+ * <p>
+ * There is no 1/1 mapping between widgets and form parts. A widget like Section
+ * can be a part by itself, but a number of widgets can gather around one form
+ * part.
+ * <p>
+ * This interface should not be extended or implemented. New form instances
+ * should be created using ManagedForm.
+ *
+ * @see ManagedForm
+ * @since 3.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IManagedForm {
+    /**
+     * Initializes the form by looping through the managed parts and
+     * initializing them. Has no effect if already called once.
+     *
+     * @since 3.1
+     */
+    public void initialize();
+
+    /**
+     * Returns the toolkit used by this form.
+     *
+     * @return the toolkit
+     */
+    public FormToolkit getToolkit();
+
+    /**
+     * Returns the form widget managed by this form.
+     *
+     * @return the form widget
+     */
+    public ScrolledForm getForm();
+
+    /**
+     * Reflows the form as a result of the layout change.
+     *
+     * @param changed
+     *            if <code>true</code>, discard cached layout information
+     */
+    public void reflow(bool changed);
+
+    /**
+     * A part can use this method to notify other parts that implement
+     * IPartSelectionListener about selection changes.
+     *
+     * @param part
+     *            the part that broadcasts the selection
+     * @param selection
+     *            the selection in the part
+     */
+    public void fireSelectionChanged(IFormPart part, ISelection selection);
+
+    /**
+     * Returns all the parts currently managed by this form.
+     *
+     * @return the managed parts
+     */
+    IFormPart[] getParts();
+
+    /**
+     * Adds the new part to the form.
+     *
+     * @param part
+     *            the part to add
+     */
+    void addPart(IFormPart part);
+
+    /**
+     * Removes the part from the form.
+     *
+     * @param part
+     *            the part to remove
+     */
+    void removePart(IFormPart part);
+
+    /**
+     * Sets the input of this page to the provided object.
+     *
+     * @param input
+     *            the new page input
+     * @return <code>true</code> if the form contains this object,
+     *         <code>false</code> otherwise.
+     */
+    bool setInput(Object input);
+
+    /**
+     * Returns the current page input.
+     *
+     * @return page input object or <code>null</code> if not applicable.
+     */
+    Object getInput();
+
+    /**
+     * Tests if form is dirty. A managed form is dirty if at least one managed
+     * part is dirty.
+     *
+     * @return <code>true</code> if at least one managed part is dirty,
+     *         <code>false</code> otherwise.
+     */
+    bool isDirty();
+
+    /**
+     * Notifies the form that the dirty state of one of its parts has changed.
+     * The global dirty state of the form can be obtained by calling 'isDirty'.
+     *
+     * @see #isDirty
+     */
+    void dirtyStateChanged();
+
+    /**
+     * Commits the dirty form. All pending changes in the widgets are flushed
+     * into the model.
+     *
+     * @param onSave
+     */
+    void commit(bool onSave);
+
+    /**
+     * Tests if form is stale. A managed form is stale if at least one managed
+     * part is stale. This can happen when the underlying model changes,
+     * resulting in the presentation of the part being out of sync with the
+     * model and needing refreshing.
+     *
+     * @return <code>true</code> if the form is stale, <code>false</code>
+     *         otherwise.
+     */
+    bool isStale();
+
+    /**
+     * Notifies the form that the stale state of one of its parts has changed.
+     * The global stale state of the form can be obtained by calling 'isStale'.
+     */
+    void staleStateChanged();
+
+    /**
+     * Refreshes the form by refreshing every part that is stale.
+     */
+    void refresh();
+
+    /**
+     * Sets the container that owns this form. Depending on the context, the
+     * container may be wizard, editor page, editor etc.
+     *
+     * @param container
+     *            the container of this form
+     */
+    void setContainer(Object container);
+
+    /**
+     * Returns the container of this form.
+     *
+     * @return the form container
+     */
+    Object getContainer();
+
+    /**
+     * Returns the message manager that will keep track of messages in this
+     * form.
+     *
+     * @return the message manager instance
+     * @since 3.3
+     */
+    IMessageManager getMessageManager();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/IMessage.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.IMessage;
+
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.jface.dialogs.IMessageProvider;
+
+import java.lang.all;
+
+/**
+ * This interface encapsulates a single message that can be shown in a form.
+ * Messages can be associated with controls, or be of a general nature.
+ *
+ * @see IMessageManager
+ * @since 3.3
+ */
+public interface IMessage : IMessageProvider {
+    /**
+     * Returns the unique message key
+     *
+     * @return the unique message key
+     */
+    Object getKey();
+
+    /**
+     * Returns data for application use
+     *
+     * @return data object
+     */
+    Object getData();
+
+    /**
+     * Returns the control this message is associated with.
+     *
+     * @return the control or <code>null</code> if this is a general message.
+     */
+    Control getControl();
+
+    /**
+     * Messages that are associated with controls can be shown with a prefix
+     * that indicates the origin of the message (e.g. the label preceeding the
+     * control).
+     *
+     * @return the message prefix or <code>null</code> if this is a general
+     *         message
+     */
+    String getPrefix();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/IMessageManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,227 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.IMessageManager;
+
+import org.eclipse.ui.forms.IMessagePrefixProvider;
+import org.eclipse.ui.forms.IMessage;
+
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.ui.forms.widgets.Form;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * This interface provides for managing typed messages in a form. Typed messages
+ * are messages associated with a type that indicates their severity (error,
+ * warning, information). The interface is responsible for:
+ * <ul>
+ * <li>Bridging the concept of typed messages and control decorations</li>
+ * <li>Adding one or more messages per control in a form</li>
+ * <li>Rolling the local messages up to the form header</li>
+ * <li>Adding one or more general messages to the form header</li>
+ * </ul>
+ * <p>
+ * To use it in a form, do the following:
+ * <ol>
+ * <li>For each interactive control, add a listener to it to monitor user input</li>
+ * <li>Every time the input changes, validate it. If there is a problem, add a
+ * message with a unique key to the manager. If there is already a message with
+ * the same key in the manager, its type and message text will be replaced (no
+ * duplicates). Note that you add can messages with different keys to the same
+ * control to track multiple problems with the user input.</li>
+ * <li>If the problem has been cleared, remove the message using the key
+ * (attempting to remove a message that is not in the manager is safe).</li>
+ * <li>If something happens in the form that is not related to any control, use
+ * the other <code>addMessage</code> method.</li>
+ * </ol>
+ * <p>
+ * This interface should only be referenced. It must not be implemented or
+ * extended.
+ * </p>
+ *
+ * @since 3.3
+ * @see IMessageProvider
+ * @see IManagedForm
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+
+public interface IMessageManager {
+    /**
+     * Adds a general message that is not associated with any decorated field.
+     * Note that subsequent calls using the same key will not result in
+     * duplicate messages. Instead, the previous message with the same key will
+     * be replaced with the new message.
+     *
+     * @param key
+     *            a unique message key that will be used to look the message up
+     *            later
+     *
+     * @param messageText
+     *            the message to add
+     * @param data
+     *            an object for application use (can be <code>null</code>)
+     * @param type
+     *            the message type as defined in <code>IMessageProvider</code>.
+     */
+    void addMessage(Object key, String messageText, Object data, int type);
+
+    /**
+     * Adds a message that should be associated with the provided control. Note
+     * that subsequent calls using the same key will not result in duplicate
+     * messages. Instead, the previous message with the same key will be
+     * replaced with the new message.
+     *
+     * @param key
+     *            the unique message key
+     * @param messageText
+     *            the message to add
+     * @param data
+     *            an object for application use (can be <code>null</code>)
+     * @param type
+     *            the message type
+     * @param control
+     *            the control to associate the message with
+     */
+    void addMessage(Object key, String messageText, Object data, int type,
+            Control control);
+
+    /**
+     * Removes the general message with the provided key. Does nothing if
+     * message for the key does not exist.
+     *
+     * @param key
+     *            the key of the message to remove
+     */
+    void removeMessage(Object key);
+
+    /**
+     * Removes all the general messages. If there are local messages associated
+     * with controls, the replacement message may show up drawing user's
+     * attention to these local messages. Otherwise, the container will clear
+     * the message area.
+     */
+    void removeMessages();
+
+    /**
+     * Removes a keyed message associated with the provided control. Does
+     * nothing if the message for that key does not exist.
+     *
+     * @param key
+     *            the id of the message to remove
+     * @param control
+     *            the control the message is associated with
+     */
+    void removeMessage(Object key, Control control);
+
+    /**
+     * Removes all the messages associated with the provided control. Does
+     * nothing if there are no messages for this control.
+     *
+     * @param control
+     *            the control the messages are associated with
+     */
+    void removeMessages(Control control);
+
+    /**
+     * Removes all the local field messages and all the general container
+     * messages.
+     */
+    void removeAllMessages();
+
+    /**
+     * Updates the message container with the messages currently in the manager.
+     * There are two scenarios in which a client may want to use this method:
+     * <ol>
+     * <li>When controls previously managed by this manager have been disposed.</li>
+     * <li>When automatic update has been turned off.</li>
+     * </ol>
+     * In all other situations, the manager will keep the form in sync
+     * automatically.
+     *
+     * @see #setAutoUpdate(bool)
+     */
+    void update();
+
+    /**
+     * Controls whether the form is automatically updated when messages are
+     * added or removed. By default, auto update is on. Clients can turn it off
+     * prior to adding or removing a number of messages as a batch. Turning it
+     * back on will trigger an update.
+     *
+     * @param enabled
+     *            sets the state of the automatic update
+     */
+    void setAutoUpdate(bool enabled);
+
+    /**
+     * Tests whether the form will be automatically updated when messages are
+     * added or removed.
+     *
+     * @return <code>true</code> if auto update is active, <code>false</code>
+     *         otherwise.
+     */
+    bool isAutoUpdate();
+
+    /**
+     * Sets the alternative message prefix provider. The default prefix provider
+     * is set by the manager.
+     *
+     * @param provider
+     *            the new prefix provider or <code>null</code> to turn the
+     *            prefix generation off.
+     */
+    void setMessagePrefixProvider(IMessagePrefixProvider provider);
+
+    /**
+     * @return the current prefix provider or <code>null</code> if prefixes
+     *         are not generated.
+     */
+    IMessagePrefixProvider getMessagePrefixProvider();
+
+    /**
+     * Message manager uses SWT.LEFT|SWT.BOTTOM for the default decoration
+     * position. Use this method to change it.
+     *
+     * @param position
+     *            the decoration position
+     * @see ControlDecoration
+     */
+    void setDecorationPosition(int position);
+
+    /**
+     * Returns the currently used decoration position for all control messages.
+     *
+     * @return the current decoration position
+     */
+
+    int getDecorationPosition();
+
+    /**
+     * When message manager is used in context of a form, and there are
+     * hyperlink listeners for messages in the header, the hyperlink event will
+     * carry an object of type <code>IMessage[]</code> as an href. You can use
+     * this method to create a summary text from this array consistent with the
+     * tool tip used by the form header.
+     *
+     * @param messages
+     *            an array of messages
+     * @return a textual representation of the messages with one message per
+     *         line.
+     * @see Form#addMessageHyperlinkListener(org.eclipse.ui.forms.events.IHyperlinkListener)
+     */
+    String createSummary(IMessage[] messages);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/IMessagePrefixProvider.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.IMessagePrefixProvider;
+
+import org.eclipse.swt.widgets.Control;
+
+import java.lang.all;
+
+/**
+ * This interface computes the prefix of a message that is created in the
+ * context of a control. Since messages are rolled up to the header in the
+ * message manager, it is important to create a prefix to indicate the context
+ * of a message in the form. Typically the prefix is computed by looking at the
+ * label that preceeds the control, if present. Alternative providers may
+ * include other text to further specify the location of the message.
+ *
+ * @see IMessageManager
+ * @see IMessage
+ * @since 3.3
+ */
+public interface IMessagePrefixProvider {
+    /**
+     * Returns the computed prefix for the provided control.
+     *
+     * @param control
+     *            the control to provide the prefix for
+     * @return the computed prefix
+     */
+    String getPrefix(Control control);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/IPartSelectionListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.ui.forms.IPartSelectionListener;
+
+import java.lang.all;
+
+import org.eclipse.ui.forms.IFormPart;
+
+import org.eclipse.jface.viewers.ISelection;
+
+/**
+ * Form parts can implement this interface if they want to be
+ * notified when another part on the same form changes selection
+ * state.
+ *
+ * @see IFormPart
+ * @since 3.0
+ */
+public interface IPartSelectionListener {
+    /**
+     * Called when the provided part has changed selection state.
+     *
+     * @param part
+     *            the selection source
+     * @param selection
+     *            the new selection
+     */
+    public void selectionChanged(IFormPart part, ISelection selection);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/ManagedForm.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,346 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.ManagedForm;
+
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.IFormPart;
+import org.eclipse.ui.forms.IMessageManager;
+import org.eclipse.ui.forms.IPartSelectionListener;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.internal.forms.MessageManager;
+
+import java.lang.all;
+import java.util.Set;
+import tango.core.Thread;
+
+/**
+ * Managed form wraps a form widget and adds life cycle methods for form parts.
+ * A form part is a portion of the form that participates in form life cycle
+ * events.
+ * <p>
+ * There is requirement for 1/1 mapping between widgets and form parts. A widget
+ * like Section can be a part by itself, but a number of widgets can join around
+ * one form part.
+ * <p>
+ * Note to developers: this class is left public to allow its use beyond the
+ * original intention (inside a multi-page editor's page). You should limit the
+ * use of this class to make new instances inside a form container (wizard page,
+ * dialog etc.). Clients that need access to the class should not do it
+ * directly. Instead, they should do it through IManagedForm interface as much
+ * as possible.
+ *
+ * @since 3.0
+ */
+public class ManagedForm : IManagedForm {
+    private Object input;
+
+    private ScrolledForm form;
+
+    private FormToolkit toolkit;
+
+    private Object container;
+
+    private bool ownsToolkit;
+
+    private bool initialized;
+
+    private MessageManager messageManager;
+
+    private Vector parts;
+
+    /**
+     * Creates a managed form in the provided parent. Form toolkit and widget
+     * will be created and owned by this object.
+     *
+     * @param parent
+     *            the parent widget
+     */
+    public this(Composite parent) {
+        parts = new Vector();
+        toolkit = new FormToolkit(parent.getDisplay());
+        ownsToolkit = true;
+        form = toolkit.createScrolledForm(parent);
+    }
+
+    /**
+     * Creates a managed form that will use the provided toolkit and
+     *
+     * @param toolkit
+     * @param form
+     */
+    public this(FormToolkit toolkit, ScrolledForm form) {
+        parts = new Vector();
+        this.form = form;
+        this.toolkit = toolkit;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IManagedForm#addPart(org.eclipse.ui.forms.IFormPart)
+     */
+    public void addPart(IFormPart part) {
+        parts.add(cast(Object)part);
+        part.initialize(this);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IManagedForm#removePart(org.eclipse.ui.forms.IFormPart)
+     */
+    public void removePart(IFormPart part) {
+        parts.remove(cast(Object)part);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IManagedForm#getParts()
+     */
+    public IFormPart[] getParts() {
+        return arraycast!(IFormPart)(parts.toArray());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IManagedForm#getToolkit()
+     */
+    public FormToolkit getToolkit() {
+        return toolkit;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IManagedForm#getForm()
+     */
+    public ScrolledForm getForm() {
+        return form;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IManagedForm#reflow(bool)
+     */
+    public void reflow(bool changed) {
+        form.reflow(changed);
+    }
+
+    /**
+     * A part can use this method to notify other parts that implement
+     * IPartSelectionListener about selection changes.
+     *
+     * @param part
+     *            the part that broadcasts the selection
+     * @param selection
+     *            the selection in the part
+     * @see IPartSelectionListener
+     */
+    public void fireSelectionChanged(IFormPart part, ISelection selection) {
+        for (int i = 0; i < parts.size(); i++) {
+            IFormPart cpart = cast(IFormPart) parts.get(i);
+            if ((cast(Object)part).opEquals(cast(Object)cpart))
+                continue;
+            if (null !is cast(IPartSelectionListener)cpart ) {
+                (cast(IPartSelectionListener) cpart).selectionChanged(part,
+                        selection);
+            }
+        }
+    }
+
+    /**
+     * Initializes the form by looping through the managed parts and
+     * initializing them. Has no effect if already called once.
+     */
+    public void initialize() {
+        if (initialized)
+            return;
+        for (int i = 0; i < parts.size(); i++) {
+            IFormPart part = cast(IFormPart) parts.get(i);
+            part.initialize(this);
+        }
+        initialized = true;
+    }
+
+    /**
+     * Disposes all the parts in this form.
+     */
+    public void dispose() {
+        for (int i = 0; i < parts.size(); i++) {
+            IFormPart part = cast(IFormPart) parts.get(i);
+            part.dispose();
+        }
+        if (ownsToolkit) {
+            toolkit.dispose();
+        }
+    }
+
+    /**
+     * Refreshes the form by refreshes all the stale parts. Since 3.1, this
+     * method is performed on a UI thread when called from another thread so it
+     * is not needed to wrap the call in <code>Display.syncExec</code> or
+     * <code>asyncExec</code>.
+     */
+    public void refresh() {
+        Thread t = Thread.getThis();
+        Thread dt = toolkit.getColors().getDisplay().getThread();
+        if (t.opEquals(dt))
+            doRefresh();
+        else {
+            toolkit.getColors().getDisplay().asyncExec(dgRunnable( {
+                doRefresh();
+            }));
+        }
+    }
+
+    private void doRefresh() {
+        int nrefreshed = 0;
+        for (int i = 0; i < parts.size(); i++) {
+            IFormPart part = cast(IFormPart) parts.get(i);
+            if (part.isStale()) {
+                part.refresh();
+                nrefreshed++;
+            }
+        }
+        if (nrefreshed > 0)
+            form.reflow(true);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IManagedForm#commit(bool)
+     */
+    public void commit(bool onSave) {
+        for (int i = 0; i < parts.size(); i++) {
+            IFormPart part = cast(IFormPart) parts.get(i);
+            if (part.isDirty())
+                part.commit(onSave);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IManagedForm#setInput(java.lang.Object)
+     */
+    public bool setInput(Object input) {
+        bool pageResult = false;
+
+        this.input = input;
+        for (int i = 0; i < parts.size(); i++) {
+            IFormPart part = cast(IFormPart) parts.get(i);
+            bool result = part.setFormInput(input);
+            if (result)
+                pageResult = true;
+        }
+        return pageResult;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IManagedForm#getInput()
+     */
+    public Object getInput() {
+        return input;
+    }
+
+    /**
+     * Transfers the focus to the first form part.
+     */
+    public void setFocus() {
+        if (parts.size() > 0) {
+            IFormPart part = cast(IFormPart) parts.get(0);
+            part.setFocus();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IManagedForm#isDirty()
+     */
+    public bool isDirty() {
+        for (int i = 0; i < parts.size(); i++) {
+            IFormPart part = cast(IFormPart) parts.get(i);
+            if (part.isDirty())
+                return true;
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IManagedForm#isStale()
+     */
+    public bool isStale() {
+        for (int i = 0; i < parts.size(); i++) {
+            IFormPart part = cast(IFormPart) parts.get(i);
+            if (part.isStale())
+                return true;
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IManagedForm#dirtyStateChanged()
+     */
+    public void dirtyStateChanged() {
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IManagedForm#staleStateChanged()
+     */
+    public void staleStateChanged() {
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IManagedForm#getContainer()
+     */
+    public Object getContainer() {
+        return container;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IManagedForm#setContainer(java.lang.Object)
+     */
+    public void setContainer(Object container) {
+        this.container = container;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.ui.forms.IManagedForm#getMessageManager()
+     */
+    public IMessageManager getMessageManager() {
+        if (messageManager is null)
+            messageManager = new MessageManager(form);
+        return messageManager;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/MasterDetailsBlock.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.MasterDetailsBlock;
+
+import org.eclipse.ui.forms.DetailsPart;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.FormColors;
+import org.eclipse.ui.forms.IFormColors;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Sash;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * This class implements the 'master/details' UI pattern suitable for inclusion
+ * in a form. The block consists of two parts: 'master' and 'details' in a sash
+ * form that allows users to change the relative ratio on the page. The master
+ * part needs to be created by the users of this class. The details part is
+ * created by the block.
+ * <p>
+ * The master part is responsible for adding itself as a form part and firing
+ * selection events. The details part catches the selection events and tries to
+ * load a page registered to handle the selected object(s). The page shows the
+ * details of the selected object(s) and allows users to edit them.
+ * <p>
+ * Details pages can be registered statically using 'registerPage' or
+ * dynamically through the use of 'IDetailsPageProvider' in case where different
+ * pages need to be shown for objects of the same type depending on their state.
+ * <p>
+ * Subclasses are required to implement abstract methods of this class. Master
+ * part must be created and at least one details page should be registered in
+ * order to show details of the objects selected in the master part. Tool bar
+ * actions can be optionally added to the tool bar manager.
+ *
+ * @see DetailsPart
+ * @see IDetailsPage
+ * @see IDetailsPageProvider
+ * @since 3.0
+ */
+public abstract class MasterDetailsBlock {
+    /**
+     * Details part created by the block. No attempt should be made to access
+     * this field inside <code>createMasterPart</code> because it has not been
+     * created yet and will be <code>null</code>.
+     */
+    protected DetailsPart detailsPart;
+
+    /**
+     * The form that is the parent of both master and details part. The form
+     * allows users to change the ratio between the two parts.
+     */
+    protected SashForm sashForm;
+
+    static const int DRAGGER_SIZE = 40;
+
+    class MDSashForm : SashForm {
+        ArrayList sashes;
+        Listener listener;
+        public this(Composite parent, int style) {
+            sashes = new ArrayList();
+            listener = dgListener ( (Event e){
+                switch (e.type) {
+                case SWT.MouseEnter:
+                    e.widget.setData("hover", Boolean.TRUE); //$NON-NLS-1$
+                    (cast(Control)e.widget).redraw();
+                    break;
+                case SWT.MouseExit:
+                    e.widget.setData("hover", null); //$NON-NLS-1$
+                    (cast(Control)e.widget).redraw();
+                    break;
+                case SWT.Paint:
+                    onSashPaint(e);
+                break;
+                case SWT.Resize:
+                    hookSashListeners();
+                break;
+                default:
+                }
+            });
+            super(parent, style);
+        }
+
+        public void layout(bool changed) {
+            super.layout(changed);
+            hookSashListeners();
+        }
+
+        public void layout(Control [] children) {
+            super.layout(children);
+            hookSashListeners();
+        }
+
+        private void hookSashListeners() {
+            purgeSashes();
+            Control [] children = getChildren();
+            for (int i=0; i<children.length; i++) {
+                if ( auto sash = cast(Sash)children[i] ) {
+                    if (sashes.contains(sash))
+                        continue;
+                    sash.addListener(SWT.Paint, listener);
+                    sash.addListener(SWT.MouseEnter, listener);
+                    sash.addListener(SWT.MouseExit, listener);
+                    sashes.add(sash);
+                }
+            }
+        }
+        private void purgeSashes() {
+            for (Iterator iter=sashes.iterator(); iter.hasNext();) {
+                Sash sash = cast(Sash)iter.next();
+                if (sash.isDisposed())
+                    iter.remove();
+            }
+        }
+    }
+
+    /**
+     * Creates the content of the master/details block inside the managed form.
+     * This method should be called as late as possible inside the parent part.
+     *
+     * @param managedForm
+     *            the managed form to create the block in
+     */
+    public void createContent(IManagedForm managedForm) {
+        final ScrolledForm form = managedForm.getForm();
+        FormToolkit toolkit = managedForm.getToolkit();
+        GridLayout layout = new GridLayout();
+        layout.marginWidth = 0;
+        layout.marginHeight = 0;
+        form.getBody().setLayout(layout);
+        sashForm = new MDSashForm(form.getBody(), SWT.NULL);
+        sashForm.setData("form", cast(Object)managedForm); //$NON-NLS-1$
+        toolkit.adapt(sashForm, false, false);
+        sashForm.setMenu(form.getBody().getMenu());
+        sashForm.setLayoutData(new GridData(GridData.FILL_BOTH));
+        createMasterPart(managedForm, sashForm);
+        createDetailsPart(managedForm, sashForm);
+        hookResizeListener();
+        createToolBarActions(managedForm);
+        form.updateToolBar();
+    }
+
+    private void hookResizeListener() {
+        Listener listener = (cast(MDSashForm)sashForm).listener;
+        Control [] children = sashForm.getChildren();
+        for (int i=0; i<children.length; i++) {
+            if (null !is cast(Sash)children[i] ) continue;
+            children[i].addListener(SWT.Resize, listener);
+        }
+    }
+
+    /**
+     * Implement this method to create a master part in the provided parent.
+     * Typical master parts are section parts that contain tree or table viewer.
+     *
+     * @param managedForm
+     *            the parent form
+     * @param parent
+     *            the parent composite
+     */
+    protected abstract void createMasterPart(IManagedForm managedForm,
+            Composite parent);
+
+    /**
+     * Implement this method to statically register pages for the expected
+     * object types. This mechanism can be used when there is 1-&gt;1 mapping
+     * between object classes and details pages.
+     *
+     * @param detailsPart
+     *            the details part
+     */
+    protected abstract void registerPages(DetailsPart detailsPart);
+
+    /**
+     * Implement this method to create form tool bar actions and add them to the
+     * form tool bar if desired.
+     *
+     * @param managedForm
+     *            the form that owns the tool bar
+     */
+    protected abstract void createToolBarActions(IManagedForm managedForm);
+
+    private void createDetailsPart(IManagedForm mform, Composite parent) {
+        detailsPart = new DetailsPart(mform, parent, SWT.NULL);
+        mform.addPart(detailsPart);
+        registerPages(detailsPart);
+    }
+
+    private void onSashPaint(Event e) {
+        Sash sash = cast(Sash)e.widget;
+        IManagedForm form = cast(IManagedForm)sash.getParent().getData("form"); //$NON-NLS-1$
+        FormColors colors = form.getToolkit().getColors();
+        bool vertical = (sash.getStyle() & SWT.VERTICAL) !is 0;
+        GC gc = e.gc;
+        Boolean hover = cast(Boolean)sash.getData("hover"); //$NON-NLS-1$
+        gc.setBackground(colors.getColor(IFormColors.TB_BG));
+        gc.setForeground(colors.getColor(IFormColors.TB_BORDER));
+        Point size = sash.getSize();
+        if (vertical) {
+            if (hover !is null)
+                gc.fillRectangle(0, 0, size.x, size.y);
+            //else
+                //gc.drawLine(1, 0, 1, size.y-1);
+        }
+        else {
+            if (hover !is null)
+                gc.fillRectangle(0, 0, size.x, size.y);
+            //else
+                //gc.drawLine(0, 1, size.x-1, 1);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/SectionPart.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.ui.forms.SectionPart;
+
+import org.eclipse.ui.forms.AbstractFormPart;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.forms.events.ExpansionAdapter;
+import org.eclipse.ui.forms.events.ExpansionEvent;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Section part implements IFormPart interface based on the Section widget. It
+ * can either wrap the widget or create one itself.
+ * <p>
+ * Subclasses should extend <code>SectionPart</code> and implement life cycle
+ * methods like <code>refresh</code>, <code>commit</code>,
+ * <code>setFocus</code> etc. Note that most of these methods are not empty -
+ * calling <code>super</code> is required.
+ *
+ * @see Section
+ * @since 3.0
+ */
+public class SectionPart : AbstractFormPart {
+    private Section section;
+
+    /**
+     * Creates a new section part based on the provided section.
+     *
+     * @param section
+     *            the section to use
+     */
+    public this(Section section) {
+        this.section = section;
+        hookListeners();
+    }
+
+    /**
+     * Creates a new section part inside the provided parent and using the
+     * provided toolkit. The section part will create the section widget.
+     *
+     * @param parent
+     *            the parent
+     * @param toolkit
+     *            the toolkit to use
+     * @param style
+     *            the section widget style
+     */
+    public this(Composite parent, FormToolkit toolkit, int style) {
+        this(toolkit.createSection(parent, style));
+    }
+
+    /**
+     * Adds listeners to the underlying widget.
+     */
+    protected void hookListeners() {
+        if ((section.getExpansionStyle() & Section.TWISTIE) !is 0
+                || (section.getExpansionStyle() & Section.TREE_NODE) !is 0) {
+            section.addExpansionListener(new class ExpansionAdapter {
+                public void expansionStateChanging(ExpansionEvent e) {
+                    this.outer.expansionStateChanging(e.getState());
+                }
+
+                public void expansionStateChanged(ExpansionEvent e) {
+                    this.outer.expansionStateChanged(e.getState());
+                }
+            });
+        }
+    }
+
+    /**
+     * Returns the section widget used in this part.
+     *
+     * @return the section widget
+     */
+    public Section getSection() {
+        return section;
+    }
+
+    /**
+     * The section is about to expand or collapse.
+     *
+     * @param expanding
+     *            <code>true</code> for expansion, <code>false</code> for
+     *            collapse.
+     */
+    protected void expansionStateChanging(bool expanding) {
+    }
+
+    /**
+     * The section has expanded or collapsed.
+     *
+     * @param expanded
+     *            <code>true</code> for expansion, <code>false</code> for
+     *            collapse.
+     */
+    protected void expansionStateChanged(bool expanded) {
+        getManagedForm().getForm().reflow(false);
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.ui.forms.AbstractFormPart#setFocus()
+     */
+    public void setFocus() {
+        Control client = section.getClient();
+        if (client !is null)
+            client.setFocus();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/events/ExpansionAdapter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.events.ExpansionAdapter;
+import org.eclipse.ui.forms.events.IExpansionListener;
+import org.eclipse.ui.forms.events.ExpansionEvent;
+import java.lang.all;
+/**
+ * This adapter class provides default implementations for the methods
+ * described by the <code>ExpansionListener</code> interface.
+ * <p>
+ * Classes that wish to deal with <code>ExpansionEvent</code>s can extend
+ * this class and override only the methods which they are interested in.
+ * </p>
+ *
+ * @see IExpansionListener
+ * @see ExpansionEvent
+ * @since 3.0
+ */
+public class ExpansionAdapter : IExpansionListener {
+    /**
+     * Sent when the link is entered. The default behaviour is to do nothing.
+     *
+     * @param e
+     *            the event
+     */
+    public void expansionStateChanging(ExpansionEvent e) {
+    }
+    /**
+     * Sent when the link is exited. The default behaviour is to do nothing.
+     *
+     * @param e
+     *            the event
+     */
+    public void expansionStateChanged(ExpansionEvent e) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/events/ExpansionEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.ui.forms.events.ExpansionEvent;
+import org.eclipse.swt.events.TypedEvent;
+import java.lang.all;
+/**
+ * Notifies listeners when expandable controls change expansion state.
+ *
+ * @since 3.0
+ */
+public final class ExpansionEvent : TypedEvent {
+    private static const long serialVersionUID = 6009335074727417445L;
+    /**
+     * Creates a new expansion ecent.
+     *
+     * @param obj
+     *            event source
+     * @param state
+     *            the new expansion state
+     */
+    public this(Object obj, bool state) {
+        super(obj);
+        data = state ? Boolean.TRUE : Boolean.FALSE;
+    }
+    /**
+     * Returns the new expansion state of the widget.
+     *
+     * @return <code>true</code> if the widget is now expaned, <code>false</code>
+     *         otherwise.
+     */
+    public bool getState() {
+        return data.opEquals(Boolean.TRUE) ? true : false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/events/HyperlinkAdapter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.events.HyperlinkAdapter;
+import org.eclipse.ui.forms.events.IHyperlinkListener;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import java.lang.all;
+/**
+ * This adapter class provides default implementations for the methods
+ * described by the <code>HyperlinkListener</code> interface.
+ * <p>
+ * Classes that wish to deal with <code>HyperlinkEvent</code> s can extend
+ * this class and override only the methods which they are interested in.
+ * </p>
+ *
+ * @see IHyperlinkListener
+ * @see HyperlinkEvent
+ * @since 3.0
+ */
+public class HyperlinkAdapter : IHyperlinkListener {
+    /**
+     * Sent when the link is entered. The default behaviour is to do nothing.
+     *
+     * @param e
+     *            the event
+     */
+    public void linkEntered(HyperlinkEvent e) {
+    }
+    /**
+     * Sent when the link is exited. The default behaviour is to do nothing.
+     *
+     * @param e
+     *            the event
+     */
+    public void linkExited(HyperlinkEvent e) {
+    }
+    /**
+     * Sent when the link is activated. The default behaviour is to do nothing.
+     *
+     * @param e
+     *            the event
+     */
+    public void linkActivated(HyperlinkEvent e) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/events/HyperlinkEvent.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.swt.events.TypedEvent;
+import org.eclipse.swt.widgets.Widget;
+import java.lang.all;
+/**
+ * Notifies listeners about a hyperlink change.
+ *
+ * @since 3.0
+ */
+public final class HyperlinkEvent : TypedEvent {
+    private static const long serialVersionUID = 6009335074727417445L;
+    private String label;
+    private int stateMask;
+    /**
+     * Creates a new hyperlink
+     *
+     * @param widget
+     *            event source
+     * @param href
+     *            the hyperlink reference that will be followed upon when the
+     *            hyperlink is activated.
+     * @param label
+     *            the name of the hyperlink (the text that is rendered as a
+     *            link in the source widget).
+     * @param stateMask
+     *            the given state mask
+     */
+    public this(Widget widget, Object href, String label, int stateMask) {
+        super(widget);
+        this.widget = widget;
+        this.data = href;
+        this.label = label;
+        this.stateMask = stateMask;
+    }
+    /**
+     * The hyperlink reference that will be followed when the hyperlink is
+     * activated.
+     *
+     * @return the hyperlink reference object
+     */
+    public Object getHref() {
+        return this.data;
+    }
+    /**
+     * The text of the hyperlink rendered in the source widget.
+     *
+     * @return the hyperlink label
+     */
+    public String getLabel() {
+        return label;
+    }
+    /**
+     * Returns the value of the keyboard state mask present when
+     * the event occured, or SWT.NULL for no modifiers.
+     * @return the keyboard state mask or <code>SWT.NULL</code>.
+     */
+    public int getStateMask() {
+        return stateMask;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/events/IExpansionListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.ui.forms.events.IExpansionListener;
+
+import java.lang.all;
+
+import org.eclipse.ui.forms.events.ExpansionEvent;
+
+/**
+ * Classes that implement this interface will be notified before and after the
+ * expandable control's expansion state changes.
+ *
+ * @since 3.0
+ */
+public interface IExpansionListener {
+    /**
+     * Notifies the listener that the expandable control is about to change its
+     * expansion state. The state provided by the event is the new state.
+     *
+     * @param e
+     *            the expansion event
+     */
+    void expansionStateChanging(ExpansionEvent e);
+    /**
+     * Notifies the listener after the expandable control has changed its
+     * expansion state. The state provided by the event is the new state.
+     *
+     * @param e
+     *            the expansion event
+     */
+    void expansionStateChanged(ExpansionEvent e);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/events/IHyperlinkListener.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.events.IHyperlinkListener;
+
+import java.lang.all;
+
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+
+/**
+ * Classes that implement this interface will be notified when hyperlinks are
+ * entered, exited and activated.
+ *
+ * @see org.eclipse.ui.forms.widgets.Hyperlink
+ * @see org.eclipse.ui.forms.widgets.ImageHyperlink
+ * @see org.eclipse.ui.forms.widgets.FormText
+ * @since 3.0
+ */
+public interface IHyperlinkListener {
+    /**
+     * Sent when hyperlink is entered either by mouse entering the link client
+     * area, or keyboard focus switching to the hyperlink.
+     *
+     * @param e
+     *            an event containing information about the hyperlink
+     */
+    void linkEntered(HyperlinkEvent e);
+    /**
+     * Sent when hyperlink is exited either by mouse exiting the link client
+     * area, or keyboard focus switching from the hyperlink.
+     *
+     * @param e
+     *            an event containing information about the hyperlink
+     */
+    void linkExited(HyperlinkEvent e);
+    /**
+     * Sent when hyperlink is activated either by mouse click inside the link
+     * client area, or by pressing 'Enter' key while hyperlink has keyboard
+     * focus.
+     *
+     * @param e
+     *            an event containing information about the hyperlink
+     */
+    void linkActivated(HyperlinkEvent e);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/AbstractHyperlink.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,344 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.widgets.AbstractHyperlink;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.accessibility.ACC;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.events.IHyperlinkListener;
+import org.eclipse.ui.internal.forms.widgets.FormsResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * This is the base class for custom hyperlink widget. It is responsible for
+ * processing mouse and keyboard events, and converting them into unified
+ * hyperlink events. Subclasses are responsible for rendering the hyperlink in
+ * the client area.
+ *
+ * @since 3.0
+ */
+public abstract class AbstractHyperlink : Canvas {
+    private bool hasFocus;
+    bool paintFocus=true;
+
+    /*
+     * Armed link is one that will activate on a mouse up event, i.e.
+     * it has received a mouse down and mouse still on top of it.
+     */
+    private bool armed;
+
+    private ListenerList listeners;
+
+    /**
+     * Amount of the margin width around the hyperlink (default is 1).
+     */
+    protected int marginWidth = 1;
+
+    /**
+     * Amount of the margin height around the hyperlink (default is 1).
+     */
+    protected int marginHeight = 1;
+
+    /**
+     * Creates a new hyperlink in the provided parent.
+     *
+     * @param parent
+     *            the control parent
+     * @param style
+     *            the widget style
+     */
+    public this(Composite parent, int style) {
+        super(parent, style);
+        addListener(SWT.KeyDown, new class Listener {
+            public void handleEvent(Event e) {
+                if (e.character is '\r') {
+                    handleActivate(e);
+                }
+            }
+        });
+        addPaintListener(new class PaintListener {
+            public void paintControl(PaintEvent e) {
+                paint(e);
+            }
+        });
+        addListener(SWT.Traverse, new class Listener {
+            public void handleEvent(Event e) {
+                switch (e.detail) {
+                case SWT.TRAVERSE_PAGE_NEXT:
+                case SWT.TRAVERSE_PAGE_PREVIOUS:
+                case SWT.TRAVERSE_ARROW_NEXT:
+                case SWT.TRAVERSE_ARROW_PREVIOUS:
+                case SWT.TRAVERSE_RETURN:
+                    e.doit = false;
+                    return;
+                default:
+                }
+                e.doit = true;
+            }
+        });
+        Listener listener = new class Listener {
+            public void handleEvent(Event e) {
+                switch (e.type) {
+                case SWT.FocusIn:
+                    hasFocus = true;
+                    handleEnter(e);
+                    break;
+                case SWT.FocusOut:
+                    hasFocus = false;
+                    handleExit(e);
+                    break;
+                case SWT.DefaultSelection:
+                    handleActivate(e);
+                    break;
+                case SWT.MouseEnter:
+                    handleEnter(e);
+                    break;
+                case SWT.MouseExit:
+                    handleExit(e);
+                    break;
+                case SWT.MouseDown:
+                    handleMouseDown(e);
+                    break;
+                case SWT.MouseUp:
+                    handleMouseUp(e);
+                    break;
+                case SWT.MouseMove:
+                    handleMouseMove(e);
+                    break;
+                default:
+                }
+            }
+        };
+        addListener(SWT.MouseEnter, listener);
+        addListener(SWT.MouseExit, listener);
+        addListener(SWT.MouseDown, listener);
+        addListener(SWT.MouseUp, listener);
+        addListener(SWT.MouseMove, listener);
+        addListener(SWT.FocusIn, listener);
+        addListener(SWT.FocusOut, listener);
+        setCursor(FormsResources.getHandCursor());
+    }
+
+    /**
+     * Adds the event listener to this hyperlink.
+     *
+     * @param listener
+     *            the event listener to add
+     */
+    public void addHyperlinkListener(IHyperlinkListener listener) {
+        if (listeners is null)
+            listeners = new ListenerList();
+        listeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes the event listener from this hyperlink.
+     *
+     * @param listener
+     *            the event listener to remove
+     */
+    public void removeHyperlinkListener(IHyperlinkListener listener) {
+        if (listeners is null)
+            return;
+        listeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Returns the selection state of the control. When focus is gained, the
+     * state will be <samp>true </samp>; it will switch to <samp>false </samp>
+     * when the control looses focus.
+     *
+     * @return <code>true</code> if the widget has focus, <code>false</code>
+     *         otherwise.
+     */
+    public bool getSelection() {
+        return hasFocus;
+    }
+
+    /**
+     * Called when hyperlink is entered. Subclasses that override this method
+     * must call 'super'.
+     */
+    protected void handleEnter(Event e) {
+        redraw();
+        if (listeners is null)
+            return;
+        int size = listeners.size();
+        HyperlinkEvent he = new HyperlinkEvent(this, getHref(), getText(),
+                e.stateMask);
+        Object[] listenerList = listeners.getListeners();
+        for (int i = 0; i < size; i++) {
+            IHyperlinkListener listener = cast(IHyperlinkListener) listenerList[i];
+            listener.linkEntered(he);
+        }
+    }
+
+    /**
+     * Called when hyperlink is exited. Subclasses that override this method
+     * must call 'super'.
+     */
+    protected void handleExit(Event e) {
+        // disarm the link; won't activate on mouseup
+        armed = false;
+        redraw();
+        if (listeners is null)
+            return;
+        int size = listeners.size();
+        HyperlinkEvent he = new HyperlinkEvent(this, getHref(), getText(),
+                e.stateMask);
+        Object[] listenerList = listeners.getListeners();
+        for (int i = 0; i < size; i++) {
+            IHyperlinkListener listener = cast(IHyperlinkListener) listenerList[i];
+            listener.linkExited(he);
+        }
+    }
+
+    /**
+     * Called when hyperlink has been activated. Subclasses that override this
+     * method must call 'super'.
+     */
+    protected void handleActivate(Event e) {
+        // disarm link, back to normal state
+        armed = false;
+        getAccessible().setFocus(ACC.CHILDID_SELF);
+        if (listeners is null)
+            return;
+        int size = listeners.size();
+        setCursor(FormsResources.getBusyCursor());
+        HyperlinkEvent he = new HyperlinkEvent(this, getHref(), getText(),
+                e.stateMask);
+        Object[] listenerList = listeners.getListeners();
+        for (int i = 0; i < size; i++) {
+            IHyperlinkListener listener = cast(IHyperlinkListener) listenerList[i];
+            listener.linkActivated(he);
+        }
+        if (!isDisposed())
+            setCursor(FormsResources.getHandCursor());
+    }
+
+    /**
+     * Sets the object associated with this hyperlink. Concrete implementation
+     * of this class can use if to store text, URLs or model objects that need
+     * to be processed on hyperlink events.
+     *
+     * @param href
+     *            the hyperlink object reference
+     */
+    public void setHref(Object href) {
+        setData("href", href); //$NON-NLS-1$
+    }
+
+    /**
+     * Returns the object associated with this hyperlink.
+     *
+     * @see #setHref
+     * @return the hyperlink object reference
+     */
+    public Object getHref() {
+        return getData("href"); //$NON-NLS-1$
+    }
+
+    /**
+     * Returns the textual representation of this hyperlink suitable for showing
+     * in tool tips or on the status line.
+     *
+     * @return the hyperlink text
+     */
+    public String getText() {
+        return getToolTipText();
+    }
+
+    /**
+     * Paints the hyperlink as a reaction to the provided paint event.
+     *
+     * @param gc
+     *            graphic context
+     */
+    protected abstract void paintHyperlink(GC gc);
+
+    /**
+     * Paints the control as a reaction to the provided paint event.
+     *
+     * @param e
+     *            the paint event
+     */
+    protected void paint(PaintEvent e) {
+        GC gc = e.gc;
+        Rectangle clientArea = getClientArea();
+        if (clientArea.width is 0 || clientArea.height is 0)
+            return;
+        paintHyperlink(gc);
+        if (paintFocus && hasFocus) {
+            Rectangle carea = getClientArea();
+            gc.setForeground(getForeground());
+            gc.drawFocus(0, 0, carea.width, carea.height);
+        }
+    }
+
+    private void handleMouseDown(Event e) {
+        if (e.button !is 1)
+            return;
+        // armed and ready to activate on mouseup
+        armed = true;
+    }
+
+    private void handleMouseUp(Event e) {
+        if (!armed || e.button !is 1)
+            return;
+        Point size = getSize();
+        // Filter out mouse up events outside
+        // the link. This can happen when mouse is
+        // clicked, dragged outside the link, then
+        // released.
+        if (e.x < 0)
+            return;
+        if (e.y < 0)
+            return;
+        if (e.x >= size.x)
+            return;
+        if (e.y >= size.y)
+            return;
+        handleActivate(e);
+    }
+
+    private void handleMouseMove(Event e) {
+        // disarm link if we move out of bounds
+        if (armed) {
+            Point size = getSize();
+            armed = (e.x >= 0 && e.y >= 0 && e.x < size.x && e.y < size.y);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.eclipse.swt.widgets.Control#setEnabled(bool)
+     */
+
+    public void setEnabled (bool enabled) {
+        super.setEnabled(enabled);
+        redraw();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ColumnLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,284 @@
+/*******************************************************************************
+ * 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
+ *     dinko.ivanov@sap.com - patch #70790
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.ui.forms.widgets.ColumnLayout;
+
+import org.eclipse.ui.forms.widgets.ILayoutExtension;
+import org.eclipse.ui.forms.widgets.ColumnLayoutData;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Layout;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * This layout manager arranges children of the composite parent in vertical
+ * columns. All the columns are identical size and children are stretched
+ * horizontally to fill the column width. The goal is to give layout some
+ * reasonable range of column numbers to allow it to handle various parent
+ * widths. That way, column number will drop to the lowest number in the range
+ * when width decreases, and grow up to the highest number in the range when
+ * allowed by the parent width.
+ * <p>
+ * In addition, the layout attempts to 'fill the space' equally i.e. to avoid
+ * large gaps at the and of the last column.
+ * <p>
+ * Child controls are layed out according to their 'natural' (preferred) size.
+ * For 'stretchy' controls that do not have natural preferred size, it is
+ * possible to set width and/or height hints using ColumnLayoutData objects.
+ *
+ * @see ColumnLayoutData
+ * @since 3.0
+ */
+public final class ColumnLayout : Layout, ILayoutExtension {
+    /**
+     * Minimum number of columns (default is 1).
+     */
+    public int minNumColumns = 1;
+    /**
+     * Maximum number of columns (default is 3).
+     */
+    public int maxNumColumns = 3;
+    /**
+     * Horizontal spacing between columns (default is 5).
+     */
+    public int horizontalSpacing = 5;
+    /**
+     * Vertical spacing between controls (default is 5).
+     */
+    public int verticalSpacing = 5;
+    /**
+     * Top margin (default is 5).
+     */
+    public int topMargin = 5;
+    /**
+     * Left margin (default is 5).
+     */
+    public int leftMargin = 5;
+    /**
+     * Bottom margin (default is 5).
+     */
+    public int bottomMargin = 5;
+    /**
+     * Right margin (default is 5).
+     */
+    public int rightMargin = 5;
+
+    /**
+     * Creates a new instance of the column layout.
+     */
+    public this() {
+    }
+
+    /+protected+/ Point computeSize(Composite composite, int wHint, int hHint, bool flushCache) {
+        if (wHint is 0)
+            return computeSize(composite, wHint, hHint, minNumColumns);
+        else if (wHint is SWT.DEFAULT)
+            return computeSize(composite, wHint, hHint, maxNumColumns);
+        else
+            return computeSize(composite, wHint, hHint, -1);
+    }
+
+    private Point computeSize(Composite parent, int wHint, int hHint, int ncolumns) {
+        Control[] children = parent.getChildren();
+        int cwidth = 0;
+        int cheight = 0;
+        Point[] sizes = new Point[children.length];
+
+        int cwHint = SWT.DEFAULT;
+        if (ncolumns !is -1) {
+            cwHint = wHint - leftMargin - rightMargin - (ncolumns - 1) * horizontalSpacing;
+            if (cwHint <= 0)
+                cwHint = 0;
+            else
+                cwHint /= ncolumns;
+        }
+
+        for (int i = 0; i < children.length; i++) {
+            sizes[i] = computeControlSize(children[i], cwHint);
+            cwidth = Math.max(cwidth, sizes[i].x);
+            cheight += sizes[i].y;
+        }
+        if (ncolumns is -1) {
+            // must compute
+            ncolumns = (wHint - leftMargin - rightMargin - horizontalSpacing) / (cwidth + horizontalSpacing);
+            ncolumns = Math.min(ncolumns, children.length);
+            ncolumns = Math.max(ncolumns, minNumColumns);
+            ncolumns = Math.min(ncolumns, maxNumColumns);
+        }
+        int perColHeight = cheight / ncolumns;
+        if (cheight % ncolumns !is 0)
+            perColHeight++;
+        int colHeight = 0;
+        int[] heights = new int[ncolumns];
+        int ncol = 0;
+
+        bool fillIn = false;
+
+        for (int i = 0; i < sizes.length; i++) {
+            int childHeight = sizes[i].y;
+            if (i>0 && colHeight + childHeight > perColHeight) {
+                heights[ncol] = colHeight;
+                ncol++;
+                if (ncol is ncolumns || fillIn) {
+                    // overflow - start filling in
+                    fillIn = true;
+                    ncol = findShortestColumn(heights);
+                }
+                colHeight = heights[ncol];
+            }
+            if (colHeight > 0)
+                colHeight += verticalSpacing;
+            colHeight += childHeight;
+        }
+        heights[ncol] = Math.max(heights[ncol],colHeight);
+
+        Point size = new Point(0, 0);
+        for (int i = 0; i < ncolumns; i++) {
+            size.y = Math.max(size.y, heights[i]);
+        }
+        size.x = cwidth * ncolumns + (ncolumns - 1) * horizontalSpacing;
+        size.x += leftMargin + rightMargin;
+        //System.out.println("ColumnLayout: whint="+wHint+", size.x="+size.x);
+        size.y += topMargin + bottomMargin;
+        return size;
+    }
+
+    private Point computeControlSize(Control c, int wHint) {
+        ColumnLayoutData cd = cast(ColumnLayoutData) c.getLayoutData();
+        int widthHint = cd !is null ? cd.widthHint : wHint;
+        int heightHint = cd !is null ? cd.heightHint : SWT.DEFAULT;
+        return c.computeSize(widthHint, heightHint);
+    }
+
+    private int findShortestColumn(int[] heights) {
+        int result = 0;
+        int height = Integer.MAX_VALUE;
+        for (int i = 0; i < heights.length; i++) {
+            if (height > heights[i]) {
+                height = heights[i];
+                result = i;
+            }
+        }
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite,
+     *      bool)
+     */
+    protected void layout(Composite parent, bool flushCache) {
+        Control[] children = parent.getChildren();
+        Rectangle carea = parent.getClientArea();
+        int cwidth = 0;
+        int cheight = 0;
+        Point[] sizes = new Point[children.length];
+        for (int i = 0; i < children.length; i++) {
+            sizes[i] = computeControlSize(children[i], SWT.DEFAULT);
+            cwidth = Math.max(cwidth, sizes[i].x);
+            cheight += sizes[i].y;
+        }
+        int ncolumns = (carea.width - leftMargin - rightMargin - horizontalSpacing) / (cwidth + horizontalSpacing);
+        ncolumns = Math.min(ncolumns, children.length);
+        ncolumns = Math.max(ncolumns, minNumColumns);
+        ncolumns = Math.min(ncolumns, maxNumColumns);
+        int realWidth = (carea.width - leftMargin - rightMargin + horizontalSpacing) / ncolumns - horizontalSpacing;
+//      int childrenPerColumn = children.length / ncolumns;
+//      if (children.length % ncolumns !is 0)
+//          childrenPerColumn++;
+//      int colWidth = 0;
+
+        int fillWidth = Math.max(cwidth, realWidth);
+
+        int perColHeight = cheight / ncolumns;
+        if (cheight % ncolumns !is 0)
+            perColHeight++;
+
+        int colHeight = 0;
+        int[] heights = new int[ncolumns];
+        int ncol = 0;
+        int x = leftMargin;
+        bool fillIn = false;
+
+        for (int i = 0; i < sizes.length; i++) {
+            Control child = children[i];
+            Point csize = sizes[i];
+            ColumnLayoutData cd = cast(ColumnLayoutData) child.getLayoutData();
+            int align_ = cd !is null ? cd.horizontalAlignment : ColumnLayoutData.FILL;
+            int childWidth = align_ is ColumnLayoutData.FILL ? fillWidth : csize.x;
+
+            if (i>0 && colHeight + csize.y > perColHeight) {
+                heights[ncol] = colHeight;
+                if (fillIn || ncol is ncolumns-1) {
+                    // overflow - start filling in
+                    fillIn = true;
+                    ncol = findShortestColumn(heights);
+
+                    x = leftMargin + ncol * (fillWidth + horizontalSpacing);
+
+                }
+                else {
+                    ncol++;
+                    x += fillWidth + horizontalSpacing;
+                }
+                colHeight = heights[ncol];
+            }
+            if (colHeight > 0)
+                colHeight += verticalSpacing;
+
+
+            switch (align_) {
+                case ColumnLayoutData.LEFT :
+                case ColumnLayoutData.FILL :
+                    child.setBounds(x, topMargin+colHeight, childWidth, csize.y);
+                    break;
+                case ColumnLayoutData.RIGHT :
+                    child.setBounds(x + fillWidth - childWidth, topMargin+colHeight, childWidth, csize.y);
+                    break;
+                case ColumnLayoutData.CENTER :
+                    child.setBounds(x + fillWidth / 2 - childWidth / 2, topMargin+colHeight, childWidth, csize.y);
+                    break;
+                default:
+            }
+
+            colHeight += csize.y;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.widgets.ILayoutExtension#computeMaximumWidth(org.eclipse.swt.widgets.Composite,
+     *      bool)
+     */
+    public int computeMaximumWidth(Composite parent, bool changed) {
+        return computeSize(parent, SWT.DEFAULT, SWT.DEFAULT, changed).x;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.widgets.ILayoutExtension#computeMinimumWidth(org.eclipse.swt.widgets.Composite,
+     *      bool)
+     */
+    public int computeMinimumWidth(Composite parent, bool changed) {
+        return computeSize(parent, 0, SWT.DEFAULT, changed).x;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ColumnLayoutData.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.ui.forms.widgets.ColumnLayoutData;
+
+import java.lang.all;
+import org.eclipse.swt.SWT;
+/**
+ * This class is used to store layout data for the <code>ColumnLayout</code>
+ * class. You can control width and height hints, as well as horizontal
+ * alignment using instances of this class. As with other layouts, they are not
+ * required to get the default behaviour.
+ *
+ * @see ColumnLayout
+ * @since 3.0
+ */
+public final class ColumnLayoutData {
+    /**
+     * Width hint that will be used instead of the computed control width when
+     * used in conjunction with <code>ColumnLayout</code> class (default is
+     * SWT.DEFAULT).
+     */
+    public int widthHint = SWT.DEFAULT;
+    /**
+     * Height hint that will be used instead of the computed control height
+     * when used in conjunction with <code>ColumnLayout</code> class (default
+     * is SWT.DEFAULT).
+     */
+    public int heightHint = SWT.DEFAULT;
+    /**
+     * Horizontal alignment constant - control will be aligned to the left.
+     */
+    public static const int LEFT = 1;
+    /**
+     * Horizontal alignment constant - control will be aligned to the right.
+     */
+    public static const int CENTER = 2;
+    /**
+     * Horizontal alignment constant - control will be centered.
+     */
+    public static const int RIGHT = 3;
+    /**
+     * Horizontal alignment constant - control will fill the column.
+     */
+    public static const int FILL = 4;
+    /**
+     * Horizontal alignment variable (default is FILL).
+     */
+    public int horizontalAlignment = FILL;
+    /**
+     * Convinience constructor for the class.
+     *
+     * @param wHint
+     *            width hint for the control
+     * @param hHint
+     *            height hint for the control
+     */
+    public this(int wHint, int hHint) {
+        this.widthHint = wHint;
+        this.heightHint = hHint;
+    }
+    /**
+     * Convinience constructor for the class.
+     *
+     * @param wHint
+     *            width hint for the control
+     */
+    public this(int wHint) {
+        this.widthHint = wHint;
+    }
+    /**
+     * The default constructor.
+     *
+     */
+    public this() {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ExpandableComposite.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1141 @@
+/*******************************************************************************
+ * 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
+ *     Kai Nacke - Fix for Bug 202382
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.ui.forms.widgets.ExpandableComposite;
+
+import org.eclipse.ui.forms.widgets.ToggleHyperlink;
+import org.eclipse.ui.forms.widgets.ILayoutExtension;
+import org.eclipse.ui.forms.widgets.SizeCache;
+import org.eclipse.ui.forms.widgets.Twistie;
+import org.eclipse.ui.forms.widgets.TreeNode;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.ui.forms.events.ExpansionEvent;
+import org.eclipse.ui.forms.events.HyperlinkAdapter;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.events.IExpansionListener;
+import org.eclipse.ui.internal.forms.widgets.FormUtil;
+import org.eclipse.ui.internal.forms.widgets.FormsResources;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * This composite is capable of expanding or collapsing a single client that is
+ * its direct child. The composite renders an expansion toggle affordance
+ * (according to the chosen style), and a title that also acts as a hyperlink
+ * (can be selected and is traversable). The client is layed out below the title
+ * when expanded, or hidden when collapsed.
+ * <p>
+ * The widget can be instantiated as-is, or subclassed to modify some aspects of
+ * it. *
+ * <p>
+ * Since 3.1, left/right arrow keys can be used to control the expansion state.
+ * If several expandable composites are created in the same parent, up/down
+ * arrow keys can be used to traverse between them. Expandable text accepts
+ * mnemonics and mnemonic activation will toggle the expansion state.
+ *
+ * <p>
+ * While expandable composite recognize that different styles can be used to
+ * render the title bar, and even defines the constants for these styles (<code>TITLE_BAR</code>
+ * and <code>SHORT_TITLE_BAR</code> the actual painting is done in the
+ * subclasses.
+ *
+ * @see Section
+ * @since 3.0
+ */
+public class ExpandableComposite : Canvas {
+    /**
+     * If this style is used, a twistie will be used to render the expansion
+     * toggle.
+     */
+    public static const int TWISTIE = 1 << 1;
+
+    /**
+     * If this style is used, a tree node with either + or - signs will be used
+     * to render the expansion toggle.
+     */
+    public static const int TREE_NODE = 1 << 2;
+
+    /**
+     * If this style is used, the title text will be rendered as a hyperlink
+     * that can individually accept focus. Otherwise, it will still act like a
+     * hyperlink, but only the toggle control will accept focus.
+     */
+    public static const int FOCUS_TITLE = 1 << 3;
+
+    /**
+     * If this style is used, the client origin will be vertically aligned with
+     * the title text. Otherwise, it will start at x = 0.
+     */
+    public static const int CLIENT_INDENT = 1 << 4;
+
+    /**
+     * If this style is used, computed size of the composite will take the
+     * client width into consideration only in the expanded state. Otherwise,
+     * client width will always be taken into acount.
+     */
+    public static const int COMPACT = 1 << 5;
+
+    /**
+     * If this style is used, the control will be created in the expanded state.
+     * This state can later be changed programmatically or by the user if
+     * TWISTIE or TREE_NODE style is used.
+     */
+    public static const int EXPANDED = 1 << 6;
+
+    /**
+     * If this style is used, title bar decoration will be painted behind the
+     * text.
+     */
+    public static const int TITLE_BAR = 1 << 8;
+
+    /**
+     * If this style is used, a short version of the title bar decoration will
+     * be painted behind the text. This style is useful when a more descrete
+     * option is needed for the title bar.
+     *
+     * @since 3.1
+     */
+    public static const int SHORT_TITLE_BAR = 1 << 9;
+
+    /**
+     * If this style is used, title will not be rendered.
+     */
+    public static const int NO_TITLE = 1 << 12;
+
+    /**
+     * By default, text client is right-aligned. If this style is used, it will
+     * be positioned after the text control and vertically centered with it.
+     */
+    public static const int LEFT_TEXT_CLIENT_ALIGNMENT = 1 << 13;
+
+    /**
+     * Width of the margin that will be added around the control (default is 0).
+     */
+    public int marginWidth = 0;
+
+    /**
+     * Height of the margin that will be added around the control (default is
+     * 0).
+     */
+    public int marginHeight = 0;
+
+    /**
+     * Vertical spacing between the title area and the composite client control
+     * (default is 3).
+     */
+    public int clientVerticalSpacing = 3;
+
+    /**
+     * Vertical spacing between the title area and the description control
+     * (default is 0). The description control is normally placed at the new
+     * line as defined in the font used to render it. This value will be added
+     * to it.
+     *
+     * @since 3.3
+     */
+    public int descriptionVerticalSpacing = 0;
+
+    /**
+     * Horizontal margin around the inside of the title bar area when TITLE_BAR
+     * or SHORT_TITLE_BAR style is used. This variable is not used otherwise.
+     *
+     * @since 3.3
+     */
+    public int titleBarTextMarginWidth = 6;
+
+    /**
+     * The toggle widget used to expand the composite.
+     */
+    protected ToggleHyperlink toggle;
+    package ToggleHyperlink toggle_package(){
+        return toggle;
+    }
+    package ToggleHyperlink toggle_package(ToggleHyperlink t){
+        return (toggle = t);
+    }
+
+    /**
+     * The text label for the title.
+     */
+    protected Control textLabel;
+    package Control textLabel_package(){
+        return textLabel;
+    }
+    package Control textLabel_package(Control t){
+        return (textLabel = t);
+    }
+
+    /**
+     * @deprecated this variable was left as protected by mistake. It will be
+     *             turned into static and hidden in the future versions. Do not
+     *             use them and do not change its value.
+     */
+    protected int VGAP = 3;
+    /**
+     * @deprecated this variable was left as protected by mistake. It will be
+     *             turned into static and hidden in the future versions. Do not
+     *             use it and do not change its value.
+     */
+    protected int GAP = 4;
+
+    static const int IGAP = 4;
+    static const int IVGAP = 3;
+
+    private static Point NULL_SIZE_;
+    private static Point NULL_SIZE(){
+        if( NULL_SIZE_ is null ){
+            synchronized(ExpandableComposite.classinfo ){
+                if( NULL_SIZE_ is null ){
+                    NULL_SIZE_ = new Point(0, 0);
+                }
+            }
+        }
+        return NULL_SIZE_;
+    }
+
+    private static const int VSPACE = 3;
+
+    private static const int SEPARATOR_HEIGHT = 2;
+
+    private int expansionStyle = TWISTIE | FOCUS_TITLE | EXPANDED;
+
+    private bool expanded;
+
+    private Control textClient;
+
+    private Control client;
+
+    private ListenerList listeners;
+
+    private Color titleBarForeground;
+
+    private class ExpandableLayout : Layout, ILayoutExtension {
+
+        private SizeCache toggleCache;
+
+        private SizeCache textClientCache;
+
+        private SizeCache textLabelCache;
+
+        private SizeCache descriptionCache;
+
+        private SizeCache clientCache;
+
+        this(){
+            toggleCache = new SizeCache();
+            textClientCache = new SizeCache();
+            textLabelCache = new SizeCache();
+            descriptionCache = new SizeCache();
+            clientCache = new SizeCache();
+        }
+        private void initCache(bool shouldFlush) {
+            toggleCache.setControl(toggle);
+            textClientCache.setControl(textClient);
+            textLabelCache.setControl(textLabel);
+            descriptionCache.setControl(getDescriptionControl());
+            clientCache.setControl(client);
+
+            if (shouldFlush) {
+                toggleCache.flush();
+                textClientCache.flush();
+                textLabelCache.flush();
+                descriptionCache.flush();
+                clientCache.flush();
+            }
+        }
+
+        protected void layout(Composite parent, bool changed) {
+            initCache(changed);
+
+            Rectangle clientArea = parent.getClientArea();
+            int thmargin = 0;
+            int tvmargin = 0;
+
+            if (hasTitleBar()) {
+                thmargin = titleBarTextMarginWidth;
+                tvmargin = IVGAP;
+            }
+            int x = marginWidth + thmargin;
+            int y = marginHeight + tvmargin;
+            Point tsize = NULL_SIZE;
+            Point tcsize = NULL_SIZE;
+            if (toggle !is null)
+                tsize = toggleCache.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+            int twidth = clientArea.width - marginWidth - marginWidth
+                    - thmargin - thmargin;
+            if (tsize.x > 0)
+                twidth -= tsize.x + IGAP;
+            if (textClient !is null) {
+                tcsize = textClientCache.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+            }
+            Point size = NULL_SIZE;
+            if (textLabel !is null) {
+                if (tcsize.x > 0 && FormUtil.isWrapControl(textClient)) {
+                    size = textLabelCache.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+                    if (twidth < size.x + IGAP + tcsize.x) {
+                        twidth -= IGAP;
+                        if (null !is cast(Label)textLabel )
+                            size = FormUtil.computeWrapSize(new GC(textLabel), (cast(Label)textLabel).getText(), cast(int) Math.round(twidth*(size.x/cast(float)(size.x+tcsize.x))));
+                        else
+                            size = textLabelCache.computeSize(cast(int) Math.round(twidth*(size.x/cast(float)(size.x+tcsize.x))), SWT.DEFAULT);
+                        tcsize = textClientCache.computeSize(twidth-size.x, SWT.DEFAULT);
+                    }
+                }
+                else {
+                    if (tcsize.x > 0)
+                        twidth -= tcsize.x + IGAP;
+                    size = textLabelCache.computeSize(twidth, SWT.DEFAULT);
+                }
+            }
+            if (null !is cast(Label)textLabel ) {
+                Point defSize = textLabelCache.computeSize(SWT.DEFAULT,
+                        SWT.DEFAULT);
+                if (defSize.y is size.y) {
+                    // One line - pick the smaller of the two widths
+                    size.x = Math.min(defSize.x, size.x);
+                }
+            }
+            if (toggle !is null) {
+                GC gc = new GC(this.outer);
+                gc.setFont(getFont());
+                FontMetrics fm = gc.getFontMetrics();
+                int textHeight = fm.getHeight();
+                gc.dispose();
+                if (textClient !is null
+                        && (expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) !is 0) {
+                    textHeight = Math.max(textHeight, tcsize.y);
+                }
+                int ty = textHeight / 2 - tsize.y / 2 + 1;
+                ty = Math.max(ty, 0);
+                ty += marginHeight + tvmargin;
+                toggle.setLocation(x, ty);
+                toggle.setSize(tsize);
+                x += tsize.x + IGAP;
+            }
+            if (textLabel !is null) {
+                int ty = y;
+                if (textClient !is null
+                        && (expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) !is 0) {
+                    if (size.y < tcsize.y)
+                        ty = tcsize.y / 2 - size.y / 2 + marginHeight
+                                + tvmargin;
+                }
+                textLabelCache.setBounds(x, ty, size.x, size.y);
+            }
+            if (textClient !is null) {
+                int tcx;
+                if ((expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) !is 0) {
+                    tcx = x + size.x + GAP;
+                } else {
+                    tcx = clientArea.width - tcsize.x - marginWidth - thmargin;
+                }
+                textClientCache.setBounds(tcx, y, tcsize.x, tcsize.y);
+            }
+            int tbarHeight = 0;
+            if (size.y > 0)
+                tbarHeight = size.y;
+            if (tcsize.y > 0)
+                tbarHeight = Math.max(tbarHeight, tcsize.y);
+            y += tbarHeight;
+            if (hasTitleBar())
+                y += tvmargin;
+            if (getSeparatorControl() !is null) {
+                y += VSPACE;
+                getSeparatorControl().setBounds(marginWidth, y,
+                        clientArea.width - marginWidth - marginWidth,
+                        SEPARATOR_HEIGHT);
+                y += SEPARATOR_HEIGHT;
+                if (expanded)
+                    y += VSPACE;
+            }
+            if (expanded) {
+                int areaWidth = clientArea.width - marginWidth - marginWidth
+                        - thmargin - thmargin;
+                int cx = marginWidth + thmargin;
+                if ((expansionStyle & CLIENT_INDENT) !is 0) {
+                    cx = x;
+                    areaWidth -= x;
+                }
+                if (client !is null) {
+                    Point dsize = null;
+                    Control desc = getDescriptionControl();
+                    if (desc !is null) {
+                        dsize = descriptionCache.computeSize(areaWidth,
+                                SWT.DEFAULT);
+                        y += descriptionVerticalSpacing;
+                        descriptionCache.setBounds(cx, y, areaWidth, dsize.y);
+                        y += dsize.y + clientVerticalSpacing;
+                    } else {
+                        y += clientVerticalSpacing;
+                        if (getSeparatorControl() !is null)
+                            y -= VSPACE;
+                    }
+                    int cwidth = areaWidth;
+                    int cheight = clientArea.height - marginHeight
+                            - marginHeight - y;
+                    clientCache.setBounds(cx, y, cwidth, cheight);
+                }
+            }
+        }
+
+        protected Point computeSize(Composite parent, int wHint, int hHint,
+                bool changed) {
+            initCache(changed);
+
+            int width = 0, height = 0;
+            Point tsize = NULL_SIZE;
+            int twidth = 0;
+            if (toggle !is null) {
+                tsize = toggleCache.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+                twidth = tsize.x + IGAP;
+            }
+            int thmargin = 0;
+            int tvmargin = 0;
+
+            if (hasTitleBar()) {
+                thmargin = titleBarTextMarginWidth;
+                tvmargin = IVGAP;
+            }
+            int innerwHint = wHint;
+            if (innerwHint !is SWT.DEFAULT)
+                innerwHint -= twidth + marginWidth + marginWidth + thmargin
+                        + thmargin;
+
+            int innertHint = innerwHint;
+
+            Point tcsize = NULL_SIZE;
+            if (textClient !is null) {
+                tcsize = textClientCache.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+            }
+            Point size = NULL_SIZE;
+
+            if (textLabel !is null) {
+                if (tcsize.x > 0 && FormUtil.isWrapControl(textClient)) {
+                    size = textLabelCache.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+                    if (innertHint !is SWT.DEFAULT && innertHint < size.x + IGAP + tcsize.x) {
+                        innertHint -= IGAP;
+                        if (null !is cast(Label)textLabel )
+                            size = FormUtil.computeWrapSize(new GC(textLabel), (cast(Label)textLabel).getText(), cast(int) Math.round(innertHint*(size.x/cast(float)(size.x+tcsize.x))));
+                        else
+                            size = textLabelCache.computeSize(cast(int) Math.round(innertHint*(size.x/cast(float)(size.x+tcsize.x))), SWT.DEFAULT);
+                        tcsize = textClientCache.computeSize(innertHint-size.x, SWT.DEFAULT);
+                    }
+                } else {
+                    if (innertHint !is SWT.DEFAULT && tcsize.x > 0)
+                        innertHint -= IGAP + tcsize.x;
+                    size = textLabelCache.computeSize(innertHint, SWT.DEFAULT);
+                }
+            }
+            if (null !is cast(Label)textLabel ) {
+                Point defSize = textLabelCache.computeSize(SWT.DEFAULT,
+                        SWT.DEFAULT);
+                if (defSize.y is size.y) {
+                    // One line - pick the smaller of the two widths
+                    size.x = Math.min(defSize.x, size.x);
+                }
+            }
+            if (size.x > 0)
+                width = size.x;
+            if (tcsize.x > 0)
+                width += IGAP + tcsize.x;
+            if (toggle !is null)
+                width += twidth;
+            height = tcsize.y > 0 ? Math.max(tcsize.y, size.y) : size.y;
+            if (getSeparatorControl() !is null) {
+                height += VSPACE + SEPARATOR_HEIGHT;
+                if (expanded && client !is null)
+                    height += VSPACE;
+            }
+            // if (hasTitleBar())
+            // height += VSPACE;
+            if ((expanded || (expansionStyle & COMPACT) is 0) && client !is null) {
+                int cwHint = wHint;
+                int clientIndent = 0;
+                if ((expansionStyle & CLIENT_INDENT) !is 0)
+                    clientIndent = twidth;
+
+                if (cwHint !is SWT.DEFAULT) {
+                    cwHint -= marginWidth + marginWidth + thmargin + thmargin;
+                    if ((expansionStyle & CLIENT_INDENT) !is 0)
+                        if (tcsize.x > 0)
+                            cwHint -= twidth;
+                }
+                Point dsize = null;
+                Point csize = clientCache.computeSize(FormUtil.getWidthHint(
+                        cwHint, client), SWT.DEFAULT);
+                if (getDescriptionControl() !is null) {
+                    int dwHint = cwHint;
+                    if (dwHint is SWT.DEFAULT) {
+                        dwHint = csize.x;
+                        if ((expansionStyle & CLIENT_INDENT) !is 0)
+                            dwHint -= twidth;
+                    }
+                    dsize = descriptionCache.computeSize(dwHint, SWT.DEFAULT);
+                }
+                if (dsize !is null) {
+                    width = Math.max(width, dsize.x + clientIndent);
+                    if (expanded)
+                        height += descriptionVerticalSpacing + dsize.y
+                                + clientVerticalSpacing;
+                } else {
+                    height += clientVerticalSpacing;
+                    if (getSeparatorControl() !is null)
+                        height -= VSPACE;
+                }
+                width = Math.max(width, csize.x + clientIndent);
+                if (expanded)
+                    height += csize.y;
+            }
+            if (toggle !is null)
+                height = height - size.y + Math.max(size.y, tsize.y);
+
+            Point result = new Point(width + marginWidth + marginWidth
+                    + thmargin + thmargin, height + marginHeight + marginHeight
+                    + tvmargin + tvmargin);
+            return result;
+        }
+
+        public int computeMinimumWidth(Composite parent, bool changed) {
+            return computeSize(parent, 0, SWT.DEFAULT, changed).x;
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.ui.forms.parts.ILayoutExtension#computeMinimumWidth(org.eclipse.swt.widgets.Composite,
+         *      bool)
+         */
+        public int computeMaximumWidth(Composite parent, bool changed) {
+            return computeSize(parent, SWT.DEFAULT, SWT.DEFAULT, changed).x;
+        }
+    }
+
+    /**
+     * Creates an expandable composite using a TWISTIE toggle.
+     *
+     * @param parent
+     *            the parent composite
+     * @param style
+     *            SWT style bits
+     */
+    public this(Composite parent, int style) {
+        this(parent, style, TWISTIE);
+    }
+
+    /**
+     * Creates the expandable composite in the provided parent.
+     *
+     * @param parent
+     *            the parent
+     * @param style
+     *            the control style (as expected by SWT subclass)
+     * @param expansionStyle
+     *            the style of the expansion widget (TREE_NODE, TWISTIE,
+     *            CLIENT_INDENT, COMPACT, FOCUS_TITLE,
+     *            LEFT_TEXT_CLIENT_ALIGNMENT, NO_TITLE)
+     */
+    public this(Composite parent, int style, int expansionStyle) {
+        listeners = new ListenerList();
+        super(parent, style);
+        this.expansionStyle = expansionStyle;
+        if ((expansionStyle & TITLE_BAR) !is 0)
+            setBackgroundMode(SWT.INHERIT_DEFAULT);
+        super.setLayout(new ExpandableLayout());
+        if (hasTitleBar()) {
+            this.addPaintListener(new class PaintListener {
+                public void paintControl(PaintEvent e) {
+                    onPaint(e);
+                }
+            });
+        }
+        if ((expansionStyle & TWISTIE) !is 0)
+            toggle = new Twistie(this, SWT.NULL);
+        else if ((expansionStyle & TREE_NODE) !is 0)
+            toggle = new TreeNode(this, SWT.NULL);
+        else
+            expanded = true;
+        if ((expansionStyle & EXPANDED) !is 0)
+            expanded = true;
+        if (toggle !is null) {
+            toggle.setExpanded(expanded);
+            toggle.addHyperlinkListener(new class HyperlinkAdapter {
+                public void linkActivated(HyperlinkEvent e) {
+                    toggleState();
+                }
+            });
+            toggle.addPaintListener(new class PaintListener {
+                public void paintControl(PaintEvent e) {
+                    if (null !is cast(Label)textLabel  && !isFixedStyle())
+                        textLabel.setForeground(toggle.hover_package ? toggle
+                                .getHoverDecorationColor()
+                                : getTitleBarForeground());
+                }
+            });
+            toggle.addKeyListener(new class KeyAdapter {
+                public void keyPressed(KeyEvent e) {
+                    if (e.keyCode is SWT.ARROW_UP) {
+                        verticalMove(false);
+                        e.doit = false;
+                    } else if (e.keyCode is SWT.ARROW_DOWN) {
+                        verticalMove(true);
+                        e.doit = false;
+                    }
+                }
+            });
+            if ((getExpansionStyle()&FOCUS_TITLE) is 0) {
+                toggle.paintFocus=false;
+                toggle.addFocusListener(new class FocusListener {
+                    public void focusGained(FocusEvent e) {
+                        textLabel.redraw();
+                    }
+
+                    public void focusLost(FocusEvent e) {
+                        textLabel.redraw();
+                    }
+                });
+            }
+        }
+        if ((expansionStyle & FOCUS_TITLE) !is 0) {
+            Hyperlink link = new Hyperlink(this, SWT.WRAP);
+            link.addHyperlinkListener(new class HyperlinkAdapter {
+                public void linkActivated(HyperlinkEvent e) {
+                    programmaticToggleState();
+                }
+            });
+            textLabel = link;
+        } else if ((expansionStyle & NO_TITLE) is 0) {
+            final Label label = new Label(this, SWT.WRAP);
+            if (!isFixedStyle()) {
+                label.setCursor(FormsResources.getHandCursor());
+    // SWT FIXME: workaround for DMD anonymous class, nested function problem
+                Listener listener = dgListener( &tst, label );
+                label.addListener(SWT.MouseDown, listener);
+                label.addListener(SWT.MouseUp, listener);
+                label.addListener(SWT.MouseEnter, listener);
+                label.addListener(SWT.MouseExit, listener);
+                label.addListener(SWT.Paint, listener);
+            }
+            textLabel = label;
+        }
+        if (textLabel !is null) {
+            textLabel.setMenu(getMenu());
+            textLabel.addTraverseListener(new class TraverseListener {
+                public void keyTraversed(TraverseEvent e) {
+                    if (e.detail is SWT.TRAVERSE_MNEMONIC) {
+                        // steal the mnemonic
+                        if (!isVisible() || !isEnabled())
+                            return;
+                        if (FormUtil.mnemonicMatch(getText(), e.character)) {
+                            e.doit = false;
+                            if (!isFixedStyle()) {
+                                programmaticToggleState();
+                            }
+                            setFocus();
+                        }
+                    }
+                }
+            });
+        }
+    }
+
+    // SWT FIXME: workaround for DMD anonymous class, nested function problem
+                void tst(Event e, Label label_) {
+                    switch (e.type) {
+                    case SWT.MouseDown:
+                        if (toggle !is null)
+                            toggle.setFocus();
+                        break;
+                    case SWT.MouseUp:
+                        label_.setCursor(FormsResources.getBusyCursor());
+                        programmaticToggleState();
+                        label_.setCursor(FormsResources.getHandCursor());
+                        break;
+                    case SWT.MouseEnter:
+                        if (toggle !is null) {
+                            label_.setForeground(toggle
+                                    .getHoverDecorationColor());
+                            toggle.hover_package = true;
+                            toggle.redraw();
+                        }
+                        break;
+                    case SWT.MouseExit:
+                        if (toggle !is null) {
+                            label_.setForeground(getTitleBarForeground());
+                            toggle.hover_package = false;
+                            toggle.redraw();
+                        }
+                        break;
+                    case SWT.Paint:
+                        if (toggle !is null) {
+                            paintTitleFocus(e.gc);
+                        }
+                        break;
+                    default:
+                    }
+                }
+    /* (non-Javadoc)
+     * @see org.eclipse.swt.widgets.Control#forceFocus()
+     */
+    public bool forceFocus() {
+        return false;
+    }
+
+    /**
+     * Overrides 'super' to pass the menu to the text label.
+     *
+     * @param menu
+     *            the menu from the parent to attach to this control.
+     */
+
+    public void setMenu(Menu menu) {
+        if (textLabel !is null)
+            textLabel.setMenu(menu);
+        super.setMenu(menu);
+    }
+
+    /**
+     * Prevents assignment of the layout manager - expandable composite uses its
+     * own layout.
+     */
+    public final void setLayout(Layout layout) {
+    }
+
+    /**
+     * Sets the background of all the custom controls in the expandable.
+     */
+    public void setBackground(Color bg) {
+        super.setBackground(bg);
+        if ((getExpansionStyle() & TITLE_BAR) is 0) {
+            if (textLabel !is null)
+                textLabel.setBackground(bg);
+            if (toggle !is null)
+                toggle.setBackground(bg);
+        }
+    }
+
+    /**
+     * Sets the foreground of all the custom controls in the expandable.
+     */
+    public void setForeground(Color fg) {
+        super.setForeground(fg);
+        if (textLabel !is null)
+            textLabel.setForeground(fg);
+        if (toggle !is null)
+            toggle.setForeground(fg);
+    }
+
+    /**
+     * Sets the color of the toggle control.
+     *
+     * @param c
+     *            the color object
+     */
+    public void setToggleColor(Color c) {
+        if (toggle !is null)
+            toggle.setDecorationColor(c);
+    }
+
+    /**
+     * Sets the active color of the toggle control (when the mouse enters the
+     * toggle area).
+     *
+     * @param c
+     *            the active color object
+     */
+    public void setActiveToggleColor(Color c) {
+        if (toggle !is null)
+            toggle.setHoverDecorationColor(c);
+    }
+
+    /**
+     * Sets the fonts of all the custom controls in the expandable.
+     */
+    public void setFont(Font font) {
+        super.setFont(font);
+        if (textLabel !is null)
+            textLabel.setFont(font);
+        if (toggle !is null)
+            toggle.setFont(font);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.swt.widgets.Control#setEnabled(bool)
+     */
+
+    public void setEnabled(bool enabled) {
+        if (textLabel !is null)
+            textLabel.setEnabled(enabled);
+        if (toggle !is null)
+            toggle.setEnabled(enabled);
+        super.setEnabled(enabled);
+    }
+
+    /**
+     * Sets the client of this expandable composite. The client must not be
+     * <samp>null </samp> and must be a direct child of this container.
+     *
+     * @param client
+     *            the client that will be expanded or collapsed
+     */
+    public void setClient(Control client) {
+        Assert.isTrue(client !is null && client.getParent().opEquals(this));
+        this.client = client;
+    }
+
+    /**
+     * Returns the current expandable client.
+     *
+     * @return the client control
+     */
+    public Control getClient() {
+        return client;
+    }
+
+    /**
+     * Sets the title of the expandable composite. The title will act as a
+     * hyperlink and activating it will toggle the client between expanded and
+     * collapsed state.
+     *
+     * @param title
+     *            the new title string
+     * @see #getText()
+     */
+    public void setText(String title) {
+        if (null !is cast(Label)textLabel )
+            (cast(Label) textLabel).setText(title);
+        else if (null !is cast(Hyperlink)textLabel )
+            (cast(Hyperlink) textLabel).setText(title);
+    }
+
+    /**
+     * Returns the title string.
+     *
+     * @return the title string
+     * @see #setText(String)
+     */
+    public String getText() {
+        if (null !is cast(Label)textLabel )
+            return (cast(Label) textLabel).getText();
+        else if (null !is cast(Hyperlink)textLabel )
+            return (cast(Hyperlink) textLabel).getText();
+        else
+            return ""; //$NON-NLS-1$
+    }
+
+    /**
+     * Tests the expanded state of the composite.
+     *
+     * @return <samp>true </samp> if expanded, <samp>false </samp> if collapsed.
+     */
+    public bool isExpanded() {
+        return expanded;
+    }
+
+    /**
+     * Returns the bitwise-ORed style bits for the expansion control.
+     *
+     * @return the bitwise-ORed style bits for the expansion control
+     */
+    public int getExpansionStyle() {
+        return expansionStyle;
+    }
+
+    /**
+     * Programmatically changes expanded state.
+     *
+     * @param expanded
+     *            the new expanded state
+     */
+    public void setExpanded(bool expanded) {
+        internalSetExpanded(expanded);
+        if (toggle !is null)
+            toggle.setExpanded(expanded);
+    }
+
+    /**
+     * Performs the expansion state change for the expandable control.
+     *
+     * @param expanded
+     *            the expansion state
+     */
+    protected void internalSetExpanded(bool expanded) {
+        if (this.expanded !is expanded) {
+            this.expanded = expanded;
+            if (getDescriptionControl() !is null)
+                getDescriptionControl().setVisible(expanded);
+            if (client !is null)
+                client.setVisible(expanded);
+            layout();
+        }
+    }
+
+    /**
+     * Adds the listener that will be notified when the expansion state changes.
+     *
+     * @param listener
+     *            the listener to add
+     */
+    public void addExpansionListener(IExpansionListener listener) {
+        listeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes the expansion listener.
+     *
+     * @param listener
+     *            the listner to remove
+     */
+    public void removeExpansionListener(IExpansionListener listener) {
+        listeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * If TITLE_BAR or SHORT_TITLE_BAR style is used, title bar decoration will
+     * be painted behind the text in this method. The default implementation
+     * does nothing - subclasses are responsible for rendering the title area.
+     *
+     * @param e
+     *            the paint event
+     */
+    protected void onPaint(PaintEvent e) {
+    }
+
+    /**
+     * Returns description control that will be placed under the title if
+     * present.
+     *
+     * @return the description control or <samp>null </samp> if not used.
+     */
+    protected Control getDescriptionControl() {
+        return null;
+    }
+
+    /**
+     * Returns the separator control that will be placed between the title and
+     * the description if present.
+     *
+     * @return the separator control or <samp>null </samp> if not used.
+     */
+    protected Control getSeparatorControl() {
+        return null;
+    }
+
+    /**
+     * Computes the size of the expandable composite.
+     *
+     * @see org.eclipse.swt.widgets.Composite#computeSize
+     */
+    public Point computeSize(int wHint, int hHint, bool changed) {
+        checkWidget();
+        Point size;
+        ExpandableLayout layout = cast(ExpandableLayout) getLayout();
+        if (wHint is SWT.DEFAULT || hHint is SWT.DEFAULT) {
+            size = layout.computeSize(this, wHint, hHint, changed);
+        } else {
+            size = new Point(wHint, hHint);
+        }
+        Rectangle trim = computeTrim(0, 0, size.x, size.y);
+        return new Point(trim.width, trim.height);
+    }
+
+    /**
+     * Returns <samp>true </samp> if the composite is fixed i.e. cannot be
+     * expanded or collapsed. Fixed control will still contain the title,
+     * separator and description (if present) as well as the client, but will be
+     * in the permanent expanded state and the toggle affordance will not be
+     * shown.
+     *
+     * @return <samp>true </samp> if the control is fixed in the expanded state,
+     *         <samp>false </samp> if it can be collapsed.
+     */
+    protected bool isFixedStyle() {
+        return (expansionStyle & TWISTIE) is 0
+                && (expansionStyle & TREE_NODE) is 0;
+    }
+
+    /**
+     * Returns the text client control.
+     *
+     * @return Returns the text client control if specified, or
+     *         <code>null</code> if not.
+     */
+    public Control getTextClient() {
+        return textClient;
+    }
+
+    /**
+     * Sets the text client control. Text client is a control that is a child of
+     * the expandable composite and is placed to the right of the text. It can
+     * be used to place small image hyperlinks. If more than one control is
+     * needed, use Composite to hold them. Care should be taken that the height
+     * of the control is comparable to the height of the text.
+     *
+     * @param textClient
+     *            the textClient to set or <code>null</code> if not needed any
+     *            more.
+     */
+    public void setTextClient(Control textClient) {
+        if (this.textClient !is null)
+            this.textClient.dispose();
+        this.textClient = textClient;
+    }
+
+    /**
+     * Returns the difference in height between the text and the text client (if
+     * set). This difference can cause vertical alignment problems when two
+     * expandable composites are placed side by side, one with and one without
+     * the text client. Use this method obtain the value to add to either
+     * <code>descriptionVerticalSpacing</code> (if you have description) or
+     * <code>clientVerticalSpacing</code> to correct the alignment of the
+     * expandable without the text client.
+     *
+     * @return the difference in height between the text and the text client or
+     *         0 if no corrective action is needed.
+     * @since 3.3
+     */
+    public int getTextClientHeightDifference() {
+        if (textClient is null || textLabel is null)
+            return 0;
+        int theight = textLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
+        int tcheight = textClient.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
+        return Math.max(tcheight - theight, 0);
+    }
+
+    /**
+     * Tests if this expandable composite renders a title bar around the text.
+     *
+     * @return <code>true</code> for <code>TITLE_BAR</code> or
+     *         <code>SHORT_TITLE_BAR</code> styles, <code>false</code>
+     *         otherwise.
+     */
+    protected bool hasTitleBar() {
+        return (getExpansionStyle() & TITLE_BAR) !is 0
+                || (getExpansionStyle() & SHORT_TITLE_BAR) !is 0;
+    }
+
+    /**
+     * Sets the color of the title bar foreground when TITLE_BAR style is used.
+     *
+     * @param color
+     *            the title bar foreground
+     */
+    public void setTitleBarForeground(Color color) {
+        titleBarForeground = color;
+        textLabel.setForeground(color);
+    }
+
+    /**
+     * Returns the title bar foreground when TITLE_BAR style is used.
+     *
+     * @return the title bar foreground
+     */
+    public Color getTitleBarForeground() {
+        return titleBarForeground;
+    }
+
+    // end of APIs
+
+    private void toggleState() {
+        bool newState = !isExpanded();
+        fireExpanding(newState, true);
+        internalSetExpanded(newState);
+        fireExpanding(newState, false);
+        if (newState)
+            FormUtil.ensureVisible(this);
+    }
+
+    private void fireExpanding(bool state, bool before) {
+        int size = listeners.size();
+        if (size is 0)
+            return;
+        ExpansionEvent e = new ExpansionEvent(this, state);
+        Object [] listenerList = listeners.getListeners();
+        for (int i = 0; i < size; i++) {
+            IExpansionListener listener = cast(IExpansionListener) listenerList[i];
+            if (before)
+                listener.expansionStateChanging(e);
+            else
+                listener.expansionStateChanged(e);
+        }
+    }
+
+    private void verticalMove(bool down) {
+        Composite parent = getParent();
+        Control[] children = parent.getChildren();
+        for (int i = 0; i < children.length; i++) {
+            Control child = children[i];
+            if (child is this) {
+                ExpandableComposite sibling = getSibling(children, i, down);
+                if (sibling !is null && sibling.toggle !is null) {
+                    sibling.setFocus();
+                }
+                break;
+            }
+        }
+    }
+
+    private ExpandableComposite getSibling(Control[] children, int index,
+            bool down) {
+        int loc = down ? index + 1 : index - 1;
+        while (loc >= 0 && loc < children.length) {
+            Control c = children[loc];
+            if (null !is cast(ExpandableComposite)c  && c.isVisible())
+                return cast(ExpandableComposite) c;
+            loc = down ? loc + 1 : loc - 1;
+        }
+        return null;
+    }
+
+    private void programmaticToggleState() {
+        if (toggle !is null)
+            toggle.setExpanded(!toggle.isExpanded());
+        toggleState();
+    }
+
+    private void paintTitleFocus(GC gc) {
+        Point size = textLabel.getSize();
+        gc.setBackground(textLabel.getBackground());
+        gc.setForeground(textLabel.getForeground());
+        if (toggle.isFocusControl())
+            gc.drawFocus(0, 0, size.x, size.y);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/Form.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,803 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.widgets.Form;
+
+import org.eclipse.ui.forms.widgets.SizeCache;
+import org.eclipse.ui.forms.widgets.FormText;
+import org.eclipse.ui.forms.widgets.ILayoutExtension;
+import org.eclipse.ui.forms.widgets.LayoutComposite;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DragSourceListener;
+import org.eclipse.swt.dnd.DropTargetListener;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.ui.forms.IFormColors;
+import org.eclipse.ui.forms.IMessage;
+import org.eclipse.ui.forms.events.IHyperlinkListener;
+import org.eclipse.ui.internal.forms.widgets.FormHeading;
+import org.eclipse.ui.internal.forms.widgets.FormUtil;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Form is a custom control that renders a title and an optional background
+ * image above the body composite. It can be used alone when part of parents
+ * that are scrolled. If scrolling is required, use <code>ScrolledForm</code>
+ * instead because it has an instance of <code>Form</code> and adds scrolling
+ * capability.
+ * <p>
+ * Form can have a title if set. If not set, title area will not be left empty -
+ * form body will be resized to fill the entire form. In addition, an optional
+ * title image can be set and is rendered to the left of the title (since 3.2).
+ * <p>
+ * Form can have a title drop down menu if the menu bar manager is not empty
+ * (since 3.3).
+ * <p>
+ * Form title can support drag and drop if drag and drop support methods are
+ * invoked. When used, additional decoration is rendered behind the title to
+ * reinforce the drag and drop ability (since 3.3).
+ * <p>
+ * The form supports status messages. These messages can have various severity
+ * (error, warning, info or none). If status hyperlink handler is specified, the
+ * messages with the specified severity other than none will be rendered as
+ * hyperlinks.
+ * <p>
+ * Form can have a background image behind the title text. The image is tiled as
+ * many times as needed to fill the title area. Alternatively, gradient
+ * background can be painted vertically or horizontally.
+ * <p>
+ * Form can be put in a 'busy' state. While in this state, title image is
+ * replaced with an animation that lasts as long as the 'busy' state is active.
+ * <p>
+ * It is possible to create an optional head client control. When created, this
+ * control is placed in the form heading as a second row.
+ * <p>
+ * Form has a custom layout manager that is wrap-enabled. If a form is placed in
+ * a composite whose layout manager implements ILayoutExtension, the body of the
+ * form will participate in wrapping as long as its layout manager implements
+ * ILayoutExtension as well.
+ * <p>
+ * Children of the form should typically be created using FormToolkit to match
+ * the appearance and behaviour. When creating children, use the form body as a
+ * parent by calling 'getBody()' on the form instance. Example:
+ *
+ * <pre>
+ * FormToolkit toolkit = new FormToolkit(parent.getDisplay());
+ * Form form = toolkit.createForm(parent);
+ * form.setText(&quot;Sample form&quot;);
+ * form.getBody().setLayout(new GridLayout());
+ * toolkit.createButton(form.getBody(), &quot;Checkbox&quot;, SWT.CHECK);
+ * </pre>
+ *
+ * <p>
+ * No layout manager has been set on the body. Clients are required to set the
+ * desired layout manager explicitly.
+ * <p>
+ * Although the class is not final, it should not be subclassed.
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class Form : Composite {
+    private FormHeading head;
+
+    private Composite body_;
+
+    private SizeCache body_Cache;
+
+    private SizeCache headCache;
+
+    private FormText selectionText;
+
+    private class FormLayout : Layout, ILayoutExtension {
+        public int computeMinimumWidth(Composite composite, bool flushCache) {
+            return computeSize(composite, 5, SWT.DEFAULT, flushCache).x;
+        }
+
+        public int computeMaximumWidth(Composite composite, bool flushCache) {
+            return computeSize(composite, SWT.DEFAULT, SWT.DEFAULT, flushCache).x;
+        }
+
+        public Point computeSize(Composite composite, int wHint, int hHint,
+                bool flushCache) {
+            if (flushCache) {
+                body_Cache.flush();
+                headCache.flush();
+            }
+            body_Cache.setControl(body_);
+            headCache.setControl(head);
+
+            int width = 0;
+            int height = 0;
+
+            Point hsize = headCache.computeSize(FormUtil.getWidthHint(wHint,
+                    head), SWT.DEFAULT);
+            width = Math.max(hsize.x, width);
+            height = hsize.y;
+
+            bool ignoreBody=getData(FormUtil.IGNORE_BODY) !is null;
+
+            Point bsize;
+            if (ignoreBody)
+                bsize = new Point(0,0);
+            else
+                bsize = body_Cache.computeSize(FormUtil.getWidthHint(wHint,
+                    body_), SWT.DEFAULT);
+            width = Math.max(bsize.x, width);
+            height += bsize.y;
+            return new Point(width, height);
+        }
+
+        protected void layout(Composite composite, bool flushCache) {
+            if (flushCache) {
+                body_Cache.flush();
+                headCache.flush();
+            }
+            body_Cache.setControl(body_);
+            headCache.setControl(head);
+            Rectangle carea = composite.getClientArea();
+
+            Point hsize = headCache.computeSize(carea.width, SWT.DEFAULT);
+            headCache.setBounds(0, 0, carea.width, hsize.y);
+            body_Cache
+                    .setBounds(0, hsize.y, carea.width, carea.height - hsize.y);
+        }
+    }
+
+    /**
+     * Creates the form content control as a child of the provided parent.
+     *
+     * @param parent
+     *            the parent widget
+     */
+    public this(Composite parent, int style) {
+
+        body_Cache = new SizeCache();
+        headCache = new SizeCache();
+
+        super(parent, SWT.NO_BACKGROUND | style);
+        super.setLayout(new FormLayout());
+        head = new FormHeading(this, SWT.NULL);
+        head.setMenu(parent.getMenu());
+        body_ = new LayoutComposite(this, SWT.NULL);
+        body_.setMenu(parent.getMenu());
+    }
+
+    /**
+     * Passes the menu to the form body.
+     *
+     * @param menu
+     *            the parent menu
+     */
+    public void setMenu(Menu menu) {
+        super.setMenu(menu);
+        head.setMenu(menu);
+        body_.setMenu(menu);
+    }
+
+    /**
+     * Fully delegates the size computation to the internal layout manager.
+     */
+    public final Point computeSize(int wHint, int hHint, bool changed) {
+        return (cast(FormLayout) getLayout()).computeSize(this, wHint, hHint,
+                changed);
+    }
+
+    /**
+     * Prevents from changing the custom control layout.
+     */
+    public final void setLayout(Layout layout) {
+    }
+
+    /**
+     * Returns the title text that will be rendered at the top of the form.
+     *
+     * @return the title text
+     */
+    public String getText() {
+        return head.getText();
+    }
+
+    /**
+     * Returns the title image that will be rendered to the left of the title.
+     *
+     * @return the title image or <code>null</code> if not set.
+     * @since 3.2
+     */
+    public Image getImage() {
+        return head.getImage();
+    }
+
+    /**
+     * Sets the foreground color of the form. This color will also be used for
+     * the body.
+     *
+     * @param fg
+     *            the foreground color
+     */
+    public void setForeground(Color fg) {
+        super.setForeground(fg);
+        head.setForeground(fg);
+        body_.setForeground(fg);
+    }
+
+    /**
+     * Sets the background color of the form. This color will also be used for
+     * the body.
+     *
+     * @param bg
+     *            the background color
+     */
+    public void setBackground(Color bg) {
+        super.setBackground(bg);
+        head.setBackground(bg);
+        body_.setBackground(bg);
+    }
+
+    /**
+     * Sets the font of the header text.
+     *
+     * @param font
+     *            the new font
+     */
+    public void setFont(Font font) {
+        super.setFont(font);
+        head.setFont(font);
+    }
+
+    /**
+     * Sets the text to be rendered at the top of the form above the body as a
+     * title.
+     * <p>
+     * <strong>Note:</strong> Mnemonics are indicated by an '&amp;' that causes
+     * the next character to be the mnemonic. Mnemonics are not applicable in
+     * the case of the form title but need to be taken into acount due to the
+     * usage of the underlying widget that renders mnemonics in the title area.
+     * The mnemonic indicator character '&amp;' can be escaped by doubling it in
+     * the string, causing a single '&amp;' to be displayed.
+     * </p>
+     *
+     * @param text
+     *            the title text
+     */
+    public void setText(String text) {
+        head.setText(text);
+        layout();
+        redraw();
+    }
+
+    /**
+     * Sets the image to be rendered to the left of the title. This image will
+     * be temporarily hidden in two cases:
+     *
+     * <ol>
+     * <li>When the form is busy - replaced with a busy animation</li>
+     * <li>When the form has message set - replaced with the image indicating
+     * message severity</li>
+     * </ol>
+     *
+     * @param image
+     *            the title image or <code>null</code> to show no image.
+     * @since 3.2
+     */
+    public void setImage(Image image) {
+        head.setImage(image);
+        layout();
+        redraw();
+    }
+
+    /**
+     * Sets the background colors to be painted behind the title text in a
+     * gradient. Note that this method will reset color previously set by
+     * {@link #setBackground(Color)}. This is necessary for the simulated
+     * transparency of the heading in all of its children control.
+     *
+     * @param gradientColors
+     *            the array of colors that form the gradient
+     * @param percents
+     *            the partition of the overall space between the gradient colors
+     * @param vertical
+     *            of <code>true</code>, the gradient will be rendered
+     *            vertically, if <code>false</code> the orientation will be
+     *            horizontal.
+     */
+
+    public void setTextBackground(Color[] gradientColors, int[] percents,
+            bool vertical) {
+        head.setTextBackground(gradientColors, percents, vertical);
+    }
+
+    /**
+     * Returns the optional background image of the form head.
+     *
+     * @return the background image or <code>null</code> if not specified.
+     */
+    public Image getBackgroundImage() {
+        return head.getHeadingBackgroundImage();
+    }
+
+    /**
+     * Sets the optional background image to be rendered behind the title
+     * starting at the position 0,0. If the image is smaller than the container
+     * in any dimension, it will be tiled.
+     *
+     * @since 3.2
+     *
+     * @param backgroundImage
+     *            the head background image.
+     *
+     */
+    public void setBackgroundImage(Image backgroundImage) {
+        head.setHeadingBackgroundImage(backgroundImage);
+    }
+
+    /**
+     * Returns the tool bar manager that is used to manage tool items in the
+     * form's title area.
+     *
+     * @return form tool bar manager
+     */
+    public IToolBarManager getToolBarManager() {
+        return head.getToolBarManager();
+    }
+
+    /**
+     * Sets the tool bar vertical alignment relative to the header. Can be
+     * useful when there is more free space at the second row (with the head
+     * client).
+     *
+     * @param alignment
+     *            SWT.TOP or SWT.BOTTOM
+     * @since 3.3
+     */
+
+    public void setToolBarVerticalAlignment(int alignment) {
+        head.setToolBarAlignment(alignment);
+    }
+
+    /**
+     * Returns the current tool bar alignment (if used).
+     *
+     * @return SWT.TOP or SWT.BOTTOM
+     * @since 3.3
+     */
+
+    public int getToolBarVerticalAlignment() {
+        return head.getToolBarAlignment();
+    }
+
+    /**
+     * Returns the menu manager that is used to manage title area drop-down menu
+     * items.
+     *
+     * @return title area drop-down menu manager
+     * @since 3.3
+     */
+    public IMenuManager getMenuManager() {
+        return head.getMenuManager();
+    }
+
+    /**
+     * Updates the local tool bar manager if used. Does nothing if local tool
+     * bar manager has not been created yet.
+     */
+    public void updateToolBar() {
+        head.updateToolBar();
+    }
+
+    /**
+     * Returns the container that occupies the head of the form (the form area
+     * above the body). Use this container as a parent for the head client.
+     *
+     * @return the head of the form.
+     * @since 3.2
+     */
+    public Composite getHead() {
+        return head;
+    }
+
+    /**
+     * Returns the optional head client if set.
+     *
+     * @return the head client or <code>null</code> if not set.
+     * @see #setHeadClient(Control)
+     * @since 3.2
+     */
+    public Control getHeadClient() {
+        return head.getHeadClient();
+    }
+
+    /**
+     * Sets the optional head client. Head client is placed after the form
+     * title. This option causes the tool bar to be placed in the second raw of
+     * the header (below the head client).
+     * <p>
+     * The head client must be a child of the composite returned by
+     * <code>getHead()</code> method.
+     *
+     * @param headClient
+     *            the optional child of the head
+     * @since 3.2
+     */
+    public void setHeadClient(Control headClient) {
+        head.setHeadClient(headClient);
+        layout();
+    }
+
+    /**
+     * Returns the container that occupies the body of the form (the form area
+     * below the title). Use this container as a parent for the controls that
+     * should be in the form. No layout manager has been set on the form body.
+     *
+     * @return Returns the body of the form.
+     */
+    public Composite getBody() {
+        return body_;
+    }
+
+    /**
+     * Tests if the background image is tiled to cover the entire area of the
+     * form heading.
+     *
+     * @return <code>true</code> if heading background image is tiled,
+     *         <code>false</code> otherwise.
+     */
+    public bool isBackgroundImageTiled() {
+        return head.isBackgroundImageTiled();
+    }
+
+    /**
+     * Sets whether the header background image is repeated to cover the entire
+     * heading area or not.
+     *
+     * @param backgroundImageTiled
+     *            set <code>true</code> to tile the image, or
+     *            <code>false</code> to paint the background image only once
+     *            at 0,0
+     */
+    public void setBackgroundImageTiled(bool backgroundImageTiled) {
+        head.setBackgroundImageTiled(backgroundImageTiled);
+    }
+
+    /**
+     * Returns the background image alignment.
+     *
+     * @deprecated due to the underlying widget limitations, background image is
+     *             either painted at 0,0 and/or tiled.
+     * @return SWT.LEFT
+     */
+    public int getBackgroundImageAlignment() {
+        return SWT.LEFT;
+    }
+
+    /**
+     * Sets the background image alignment.
+     *
+     * @deprecated due to the underlying widget limitations, background image is
+     *             always tiled and alignment cannot be controlled.
+     * @param backgroundImageAlignment
+     *            The backgroundImageAlignment to set.
+     * @since 3.1
+     */
+    public void setBackgroundImageAlignment(int backgroundImageAlignment) {
+    }
+
+    /**
+     * Tests if background image is clipped.
+     *
+     * @deprecated due to the underlying widget limitations, background image is
+     *             always clipped.
+     * @return true
+     * @since 3.1
+     */
+    public bool isBackgroundImageClipped() {
+        return true;
+    }
+
+    /**
+     * Sets whether the background image is clipped.
+     *
+     * @deprecated due to the underlying widget limitations, background image is
+     *             always clipped.
+     * @param backgroundImageClipped
+     *            the value to set
+     * @since 3.1
+     */
+    public void setBackgroundImageClipped(bool backgroundImageClipped) {
+    }
+
+    /**
+     * Tests if the form head separator is visible.
+     *
+     * @return <code>true</code> if the head/body separator is visible,
+     *         <code>false</code> otherwise
+     * @since 3.2
+     */
+    public bool isSeparatorVisible() {
+        return head.isSeparatorVisible();
+    }
+
+    /**
+     * If set, adds a separator between the head and body. Since 3.3, the colors
+     * that are used to render it are {@link IFormColors#H_BOTTOM_KEYLINE1} and
+     * {@link IFormColors#H_BOTTOM_KEYLINE2}.
+     *
+     * @param addSeparator
+     *            <code>true</code> to make the separator visible,
+     *            <code>false</code> otherwise.
+     * @since 3.2
+     */
+    public void setSeparatorVisible(bool addSeparator) {
+        head.setSeparatorVisible(addSeparator);
+    }
+
+    /**
+     * Returns the color used to render the optional head separator. If gradient
+     * text background is used additional colors from the gradient will be used
+     * to render the separator.
+     *
+     * @return separator color or <code>null</code> if not set.
+     * @since 3.2
+     * @deprecated use <code>getHeadColor(IFormColors.H_BOTTOM_KEYLINE2)</code>
+     */
+
+    public Color getSeparatorColor() {
+        return head.getColor(IFormColors.H_BOTTOM_KEYLINE2);
+    }
+
+    /**
+     * Sets the color to be used to render the optional head separator.
+     *
+     * @param separatorColor
+     *            the color to render the head separator or <code>null</code>
+     *            to use the default color.
+     * @since 3.2
+     * @deprecated use
+     *             <code>setHeadColor(IFormColors.H_BOTTOM_KEYLINE2, separatorColor)</code>
+     */
+    public void setSeparatorColor(Color separatorColor) {
+        head.putColor(IFormColors.H_BOTTOM_KEYLINE2, separatorColor);
+    }
+
+    /**
+     * Sets the color used to paint an aspect of the form heading.
+     *
+     * @param key
+     *            a valid form heading color key as defined in
+     *            {@link IFormColors}. Relevant keys all start with an H_
+     *            prefix.
+     * @param color
+     *            the color to use for the provided key
+     * @since 3.3
+     */
+
+    public void setHeadColor(String key, Color color) {
+        head.putColor(key, color);
+    }
+
+    /**
+     * Returns the color that is currently use to paint an aspect of the form
+     * heading, or <code>null</code> if not defined.
+     *
+     * @param key
+     *            the color key
+     * @return the color object or <code>null</code> if not set.
+     * @since 3.3
+     */
+
+    public Color getHeadColor(String key) {
+        return head.getColor(key);
+    }
+
+    /**
+     * Sets the message for this form. Message text is rendered in the form head
+     * when shown.
+     *
+     * @param message
+     *            the message, or <code>null</code> to clear the message
+     * @see #setMessage(String, int)
+     * @since 3.2
+     */
+    public void setMessage(String message) {
+        this.setMessage(message, 0, null);
+    }
+
+    /**
+     * Sets the message for this form with an indication of what type of message
+     * it is.
+     * <p>
+     * The valid message types are one of <code>NONE</code>,
+     * <code>INFORMATION</code>,<code>WARNING</code>, or
+     * <code>ERROR</code> defined in IMessageProvider interface.
+     * </p>
+     *
+     * @param newMessage
+     *            the message, or <code>null</code> to clear the message
+     * @param newType
+     *            the message type
+     * @see org.eclipse.jface.dialogs.IMessageProvider
+     * @since 3.2
+     */
+
+    public void setMessage(String newMessage, int newType) {
+        this.setMessage(newMessage, newType, null);
+    }
+
+    /**
+     * Sets the message for this form with an indication of what type of message
+     * it is.
+     * <p>
+     * The valid message types are one of <code>NONE</code>,
+     * <code>INFORMATION</code>,<code>WARNING</code>, or
+     * <code>ERROR</code> defined in IMessageProvider interface.
+     * </p>
+     * <p>
+     * In addition to the summary message, this method also sets an array of
+     * individual messages.
+     *
+     *
+     * @param newMessage
+     *            the message, or <code>null</code> to clear the message
+     * @param newType
+     *            the message type
+     * @param children
+     *            the individual messages that contributed to the overall
+     *            message
+     * @see org.eclipse.jface.dialogs.IMessageProvider
+     * @since 3.3
+     */
+
+    public void setMessage(String newMessage, int newType, IMessage[] children) {
+        head.showMessage(newMessage, newType, children);
+        layout();
+    }
+
+    /**
+     * Adds a message hyperlink listener. If at least one listener is present,
+     * messages will be rendered as hyperlinks.
+     *
+     * @param listener
+     * @see #removeMessageHyperlinkListener(IHyperlinkListener)
+     * @since 3.3
+     */
+    public void addMessageHyperlinkListener(IHyperlinkListener listener) {
+        head.addMessageHyperlinkListener(listener);
+    }
+
+    /**
+     * Remove the message hyperlink listener.
+     *
+     * @param listener
+     * @see #addMessageHyperlinkListener(IHyperlinkListener)
+     * @since 3.3
+     */
+    public void removeMessageHyperlinkListener(IHyperlinkListener listener) {
+        head.removeMessageHyperlinkListener(listener);
+    }
+
+    /**
+     * Tests if the form is in the 'busy' state. Busy form displays 'busy'
+     * animation in the area of the title image.
+     *
+     * @return <code>true</code> if busy, <code>false</code> otherwise.
+     * @since 3.2
+     */
+
+    public bool isBusy() {
+        return head.isBusy();
+    }
+
+    /**
+     * Sets the form's busy state. Busy form will display 'busy' animation in
+     * the area of the title image.
+     *
+     * @param busy
+     *            the form's busy state
+     * @since 3.2
+     */
+
+    public void setBusy(bool busy) {
+        head.setBusy(busy);
+    }
+
+    /**
+     * Adds support for dragging items out of the form title area via a user
+     * drag-and-drop operation.
+     *
+     * @param operations
+     *            a bitwise OR of the supported drag and drop operation types (
+     *            <code>DROP_COPY</code>,<code>DROP_LINK</code>, and
+     *            <code>DROP_MOVE</code>)
+     * @param transferTypes
+     *            the transfer types that are supported by the drag operation
+     * @param listener
+     *            the callback that will be invoked to set the drag data and to
+     *            cleanup after the drag and drop operation finishes
+     * @see org.eclipse.swt.dnd.DND
+     * @since 3.3
+     */
+    public void addTitleDragSupport(int operations, Transfer[] transferTypes,
+            DragSourceListener listener) {
+        head.addDragSupport(operations, transferTypes, listener);
+    }
+
+    /**
+     * Adds support for dropping items into the form title area via a user
+     * drag-and-drop operation.
+     *
+     * @param operations
+     *            a bitwise OR of the supported drag and drop operation types (
+     *            <code>DROP_COPY</code>,<code>DROP_LINK</code>, and
+     *            <code>DROP_MOVE</code>)
+     * @param transferTypes
+     *            the transfer types that are supported by the drop operation
+     * @param listener
+     *            the callback that will be invoked after the drag and drop
+     *            operation finishes
+     * @see org.eclipse.swt.dnd.DND
+     * @since 3.3
+     */
+    public void addTitleDropSupport(int operations, Transfer[] transferTypes,
+            DropTargetListener listener) {
+        head.addDropSupport(operations, transferTypes, listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.IMessageProvider#getMessage()
+     */
+    public String getMessage() {
+        return head.getMessage();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.IMessageProvider#getMessageType()
+     */
+    public int getMessageType() {
+        return head.getMessageType();
+    }
+
+    /**
+     * Returns the children messages that the cause of the summary message
+     * currently set on the form.
+     *
+     * @return an array of children messages or <code>null</code> if not set.
+     * @see #setMessage(String, int, IMessage[])
+     * @since 3.3
+     */
+    public IMessage[] getChildrenMessages() {
+        return head.getChildrenMessages();
+    }
+
+    void setSelectionText(FormText text) {
+        if (selectionText !is null && selectionText !is text) {
+            selectionText.clearSelection();
+        }
+        this.selectionText = text;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/FormText.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1716 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.widgets.FormText;
+
+import org.eclipse.ui.forms.widgets.ILayoutExtension;
+import org.eclipse.ui.forms.widgets.Form;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.accessibility.ACC;
+import org.eclipse.swt.accessibility.Accessible;
+import org.eclipse.swt.accessibility.AccessibleAdapter;
+import org.eclipse.swt.accessibility.AccessibleControlAdapter;
+import org.eclipse.swt.accessibility.AccessibleControlEvent;
+import org.eclipse.swt.accessibility.AccessibleEvent;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.MenuEvent;
+import org.eclipse.swt.events.MenuListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.TypedListener;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.ui.forms.HyperlinkSettings;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.events.IHyperlinkListener;
+import org.eclipse.ui.internal.forms.Messages;
+import org.eclipse.ui.internal.forms.widgets.ControlSegment;
+import org.eclipse.ui.internal.forms.widgets.FormFonts;
+import org.eclipse.ui.internal.forms.widgets.FormTextModel;
+import org.eclipse.ui.internal.forms.widgets.FormUtil;
+import org.eclipse.ui.internal.forms.widgets.IFocusSelectable;
+import org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment;
+import org.eclipse.ui.internal.forms.widgets.ImageSegment;
+import org.eclipse.ui.internal.forms.widgets.Locator;
+import org.eclipse.ui.internal.forms.widgets.Paragraph;
+import org.eclipse.ui.internal.forms.widgets.ParagraphSegment;
+import org.eclipse.ui.internal.forms.widgets.SelectionData;
+import org.eclipse.ui.internal.forms.widgets.TextSegment;
+
+import java.lang.all;
+import java.util.Enumeration;
+import java.util.ArrayList;
+import java.util.Set;
+import java.io.InputStream;
+import tango.io.Stdout;
+
+/**
+ * This class is a read-only text control that is capable of rendering wrapped
+ * text. Text can be rendered as-is or by parsing the formatting XML tags.
+ * Independently, words that start with http:// can be converted into hyperlinks
+ * on the fly.
+ * <p>
+ * When configured to use formatting XML, the control requires the root element
+ * <code>form</code> to be used. The following tags can be children of the
+ * <code>form</code> element:
+ * </p>
+ * <ul>
+ * <li><b>p </b>- for defining paragraphs. The following attributes are
+ * allowed:
+ * <ul>
+ * <li><b>vspace </b>- if set to 'false', no vertical space will be added
+ * (default is 'true')</li>
+ * </ul>
+ * </li>
+ * <li><b>li </b>- for defining list items. The following attributes are
+ * allowed:
+ * <ul>
+ * <li><b>vspace </b>- the same as with the <b>p </b> tag</li>
+ * <li><b>style </b>- could be 'bullet' (default), 'text' and 'image'</li>
+ * <li><b>value </b>- not used for 'bullet'. For text, it is the value of the
+ * text that is rendered as a bullet. For image, it is the href of the image to
+ * be rendered as a bullet.</li>
+ * <li><b>indent </b>- the number of pixels to indent the text in the list item
+ * </li>
+ * <li><b>bindent </b>- the number of pixels to indent the bullet itself</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * <p>
+ * Text in paragraphs and list items will be wrapped according to the width of
+ * the control. The following tags can appear as children of either <b>p </b> or
+ * <b>li </b> elements:
+ * <ul>
+ * <li><b>img </b>- to render an image. Element accepts attribute 'href' that
+ * is a key to the <code>Image</code> set using 'setImage' method. Vertical
+ * position of image relative to surrounding text is optionally controlled by
+ * the attribute <b>align</b> that can have values <b>top</b>, <b>middle</b>
+ * and <b>bottom</b></li>
+ * <li><b>a </b>- to render a hyperlink. Element accepts attribute 'href' that
+ * will be provided to the hyperlink listeners via HyperlinkEvent object. The
+ * element also accepts 'nowrap' attribute (default is false). When set to
+ * 'true', the hyperlink will not be wrapped. Hyperlinks automatically created
+ * when 'http://' is encountered in text are not wrapped.</li>
+ * <li><b>b </b>- the enclosed text will use bold font.</li>
+ * <li><b>br </b>- forced line break (no attributes).</li>
+ * <li><b>span </b>- the enclosed text will have the color and font specified
+ * in the element attributes. Color is provided using 'color' attribute and is a
+ * key to the Color object set by 'setColor' method. Font is provided using
+ * 'font' attribute and is a key to the Font object set by 'setFont' method. As with
+ * hyperlinks, it is possible to block wrapping by setting 'nowrap' to true
+ * (false by default).
+ * </li>
+ * <li><b>control (new in 3.1)</b> - to place a control that is a child of the
+ * text control. Element accepts attribute 'href' that is a key to the Control
+ * object set using 'setControl' method. Optionally, attribute 'fill' can be set
+ * to <code>true</code> to make the control fill the entire width of the text.
+ * Form text is not responsible for creating or disposing controls, it only
+ * places them relative to the surrounding text. Similar to <b>img</b>,
+ * vertical position of the control can be set using the <b>align</b>
+ * attribute. In addition, <b>width</b> and <b>height</b> attributes can
+ * be used to force the dimensions of the control. If not used,
+ * the preferred control size will be used.
+ * </ul>
+ * <p>
+ * None of the elements can nest. For example, you cannot have <b>b </b> inside
+ * a <b>span </b>. This was done to keep everything simple and transparent.
+ * Since 3.1, an exception to this rule has been added to support nesting images
+ * and text inside the hyperlink tag (<b>a</b>). Image enclosed in the
+ * hyperlink tag acts as a hyperlink, can be clicked on and can accept and
+ * render selection focus. When both text and image is enclosed, selection and
+ * rendering will affect both as a single hyperlink.
+ * </p>
+ * <p>
+ * Since 3.1, it is possible to select text. Text selection can be
+ * programmatically accessed and also copied to clipboard. Non-textual objects
+ * (images, controls etc.) in the selection range are ignored.
+ * <p>
+ * Care should be taken when using this control. Form text is not an HTML
+ * browser and should not be treated as such. If you need complex formatting
+ * capabilities, use Browser widget. If you need editing capabilities and
+ * font/color styles of text segments is all you need, use StyleText widget.
+ * Finally, if all you need is to wrap text, use SWT Label widget and create it
+ * with SWT.WRAP style.
+ *
+ * @see FormToolkit
+ * @see TableWrapLayout
+ * @since 3.0
+ */
+public class FormText : Canvas {
+    /**
+     * The object ID to be used when registering action to handle URL hyperlinks
+     * (those that should result in opening the web browser). Value is
+     * "urlHandler".
+     */
+    public static const String URL_HANDLER_ID = "urlHandler"; //$NON-NLS-1$
+
+    /**
+     * Value of the horizontal margin (default is 0).
+     */
+    public int marginWidth = 0;
+
+    /**
+     * Value of tue vertical margin (default is 1).
+     */
+    public int marginHeight = 1;
+
+    // private fields
+    private static const bool DEBUG_TEXT = false;//"true".equalsIgnoreCase(Platform.getDebugOption(FormUtil.DEBUG_TEXT));
+    private static const bool DEBUG_TEXTSIZE = false;//"true".equalsIgnoreCase(Platform.getDebugOption(FormUtil.DEBUG_TEXTSIZE));
+
+    private static const bool DEBUG_FOCUS = false;//"true".equalsIgnoreCase(Platform.getDebugOption(FormUtil.DEBUG_FOCUS));
+
+    private bool hasFocus;
+
+    private bool paragraphsSeparated = true;
+
+    private FormTextModel model;
+
+    private ListenerList listeners;
+
+    private Hashtable resourceTable;
+
+    private IHyperlinkSegment entered;
+
+    private IHyperlinkSegment armed;
+
+    private bool mouseFocus = false;
+
+    private bool controlFocusTransfer = false;
+
+    private bool inSelection = false;
+
+    private SelectionData selData;
+
+    private static const String INTERNAL_MENU = "__internal_menu__"; //$NON-NLS-1$
+
+    private static const String CONTROL_KEY = "__segment__"; //$NON-NLS-1$
+
+    private class FormTextLayout : Layout, ILayoutExtension {
+        public this() {
+        }
+
+        public int computeMaximumWidth(Composite parent, bool changed) {
+            return computeSize(parent, SWT.DEFAULT, SWT.DEFAULT, changed).x;
+        }
+
+        public int computeMinimumWidth(Composite parent, bool changed) {
+            return computeSize(parent, 5, SWT.DEFAULT, true).x;
+        }
+
+        /*
+         * @see Layout#computeSize(Composite, int, int, bool)
+         */
+        public Point computeSize(Composite composite, int wHint, int hHint,
+                bool changed) {
+            long start = 0;
+
+            if (DEBUG_TEXT)
+                start = System.currentTimeMillis();
+            int innerWidth = wHint;
+            if (innerWidth !is SWT.DEFAULT)
+                innerWidth -= marginWidth * 2;
+            Point textSize = computeTextSize(innerWidth);
+            int textWidth = textSize.x + 2 * marginWidth;
+            int textHeight = textSize.y + 2 * marginHeight;
+            Point result = new Point(textWidth, textHeight);
+            if (DEBUG_TEXT) {
+                long stop = System.currentTimeMillis();
+                Stdout.formatln("FormText computeSize: {}ms", (stop - start)); //$NON-NLS-1$
+            }
+            if (DEBUG_TEXTSIZE) {
+                Stdout.formatln("FormText ({}), computeSize: wHint={}, result={}", model.getAccessibleText(), wHint, result); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            }
+            return result;
+        }
+
+        private Point computeTextSize(int wHint) {
+            Paragraph[] paragraphs = model.getParagraphs();
+            GC gc = new GC(this.outer);
+            gc.setFont(getFont());
+            Locator loc = new Locator();
+            int width = wHint !is SWT.DEFAULT ? wHint : 0;
+            FontMetrics fm = gc.getFontMetrics();
+            int lineHeight = fm.getHeight();
+            bool selectableInTheLastRow = false;
+            for (int i = 0; i < paragraphs.length; i++) {
+                Paragraph p = paragraphs[i];
+                if (i > 0 && getParagraphsSeparated()
+                        && p.getAddVerticalSpace())
+                    loc.y += getParagraphSpacing(lineHeight);
+                loc.rowHeight = 0;
+                loc.indent = p.getIndent();
+                loc.x = p.getIndent();
+                ParagraphSegment[] segments = p.getSegments();
+                if (segments.length > 0) {
+                    selectableInTheLastRow = false;
+                    int pwidth = 0;
+                    for (int j = 0; j < segments.length; j++) {
+                        ParagraphSegment segment = segments[j];
+                        segment.advanceLocator(gc, wHint, loc, resourceTable,
+                                false);
+                        if (wHint !is SWT.DEFAULT) {
+                            width = Math.max(width, loc.width);
+                        } else {
+                            pwidth += loc.width;
+                        }
+                        if (null !is cast(IFocusSelectable)segment )
+                            selectableInTheLastRow = true;
+                    }
+                    if (wHint is SWT.DEFAULT)
+                        width = Math.max(width, pwidth);
+                    loc.y += loc.rowHeight;
+                } else {
+                    // empty new line
+                    loc.y += lineHeight;
+                }
+            }
+            gc.dispose();
+            if (selectableInTheLastRow)
+                loc.y += 1;
+            return new Point(width, loc.y);
+        }
+
+        protected void layout(Composite composite, bool flushCache) {
+            long start = 0;
+
+            if (DEBUG_TEXT) {
+                start = System.currentTimeMillis();
+            }
+            selData = null;
+            Rectangle carea = composite.getClientArea();
+            if (DEBUG_TEXTSIZE) {
+                Stdout.formatln("FormText layout ({}), carea={}",model.getAccessibleText(),carea); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+            GC gc = new GC(composite);
+            gc.setFont(getFont());
+            ensureBoldFontPresent(getFont());
+            gc.setForeground(getForeground());
+            gc.setBackground(getBackground());
+
+            Locator loc = new Locator();
+            loc.marginWidth = marginWidth;
+            loc.marginHeight = marginHeight;
+            loc.x = marginWidth;
+            loc.y = marginHeight;
+            FontMetrics fm = gc.getFontMetrics();
+            int lineHeight = fm.getHeight();
+
+            Paragraph[] paragraphs = model.getParagraphs();
+            IHyperlinkSegment selectedLink = getSelectedLink();
+            for (int i = 0; i < paragraphs.length; i++) {
+                Paragraph p = paragraphs[i];
+                if (i > 0 && paragraphsSeparated && p.getAddVerticalSpace())
+                    loc.y += getParagraphSpacing(lineHeight);
+                loc.indent = p.getIndent();
+                loc.resetCaret();
+                loc.rowHeight = 0;
+                p.layout(gc, carea.width, loc, lineHeight, resourceTable,
+                        selectedLink);
+            }
+            gc.dispose();
+            if (DEBUG_TEXT) {
+                long stop = System.currentTimeMillis();
+                Stdout.formatln("FormText.layout: {}ms", (stop - start)); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+        }
+    }
+
+    /**
+     * Contructs a new form text widget in the provided parent and using the
+     * styles.
+     *
+     * @param parent
+     *            form text parent control
+     * @param style
+     *            the widget style
+     */
+    public this(Composite parent, int style) {
+        resourceTable = new Hashtable();
+        super(parent, SWT.NO_BACKGROUND | SWT.WRAP | style);
+        setLayout(new FormTextLayout());
+        model = new FormTextModel();
+        addDisposeListener(new class DisposeListener {
+            public void widgetDisposed(DisposeEvent e) {
+                model.dispose();
+                disposeResourceTable(true);
+            }
+        });
+        addPaintListener(new class PaintListener {
+            public void paintControl(PaintEvent e) {
+                paint(e);
+            }
+        });
+        addListener(SWT.KeyDown, new class Listener {
+            public void handleEvent(Event e) {
+                if (e.character is '\r') {
+                    activateSelectedLink();
+                    return;
+                }
+            }
+        });
+        addListener(SWT.Traverse, new class Listener {
+            public void handleEvent(Event e) {
+                if (DEBUG_FOCUS)
+                    Stdout.formatln("Traversal: {}", e); //$NON-NLS-1$
+                switch (e.detail) {
+                case SWT.TRAVERSE_PAGE_NEXT:
+                case SWT.TRAVERSE_PAGE_PREVIOUS:
+                case SWT.TRAVERSE_ARROW_NEXT:
+                case SWT.TRAVERSE_ARROW_PREVIOUS:
+                    e.doit = false;
+                    return;
+                default:
+                }
+                if (!model.hasFocusSegments()) {
+                    e.doit = true;
+                    return;
+                }
+                if (e.detail is SWT.TRAVERSE_TAB_NEXT)
+                    e.doit = advance(true);
+                else if (e.detail is SWT.TRAVERSE_TAB_PREVIOUS)
+                    e.doit = advance(false);
+                else if (e.detail !is SWT.TRAVERSE_RETURN)
+                    e.doit = true;
+            }
+        });
+        addFocusListener(new class FocusListener {
+            public void focusGained(FocusEvent e) {
+                if (!hasFocus) {
+                    hasFocus = true;
+                    if (DEBUG_FOCUS) {
+                        Stdout.formatln("FormText: focus gained"); //$NON-NLS-1$
+                    }
+                    if (!mouseFocus && !controlFocusTransfer) {
+                        handleFocusChange();
+                    }
+                }
+            }
+
+            public void focusLost(FocusEvent e) {
+                if (DEBUG_FOCUS) {
+                    Stdout.formatln("FormText: focus lost"); //$NON-NLS-1$
+                }
+                if (hasFocus) {
+                    hasFocus = false;
+                    if (!controlFocusTransfer)
+                        handleFocusChange();
+                }
+            }
+        });
+        addMouseListener(new class MouseListener {
+            public void mouseDoubleClick(MouseEvent e) {
+            }
+
+            public void mouseDown(MouseEvent e) {
+                // select a link
+                handleMouseClick(e, true);
+            }
+
+            public void mouseUp(MouseEvent e) {
+                // activate a link
+                handleMouseClick(e, false);
+            }
+        });
+        addMouseTrackListener(new class MouseTrackListener {
+            public void mouseEnter(MouseEvent e) {
+                handleMouseMove(e);
+            }
+
+            public void mouseExit(MouseEvent e) {
+                if (entered !is null) {
+                    exitLink(entered, e.stateMask);
+                    paintLinkHover(entered, false);
+                    entered = null;
+                    setCursor(null);
+                }
+            }
+
+            public void mouseHover(MouseEvent e) {
+                handleMouseHover(e);
+            }
+        });
+        addMouseMoveListener(new class MouseMoveListener {
+            public void mouseMove(MouseEvent e) {
+                handleMouseMove(e);
+            }
+        });
+        initAccessible();
+        ensureBoldFontPresent(getFont());
+        createMenu();
+        // we will handle traversal of controls, if any
+        setTabList(cast(Control[])null);
+    }
+
+    /**
+     * Test for focus.
+     *
+     * @return <samp>true </samp> if the widget has focus.
+     */
+    public bool getFocus() {
+        return hasFocus;
+    }
+
+    /**
+     * Test if the widget is currently processing the text it is about to
+     * render.
+     *
+     * @return <samp>true </samp> if the widget is still loading the text,
+     *         <samp>false </samp> otherwise.
+     * @deprecated not used any more - returns <code>false</code>
+     */
+    public bool isLoading() {
+        return false;
+    }
+
+    /**
+     * Returns the text that will be shown in the control while the real content
+     * is loading.
+     *
+     * @return loading text message
+     * @deprecated loading text is not used since 3.1
+     */
+    public String getLoadingText() {
+        return null;
+    }
+
+    /**
+     * Sets the text that will be shown in the control while the real content is
+     * loading. This is significant when content to render is loaded from the
+     * input stream that was created from a remote URL, and the time to load the
+     * entire content is nontrivial.
+     *
+     * @param loadingText
+     *            loading text message
+     * @deprecated use setText(loadingText, false, false);
+     */
+    public void setLoadingText(String loadingText) {
+        setText(loadingText, false, false);
+    }
+
+    /**
+     * If paragraphs are separated, spacing will be added between them.
+     * Otherwise, new paragraphs will simply start on a new line with no
+     * spacing.
+     *
+     * @param value
+     *            <samp>true </samp> if paragraphs are separated, </samp> false
+     *            </samp> otherwise.
+     */
+    public void setParagraphsSeparated(bool value) {
+        paragraphsSeparated = value;
+    }
+
+    /**
+     * Tests if there is some inter-paragraph spacing.
+     *
+     * @return <samp>true </samp> if paragraphs are separated, <samp>false
+     *         </samp> otherwise.
+     */
+    public bool getParagraphsSeparated() {
+        return paragraphsSeparated;
+    }
+
+    /**
+     * Registers the image referenced by the provided key.
+     * <p>
+     * For <samp>img </samp> tags, an object of a type <samp>Image </samp> must
+     * be registered using the key equivalent to the value of the <samp>href
+     * </samp> attribute used in the tag.
+     *
+     * @param key
+     *            unique key that matches the value of the <samp>href </samp>
+     *            attribute.
+     * @param image
+     *            an object of a type <samp>Image </samp>.
+     */
+    public void setImage(String key, Image image) {
+        resourceTable.put("i." ~ key, image); //$NON-NLS-1$
+    }
+
+    /**
+     * Registers the color referenced by the provided key.
+     * <p>
+     * For <samp>span </samp> tags, an object of a type <samp>Color </samp> must
+     * be registered using the key equivalent to the value of the <samp>color
+     * </samp> attribute.
+     *
+     * @param key
+     *            unique key that matches the value of the <samp>color </samp>
+     *            attribute.
+     * @param color
+     *            an object of the type <samp>Color </samp> or <samp>null</samp>
+     *            if the key needs to be cleared.
+     */
+    public void setColor(String key, Color color) {
+        String fullKey = "c." ~ key; //$NON-NLS-1$
+        if (color is null)
+            resourceTable.remove(fullKey);
+        else
+            resourceTable.put(fullKey, color);
+    }
+
+    /**
+     * Registers the font referenced by the provided key.
+     * <p>
+     * For <samp>span </samp> tags, an object of a type <samp>Font </samp> must
+     * be registered using the key equivalent to the value of the <samp>font
+     * </samp> attribute.
+     *
+     * @param key
+     *            unique key that matches the value of the <samp>font </samp>
+     *            attribute.
+     * @param font
+     *            an object of the type <samp>Font </samp> or <samp>null</samp>
+     *            if the key needs to be cleared.
+     */
+    public void setFont(String key, Font font) {
+        String fullKey = "f." ~ key; //$NON-NLS-1$
+        if (font is null)
+            resourceTable.remove(fullKey);
+        else
+            resourceTable.put(fullKey, font);
+        model.clearCache(fullKey);
+    }
+
+    /**
+     * Registers the control referenced by the provided key.
+     * <p>
+     * For <samp>control</samp> tags, an object of a type <samp>Control</samp>
+     * must be registered using the key equivalent to the value of the
+     * <samp>control</samp> attribute.
+     *
+     * @param key
+     *            unique key that matches the value of the <samp>control</samp>
+     *            attribute.
+     * @param control
+     *            an object of the type <samp>Control</samp> or <samp>null</samp>
+     *            if the existing control at the specified key needs to be
+     *            removed.
+     * @since 3.1
+     */
+    public void setControl(String key, Control control) {
+        String fullKey = "o." ~ key; //$NON-NLS-1$
+        if (control is null)
+            resourceTable.remove(fullKey);
+        else
+            resourceTable.put(fullKey, control);
+    }
+
+    /**
+     * Sets the font to use to render the default text (text that does not have
+     * special font property assigned). Bold font will be constructed from this
+     * font.
+     *
+     * @param font
+     *            the default font to use
+     */
+    public void setFont(Font font) {
+        super.setFont(font);
+        model.clearCache(null);
+        Font boldFont = cast(Font) resourceTable.get(FormTextModel.BOLD_FONT_ID);
+        if (boldFont !is null) {
+            FormFonts.getInstance().markFinished(boldFont);
+            resourceTable.remove(FormTextModel.BOLD_FONT_ID);
+        }
+        ensureBoldFontPresent(getFont());
+    }
+
+    /**
+     * Sets the provided text. Text can be rendered as-is, or by parsing the
+     * formatting tags. Optionally, sections of text starting with http:// will
+     * be converted to hyperlinks.
+     *
+     * @param text
+     *            the text to render
+     * @param parseTags
+     *            if <samp>true </samp>, formatting tags will be parsed.
+     *            Otherwise, text will be rendered as-is.
+     * @param expandURLs
+     *            if <samp>true </samp>, URLs found in the untagged text will be
+     *            converted into hyperlinks.
+     */
+    public void setText(String text, bool parseTags, bool expandURLs) {
+        disposeResourceTable(false);
+        entered = null;
+        if (parseTags)
+            model.parseTaggedText(text, expandURLs);
+        else
+            model.parseRegularText(text, expandURLs);
+        hookControlSegmentFocus();
+        layout();
+        redraw();
+    }
+
+    /**
+     * Sets the contents of the stream. Optionally, URLs in untagged text can be
+     * converted into hyperlinks. The caller is responsible for closing the
+     * stream.
+     *
+     * @param is
+     *            stream to render
+     * @param expandURLs
+     *            if <samp>true </samp>, URLs found in untagged text will be
+     *            converted into hyperlinks.
+     */
+    public void setContents(InputStream is_, bool expandURLs) {
+        entered = null;
+        disposeResourceTable(false);
+        model.parseInputStream(is_, expandURLs);
+        hookControlSegmentFocus();
+        layout();
+        redraw();
+    }
+
+    private void hookControlSegmentFocus() {
+        Paragraph[] paragraphs = model.getParagraphs();
+        if (paragraphs is null)
+            return;
+        Listener listener = new class Listener {
+            public void handleEvent(Event e) {
+                switch (e.type) {
+                case SWT.FocusIn:
+                    if (!controlFocusTransfer)
+                        syncControlSegmentFocus(cast(Control) e.widget);
+                    break;
+                case SWT.Traverse:
+                    if (DEBUG_FOCUS)
+                        Stdout.formatln("Control traversal: {}", e); //$NON-NLS-1$
+                    switch (e.detail) {
+                    case SWT.TRAVERSE_PAGE_NEXT:
+                    case SWT.TRAVERSE_PAGE_PREVIOUS:
+                    case SWT.TRAVERSE_ARROW_NEXT:
+                    case SWT.TRAVERSE_ARROW_PREVIOUS:
+                        e.doit = false;
+                        return;
+                    default:
+                    }
+                    Control c = cast(Control) e.widget;
+                    ControlSegment segment = cast(ControlSegment) c
+                            .getData(CONTROL_KEY);
+                    if (e.detail is SWT.TRAVERSE_TAB_NEXT)
+                        e.doit = advanceControl(c, segment, true);
+                    else if (e.detail is SWT.TRAVERSE_TAB_PREVIOUS)
+                        e.doit = advanceControl(c, segment, false);
+                    if (!e.doit)
+                        e.detail = SWT.TRAVERSE_NONE;
+                    break;
+                default:
+                }
+            }
+        };
+        for (int i = 0; i < paragraphs.length; i++) {
+            Paragraph p = paragraphs[i];
+            ParagraphSegment[] segments = p.getSegments();
+            for (int j = 0; j < segments.length; j++) {
+                if (auto cs = cast(ControlSegment)segments[j] ) {
+                    Control c = cs.getControl(resourceTable);
+                    if (c !is null) {
+                        if (c.getData(CONTROL_KEY) is null) {
+                            // first time - hook
+                            c.setData(CONTROL_KEY, cs);
+                            attachTraverseListener(c, listener);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private void attachTraverseListener(Control c, Listener listener) {
+        if ( auto parent = cast(Composite) c ) {
+            Control[] children = parent.getChildren();
+            for (int i = 0; i < children.length; i++) {
+                attachTraverseListener(children[i], listener);
+            }
+            if (auto canv = cast(Canvas)c ) {
+                // If Canvas, the control iteself can accept
+                // traverse events and should be monitored
+                c.addListener(SWT.Traverse, listener);
+                c.addListener(SWT.FocusIn, listener);
+            }
+        } else {
+            c.addListener(SWT.Traverse, listener);
+            c.addListener(SWT.FocusIn, listener);
+        }
+    }
+
+    /**
+     * If we click on the control randomly, our internal book-keeping will be
+     * off. We need to update the model and mark the control segment and
+     * currently selected. Hyperlink that may have had focus must also be
+     * exited.
+     *
+     * @param control
+     *            the control that got focus
+     */
+    private void syncControlSegmentFocus(Control control) {
+        ControlSegment cs = null;
+
+        while (control !is null) {
+            cs = cast(ControlSegment) control.getData(CONTROL_KEY);
+            if (cs !is null)
+                break;
+            control = control.getParent();
+        }
+        if (cs is null)
+            return;
+        IFocusSelectable current = model.getSelectedSegment();
+        // If the model and the control match, all is well
+        if (current is cs)
+            return;
+        IHyperlinkSegment oldLink = null;
+        if (current !is null && null !is cast(IHyperlinkSegment)current ) {
+            oldLink = cast(IHyperlinkSegment) current;
+            exitLink(oldLink, SWT.NULL);
+        }
+        if (DEBUG_FOCUS)
+            Stdout.formatln("Sync control: {}, oldLink={}", cs, oldLink); //$NON-NLS-1$ //$NON-NLS-2$
+        model.select(cs);
+        if (oldLink !is null)
+            paintFocusTransfer(oldLink, null);
+        // getAccessible().setFocus(model.getSelectedSegmentIndex());
+    }
+
+    private bool advanceControl(Control c, ControlSegment segment,
+            bool next) {
+        Composite parent = c.getParent();
+        if (parent is this) {
+            // segment-level control
+            IFocusSelectable nextSegment = model.getNextFocusSegment(next);
+            if (nextSegment !is null) {
+                controlFocusTransfer = true;
+                super.forceFocus();
+                controlFocusTransfer = false;
+                model.select(segment);
+                return advance(next);
+            }
+            // nowhere to go
+            return setFocusToNextSibling(this, next);
+        }
+        if (setFocusToNextSibling(c, next))
+            return true;
+        // still here - must go one level up
+        segment = cast(ControlSegment) parent.getData(CONTROL_KEY);
+        return advanceControl(parent, segment, next);
+    }
+
+    private bool setFocusToNextSibling(Control c, bool next) {
+        Composite parent = c.getParent();
+        Control[] children = parent.getTabList();
+        for (int i = 0; i < children.length; i++) {
+            Control child = children[i];
+            if (child is c) {
+                // here
+                if (next) {
+                    for (int j = i + 1; j < children.length; j++) {
+                        Control nc = children[j];
+                        if (nc.setFocus())
+                            return false;
+                    }
+                } else {
+                    for (int j = i - 1; j >= 0; j--) {
+                        Control pc = children[j];
+                        if (pc.setFocus())
+                            return false;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Controls whether whitespace inside paragraph and list items is
+     * normalized. Note that the new value will not affect the current text in
+     * the control, only subsequent calls to <code>setText</code> or
+     * <code>setContents</code>.
+     * <p>
+     * If normalized:
+     * <ul>
+     * <li>all white space characters will be condensed into at most one when
+     * between words.</li>
+     * <li>new line characters will be ignored and replaced with one white
+     * space character</li>
+     * <li>white space characters after the opening tags and before the closing
+     * tags will be trimmed</li>
+     *
+     * @param value
+     *            <code>true</code> if whitespace is normalized,
+     *            <code>false</code> otherwise.
+     */
+    public void setWhitespaceNormalized(bool value) {
+        model.setWhitespaceNormalized(value);
+    }
+
+    /**
+     * Tests whether whitespace inside paragraph and list item is normalized.
+     *
+     * @see #setWhitespaceNormalized(bool)
+     * @return <code>true</code> if whitespace is normalized,
+     *         <code>false</code> otherwise.
+     */
+    public bool isWhitespaceNormalized() {
+        return model.isWhitespaceNormalized();
+    }
+
+    /**
+     * Disposes the internal menu if created and sets the menu provided as a
+     * parameter.
+     *
+     * @param menu
+     *            the menu to associate with this text control
+     */
+    public void setMenu(Menu menu) {
+        Menu currentMenu = super.getMenu();
+        if (currentMenu !is null && INTERNAL_MENU.equals(stringcast(currentMenu.getData()))) {
+            // internal menu set
+            if (menu !is null) {
+                currentMenu.dispose();
+                super.setMenu(menu);
+            }
+        } else
+            super.setMenu(menu);
+    }
+
+    private void createMenu() {
+        Menu menu = new Menu(this);
+        final MenuItem copyItem = new MenuItem(menu, SWT.PUSH);
+        copyItem.setText(Messages.FormText_copy);
+
+        SelectionListener listener = new class SelectionAdapter {
+            public void widgetSelected(SelectionEvent e) {
+                if (e.widget is copyItem) {
+                    copy();
+                }
+            }
+        };
+        copyItem.addSelectionListener(listener);
+        menu.addMenuListener(new class MenuListener {
+            public void menuShown(MenuEvent e) {
+                copyItem.setEnabled(canCopy());
+            }
+
+            public void menuHidden(MenuEvent e) {
+            }
+        });
+        menu.setData(stringcast(INTERNAL_MENU));
+        super.setMenu(menu);
+    }
+
+    /**
+     * Returns the hyperlink settings that are in effect for this control.
+     *
+     * @return current hyperlinks settings
+     */
+    public HyperlinkSettings getHyperlinkSettings() {
+        return model.getHyperlinkSettings();
+    }
+
+    /**
+     * Sets the hyperlink settings to be used for this control. Settings will
+     * affect things like hyperlink color, rendering style, cursor etc.
+     *
+     * @param settings
+     *            hyperlink settings for this control
+     */
+    public void setHyperlinkSettings(HyperlinkSettings settings) {
+        model.setHyperlinkSettings(settings);
+    }
+
+    /**
+     * Adds a listener that will handle hyperlink events.
+     *
+     * @param listener
+     *            the listener to add
+     */
+    public void addHyperlinkListener(IHyperlinkListener listener) {
+        if (listeners is null)
+            listeners = new ListenerList();
+        listeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes the hyperlink listener.
+     *
+     * @param listener
+     *            the listener to remove
+     */
+    public void removeHyperlinkListener(IHyperlinkListener listener) {
+        if (listeners is null)
+            return;
+        listeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Adds a selection listener. A Selection event is sent by the widget when
+     * the selection has changed.
+     * <p>
+     * <code>widgetDefaultSelected</code> is not called for FormText.
+     * </p>
+     *
+     * @param listener
+     *            the listener
+     * @exception SWTException
+     *                <ul>
+     *                <li>ERROR_WIDGET_DISPOSED - if the receiver has been
+     *                disposed</li>
+     *                <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
+     *                thread that created the receiver</li>
+     *                </ul>
+     * @exception IllegalArgumentException
+     *                <ul>
+     *                <li>ERROR_NULL_ARGUMENT when listener is null</li>
+     *                </ul>
+     * @since 3.1
+     */
+    public void addSelectionListener(SelectionListener listener) {
+        checkWidget();
+        if (listener is null) {
+            SWT.error(SWT.ERROR_NULL_ARGUMENT);
+        }
+        TypedListener typedListener = new TypedListener(listener);
+        addListener(SWT.Selection, typedListener);
+    }
+
+    /**
+     * Removes the specified selection listener.
+     * <p>
+     *
+     * @param listener
+     *            the listener
+     * @exception SWTException
+     *                <ul>
+     *                <li>ERROR_WIDGET_DISPOSED - if the receiver has been
+     *                disposed</li>
+     *                <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
+     *                thread that created the receiver</li>
+     *                </ul>
+     * @exception IllegalArgumentException
+     *                <ul>
+     *                <li>ERROR_NULL_ARGUMENT when listener is null</li>
+     *                </ul>
+     * @since 3.1
+     */
+    public void removeSelectionListener(SelectionListener listener) {
+        checkWidget();
+        if (listener is null) {
+            SWT.error(SWT.ERROR_NULL_ARGUMENT);
+        }
+        removeListener(SWT.Selection, listener);
+    }
+
+    /**
+     * Returns the selected text.
+     * <p>
+     *
+     * @return selected text, or an empty String if there is no selection.
+     * @exception SWTException
+     *                <ul>
+     *                <li>ERROR_WIDGET_DISPOSED - if the receiver has been
+     *                disposed</li>
+     *                <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
+     *                thread that created the receiver</li>
+     *                </ul>
+     * @since 3.1
+     */
+
+    public String getSelectionText() {
+        checkWidget();
+        if (selData !is null)
+            return selData.getSelectionText();
+        return ""; //$NON-NLS-1$
+    }
+
+    /**
+     * Tests if the text is selected and can be copied into the clipboard.
+     *
+     * @return <code>true</code> if the selected text can be copied into the
+     *         clipboard, <code>false</code> otherwise.
+     * @since 3.1
+     */
+    public bool canCopy() {
+        return selData !is null && selData.canCopy();
+    }
+
+    /**
+     * Copies the selected text into the clipboard. Does nothing if no text is
+     * selected or the text cannot be copied for any other reason.
+     *
+     * @since 3.1
+     */
+
+    public void copy() {
+        if (!canCopy())
+            return;
+        Clipboard clipboard = new Clipboard(getDisplay());
+        Object[] o = [ stringcast(getSelectionText()) ];
+        Transfer[] t = [ TextTransfer.getInstance() ];
+        clipboard.setContents(o, t);
+        clipboard.dispose();
+    }
+
+    /**
+     * Returns the reference of the hyperlink that currently has keyboard focus,
+     * or <code>null</code> if there are no hyperlinks in the receiver or no
+     * hyperlink has focus at the moment.
+     *
+     * @return href of the selected hyperlink or <code>null</code> if none
+     *         selected.
+     * @since 3.1
+     */
+    public Object getSelectedLinkHref() {
+        IHyperlinkSegment link = getSelectedLink();
+        return link !is null ? stringcast(link.getHref()) : null;
+    }
+
+    /**
+     * Returns the text of the hyperlink that currently has keyboard focus, or
+     * <code>null</code> if there are no hyperlinks in the receiver or no
+     * hyperlink has focus at the moment.
+     *
+     * @return text of the selected hyperlink or <code>null</code> if none
+     *         selected.
+     * @since 3.1
+     */
+    public String getSelectedLinkText() {
+        IHyperlinkSegment link = getSelectedLink();
+        return link !is null ? link.getText() : null;
+    }
+
+    private IHyperlinkSegment getSelectedLink() {
+        IFocusSelectable segment = model.getSelectedSegment();
+        if (segment !is null && null !is cast(IHyperlinkSegment)segment )
+            return cast(IHyperlinkSegment) segment;
+        return null;
+    }
+
+    private void initAccessible() {
+        Accessible accessible = getAccessible();
+        accessible.addAccessibleListener(new class AccessibleAdapter {
+            public void getName(AccessibleEvent e) {
+                if (e.childID is ACC.CHILDID_SELF)
+                    e.result = model.getAccessibleText();
+                else {
+                    int linkCount = model.getHyperlinkCount();
+                    if (e.childID >= 0 && e.childID < linkCount) {
+                        IHyperlinkSegment link = model.getHyperlink(e.childID);
+                        e.result = link.getText();
+                    }
+                }
+            }
+
+            public void getHelp(AccessibleEvent e) {
+                e.result = getToolTipText();
+                int linkCount = model.getHyperlinkCount();
+                if (e.result is null && e.childID >= 0 && e.childID < linkCount) {
+                    IHyperlinkSegment link = model.getHyperlink(e.childID);
+                    e.result = link.getText();
+                }
+            }
+        });
+        accessible.addAccessibleControlListener(new class AccessibleControlAdapter {
+            public void getChildAtPoint(AccessibleControlEvent e) {
+                Point pt = toControl(new Point(e.x, e.y));
+                IHyperlinkSegment link = model.findHyperlinkAt(pt.x, pt.y);
+                if (link !is null)
+                    e.childID = model.indexOf(link);
+                else
+                    e.childID = ACC.CHILDID_SELF;
+            }
+
+            public void getLocation(AccessibleControlEvent e) {
+                Rectangle location = null;
+                if (e.childID !is ACC.CHILDID_SELF
+                        && e.childID !is ACC.CHILDID_NONE) {
+                    int index = e.childID;
+                    IHyperlinkSegment link = model.getHyperlink(index);
+                    if (link !is null) {
+                        location = link.getBounds();
+                    }
+                }
+                if (location is null) {
+                    location = getBounds();
+                }
+                Point pt = toDisplay(new Point(location.x, location.y));
+                e.x = pt.x;
+                e.y = pt.y;
+                e.width = location.width;
+                e.height = location.height;
+            }
+
+            public void getFocus(AccessibleControlEvent e) {
+                int childID = ACC.CHILDID_NONE;
+
+                if (model.hasFocusSegments()) {
+                    int selectedIndex = model.getSelectedSegmentIndex();
+                    if (selectedIndex !is -1) {
+                        childID = selectedIndex;
+                    }
+                }
+                e.childID = childID;
+            }
+
+            public void getDefaultAction (AccessibleControlEvent e) {
+                if (model.getHyperlinkCount() > 0) {
+                    e.result = SWT.getMessage ("SWT_Press"); //$NON-NLS-1$
+                }
+            }
+
+            public void getChildCount(AccessibleControlEvent e) {
+                e.detail = model.getHyperlinkCount();
+            }
+
+            public void getRole(AccessibleControlEvent e) {
+                int role = 0;
+                int childID = e.childID;
+                int linkCount = model.getHyperlinkCount();
+                if (childID is ACC.CHILDID_SELF) {
+                    if (linkCount > 0) {
+                        role = ACC.ROLE_LINK;
+                    } else {
+                        role = ACC.ROLE_TEXT;
+                    }
+                } else if (childID >= 0 && childID < linkCount) {
+                    role = ACC.ROLE_LINK;
+                }
+                e.detail = role;
+            }
+
+            public void getSelection(AccessibleControlEvent e) {
+                int selectedIndex = model.getSelectedSegmentIndex();
+                e.childID = (selectedIndex is -1) ? ACC.CHILDID_NONE
+                        : selectedIndex;
+            }
+
+            public void getState(AccessibleControlEvent e) {
+                int linkCount = model.getHyperlinkCount();
+                int selectedIndex = model.getSelectedSegmentIndex();
+                int state = 0;
+                int childID = e.childID;
+                if (childID is ACC.CHILDID_SELF) {
+                    state = ACC.STATE_NORMAL;
+                } else if (childID >= 0 && childID < linkCount) {
+                    state = ACC.STATE_SELECTABLE;
+                    if (isFocusControl()) {
+                        state |= ACC.STATE_FOCUSABLE;
+                    }
+                    if (selectedIndex is childID) {
+                        state |= ACC.STATE_SELECTED;
+                        if (isFocusControl()) {
+                            state |= ACC.STATE_FOCUSED;
+                        }
+                    }
+                }
+                state |= ACC.STATE_READONLY;
+                e.detail = state;
+            }
+
+            public void getChildren(AccessibleControlEvent e) {
+                int linkCount = model.getHyperlinkCount();
+                Object[] children = new Object[linkCount];
+                for (int i = 0; i < linkCount; i++) {
+                    children[i] = new Integer(i);
+                }
+                e.children = children;
+            }
+
+            public void getValue(AccessibleControlEvent e) {
+                // e.result = model.getAccessibleText();
+            }
+        });
+    }
+
+    private void startSelection(MouseEvent e) {
+        inSelection = true;
+        selData = new SelectionData(e);
+        redraw();
+        Form form = FormUtil.getForm(this);
+        if (form !is null)
+            form.setSelectionText(this);
+    }
+
+    private void endSelection(MouseEvent e) {
+        inSelection = false;
+        if (selData !is null) {
+            if (!selData.isEnclosed())
+                selData = null;
+            else
+                computeSelection();
+        }
+        notifySelectionChanged();
+    }
+
+    private void computeSelection() {
+        GC gc = new GC(this);
+        Paragraph[] paragraphs = model.getParagraphs();
+        IHyperlinkSegment selectedLink = getSelectedLink();
+        if (getDisplay().getFocusControl() !is this)
+            selectedLink = null;
+        for (int i = 0; i < paragraphs.length; i++) {
+            Paragraph p = paragraphs[i];
+            if (i > 0)
+                selData.markNewLine();
+            p.computeSelection(gc, resourceTable, selectedLink, selData);
+        }
+        gc.dispose();
+    }
+
+    void clearSelection() {
+        selData = null;
+        if (!isDisposed()) {
+            redraw();
+            notifySelectionChanged();
+        }
+    }
+
+    private void notifySelectionChanged() {
+        Event event = new Event();
+        event.widget = this;
+        event.display = this.getDisplay();
+        event.type = SWT.Selection;
+        notifyListeners(SWT.Selection, event);
+        getAccessible().selectionChanged();
+    }
+
+    private void handleDrag(MouseEvent e) {
+        if (selData !is null) {
+            ScrolledComposite scomp = FormUtil.getScrolledComposite(this);
+            if (scomp !is null) {
+                FormUtil.ensureVisible(scomp, this, e);
+            }
+            selData.update(e);
+            redraw();
+        }
+    }
+
+    private void handleMouseClick(MouseEvent e, bool down) {
+        if (DEBUG_FOCUS)
+            Stdout.formatln("FormText: mouse click({})", down ); //$NON-NLS-1$ //$NON-NLS-2$
+        if (down) {
+            // select a hyperlink
+            mouseFocus = true;
+            IHyperlinkSegment segmentUnder = model.findHyperlinkAt(e.x, e.y);
+            if (segmentUnder !is null) {
+                IHyperlinkSegment oldLink = getSelectedLink();
+                if (getDisplay().getFocusControl() !is this) {
+                    setFocus();
+                }
+                model.selectLink(segmentUnder);
+                enterLink(segmentUnder, e.stateMask);
+                paintFocusTransfer(oldLink, segmentUnder);
+            }
+            if (e.button is 1) {
+                startSelection(e);
+                armed = segmentUnder;
+            }
+            else {
+            }
+        } else {
+            if (e.button is 1) {
+                endSelection(e);
+                IHyperlinkSegment segmentUnder = model
+                        .findHyperlinkAt(e.x, e.y);
+                if (segmentUnder !is null && armed is segmentUnder && selData is null) {
+                    activateLink(segmentUnder, e.stateMask);
+                    armed = null;
+                }
+            }
+            mouseFocus = false;
+        }
+    }
+
+    private void handleMouseHover(MouseEvent e) {
+    }
+
+    private void updateTooltipText(ParagraphSegment segment) {
+        String tooltipText = null;
+        if (segment !is null) {
+            tooltipText = segment.getTooltipText();
+        }
+        String currentTooltipText = getToolTipText();
+
+        if ((currentTooltipText !is null && tooltipText is null)
+                || (currentTooltipText is null && tooltipText !is null))
+            setToolTipText(tooltipText);
+    }
+
+    private void handleMouseMove(MouseEvent e) {
+        if (inSelection) {
+            handleDrag(e);
+            return;
+        }
+        ParagraphSegment segmentUnder = model.findSegmentAt(e.x, e.y);
+        updateTooltipText(segmentUnder);
+        if (segmentUnder is null) {
+            if (entered !is null) {
+                exitLink(entered, e.stateMask);
+                paintLinkHover(entered, false);
+                entered = null;
+            }
+            setCursor(null);
+        } else {
+            if (auto linkUnder = cast(IHyperlinkSegment) segmentUnder ) {
+                if (entered !is null && linkUnder !is entered) {
+                    // Special case: links are so close that there are 0 pixels between.
+                    // Must exit the link before entering the next one.
+                    exitLink(entered, e.stateMask);
+                    paintLinkHover(entered, false);
+                    entered = null;
+                }
+                if (entered is null) {
+                    entered = linkUnder;
+                    enterLink(linkUnder, e.stateMask);
+                    paintLinkHover(entered, true);
+                    setCursor(model.getHyperlinkSettings().getHyperlinkCursor());
+                }
+            } else {
+                if (entered !is null) {
+                    exitLink(entered, e.stateMask);
+                    paintLinkHover(entered, false);
+                    entered = null;
+                }
+                if (null !is cast(TextSegment)segmentUnder )
+                    setCursor(model.getHyperlinkSettings().getTextCursor());
+                else
+                    setCursor(null);
+            }
+        }
+    }
+
+    private bool advance(bool next) {
+        if (DEBUG_FOCUS)
+            Stdout.formatln("Advance: next={}", next); //$NON-NLS-1$
+        IFocusSelectable current = model.getSelectedSegment();
+        if (current !is null && null !is cast(IHyperlinkSegment)current )
+            exitLink(cast(IHyperlinkSegment) current, SWT.NULL);
+        IFocusSelectable newSegment = null;
+        bool valid = false;
+        // get the next segment that can accept focus. Links
+        // can always accept focus but controls may not
+        while (!valid) {
+            if (!model.traverseFocusSelectableObjects(next))
+                break;
+            newSegment = model.getSelectedSegment();
+            if (newSegment is null)
+                break;
+            valid = setControlFocus(next, newSegment);
+        }
+        IHyperlinkSegment newLink = null !is cast(IHyperlinkSegment)newSegment ? cast(IHyperlinkSegment) newSegment
+                : null;
+        if (valid)
+            enterLink(newLink, SWT.NULL);
+        IHyperlinkSegment oldLink = null !is cast(IHyperlinkSegment)current ? cast(IHyperlinkSegment) current
+                : null;
+        if (oldLink !is null || newLink !is null)
+            paintFocusTransfer(oldLink, newLink);
+        if (newLink !is null)
+            ensureVisible(newLink);
+        if (newLink !is null)
+            getAccessible().setFocus(model.getSelectedSegmentIndex());
+        return !valid;
+    }
+
+    private bool setControlFocus(bool next, IFocusSelectable selectable) {
+        controlFocusTransfer = true;
+        bool result = selectable.setFocus(resourceTable, next);
+        controlFocusTransfer = false;
+        return result;
+    }
+
+    private void handleFocusChange() {
+        if (DEBUG_FOCUS) {
+            Stdout.formatln("Handle focus change: hasFocus={}, mouseFocus={}", hasFocus, //$NON-NLS-1$
+                    mouseFocus); //$NON-NLS-1$
+        }
+        if (hasFocus) {
+            bool advance = true;
+            if (!mouseFocus) {
+                // if (model.restoreSavedLink() is false)
+                bool valid = false;
+                IFocusSelectable selectable = null;
+                while (!valid) {
+                    if (!model.traverseFocusSelectableObjects(advance))
+                        break;
+                    selectable = model.getSelectedSegment();
+                    if (selectable is null)
+                        break;
+                    valid = setControlFocus(advance, selectable);
+                }
+                if (selectable is null)
+                    setFocusToNextSibling(this, true);
+                else
+                    ensureVisible(selectable);
+                if ( auto hls = cast(IHyperlinkSegment)selectable ) {
+                    enterLink(hls, SWT.NULL);
+                    paintFocusTransfer(null, hls);
+                }
+            }
+        } else {
+            paintFocusTransfer(getSelectedLink(), null);
+            model.selectLink(null);
+        }
+    }
+
+    private void enterLink(IHyperlinkSegment link, int stateMask) {
+        if (link is null || listeners is null)
+            return;
+        int size = listeners.size();
+        HyperlinkEvent he = new HyperlinkEvent(this, stringcast(link.getHref()), link
+                .getText(), stateMask);
+        Object [] listenerList = listeners.getListeners();
+        for (int i = 0; i < size; i++) {
+            IHyperlinkListener listener = cast(IHyperlinkListener) listenerList[i];
+            listener.linkEntered(he);
+        }
+    }
+
+    private void exitLink(IHyperlinkSegment link, int stateMask) {
+        if (link is null || listeners is null)
+            return;
+        int size = listeners.size();
+        HyperlinkEvent he = new HyperlinkEvent(this, stringcast(link.getHref()), link
+                .getText(), stateMask);
+        Object [] listenerList = listeners.getListeners();
+        for (int i = 0; i < size; i++) {
+            IHyperlinkListener listener = cast(IHyperlinkListener) listenerList[i];
+            listener.linkExited(he);
+        }
+    }
+
+    private void paintLinkHover(IHyperlinkSegment link, bool hover) {
+        GC gc = new GC(this);
+        HyperlinkSettings settings = getHyperlinkSettings();
+        Color newFg = hover ? settings.getActiveForeground() : settings
+                .getForeground();
+        if (newFg !is null)
+            gc.setForeground(newFg);
+        gc.setBackground(getBackground());
+        gc.setFont(getFont());
+        bool selected = (link is getSelectedLink());
+        (cast(ParagraphSegment) link).paint(gc, hover, resourceTable, selected,
+                selData, null);
+        gc.dispose();
+    }
+
+    private void activateSelectedLink() {
+        IHyperlinkSegment link = getSelectedLink();
+        if (link !is null)
+            activateLink(link, SWT.NULL);
+    }
+
+    private void activateLink(IHyperlinkSegment link, int stateMask) {
+        setCursor(model.getHyperlinkSettings().getBusyCursor());
+        if (listeners !is null) {
+            int size = listeners.size();
+            HyperlinkEvent e = new HyperlinkEvent(this, stringcast(link.getHref()), link
+                    .getText(), stateMask);
+            Object [] listenerList = listeners.getListeners();
+            for (int i = 0; i < size; i++) {
+                IHyperlinkListener listener = cast(IHyperlinkListener) listenerList[i];
+                listener.linkActivated(e);
+            }
+        }
+        if (!isDisposed() && model.linkExists(link)) {
+            setCursor(model.getHyperlinkSettings().getHyperlinkCursor());
+        }
+    }
+
+    private void ensureBoldFontPresent(Font regularFont) {
+        Font boldFont = cast(Font) resourceTable.get(FormTextModel.BOLD_FONT_ID);
+        if (boldFont !is null)
+            return;
+        boldFont = FormFonts.getInstance().getBoldFont(getDisplay(), regularFont);
+        resourceTable.put(FormTextModel.BOLD_FONT_ID, boldFont);
+    }
+
+    private void paint(PaintEvent e) {
+        GC gc = e.gc;
+        gc.setFont(getFont());
+        ensureBoldFontPresent(getFont());
+        gc.setForeground(getForeground());
+        gc.setBackground(getBackground());
+        repaint(gc, e.x, e.y, e.width, e.height);
+    }
+
+    private void repaint(GC gc, int x, int y, int width, int height) {
+        Image textBuffer = new Image(getDisplay(), width, height);
+        Color bg = getBackground();
+        Color fg = getForeground();
+        if (!getEnabled()) {
+            bg = getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
+            fg = getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
+        }
+        GC textGC = new GC(textBuffer, gc.getStyle());
+        textGC.setForeground(fg);
+        textGC.setBackground(bg);
+        textGC.setFont(getFont());
+        textGC.fillRectangle(0, 0, width, height);
+        Rectangle repaintRegion = new Rectangle(x, y, width, height);
+
+        Paragraph[] paragraphs = model.getParagraphs();
+        IHyperlinkSegment selectedLink = getSelectedLink();
+        if (getDisplay().getFocusControl() !is this)
+            selectedLink = null;
+        for (int i = 0; i < paragraphs.length; i++) {
+            Paragraph p = paragraphs[i];
+            p
+                    .paint(textGC, repaintRegion, resourceTable, selectedLink,
+                            selData);
+        }
+        textGC.dispose();
+        gc.drawImage(textBuffer, x, y);
+        textBuffer.dispose();
+    }
+
+    private int getParagraphSpacing(int lineHeight) {
+        return lineHeight / 2;
+    }
+
+    private void paintFocusTransfer(IHyperlinkSegment oldLink,
+            IHyperlinkSegment newLink) {
+        GC gc = new GC(this);
+        Color bg = getBackground();
+        Color fg = getForeground();
+        gc.setFont(getFont());
+        if (oldLink !is null) {
+            gc.setBackground(bg);
+            gc.setForeground(fg);
+            oldLink.paintFocus(gc, bg, fg, false, null);
+        }
+        if (newLink !is null) {
+            // ensureVisible(newLink);
+            gc.setBackground(bg);
+            gc.setForeground(fg);
+            newLink.paintFocus(gc, bg, fg, true, null);
+        }
+        gc.dispose();
+    }
+
+    private void ensureVisible(IFocusSelectable segment) {
+        if (mouseFocus) {
+            mouseFocus = false;
+            return;
+        }
+        if (segment is null)
+            return;
+        Rectangle bounds = segment.getBounds();
+        ScrolledComposite scomp = FormUtil.getScrolledComposite(this);
+        if (scomp is null)
+            return;
+        Point origin = FormUtil.getControlLocation(scomp, this);
+        origin.x += bounds.x;
+        origin.y += bounds.y;
+        FormUtil.ensureVisible(scomp, origin, new Point(bounds.width,
+                bounds.height));
+    }
+
+    /**
+     * Overrides the method by fully trusting the layout manager (computed width
+     * or height may be larger than the provider width or height hints). Callers
+     * should be prepared that the computed width is larger than the provided
+     * wHint.
+     *
+     * @see org.eclipse.swt.widgets.Composite#computeSize(int, int, bool)
+     */
+    public Point computeSize(int wHint, int hHint, bool changed) {
+        checkWidget();
+        Point size;
+        FormTextLayout layout = cast(FormTextLayout) getLayout();
+        if (wHint is SWT.DEFAULT || hHint is SWT.DEFAULT) {
+            size = layout.computeSize(this, wHint, hHint, changed);
+        } else {
+            size = new Point(wHint, hHint);
+        }
+        Rectangle trim = computeTrim(0, 0, size.x, size.y);
+        if (DEBUG_TEXTSIZE)
+            Stdout.formatln("FormText Computed size: {}",trim); //$NON-NLS-1$
+        return new Point(trim.width, trim.height);
+    }
+
+    private void disposeResourceTable(bool disposeBoldFont) {
+        if (disposeBoldFont) {
+            Font boldFont = cast(Font) resourceTable
+                    .get(FormTextModel.BOLD_FONT_ID);
+            if (boldFont !is null) {
+                FormFonts.getInstance().markFinished(boldFont);
+                resourceTable.remove(FormTextModel.BOLD_FONT_ID);
+            }
+        }
+        ArrayList imagesToRemove = new ArrayList();
+        for (Enumeration enm = resourceTable.keys(); enm.hasMoreElements();) {
+            String key = stringcast( enm.nextElement());
+            if (key.startsWith(ImageSegment.SEL_IMAGE_PREFIX)) {
+                Object obj = resourceTable.get(key);
+                if (auto image = cast(Image)obj ) {
+                    if (!image.isDisposed()) {
+                        image.dispose();
+                        imagesToRemove.add(key);
+                    }
+                }
+            }
+        }
+        for (int i = 0; i < imagesToRemove.size(); i++) {
+            resourceTable.remove(imagesToRemove.get(i));
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.swt.widgets.Control#setEnabled(bool)
+     */
+    public void setEnabled(bool enabled) {
+        super.setEnabled(enabled);
+        redraw();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.swt.widgets.Control#setFocus()
+     */
+    public bool setFocus() {
+        FormUtil.setFocusScrollingEnabled(this, false);
+        bool result = super.setFocus();
+        FormUtil.setFocusScrollingEnabled(this, true);
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/FormToolkit.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,919 @@
+/*******************************************************************************
+ * 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
+ *     Michael Williamson (eclipse-bugs@magnaworks.com) - patch (see Bugzilla #92545)
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.ui.forms.widgets.FormToolkit;
+
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.ImageHyperlink;
+import org.eclipse.ui.forms.widgets.ExpandableComposite;
+import org.eclipse.ui.forms.widgets.Form;
+import org.eclipse.ui.forms.widgets.ScrolledPageBook;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.FormText;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.LayoutComposite;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.window.Window;
+import org.eclipse.ui.forms.FormColors;
+import org.eclipse.ui.forms.HyperlinkGroup;
+import org.eclipse.ui.forms.IFormColors;
+import org.eclipse.ui.internal.forms.widgets.FormFonts;
+import org.eclipse.ui.internal.forms.widgets.FormUtil;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The toolkit is responsible for creating SWT controls adapted to work in
+ * Eclipse forms. In addition to changing their presentation properties (fonts,
+ * colors etc.), various listeners are attached to make them behave correctly in
+ * the form context.
+ * <p>
+ * In addition to being the control factory, the toolkit is also responsible for
+ * painting flat borders for select controls, managing hyperlink groups and
+ * control colors.
+ * <p>
+ * The toolkit creates some of the most common controls used to populate Eclipse
+ * forms. Controls that must be created using their constructors,
+ * <code>adapt()</code> method is available to change its properties in the
+ * same way as with the supported toolkit controls.
+ * <p>
+ * Typically, one toolkit object is created per workbench part (for example, an
+ * editor or a form wizard). The toolkit is disposed when the part is disposed.
+ * To conserve resources, it is possible to create one color object for the
+ * entire plug-in and share it between several toolkits. The plug-in is
+ * responsible for disposing the colors (disposing the toolkit that uses shared
+ * color object will not dispose the colors).
+ * <p>
+ * FormToolkit is normally instantiated, but can also be subclassed if some of
+ * the methods needs to be modified. In those cases, <code>super</code> must
+ * be called to preserve normal behaviour.
+ *
+ * @since 3.0
+ */
+public class FormToolkit {
+    public static const String KEY_DRAW_BORDER = "FormWidgetFactory.drawBorder"; //$NON-NLS-1$
+
+    public static const String TREE_BORDER = "treeBorder"; //$NON-NLS-1$
+
+    public static const String TEXT_BORDER = "textBorder"; //$NON-NLS-1$
+
+    private int borderStyle = SWT.NULL;
+
+    private FormColors colors;
+
+    private int orientation;
+
+    // private KeyListener deleteListener;
+    private BorderPainter borderPainter;
+
+    private BoldFontHolder boldFontHolder;
+
+    private HyperlinkGroup hyperlinkGroup;
+
+    /* default */
+    VisibilityHandler visibilityHandler;
+
+    /* default */
+    KeyboardHandler keyboardHandler;
+
+    private class BorderPainter : PaintListener {
+        public void paintControl(PaintEvent event) {
+            Composite composite = cast(Composite) event.widget;
+            Control[] children = composite.getChildren();
+            for (int i = 0; i < children.length; i++) {
+                Control c = children[i];
+                bool inactiveBorder = false;
+                bool textBorder = false;
+                if (!c.isVisible())
+                    continue;
+                /*
+                 * if (c.getEnabled() is false && !(c instanceof CCombo))
+                 * continue;
+                 */
+                if (null !is cast(Hyperlink)c )
+                    continue;
+                Object flag = c.getData(KEY_DRAW_BORDER);
+                if (flag !is null) {
+                    if (flag.opEquals(Boolean.FALSE))
+                        continue;
+                    if (flag.opEquals(stringcast(TREE_BORDER)))
+                        inactiveBorder = true;
+                    else if (flag.opEquals(stringcast(TEXT_BORDER)))
+                        textBorder = true;
+                }
+                if (getBorderStyle() is SWT.BORDER) {
+                    if (!inactiveBorder && !textBorder) {
+                        continue;
+                    }
+                    if (null !is cast(Text)c  || null !is cast(Table)c
+                            || null !is cast(Tree)c )
+                        continue;
+                }
+                if (!inactiveBorder
+                        && (null !is cast(Text)c  || null !is cast(CCombo)c  || textBorder)) {
+                    Rectangle b = c.getBounds();
+                    GC gc = event.gc;
+                    gc.setForeground(c.getBackground());
+                    gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1,
+                            b.height + 1);
+                    // gc.setForeground(getBorderStyle() is SWT.BORDER ? colors
+                    // .getBorderColor() : colors.getForeground());
+                    gc.setForeground(colors.getBorderColor());
+                    if (null !is cast(CCombo)c )
+                        gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1,
+                                b.height + 1);
+                    else
+                        gc.drawRectangle(b.x - 1, b.y - 2, b.width + 1,
+                                b.height + 3);
+                } else if (inactiveBorder || null !is cast(Table)c
+                        || null !is cast(Tree)c ) {
+                    Rectangle b = c.getBounds();
+                    GC gc = event.gc;
+                    gc.setForeground(colors.getBorderColor());
+                    gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1,
+                            b.height + 1);
+                }
+            }
+        }
+    }
+
+    private static class VisibilityHandler : FocusAdapter {
+        public void focusGained(FocusEvent e) {
+            Widget w = e.widget;
+            if (null !is cast(Control)w ) {
+                FormUtil.ensureVisible(cast(Control) w);
+            }
+        }
+    }
+
+    private static class KeyboardHandler : KeyAdapter {
+        public void keyPressed(KeyEvent e) {
+            Widget w = e.widget;
+            if (null !is cast(Control)w ) {
+                if (e.doit)
+                    FormUtil.processKey(e.keyCode, cast(Control) w);
+            }
+        }
+    }
+
+    private class BoldFontHolder {
+        private Font normalFont;
+
+        private Font boldFont;
+
+        public this() {
+        }
+
+        public Font getBoldFont(Font font) {
+            createBoldFont(font);
+            return boldFont;
+        }
+
+        private void createBoldFont(Font font) {
+            if (normalFont is null || !normalFont.opEquals(font)) {
+                normalFont = font;
+                dispose();
+            }
+            if (boldFont is null) {
+                boldFont = FormFonts.getInstance().getBoldFont(colors.getDisplay(),
+                        normalFont);
+            }
+        }
+
+        public void dispose() {
+            if (boldFont !is null) {
+                FormFonts.getInstance().markFinished(boldFont);
+                boldFont = null;
+            }
+        }
+    }
+
+    /**
+     * Creates a toolkit that is self-sufficient (will manage its own colors).
+     *
+     */
+    public this(Display display) {
+        this(new FormColors(display));
+    }
+
+    /**
+     * Creates a toolkit that will use the provided (shared) colors. The toolkit
+     * will dispose the colors if and only if they are <b>not</b> marked as
+     * shared via the <code>markShared()</code> method.
+     *
+     * @param colors
+     *            the shared colors
+     */
+    public this(FormColors colors) {
+        orientation = Window.getDefaultOrientation();
+        this.colors = colors;
+        initialize();
+    }
+
+    /**
+     * Creates a button as a part of the form.
+     *
+     * @param parent
+     *            the button parent
+     * @param text
+     *            an optional text for the button (can be <code>null</code>)
+     * @param style
+     *            the button style (for example, <code>SWT.PUSH</code>)
+     * @return the button widget
+     */
+    public Button createButton(Composite parent, String text, int style) {
+        Button button = new Button(parent, style | SWT.FLAT | orientation);
+        if (text !is null)
+            button.setText(text);
+        adapt(button, true, true);
+        return button;
+    }
+
+    /**
+     * Creates the composite as a part of the form.
+     *
+     * @param parent
+     *            the composite parent
+     * @return the composite widget
+     */
+    public Composite createComposite(Composite parent) {
+        return createComposite(parent, SWT.NULL);
+    }
+
+    /**
+     * Creates the composite as part of the form using the provided style.
+     *
+     * @param parent
+     *            the composite parent
+     * @param style
+     *            the composite style
+     * @return the composite widget
+     */
+    public Composite createComposite(Composite parent, int style) {
+        Composite composite = new LayoutComposite(parent, style | orientation);
+        adapt(composite);
+        return composite;
+    }
+
+    /**
+     * Creats the composite that can server as a separator between various parts
+     * of a form. Separator height should be controlled by setting the height
+     * hint on the layout data for the composite.
+     *
+     * @param parent
+     *            the separator parent
+     * @return the separator widget
+     */
+    public Composite createCompositeSeparator(Composite parent) {
+        final Composite composite = new Composite(parent, orientation);
+    // SWT FIXME: problem with DMD and anonclass/nested func
+        composite.addListener(SWT.Paint, dgListener( &dwtWorkaround, composite));
+        if (null !is cast(Section)parent )
+            (cast(Section) parent).setSeparatorControl(composite);
+        return composite;
+    }
+    // SWT FIXME: problem with DMD and anonclass/nested func
+        private void dwtWorkaround (Event e, Composite composite) {
+            if (composite.isDisposed())
+                return;
+            Rectangle bounds = composite.getBounds();
+            GC gc = e.gc;
+            gc.setForeground(colors.getColor(IFormColors.SEPARATOR));
+            if (colors.getBackground() !is null)
+                gc.setBackground(colors.getBackground());
+            gc.fillGradientRectangle(0, 0, bounds.width, bounds.height,
+                    false);
+        }
+    /**
+     * Creates a label as a part of the form.
+     *
+     * @param parent
+     *            the label parent
+     * @param text
+     *            the label text
+     * @return the label widget
+     */
+    public Label createLabel(Composite parent, String text) {
+        return createLabel(parent, text, SWT.NONE);
+    }
+
+    /**
+     * Creates a label as a part of the form.
+     *
+     * @param parent
+     *            the label parent
+     * @param text
+     *            the label text
+     * @param style
+     *            the label style
+     * @return the label widget
+     */
+    public Label createLabel(Composite parent, String text, int style) {
+        Label label = new Label(parent, style | orientation);
+        if (text !is null)
+            label.setText(text);
+        adapt(label, false, false);
+        return label;
+    }
+
+    /**
+     * Creates a hyperlink as a part of the form. The hyperlink will be added to
+     * the hyperlink group that belongs to this toolkit.
+     *
+     * @param parent
+     *            the hyperlink parent
+     * @param text
+     *            the text of the hyperlink
+     * @param style
+     *            the hyperlink style
+     * @return the hyperlink widget
+     */
+    public Hyperlink createHyperlink(Composite parent, String text, int style) {
+        Hyperlink hyperlink = new Hyperlink(parent, style | orientation);
+        if (text !is null)
+            hyperlink.setText(text);
+        hyperlink.addFocusListener(visibilityHandler);
+        hyperlink.addKeyListener(keyboardHandler);
+        hyperlinkGroup.add(hyperlink);
+        return hyperlink;
+    }
+
+    /**
+     * Creates an image hyperlink as a part of the form. The hyperlink will be
+     * added to the hyperlink group that belongs to this toolkit.
+     *
+     * @param parent
+     *            the hyperlink parent
+     * @param style
+     *            the hyperlink style
+     * @return the image hyperlink widget
+     */
+    public ImageHyperlink createImageHyperlink(Composite parent, int style) {
+        ImageHyperlink hyperlink = new ImageHyperlink(parent, style
+                | orientation);
+        hyperlink.addFocusListener(visibilityHandler);
+        hyperlink.addKeyListener(keyboardHandler);
+        hyperlinkGroup.add(hyperlink);
+        return hyperlink;
+    }
+
+    /**
+     * Creates a rich text as a part of the form.
+     *
+     * @param parent
+     *            the rich text parent
+     * @param trackFocus
+     *            if <code>true</code>, the toolkit will monitor focus
+     *            transfers to ensure that the hyperlink in focus is visible in
+     *            the form.
+     * @return the rich text widget
+     */
+    public FormText createFormText(Composite parent, bool trackFocus) {
+        FormText engine = new FormText(parent, SWT.WRAP | orientation);
+        engine.marginWidth = 1;
+        engine.marginHeight = 0;
+        engine.setHyperlinkSettings(getHyperlinkGroup());
+        adapt(engine, trackFocus, true);
+        engine.setMenu(parent.getMenu());
+        return engine;
+    }
+
+    /**
+     * Adapts a control to be used in a form that is associated with this
+     * toolkit. This involves adjusting colors and optionally adding handlers to
+     * ensure focus tracking and keyboard management.
+     *
+     * @param control
+     *            a control to adapt
+     * @param trackFocus
+     *            if <code>true</code>, form will be scrolled horizontally
+     *            and/or vertically if needed to ensure that the control is
+     *            visible when it gains focus. Set it to <code>false</code> if
+     *            the control is not capable of gaining focus.
+     * @param trackKeyboard
+     *            if <code>true</code>, the control that is capable of
+     *            gaining focus will be tracked for certain keys that are
+     *            important to the underlying form (for example, PageUp,
+     *            PageDown, ScrollUp, ScrollDown etc.). Set it to
+     *            <code>false</code> if the control is not capable of gaining
+     *            focus or these particular key event are already used by the
+     *            control.
+     */
+    public void adapt(Control control, bool trackFocus, bool trackKeyboard) {
+        control.setBackground(colors.getBackground());
+        control.setForeground(colors.getForeground());
+        if (null !is cast(ExpandableComposite)control ) {
+            ExpandableComposite ec = cast(ExpandableComposite) control;
+            if (ec.toggle_package !is null) {
+                if (trackFocus)
+                    ec.toggle_package.addFocusListener(visibilityHandler);
+                if (trackKeyboard)
+                    ec.toggle_package.addKeyListener(keyboardHandler);
+            }
+            if (ec.textLabel_package !is null) {
+                if (trackFocus)
+                    ec.textLabel_package.addFocusListener(visibilityHandler);
+                if (trackKeyboard)
+                    ec.textLabel_package.addKeyListener(keyboardHandler);
+            }
+            return;
+        }
+        if (trackFocus)
+            control.addFocusListener(visibilityHandler);
+        if (trackKeyboard)
+            control.addKeyListener(keyboardHandler);
+    }
+
+    /**
+     * Adapts a composite to be used in a form associated with this toolkit.
+     *
+     * @param composite
+     *            the composite to adapt
+     */
+    public void adapt(Composite composite) {
+        composite.setBackground(colors.getBackground());
+        composite.addMouseListener(new class MouseAdapter {
+            public void mouseDown(MouseEvent e) {
+                (cast(Control) e.widget).setFocus();
+            }
+        });
+        composite.setMenu(composite.getParent().getMenu());
+    }
+
+    /**
+     * A helper method that ensures the provided control is visible when
+     * ScrolledComposite is somewhere in the parent chain. If scroll bars are
+     * visible and the control is clipped, the client of the scrolled composite
+     * will be scrolled to reveal the control.
+     *
+     * @param c
+     *            the control to reveal
+     */
+    public static void ensureVisible(Control c) {
+        FormUtil.ensureVisible(c);
+    }
+
+    /**
+     * Creates a section as a part of the form.
+     *
+     * @param parent
+     *            the section parent
+     * @param sectionStyle
+     *            the section style
+     * @return the section widget
+     */
+    public Section createSection(Composite parent, int sectionStyle) {
+        Section section = new Section(parent, orientation, sectionStyle);
+        section.setMenu(parent.getMenu());
+        adapt(section, true, true);
+        if (section.toggle_package !is null) {
+            section.toggle_package.setHoverDecorationColor(colors
+                    .getColor(IFormColors.TB_TOGGLE_HOVER));
+            section.toggle_package.setDecorationColor(colors
+                    .getColor(IFormColors.TB_TOGGLE));
+        }
+        section.setFont(boldFontHolder.getBoldFont(parent.getFont()));
+        if ((sectionStyle & Section.TITLE_BAR) !is 0
+                || (sectionStyle & Section.SHORT_TITLE_BAR) !is 0) {
+            colors.initializeSectionToolBarColors();
+            section.setTitleBarBackground(colors.getColor(IFormColors.TB_BG));
+            section.setTitleBarBorderColor(colors
+                    .getColor(IFormColors.TB_BORDER));
+            section.setTitleBarForeground(colors
+                    .getColor(IFormColors.TB_TOGGLE));
+        }
+        return section;
+    }
+
+    /**
+     * Creates an expandable composite as a part of the form.
+     *
+     * @param parent
+     *            the expandable composite parent
+     * @param expansionStyle
+     *            the expandable composite style
+     * @return the expandable composite widget
+     */
+    public ExpandableComposite createExpandableComposite(Composite parent,
+            int expansionStyle) {
+        ExpandableComposite ec = new ExpandableComposite(parent, orientation,
+                expansionStyle);
+        ec.setMenu(parent.getMenu());
+        adapt(ec, true, true);
+        ec.setFont(boldFontHolder.getBoldFont(ec.getFont()));
+        return ec;
+    }
+
+    /**
+     * Creates a separator label as a part of the form.
+     *
+     * @param parent
+     *            the separator parent
+     * @param style
+     *            the separator style
+     * @return the separator label
+     */
+    public Label createSeparator(Composite parent, int style) {
+        Label label = new Label(parent, SWT.SEPARATOR | style | orientation);
+        label.setBackground(colors.getBackground());
+        label.setForeground(colors.getBorderColor());
+        return label;
+    }
+
+    /**
+     * Creates a table as a part of the form.
+     *
+     * @param parent
+     *            the table parent
+     * @param style
+     *            the table style
+     * @return the table widget
+     */
+    public Table createTable(Composite parent, int style) {
+        Table table = new Table(parent, style | borderStyle | orientation);
+        adapt(table, false, false);
+        // hookDeleteListener(table);
+        return table;
+    }
+
+    /**
+     * Creates a text as a part of the form.
+     *
+     * @param parent
+     *            the text parent
+     * @param value
+     *            the text initial value
+     * @return the text widget
+     */
+    public Text createText(Composite parent, String value) {
+        return createText(parent, value, SWT.SINGLE);
+    }
+
+    /**
+     * Creates a text as a part of the form.
+     *
+     * @param parent
+     *            the text parent
+     * @param value
+     *            the text initial value
+     * @param style
+     *            the text style
+     * @return the text widget
+     */
+    public Text createText(Composite parent, String value, int style) {
+        Text text = new Text(parent, borderStyle | style | orientation);
+        if (value !is null)
+            text.setText(value);
+        text.setForeground(colors.getForeground());
+        text.setBackground(colors.getBackground());
+        text.addFocusListener(visibilityHandler);
+        return text;
+    }
+
+    /**
+     * Creates a tree widget as a part of the form.
+     *
+     * @param parent
+     *            the tree parent
+     * @param style
+     *            the tree style
+     * @return the tree widget
+     */
+    public Tree createTree(Composite parent, int style) {
+        Tree tree = new Tree(parent, borderStyle | style | orientation);
+        adapt(tree, false, false);
+        // hookDeleteListener(tree);
+        return tree;
+    }
+
+    /**
+     * Creates a scrolled form widget in the provided parent. If you do not
+     * require scrolling because there is already a scrolled composite up the
+     * parent chain, use 'createForm' instead.
+     *
+     * @param parent
+     *            the scrolled form parent
+     * @return the form that can scroll itself
+     * @see #createForm
+     */
+    public ScrolledForm createScrolledForm(Composite parent) {
+        ScrolledForm form = new ScrolledForm(parent, SWT.V_SCROLL
+                | SWT.H_SCROLL | orientation);
+        form.setExpandHorizontal(true);
+        form.setExpandVertical(true);
+        form.setBackground(colors.getBackground());
+        form.setForeground(colors.getColor(IFormColors.TITLE));
+        form.setFont(JFaceResources.getHeaderFont());
+        return form;
+    }
+
+    /**
+     * Creates a form widget in the provided parent. Note that this widget does
+     * not scroll its content, so make sure there is a scrolled composite up the
+     * parent chain. If you require scrolling, use 'createScrolledForm' instead.
+     *
+     * @param parent
+     *            the form parent
+     * @return the form that does not scroll
+     * @see #createScrolledForm
+     */
+    public Form createForm(Composite parent) {
+        Form formContent = new Form(parent, orientation);
+        formContent.setBackground(colors.getBackground());
+        formContent.setForeground(colors.getColor(IFormColors.TITLE));
+        formContent.setFont(JFaceResources.getHeaderFont());
+        return formContent;
+    }
+
+    /**
+     * Takes advantage of the gradients and other capabilities to decorate the
+     * form heading using colors computed based on the current skin and
+     * operating system.
+     *
+     * @since 3.3
+     * @param form
+     *            the form to decorate
+     */
+
+    public void decorateFormHeading(Form form) {
+        Color top = colors.getColor(IFormColors.H_GRADIENT_END);
+        Color bot = colors.getColor(IFormColors.H_GRADIENT_START);
+        form.setTextBackground([ top, bot ], [ 100 ],
+                true);
+        form.setHeadColor(IFormColors.H_BOTTOM_KEYLINE1, colors
+                .getColor(IFormColors.H_BOTTOM_KEYLINE1));
+        form.setHeadColor(IFormColors.H_BOTTOM_KEYLINE2, colors
+                .getColor(IFormColors.H_BOTTOM_KEYLINE2));
+        form.setHeadColor(IFormColors.H_HOVER_LIGHT, colors
+                .getColor(IFormColors.H_HOVER_LIGHT));
+        form.setHeadColor(IFormColors.H_HOVER_FULL, colors
+                .getColor(IFormColors.H_HOVER_FULL));
+        form.setHeadColor(IFormColors.TB_TOGGLE, colors
+                .getColor(IFormColors.TB_TOGGLE));
+        form.setHeadColor(IFormColors.TB_TOGGLE_HOVER, colors
+                .getColor(IFormColors.TB_TOGGLE_HOVER));
+        form.setSeparatorVisible(true);
+    }
+
+    /**
+     * Creates a scrolled page book widget as a part of the form.
+     *
+     * @param parent
+     *            the page book parent
+     * @param style
+     *            the text style
+     * @return the scrolled page book widget
+     */
+    public ScrolledPageBook createPageBook(Composite parent, int style) {
+        ScrolledPageBook book = new ScrolledPageBook(parent, style
+                | orientation);
+        adapt(book, true, true);
+        book.setMenu(parent.getMenu());
+        return book;
+    }
+
+    /**
+     * Disposes the toolkit.
+     */
+    public void dispose() {
+        if (colors.isShared() is false) {
+            colors.dispose();
+            colors = null;
+        }
+        boldFontHolder.dispose();
+    }
+
+    /**
+     * Returns the hyperlink group that manages hyperlinks for this toolkit.
+     *
+     * @return the hyperlink group
+     */
+    public HyperlinkGroup getHyperlinkGroup() {
+        return hyperlinkGroup;
+    }
+
+    /**
+     * Sets the background color for the entire toolkit. The method delegates
+     * the call to the FormColors object and also updates the hyperlink group so
+     * that hyperlinks and other objects are in sync.
+     *
+     * @param bg
+     *            the new background color
+     */
+    public void setBackground(Color bg) {
+        hyperlinkGroup.setBackground(bg);
+        colors.setBackground(bg);
+    }
+
+    /**
+     * Refreshes the hyperlink colors by loading from JFace settings.
+     */
+    public void refreshHyperlinkColors() {
+        hyperlinkGroup.initializeDefaultForegrounds(colors.getDisplay());
+    }
+
+    /**
+     * Paints flat borders for widgets created by this toolkit within the
+     * provided parent. Borders will not be painted if the global border style
+     * is SWT.BORDER (i.e. if native borders are used). Call this method during
+     * creation of a form composite to get the borders of its children painted.
+     * Care should be taken when selection layout margins. At least one pixel
+     * pargin width and height must be chosen to allow the toolkit to paint the
+     * border on the parent around the widgets.
+     * <p>
+     * Borders are painted for some controls that are selected by the toolkit by
+     * default. If a control needs a border but is not on its list, it is
+     * possible to force border in the following way:
+     *
+     * <pre>
+     *
+     *
+     *
+     *             widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER);
+     *
+     *             or
+     *
+     *             widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
+     *
+     *
+     *
+     * </pre>
+     *
+     * @param parent
+     *            the parent that owns the children for which the border needs
+     *            to be painted.
+     */
+    public void paintBordersFor(Composite parent) {
+        // if (borderStyle is SWT.BORDER)
+        // return;
+        if (borderPainter is null)
+            borderPainter = new BorderPainter();
+        parent.addPaintListener(borderPainter);
+    }
+
+    /**
+     * Returns the colors used by this toolkit.
+     *
+     * @return the color object
+     */
+    public FormColors getColors() {
+        return colors;
+    }
+
+    /**
+     * Returns the border style used for various widgets created by this
+     * toolkit. The intent of the toolkit is to create controls with styles that
+     * yield a 'flat' appearance. On systems where the native borders are
+     * already flat, we set the style to SWT.BORDER and don't paint the borders
+     * ourselves. Otherwise, the style is set to SWT.NULL, and borders are
+     * painted by the toolkit.
+     *
+     * @return the global border style
+     */
+    public int getBorderStyle() {
+        return borderStyle;
+    }
+
+    /**
+     * Returns the margin required around the children whose border is being
+     * painted by the toolkit using {@link #paintBordersFor(Composite)}. Since
+     * the border is painted around the controls on the parent, a number of
+     * pixels needs to be reserved for this border. For windowing systems where
+     * the native border is used, this margin is 0.
+     *
+     * @return the margin in the parent when children have their border painted
+     * @since 3.3
+     */
+    public int getBorderMargin() {
+        return getBorderStyle() is SWT.BORDER ? 0 : 2;
+    }
+
+    /**
+     * Sets the border style to be used when creating widgets. The toolkit
+     * chooses the correct style based on the platform but this value can be
+     * changed using this method.
+     *
+     * @param style
+     *            <code>SWT.BORDER</code> or <code>SWT.NULL</code>
+     * @see #getBorderStyle
+     */
+    public void setBorderStyle(int style) {
+        this.borderStyle = style;
+    }
+
+    /**
+     * A utility method that ensures that the control is visible in the scrolled
+     * composite. The prerequisite for this method is that the control has a
+     * class that extends ScrolledComposite somewhere in the parent chain. If
+     * the control is partially or fully clipped, the composite is scrolled to
+     * set by setting the origin to the control origin.
+     *
+     * @param c
+     *            the control to make visible
+     * @param verticalOnly
+     *            if <code>true</code>, the scrolled composite will be
+     *            scrolled only vertically if needed. Otherwise, the scrolled
+     *            composite origin will be set to the control origin.
+     * @since 3.1
+     */
+    public static void setControlVisible(Control c, bool verticalOnly) {
+        ScrolledComposite scomp = FormUtil.getScrolledComposite(c);
+        if (scomp is null)
+            return;
+        Point location = FormUtil.getControlLocation(scomp, c);
+        scomp.setOrigin(location);
+    }
+
+    private void initialize() {
+        initializeBorderStyle();
+        hyperlinkGroup = new HyperlinkGroup(colors.getDisplay());
+        hyperlinkGroup.setBackground(colors.getBackground());
+        visibilityHandler = new VisibilityHandler();
+        keyboardHandler = new KeyboardHandler();
+        boldFontHolder = new BoldFontHolder();
+    }
+
+    private void initializeBorderStyle() {
+        String osname = System.getProperty("os.name"); //$NON-NLS-1$
+        String osversion = System.getProperty("os.version"); //$NON-NLS-1$
+        if (osname.startsWith("Windows") && "5.1".compareTo(osversion) <= 0) { //$NON-NLS-1$ //$NON-NLS-2$
+            // Skinned widgets used on newer Windows (e.g. XP (5.1), Vista
+            // (6.0))
+            // Check for Windows Classic. If not used, set the style to BORDER
+            RGB rgb = colors.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
+            if (rgb.red !is 212 || rgb.green !is 208 || rgb.blue !is 200)
+                borderStyle = SWT.BORDER;
+        } else if (osname.startsWith("Mac")) //$NON-NLS-1$
+            borderStyle = SWT.BORDER;
+    }
+
+    /**
+     * Returns the orientation that all the widgets created by this toolkit will
+     * inherit, if set. Can be <code>SWT.NULL</code>,
+     * <code>SWT.LEFT_TO_RIGHT</code> and <code>SWT.RIGHT_TO_LEFT</code>.
+     *
+     * @return orientation style for this toolkit, or <code>SWT.NULL</code> if
+     *         not set. The default orientation is inherited from the Window
+     *         default orientation.
+     * @see org.eclipse.jface.window.Window#getDefaultOrientation()
+     * @since 3.1
+     */
+
+    public int getOrientation() {
+        return orientation;
+    }
+
+    /**
+     * Sets the orientation that all the widgets created by this toolkit will
+     * inherit. Can be <code>SWT.NULL</code>, <code>SWT.LEFT_TO_RIGHT</code>
+     * and <code>SWT.RIGHT_TO_LEFT</code>.
+     *
+     * @param orientation
+     *            style for this toolkit.
+     * @since 3.1
+     */
+
+    public void setOrientation(int orientation) {
+        this.orientation = orientation;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/Hyperlink.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,292 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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 org.eclipse.ui.forms.widgets.Hyperlink;
+
+import org.eclipse.ui.forms.widgets.AbstractHyperlink;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.accessibility.ACC;
+import org.eclipse.swt.accessibility.Accessible;
+import org.eclipse.swt.accessibility.AccessibleAdapter;
+import org.eclipse.swt.accessibility.AccessibleControlAdapter;
+import org.eclipse.swt.accessibility.AccessibleControlEvent;
+import org.eclipse.swt.accessibility.AccessibleEvent;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.ui.internal.forms.widgets.FormUtil;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Hyperlink is a concrete implementation of the abstract base class that draws
+ * text in the client area. Text can be wrapped and underlined. Hyperlink is
+ * typically added to the hyperlink group so that certain properties are managed
+ * for all the hyperlinks that belong to it.
+ * <p>
+ * Hyperlink can be extended.
+ *
+ * @see org.eclipse.ui.forms.HyperlinkGroup
+ * @since 3.0
+ */
+public class Hyperlink : AbstractHyperlink {
+    private String text;
+    private static const String ELLIPSIS = "..."; //$NON-NLS-1$
+    private bool underlined;
+    // The tooltip is used for two purposes - the application can set
+    // a tooltip or the tooltip can be used to display the full text when the
+    // the text has been truncated due to the label being too short.
+    // The appToolTip stores the tooltip set by the application.  Control.tooltiptext
+    // contains whatever tooltip is currently being displayed.
+    private String appToolTipText;
+
+    /**
+     * Creates a new hyperlink control in the provided parent.
+     *
+     * @param parent
+     *            the control parent
+     * @param style
+     *            the widget style
+     */
+    public this(Composite parent, int style) {
+        super(parent, style);
+        initAccessible();
+    }
+
+    protected void initAccessible() {
+        Accessible accessible = getAccessible();
+        accessible.addAccessibleListener(new class AccessibleAdapter {
+            public void getName(AccessibleEvent e) {
+                e.result = getText();
+                if (e.result is null)
+                    getHelp(e);
+            }
+
+            public void getHelp(AccessibleEvent e) {
+                e.result = getToolTipText();
+            }
+        });
+        accessible.addAccessibleControlListener(new class AccessibleControlAdapter {
+            public void getChildAtPoint(AccessibleControlEvent e) {
+                Point pt = toControl(new Point(e.x, e.y));
+                e.childID = (getBounds().contains(pt)) ? ACC.CHILDID_SELF
+                        : ACC.CHILDID_NONE;
+            }
+
+            public void getLocation(AccessibleControlEvent e) {
+                Rectangle location = getBounds();
+                Point pt = toDisplay(new Point(location.x, location.y));
+                e.x = pt.x;
+                e.y = pt.y;
+                e.width = location.width;
+                e.height = location.height;
+            }
+
+            public void getChildCount(AccessibleControlEvent e) {
+                e.detail = 0;
+            }
+
+            public void getRole(AccessibleControlEvent e) {
+                e.detail = ACC.ROLE_LINK;
+            }
+
+            public void getDefaultAction (AccessibleControlEvent e) {
+                e.result = SWT.getMessage ("SWT_Press"); //$NON-NLS-1$
+            }
+
+            public void getState(AccessibleControlEvent e) {
+                int state = ACC.STATE_NORMAL;
+                if (this.outer.getSelection())
+                    state = ACC.STATE_SELECTED | ACC.STATE_FOCUSED;
+                e.detail = state;
+            }
+        });
+    }
+
+    /**
+     * Sets the underlined state. It is not necessary to call this method when
+     * in a hyperlink group.
+     *
+     * @param underlined
+     *            if <samp>true </samp>, a line will be drawn below the text for
+     *            each wrapped line.
+     */
+    public void setUnderlined(bool underlined) {
+        this.underlined = underlined;
+        redraw();
+    }
+
+    /**
+     * Returns the underline state of the hyperlink.
+     *
+     * @return <samp>true </samp> if text is underlined, <samp>false </samp>
+     *         otherwise.
+     */
+    public bool isUnderlined() {
+        return underlined;
+    }
+
+    /**
+     * Overrides the parent by incorporating the margin.
+     */
+    public Point computeSize(int wHint, int hHint, bool changed) {
+        checkWidget();
+        int innerWidth = wHint;
+        if (innerWidth !is SWT.DEFAULT)
+            innerWidth -= marginWidth * 2;
+        Point textSize = computeTextSize(innerWidth, hHint);
+        int textWidth = textSize.x + 2 * marginWidth;
+        int textHeight = textSize.y + 2 * marginHeight;
+        return new Point(textWidth, textHeight);
+    }
+
+    /**
+     * Returns the current hyperlink text.
+     *
+     * @return hyperlink text
+     */
+    public String getText() {
+        return text;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.swt.widgets.Control#getToolTipText()
+     */
+    public String getToolTipText () {
+        checkWidget();
+        return appToolTipText;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.swt.widgets.Control#setToolTipText(java.lang.String)
+     */
+    public void setToolTipText (String string) {
+        super.setToolTipText (string);
+        appToolTipText = super.getToolTipText();
+    }
+
+    /**
+     * Sets the text of this hyperlink.
+     *
+     * @param text
+     *            the hyperlink text
+     */
+    public void setText(String text) {
+        if (text !is null)
+            this.text = text;
+        else
+            this.text = ""; //$NON-NLS-1$
+        redraw();
+    }
+
+    /**
+     * Paints the hyperlink text.
+     *
+     * @param gc
+     *            graphic context
+     */
+    protected void paintHyperlink(GC gc) {
+        Rectangle carea = getClientArea();
+        Rectangle bounds = new Rectangle(marginWidth, marginHeight, carea.width
+                - marginWidth - marginWidth, carea.height - marginHeight
+                - marginHeight);
+        paintText(gc, bounds);
+    }
+
+    /**
+     * Paints the hyperlink text in provided bounding rectangle.
+     *
+     * @param gc
+     *            graphic context
+     * @param bounds
+     *            the bounding rectangle in which to paint the text
+     */
+    protected void paintText(GC gc, Rectangle bounds) {
+        gc.setFont(getFont());
+        gc.setForeground(getForeground());
+        if ((getStyle() & SWT.WRAP) !is 0) {
+            FormUtil.paintWrapText(gc, text, bounds, underlined);
+        } else {
+            Point totalSize = computeTextSize(SWT.DEFAULT, SWT.DEFAULT);
+            bool shortenText_ =false;
+            if (bounds.width<totalSize.x) {
+                // shorten
+                shortenText_=true;
+            }
+            int textWidth = Math.min(bounds.width, totalSize.x);
+            int textHeight = totalSize.y;
+            String textToDraw = getText();
+            if (shortenText_) {
+                textToDraw = shortenText(gc, getText(), bounds.width);
+                if (appToolTipText is null) {
+                    super.setToolTipText(getText());
+                }
+            }
+            else {
+                super.setToolTipText(appToolTipText);
+            }
+            gc.drawText(textToDraw, bounds.x, bounds.y, true);
+            if (underlined) {
+                int descent = gc.getFontMetrics().getDescent();
+                int lineY = bounds.y + textHeight - descent + 1;
+                gc.drawLine(bounds.x, lineY, bounds.x + textWidth, lineY);
+            }
+        }
+    }
+
+    protected String shortenText(GC gc, String t, int width) {
+        if (t is null) return null;
+        int w = gc.textExtent(ELLIPSIS).x;
+        if (width<=w) return t;
+        int l = t.length;
+        int max = l/2;
+        int min = 0;
+        int mid = (max+min)/2 - 1;
+        if (mid <= 0) return t;
+        while (min < mid && mid < max) {
+            String s1 = t.substring(0, mid);
+            String s2 = t.substring(l-mid, l);
+            int l1 = gc.textExtent(s1).x;
+            int l2 = gc.textExtent(s2).x;
+            if (l1+w+l2 > width) {
+                max = mid;
+                mid = (max+min)/2;
+            } else if (l1+w+l2 < width) {
+                min = mid;
+                mid = (max+min)/2;
+            } else {
+                min = max;
+            }
+        }
+        if (mid is 0) return t;
+        return t.substring(0, mid)~ELLIPSIS~t.substring(l-mid, l);
+    }
+
+    protected Point computeTextSize(int wHint, int hHint) {
+        Point extent;
+        GC gc = new GC(this);
+        gc.setFont(getFont());
+        if ((getStyle() & SWT.WRAP) !is 0 && wHint !is SWT.DEFAULT) {
+            extent = FormUtil.computeWrapSize(gc, getText(), wHint);
+        } else {
+            extent = gc.textExtent(getText());
+            if ((getStyle() & SWT.WRAP) is 0 && wHint !is SWT.DEFAULT)
+                extent.x = wHint;
+        }
+        gc.dispose();
+        return extent;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ILayoutExtension.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.ui.forms.widgets.ILayoutExtension;
+
+import java.lang.all;
+import org.eclipse.swt.widgets.Composite;
+/**
+ * Classes that extend abstract class Layout and implement this interface can
+ * take part in layout computation of the TableWrapLayout manager. This layout
+ * uses alternative algorithm that computes columns before rows. It allows it
+ * to 'flow' wrapped text proportionally (similar to the way web browser
+ * renders tables). Custom layout managers that implement this interface will
+ * allow TableWrapLayout to properly compute width hint to pass.
+ * 
+ * @see TableWrapLayout
+ * @see ColumnLayout
+ * @since 3.0
+ */
+public interface ILayoutExtension {
+    /**
+     * Computes the minimum width of the parent. All widgets capable of word
+     * wrapping should return the width of the longest word that cannot be
+     * broken any further.
+     * 
+     * @param parent the parent composite
+     * @param changed <code>true</code> if the cached information should be
+     * flushed, <code>false</code> otherwise.
+     * @return the minimum width of the parent composite
+     */
+    public int computeMinimumWidth(Composite parent, bool changed);
+    /**
+     * Computes the maximum width of the parent. All widgets capable of word
+     * wrapping should return the length of the entire text with wrapping
+     * turned off.
+     * 
+     * @param parent the parent composite
+     * @param changed <code>true</code> if the cached information
+     * should be flushed, <code>false</code> otherwise.
+     * @return the maximum width of the parent composite
+     */
+    public int computeMaximumWidth(Composite parent, bool changed);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ImageHyperlink.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,300 @@
+/*******************************************************************************
+ * 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
+ *     Chriss Gross (schtoo@schtoo.com) - fix for 61670
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.ui.forms.widgets.ImageHyperlink;
+
+import org.eclipse.ui.forms.widgets.Hyperlink;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * This class extends hyperlink widget by adding the capability to render an
+ * image relative to the text. If no text has been set, only image will be
+ * shown. Images for hover and active states can be set in addition to the
+ * normal state image.
+ * <p>
+ * When image is taller than the text, additional style can be provided to
+ * control vertical alignment (supported values are SWT.TOP, SWT.BOTTOM and
+ * SWT.CENTER).
+ * <p>
+ * The class does not need to be sublassed but it is allowed to do so if some
+ * aspect of the image hyperlink needs to be modified.
+ *
+ * @since 3.0
+ */
+public class ImageHyperlink : Hyperlink {
+    /**
+     * Amount of pixels between the image and the text (default is 5).
+     */
+    public int textSpacing = 5;
+
+    private Image image;
+
+    private Image hoverImage;
+
+    private Image activeImage;
+
+    private int state;
+
+    private static const int HOVER = 1 << 1;
+
+    private static const int ACTIVE = 1 << 2;
+
+    private int verticalAlignment = SWT.CENTER;
+
+    private int horizontalAlignment = SWT.LEFT;
+
+    /**
+     * Creates the image hyperlink instance.
+     *
+     * @param parent
+     *            the control parent
+     * @param style
+     *            the control style (SWT.WRAP, BOTTOM, TOP, MIDDLE, LEFT, RIGHT)
+     */
+    public this(Composite parent, int style) {
+        super(parent, removeAlignment(style));
+        extractAlignment(style);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.widgets.AbstractHyperlink#paintHyperlink(org.eclipse.swt.events.PaintEvent)
+     */
+    protected void paintHyperlink(GC gc) {
+        paintHyperlink(gc, getClientArea());
+    }
+
+    protected void paintHyperlink(GC gc, Rectangle bounds) {
+        Image image = null;
+        if ((state & ACTIVE) !is 0)
+            image = activeImage;
+        else if ((state & HOVER) !is 0)
+            image = hoverImage;
+        if (image is null)
+            image = this.image;
+        Rectangle ibounds = image !is null ? image.getBounds() : new Rectangle(0, 0, 0, 0);
+        Point maxsize = computeMaxImageSize();
+        int spacing = image !is null?textSpacing:0;
+        int textWidth = bounds.width - maxsize.x - spacing
+                - marginWidth - marginWidth;
+        int y = bounds.y+marginHeight + maxsize.y / 2 - ibounds.height / 2;
+
+        if (horizontalAlignment is SWT.LEFT) {
+            int x = bounds.x+marginWidth + maxsize.x / 2 - ibounds.width / 2;
+            int textX = bounds.x + marginWidth + maxsize.x + spacing;
+            if (image !is null)
+                gc.drawImage(image, x, y);
+            if (getText() !is null)
+                drawText(gc, bounds, textX, textWidth);
+        } else if (horizontalAlignment is SWT.RIGHT) {
+            int x = bounds.x+marginWidth;
+            if (getText() !is null) {
+                x += drawText(gc, bounds, x, textWidth);
+            }
+            x += maxsize.x / 2 - ibounds.width / 2 + spacing;
+            if (image !is null)
+                gc.drawImage(image, x, y);
+        }
+    }
+
+    private int drawText(GC gc, Rectangle clientArea, int textX, int textWidth) {
+        Point textSize = computeTextSize(textWidth, SWT.DEFAULT);
+        int slotHeight = clientArea.height - marginHeight - marginHeight;
+        int textY;
+        textWidth = textSize.x;
+        int textHeight = textSize.y;
+        if (verticalAlignment is SWT.BOTTOM) {
+            textY = marginHeight + slotHeight - textHeight;
+        } else if (verticalAlignment is SWT.CENTER) {
+            textY = marginHeight + slotHeight / 2 - textHeight / 2;
+        } else {
+            textY = marginHeight;
+        }
+        paintText(gc, new Rectangle(textX, textY, textWidth, textHeight));
+        return textWidth;
+    }
+
+    /**
+     * Computes the control size by reserving space for images in addition to
+     * text.
+     *
+     * @param wHint
+     *            width hint
+     * @param hHint
+     *            height hint
+     * @param changed
+     *            if <code>true</code>, any cached layout data should be
+     *            computed anew
+     */
+    public Point computeSize(int wHint, int hHint, bool changed) {
+        checkWidget();
+        Point isize = computeMaxImageSize();
+        int spacing = isize.x>0?textSpacing:0;
+        Point textSize = null;
+        if (getText() !is null) {
+            int innerWHint = wHint;
+            if (wHint !is SWT.DEFAULT) {
+                innerWHint = wHint - 2 * marginWidth - isize.x - spacing;
+            }
+            textSize = super.computeSize(innerWHint, hHint, changed);
+        }
+        int width = isize.x;
+        int height = isize.y;
+        if (textSize !is null) {
+            width += spacing;
+            width += textSize.x;
+            height = Math.max(height, textSize.y);
+        }
+        width += 2 * marginWidth;
+        height += 2 * marginHeight;
+        return new Point(width, height);
+    }
+
+    protected void handleEnter(Event e) {
+        state = HOVER;
+        super.handleEnter(e);
+    }
+
+    protected void handleExit(Event e) {
+        state = 0;
+        super.handleExit(e);
+    }
+
+    protected void handleActivate(Event e) {
+        state &= ACTIVE;
+        redraw();
+        super.handleActivate(e);
+        state &= ~ACTIVE;
+        if (!isDisposed())
+            redraw();
+    }
+
+    /**
+     * Returns active image.
+     *
+     * @return active image or <code>null</code> if not set.
+     */
+    public Image getActiveImage() {
+        return activeImage;
+    }
+
+    /**
+     * Sets the image to show when link is activated.
+     *
+     * @param activeImage
+     *
+     */
+    public void setActiveImage(Image activeImage) {
+        this.activeImage = activeImage;
+    }
+
+    /**
+     * Returns the hover image.
+     *
+     * @return hover image or <code>null</code> if not set.
+     */
+    public Image getHoverImage() {
+        return hoverImage;
+    }
+
+    /**
+     * Sets the image to show when link is hover state (on mouse over).
+     *
+     * @param hoverImage
+     */
+    public void setHoverImage(Image hoverImage) {
+        this.hoverImage = hoverImage;
+    }
+
+    /**
+     * Returns the image to show in the normal state.
+     *
+     * @return normal image or <code>null</code> if not set.
+     */
+    public Image getImage() {
+        return image;
+    }
+
+    /**
+     * Sets the image to show when link is in the normal state.
+     *
+     * @param image
+     */
+    public void setImage(Image image) {
+        this.image = image;
+    }
+
+    private Point computeMaxImageSize() {
+        int x = 0;
+        int y = 0;
+        if (image !is null) {
+            x = Math.max(image.getBounds().width, x);
+            y = Math.max(image.getBounds().height, y);
+        }
+        if (hoverImage !is null) {
+            x = Math.max(hoverImage.getBounds().width, x);
+            y = Math.max(hoverImage.getBounds().height, y);
+        }
+        if (activeImage !is null) {
+            x = Math.max(activeImage.getBounds().width, x);
+            y = Math.max(activeImage.getBounds().height, y);
+        }
+        return new Point(x, y);
+    }
+
+    private static int removeAlignment(int style) {
+        int resultStyle = style;
+        if ((style & SWT.CENTER) !is 0) {
+            resultStyle &= (~SWT.CENTER);
+        }
+        if ((style & SWT.TOP) !is 0) {
+            resultStyle &= (~SWT.TOP);
+        }
+        if ((style & SWT.BOTTOM) !is 0) {
+            resultStyle &= (~SWT.BOTTOM);
+        }
+        if ((style & SWT.LEFT) !is 0) {
+            resultStyle &= (~SWT.LEFT);
+        }
+        if ((style & SWT.RIGHT) !is 0) {
+            resultStyle &= (~SWT.RIGHT);
+        }
+        return resultStyle;
+    }
+
+    private void extractAlignment(int style) {
+        if ((style & SWT.CENTER) !is 0) {
+            verticalAlignment = SWT.CENTER;
+        } else if ((style & SWT.TOP) !is 0) {
+            verticalAlignment = SWT.TOP;
+        } else if ((style & SWT.BOTTOM) !is 0) {
+            verticalAlignment = SWT.BOTTOM;
+        }
+        if ((style & SWT.LEFT) !is 0) {
+            horizontalAlignment = SWT.LEFT;
+        } else if ((style & SWT.RIGHT) !is 0) {
+            horizontalAlignment = SWT.RIGHT;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/LayoutCache.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.widgets.LayoutCache;
+
+import org.eclipse.ui.forms.widgets.SizeCache;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Caches the preferred sizes of an array of controls
+ *
+ * @since 3.0
+ */
+public class LayoutCache {
+    private SizeCache[] caches;
+
+    /**
+     * Creates an empty layout cache
+     */
+    public this() {
+    }
+
+    /**
+     * Creates a cache for the given array of controls
+     *
+     * @param controls
+     */
+    public this(Control[] controls) {
+        rebuildCache(controls);
+    }
+
+    /**
+     * Returns the size cache for the given control
+     *
+     * @param idx
+     * @return the size cache for the given control
+     */
+    public SizeCache getCache(int idx) {
+        return caches[idx];
+    }
+
+    /**
+     * Sets the controls that are being cached here. If these are the same
+     * controls that were used last time, this method does nothing. Otherwise,
+     * the cache is flushed and a new cache is created for the new controls.
+     *
+     * @param controls
+     */
+    public void setControls(Control[] controls) {
+        // If the number of controls has changed, discard the entire cache
+        if (controls.length !is caches.length) {
+            rebuildCache(controls);
+            return;
+        }
+
+        for (int idx = 0; idx < controls.length; idx++) {
+            caches[idx].setControl(controls[idx]);
+        }
+    }
+
+    /**
+     * Creates a new size cache for the given set of controls, discarding any
+     * existing cache.
+     *
+     * @param controls the controls whose size is being cached
+     */
+    private void rebuildCache(Control[] controls) {
+        SizeCache[] newCache = new SizeCache[controls.length];
+
+        for (int idx = 0; idx < controls.length; idx++) {
+            // Try to reuse existing caches if possible
+            if (idx < caches.length) {
+                newCache[idx] = caches[idx];
+                newCache[idx].setControl(controls[idx]);
+            } else {
+                newCache[idx] = new SizeCache(controls[idx]);
+            }
+        }
+
+        caches = newCache;
+    }
+
+    /**
+     * Computes the preferred size of the nth control
+     *
+     * @param controlIndex index of the control whose size will be computed
+     * @param widthHint width of the control (or SWT.DEFAULT if unknown)
+     * @param heightHint height of the control (or SWT.DEFAULT if unknown)
+     * @return the preferred size of the control
+     */
+    public Point computeSize(int controlIndex, int widthHint, int heightHint) {
+        return caches[controlIndex].computeSize(widthHint, heightHint);
+    }
+
+    /**
+     * Flushes the cache for the given control. This should be called if exactly
+     * one of the controls has changed but the remaining controls remain unmodified
+     *
+     * @param controlIndex
+     */
+    public void flush(int controlIndex) {
+        caches[controlIndex].flush();
+    }
+
+    /**
+     * Flushes the cache.
+     */
+    public void flush() {
+        for (int idx = 0; idx < caches.length; idx++) {
+            caches[idx].flush();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/LayoutComposite.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.ui.forms.widgets.LayoutComposite;
+
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+import org.eclipse.ui.forms.widgets.ColumnLayout;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Layout;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * The class overrides default method for computing size in Composite by
+ * accepting size returned from layout managers as-is. The default code accepts
+ * width or height hint assuming it is correct. However, it is possible that
+ * the computation using the provided width hint results in a real size that is
+ * larger. This can result in wrapped text widgets being clipped, asking to
+ * render in bounds narrower than the longest word.
+ */
+/* package */class LayoutComposite : Composite {
+    public this(Composite parent, int style) {
+        super(parent, style);
+        setMenu(parent.getMenu());
+    }
+    public Point computeSize(int wHint, int hHint, bool changed) {
+        Layout layout = getLayout();
+        if (null !is cast(TableWrapLayout)layout )
+            return (cast(TableWrapLayout) layout).computeSize(cast(Composite)this, wHint, hHint,
+                    changed);
+        if (null !is cast(ColumnLayout)layout )
+            return (cast(ColumnLayout) layout).computeSize(cast(Composite)this, wHint, hHint,
+                    changed);
+        return super.computeSize(wHint, hHint, changed);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ScrolledForm.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,295 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.widgets.ScrolledForm;
+
+import org.eclipse.ui.forms.widgets.SharedScrolledComposite;
+import org.eclipse.ui.forms.widgets.Form;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.ui.forms.IMessage;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * ScrolledForm is a control that is capable of scrolling an instance of the
+ * Form class. It should be created in a parent that will allow it to use all
+ * the available area (for example, a shell, a view or an editor).
+ * <p>
+ * Children of the form should typically be created using FormToolkit to match
+ * the appearance and behaviour. When creating children, use a form body as a
+ * parent by calling 'getBody()' on the form instance. Example:
+ *
+ * <pre>
+ * FormToolkit toolkit = new FormToolkit(parent.getDisplay());
+ * ScrolledForm form = toolkit.createScrolledForm(parent);
+ * form.setText(&quot;Sample form&quot;);
+ * form.getBody().setLayout(new GridLayout());
+ * toolkit.createButton(form.getBody(), &quot;Checkbox&quot;, SWT.CHECK);
+ * </pre>
+ *
+ * <p>
+ * No layout manager has been set on the body. Clients are required to set the
+ * desired layout manager explicitly.
+ * <p>
+ * Although the class is not final, it is not expected to be be extended.
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ScrolledForm : SharedScrolledComposite {
+    private Form content;
+
+    public this(Composite parent) {
+        this(parent, SWT.V_SCROLL | SWT.H_SCROLL);
+    }
+
+    /**
+     * Creates the form control as a child of the provided parent.
+     *
+     * @param parent
+     *            the parent widget
+     */
+    public this(Composite parent, int style) {
+        super(parent, style);
+        super.setMenu(parent.getMenu());
+        content = new Form(this, SWT.NULL);
+        super.setContent(content);
+        content.setMenu(getMenu());
+    }
+
+    /**
+     * Passes the menu to the body.
+     *
+     * @param menu
+     */
+    public void setMenu(Menu menu) {
+        super.setMenu(menu);
+        if (content !is null)
+            content.setMenu(menu);
+    }
+
+    /**
+     * Returns the title text that will be rendered at the top of the form.
+     *
+     * @return the title text
+     */
+    public String getText() {
+        return content.getText();
+    }
+
+    /**
+     * Returns the title image that will be rendered to the left of the title.
+     *
+     * @return the title image
+     */
+    public Image getImage() {
+        return content.getImage();
+    }
+
+    /**
+     * Sets the foreground color of the form. This color will also be used for
+     * the body.
+     */
+    public void setForeground(Color fg) {
+        super.setForeground(fg);
+        content.setForeground(fg);
+    }
+
+    /**
+     * Sets the background color of the form. This color will also be used for
+     * the body.
+     */
+    public void setBackground(Color bg) {
+        super.setBackground(bg);
+        content.setBackground(bg);
+    }
+
+    /**
+     * The form sets the content widget. This method should not be called by
+     * classes that instantiate this widget.
+     */
+    public final void setContent(Control c) {
+    }
+
+    /**
+     * Sets the text to be rendered at the top of the form above the body as a
+     * title.
+     * <p>
+     * <strong>Note:</strong> Mnemonics are indicated by an '&amp;' that causes
+     * the next character to be the mnemonic. Mnemonics are not applicable in
+     * the case of the form title but need to be taken into acount due to the
+     * usage of the underlying widget that renders mnemonics in the title area.
+     * The mnemonic indicator character '&amp;' can be escaped by doubling it in
+     * the string, causing a single '&amp;' to be displayed.
+     * </p>
+     *
+     * @param text
+     *            the title text
+     */
+    public void setText(String text) {
+        content.setText(text);
+        reflow(true);
+    }
+
+    /**
+     * Sets the image to be rendered to the left of the title.
+     *
+     * @param image
+     *            the title image or <code>null</code> for no image.
+     */
+    public void setImage(Image image) {
+        content.setImage(image);
+        reflow(true);
+    }
+
+    /**
+     * Returns the optional background image of this form. The image is rendered
+     * starting at the position 0,0 and is painted behind the title.
+     *
+     * @return Returns the background image.
+     */
+    public Image getBackgroundImage() {
+        return content.getBackgroundImage();
+    }
+
+    /**
+     * Sets the optional background image to be rendered behind the title
+     * starting at the position 0,0.
+     *
+     * @param backgroundImage
+     *            The backgroundImage to set.
+     */
+    public void setBackgroundImage(Image backgroundImage) {
+        content.setBackgroundImage(backgroundImage);
+    }
+
+    /**
+     * Returns the tool bar manager that is used to manage tool items in the
+     * form's title area.
+     *
+     * @return form tool bar manager
+     */
+    public IToolBarManager getToolBarManager() {
+        return content.getToolBarManager();
+    }
+
+    /**
+     * Updates the local tool bar manager if used. Does nothing if local tool
+     * bar manager has not been created yet.
+     */
+    public void updateToolBar() {
+        content.updateToolBar();
+    }
+
+    /**
+     * Returns the container that occupies the body of the form (the form area
+     * below the title). Use this container as a parent for the controls that
+     * should be in the form. No layout manager has been set on the form body.
+     *
+     * @return Returns the body of the form.
+     */
+    public Composite getBody() {
+        return content.getBody();
+    }
+
+    /**
+     * Returns the instance of the form owned by the scrolled form.
+     *
+     * @return the form instance
+     */
+    public Form getForm() {
+        return content;
+    }
+
+    /**
+     * Sets the form's busy state. Busy form will display 'busy' animation in
+     * the area of the title image.
+     *
+     * @param busy
+     *            the form's busy state
+     * @see Form#setBusy(bool)
+     * @since 3.3
+     */
+
+    public void setBusy(bool busy) {
+        content.setBusy(busy);
+        reflow(true);
+    }
+
+    /**
+     * Sets the optional head client.
+     *
+     * @param headClient
+     *            the optional child of the head
+     * @see Form#setHeadClient(Control)
+     * @since 3.3
+     */
+    public void setHeadClient(Control headClient) {
+        content.setHeadClient(headClient);
+        reflow(true);
+    }
+
+    /**
+     * Sets the form message.
+     *
+     * @param newMessage
+     *            the message text or <code>null</code> to reset.
+     * @param newType
+     *            as defined in
+     *            {@link org.eclipse.jface.dialogs.IMessageProvider}.
+     * @param messages
+     *           an optional array of children that itemize individual
+     *          messages or <code>null</code> for a simple message.
+     * @since 3.3
+     * @see Form#setMessage(String, int)
+     */
+    public void setMessage(String newMessage, int newType, IMessage[] messages) {
+        content.setMessage(newMessage, newType, messages);
+        reflow(true);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageContainer#setMessage(java.lang.String,
+     *      int)
+     */
+    public void setMessage(String newMessage, int newType) {
+        this.setMessage(newMessage, newType, null);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.IMessageProvider#getMessage()
+     */
+    public String getMessage() {
+        return content.getMessage();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.jface.dialogs.IMessageProvider#getMessageType()
+     */
+    public int getMessageType() {
+        return content.getMessageType();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ScrolledFormText.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,175 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.widgets.ScrolledFormText;
+
+import org.eclipse.ui.forms.widgets.SharedScrolledComposite;
+import org.eclipse.ui.forms.widgets.FormText;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+import java.lang.all;
+import java.util.Set;
+import java.io.InputStream;
+
+/**
+ * ScrolledFormText is a control that is capable of scrolling an instance of
+ * the FormText class. It should be created in a parent that will allow it to
+ * use all the available area (for example, a shell, a view or an editor). The
+ * form text can be created by the class itself, or set from outside. In the
+ * later case, the form text instance must be a direct child of the
+ * ScrolledFormText instance.
+ * <p>
+ * The class assumes that text to be rendered contains formatting tags. In case
+ * of a string, it will enclose the text in 'form' root element if missing from
+ * the text as a convinience. For example:
+ *
+ * <pre>
+ *  ftext.setText(&quot;&lt;p&gt;Some text here&lt;/&gt;&quot;);
+ * </pre>
+ *
+ * will not cause an error. The same behavior does not exist for content from
+ * the input stream, however - it must be well formed in that case.
+ * </p>
+
+ * @since 3.0
+ * @see FormText
+ */
+public class ScrolledFormText : SharedScrolledComposite {
+    private FormText content;
+    private String text;
+    /**
+     * Creates the new scrolled text instance in the provided parent
+     *
+     * @param parent
+     *            the parent composite
+     * @param createFormText
+     *            if <code>true</code>, enclosing form text instance will be
+     *            created in this constructor.
+     */
+    public this(Composite parent, bool createFormText) {
+        this(parent, SWT.V_SCROLL | SWT.H_SCROLL, createFormText);
+    }
+    /**
+     * Creates the new scrolled text instance in the provided parent
+     *
+     * @param parent
+     *            the parent composite
+     * @param style
+     *            the style to pass to the scrolled composite
+     * @param createFormText
+     *            if <code>true</code>, enclosing form text instance will be
+     *            created in this constructor.
+     */
+    public this(Composite parent, int style, bool createFormText) {
+        super(parent, style);
+        if (createFormText)
+            setFormText(new FormText(this, SWT.NULL));
+    }
+    /**
+     * Sets the form text to be managed by this scrolled form text. The
+     * instance must be a direct child of this class. If this method is used,
+     * <code>false</code> must be passed in either of the constructors to
+     * avoid creating form text instance.
+     *
+     * @param formText
+     *            the form text instance to use.
+     */
+    public void setFormText(FormText formText) {
+        this.content = formText;
+        super.setContent(content);
+        content.setMenu(getMenu());
+        if (text !is null)
+            loadText(text);
+    }
+    /**
+     * Sets the foreground color of the scrolled form text.
+     *
+     * @param fg
+     *            the foreground color
+     */
+    public void setForeground(Color fg) {
+        super.setForeground(fg);
+        if (content !is null)
+            content.setForeground(fg);
+    }
+    /**
+     * Sets the background color of the scrolled form text.
+     *
+     * @param bg
+     *            the background color
+     */
+    public void setBackground(Color bg) {
+        super.setBackground(bg);
+        if (content !is null)
+            content.setBackground(bg);
+    }
+    /**
+     * The class sets the content widget. This method should not be called by
+     * classes that instantiate this widget.
+     *
+     * @param c
+     *            content control
+     */
+    public final void setContent(Control c) {
+    }
+    /**
+     * Sets the text to be rendered in the scrolled form text. The text must
+     * contain formatting tags.
+     *
+     * @param text
+     *            the text to be rendered
+     */
+    public void setText(String text) {
+        this.text = text;
+        loadText(text);
+        reflow(true);
+    }
+    /**
+     * Sets the contents to rendered in the scrolled form text. The stream must
+     * contain formatting tags. The caller is responsible for closing the input
+     * stream. The call may be long running. For best results, call this method
+     * from another thread and call 'reflow' when done (but make both calls
+     * using 'Display.asyncExec' because these calls must be made in the event
+     * dispatching thread).
+     *
+     * @param is
+     *            content input stream
+     */
+    public void setContents(InputStream is_) {
+        loadContents(is_);
+    }
+    /**
+     * Returns the instance of the form text.
+     *
+     * @return the form text instance
+     */
+    public FormText getFormText() {
+        return content;
+    }
+    private void loadText(String text) {
+        if (content !is null) {
+            String markup = text;
+            if (!markup.startsWith("<form>")) //$NON-NLS-1$
+                markup = "<form>" ~ text ~ "</form>";  //$NON-NLS-1$//$NON-NLS-2$
+            content.setText(markup, true, false);
+        }
+    }
+    private void loadContents(InputStream is_) {
+        if (content !is null) {
+            content.setContents(is_, false);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ScrolledPageBook.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,227 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.widgets.ScrolledPageBook;
+
+import org.eclipse.ui.forms.widgets.SharedScrolledComposite;
+import org.eclipse.ui.forms.widgets.LayoutComposite;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.internal.forms.widgets.WrappedPageBook;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * ScrolledPageBook is a class that is capable of stacking several composites
+ * (pages), while showing one at a time. The content is scrolled if there is
+ * not enough space to fit it in the client area.
+ *
+ * @since 3.0
+ */
+public class ScrolledPageBook : SharedScrolledComposite {
+    private WrappedPageBook pageBook;
+    private Hashtable pages;
+    private Composite emptyPage;
+    private Control currentPage;
+    /**
+     * Creates a new instance in the provided parent
+     *
+     * @param parent
+     */
+    public this(Composite parent) {
+        this(parent, SWT.H_SCROLL | SWT.V_SCROLL);
+    }
+    /**
+     * Creates a new instance in the provided parent and with the provided
+     * style.
+     *
+     * @param parent
+     *            the control parent
+     * @param style
+     *            the style to use
+     */
+    public this(Composite parent, int style) {
+        super(parent, style);
+        pageBook = new WrappedPageBook(this, SWT.NULL);
+        setContent(pageBook);
+        pages = new Hashtable();
+        setExpandHorizontal(true);
+        setExpandVertical(true);
+        this.addListener(SWT.Traverse, dgListener( (Event e) {
+            switch (e.detail) {
+                case SWT.TRAVERSE_ESCAPE :
+                case SWT.TRAVERSE_RETURN :
+                case SWT.TRAVERSE_TAB_NEXT :
+                case SWT.TRAVERSE_TAB_PREVIOUS :
+                    e.doit = true;
+                    break;
+                default:
+            }
+        }));
+    }
+    /**
+     * Removes the default size of the composite, allowing the control to
+     * shrink to the trim.
+     *
+     * @param wHint
+     *            the width hint
+     * @param hHint
+     *            the height hint
+     * @param changed
+     *            if <code>true</code>, do not use cached values
+     */
+    public Point computeSize(int wHint, int hHint, bool changed) {
+        Rectangle trim = computeTrim(0, 0, 10, 10);
+        return new Point(trim.width, trim.height);
+    }
+    /**
+     * Tests if the page under the provided key is currently in the book.
+     *
+     * @param key
+     *            the page key
+     * @return <code>true</code> if page exists, <code>false</code>
+     *         otherwise.
+     */
+    public bool hasPage(Object key) {
+        return pages.containsKey(key);
+    }
+    /**
+     * Creates a new page for the provided key. Use the returned composite to
+     * create children in it.
+     *
+     * @param key
+     *            the page key
+     * @return the newly created page composite
+     */
+    public Composite createPage(Object key) {
+        Composite page = createPage();
+        pages.put(key, page);
+        return page;
+    }
+    /**
+     * Returns the page book container.
+     *
+     * @return the page book container
+     */
+    public Composite getContainer() {
+        return pageBook;
+    }
+    /**
+     * Registers a page under the privided key to be managed by the page book.
+     * The page must be a direct child of the page book container.
+     *
+     * @param key
+     *            the page key
+     * @param page
+     *            the page composite to register
+     * @see #createPage(Object)
+     * @see #getContainer
+     */
+    public void registerPage(Object key, Control page) {
+        pages.put(key, page);
+    }
+    /**
+     * Removes the page under the provided key from the page book. Does nothing
+     * if page with that key does not exist.
+     *
+     * @param key
+     *            the page key.
+     */
+    public void removePage(Object key) {
+        removePage(key, true);
+    }
+    /**
+     * Removes the page under the provided key from the page book. Does nothing
+     * if page with that key does not exist.
+     *
+     * @param key
+     *            the page key.
+     * @param showEmptyPage
+     *            if <code>true</code>, shows the empty page
+     *            after page removal.
+     */
+    public void removePage(Object key, bool showEmptyPage_) {
+        Control page = cast(Control) pages.get(key);
+        if (page !is null) {
+            pages.remove(key);
+            page.dispose();
+            if (showEmptyPage_)
+                showEmptyPage();
+        }
+    }
+    /**
+     * Shows the page with the provided key and hides the page previously
+     * showing. Does nothing if the page with that key does not exist.
+     *
+     * @param key
+     *            the page key
+     */
+    public void showPage(Object key) {
+        Control page = cast(Control) pages.get(key);
+        if (page !is null) {
+            pageBook.showPage(page);
+            if (currentPage !is null && currentPage !is page) {
+                // switching pages - force layout
+                if (null !is cast(Composite)page )
+                    (cast(Composite) page).layout(false);
+            }
+            currentPage = page;
+        } else {
+            showEmptyPage();
+        }
+        reflow(true);
+    }
+    /**
+     * Shows a page with no children to be used if the desire is to not show
+     * any registered page.
+     */
+    public void showEmptyPage() {
+        if (emptyPage is null) {
+            emptyPage = createPage();
+            emptyPage.setLayout(new GridLayout());
+        }
+        pageBook.showPage(emptyPage);
+        currentPage = emptyPage;
+        reflow(true);
+    }
+    /**
+     * Sets focus on the current page if shown.
+     */
+    public bool setFocus() {
+        if (currentPage !is null)
+            return currentPage.setFocus();
+        return super.setFocus();
+    }
+    /**
+     * Returns the page currently showing.
+     *
+     * @return the current page
+     */
+    public Control getCurrentPage() {
+        return currentPage;
+    }
+    private Composite createPage() {
+        Composite page = new LayoutComposite(pageBook, SWT.NULL);
+        page.setBackground(getBackground());
+        page.setForeground(getForeground());
+        page.setMenu(pageBook.getMenu());
+        return page;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/Section.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,483 @@
+/*******************************************************************************
+ * 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
+ *     Michael Williamson (eclipse-bugs@magnaworks.com) - patch (see Bugzilla #92545)
+ *
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.ui.forms.widgets.Section;
+
+import org.eclipse.ui.forms.widgets.ExpandableComposite;
+import org.eclipse.ui.forms.widgets.SharedScrolledComposite;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.ui.internal.forms.widgets.FormImages;
+import org.eclipse.ui.internal.forms.widgets.FormUtil;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A variation of the expandable composite that adds optional description below
+ * the title. Section is often used as a basic building block in forms because
+ * it provides for logical grouping of information.
+ * <p>
+ * In case of the TITLE_BAR style, Section renders the title bar in a way
+ * compatible with the rest of the workbench. Since it is a widget, all the
+ * colors must be supplied directly. When created by the form toolkit, these
+ * colors are supplied by the toolkit. The toolkit initializes these colors
+ * based on the system colors. For this reason, it is recommended to create the
+ * section by the toolkit instead of through its own constructor.
+ * <p>
+ * Since 3.1, it is possible to set a control to be used for section
+ * description. If used, <code>DESCRIPTION</code> style should not be set. A
+ * typical way to take advantage of the new method is to set an instance of
+ * <code>FormText</code> to provide for hyperlinks and images in the
+ * description area.
+ *
+ * @since 3.0
+ */
+public class Section : ExpandableComposite {
+    /**
+     * Description style. If used, description will be rendered below the title.
+     */
+    public static const int DESCRIPTION = 1 << 7;
+
+    private Control descriptionControl;
+
+    private Control separator;
+
+    private Hashtable titleColors;
+
+    private static const String COLOR_BG = "bg"; //$NON-NLS-1$
+
+    private static const String COLOR_GBG = "gbg"; //$NON-NLS-1$
+
+    private static const String COLOR_BORDER = "border"; //$NON-NLS-1$
+
+    /**
+     * Creates a new section instance in the provided parent.
+     *
+     * @param parent
+     *            the parent composite
+     * @param style
+     *            the style to use
+     */
+    public this(Composite parent, int style) {
+        this(parent, SWT.NULL, style);
+    }
+
+    this(Composite parent, int cstyle, int style) {
+        super(parent, cstyle | getBackgroundStyle(style), style);
+        int rtl = cstyle & SWT.RIGHT_TO_LEFT;
+        if ((style & DESCRIPTION) !is 0) {
+            descriptionControl = new Text(this, SWT.READ_ONLY | SWT.WRAP | rtl);
+        }
+        if ((style & TITLE_BAR) !is 0) {
+            Listener listener = new class Listener{
+                public void handleEvent(Event e) {
+                    Image image = this.outer.callSuperGetBackgroundImage();
+                    if (image !is null) {
+                        FormImages.getInstance().markFinished(image);
+                    }
+                    this.outer.callSuperSetBackgroundImage(null);
+                }
+            };
+            addListener(SWT.Dispose, listener);
+            addListener(SWT.Resize, listener);
+        }
+    }
+
+    private static int getBackgroundStyle(int estyle) {
+        return ((estyle & TITLE_BAR) !is 0) ? SWT.NO_BACKGROUND : SWT.NULL;
+    }
+
+    private Image callSuperGetBackgroundImage(){
+        return super.getBackgroundImage();
+    }
+    private void callSuperSetBackgroundImage(Image image){
+        return super.setBackgroundImage(image);
+    }
+    protected void internalSetExpanded(bool expanded) {
+        super.internalSetExpanded(expanded);
+        if ((getExpansionStyle() & TITLE_BAR) !is 0) {
+            if (!expanded)
+                super.setBackgroundImage(cast(Image)null);
+        }
+        reflow();
+    }
+
+    /**
+     * Reflows this section and all the parents up the hierarchy until a
+     * SharedScrolledComposite is reached.
+     */
+    protected void reflow() {
+        Composite c = this;
+        while (c !is null) {
+            c.setRedraw(false);
+            c = c.getParent();
+            if (null !is cast(SharedScrolledComposite)c ) {
+                break;
+            }
+        }
+        c = this;
+        while (c !is null) {
+            c.layout(true);
+            c = c.getParent();
+            if (null !is cast(SharedScrolledComposite)c ) {
+                (cast(SharedScrolledComposite) c).reflow(true);
+                break;
+            }
+        }
+        c = this;
+        while (c !is null) {
+            c.setRedraw(true);
+            c = c.getParent();
+            if (null !is cast(SharedScrolledComposite)c ) {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Sets the description text. Has no effect if DESCRIPTION style was not
+     * used to create the control.
+     *
+     * @param description
+     */
+    public void setDescription(String description) {
+        if (null !is cast(Text)descriptionControl )
+            (cast(Text) descriptionControl).setText(description);
+    }
+
+    /**
+     * Returns the current description text.
+     *
+     * @return description text or <code>null</code> if DESCRIPTION style was
+     *         not used to create the control.
+     */
+    public String getDescription() {
+        if (null !is cast(Text)descriptionControl )
+            return (cast(Text) descriptionControl).getText();
+        return null;
+    }
+
+    /**
+     * Sets the separator control of this section. The separator must not be
+     * <samp>null </samp> and must be a direct child of this container. If
+     * defined, separator will be placed below the title text and will remain
+     * visible regardless of the expansion state.
+     *
+     * @param separator
+     *            the separator that will be placed below the title text.
+     */
+    public void setSeparatorControl(Control separator) {
+        Assert.isTrue(separator !is null && separator.getParent().opEquals(this));
+        this.separator = separator;
+    }
+
+    /**
+     * Returns the control that is used as a separator betweeen the title and
+     * the client, or <samp>null </samp> if not set.
+     *
+     * @return separator control or <samp>null </samp> if not set.
+     */
+    public Control getSeparatorControl() {
+        return separator;
+    }
+
+    /**
+     * Sets the background of the section.
+     *
+     * @param bg
+     *            the new background
+     */
+    public void setBackground(Color bg) {
+        super.setBackground(bg);
+        if (descriptionControl !is null
+                && (getExpansionStyle() & DESCRIPTION) !is 0)
+            descriptionControl.setBackground(bg);
+    }
+
+    /**
+     * Sets the foreground of the section.
+     *
+     * @param fg
+     *            the new foreground.
+     */
+    public void setForeground(Color fg) {
+        super.setForeground(fg);
+        if (descriptionControl !is null
+                && (getExpansionStyle() & DESCRIPTION) !is 0)
+            descriptionControl.setForeground(fg);
+    }
+
+    /**
+     * Returns the control used to render the description. In 3.1, this method
+     * was promoted to public.
+     *
+     * @return description control or <code>null</code> if DESCRIPTION style
+     *         was not used to create the control and description control was
+     *         not set by the client.
+     * @see #setDescriptionControl(org.eclipse.swt.widgets.Control)
+     */
+    public Control getDescriptionControl() {
+        return descriptionControl;
+    }
+
+    /**
+     * Sets the description control of this section. The control must not be
+     * <samp>null</samp> and must be a direct child of this container. If
+     * defined, contol will be placed below the title text and the separator and
+     * will be hidden int he collapsed state.
+     * <p>
+     * This method and <code>DESCRIPTION</code> style are mutually exclusive.
+     * Use the method only if you want to create the description control
+     * yourself.
+     *
+     * @since 3.1
+     * @param descriptionControl
+     *            the control that will be placed below the title text.
+     */
+    public void setDescriptionControl(Control descriptionControl) {
+        Assert.isTrue((getExpansionStyle() & DESCRIPTION) is 0);
+        Assert.isTrue(descriptionControl !is null
+                && descriptionControl.getParent().opEquals(this));
+        this.descriptionControl = descriptionControl;
+    }
+
+    /**
+     * Sets the color of the title bar border when TITLE_BAR style is used.
+     *
+     * @param color
+     *            the title bar border color
+     */
+    public void setTitleBarBorderColor(Color color) {
+        putTitleBarColor(COLOR_BORDER, color);
+    }
+
+    /**
+     * Sets the color of the title bar background when TITLE_BAR style is used.
+     * This color is used as a starting color for the vertical gradient.
+     *
+     * @param color
+     *            the title bar border background
+     */
+    public void setTitleBarBackground(Color color) {
+        putTitleBarColor(COLOR_BG, color);
+    }
+
+    /**
+     * Sets the color of the title bar gradient background when TITLE_BAR style
+     * is used. This color is used at the height where title controls end
+     * (toggle, tool bar).
+     *
+     * @param color
+     *            the title bar gradient background
+     */
+    public void setTitleBarGradientBackground(Color color) {
+        putTitleBarColor(COLOR_GBG, color);
+    }
+
+    /**
+     * Returns the title bar border color when TITLE_BAR style is used.
+     *
+     * @return the title bar border color
+     */
+    public Color getTitleBarBorderColor() {
+        if (titleColors is null)
+            return null;
+        return cast(Color) titleColors.get(COLOR_BORDER);
+    }
+
+    /**
+     * Returns the title bar gradient background color when TITLE_BAR style is
+     * used.
+     *
+     * @return the title bar gradient background
+     */
+    public Color getTitleBarGradientBackground() {
+        if (titleColors is null)
+            return null;
+        if ((getExpansionStyle() & SHORT_TITLE_BAR) !is 0)
+            return getBackground();
+        return cast(Color) titleColors.get(COLOR_GBG);
+    }
+
+    /**
+     * Returns the title bar background when TITLE_BAR style is used.
+     *
+     * @return the title bar background
+     */
+    public Color getTitleBarBackground() {
+        if (titleColors is null)
+            return null;
+        return cast(Color) titleColors.get(COLOR_BG);
+    }
+
+    private void putTitleBarColor(String key, Color color) {
+        if (color is null)
+            return;
+        if (titleColors is null)
+            titleColors = new Hashtable();
+        titleColors.put(key, color);
+    }
+
+    protected void onPaint(PaintEvent e) {
+        Color bg = null;
+        Color fg = null;
+        Color border = null;
+
+        GC gc = e.gc;
+        Image buffer = null;
+        Rectangle bounds = getClientArea();
+
+        if ((getExpansionStyle() & TITLE_BAR) !is 0) {
+            buffer = new Image(getDisplay(), bounds.width, bounds.height);
+            buffer.setBackground(getBackground());
+            gc = new GC(buffer);
+        }
+        if (titleColors !is null) {
+            bg = cast(Color) titleColors.get(COLOR_BG);
+            fg = getTitleBarForeground();
+            border = cast(Color) titleColors.get(COLOR_BORDER);
+        }
+        if (bg is null)
+            bg = getBackground();
+        if (fg is null)
+            fg = getForeground();
+        if (border is null)
+            border = fg;
+        int theight = 0;
+        int gradientheight = 0;
+        int tvmargin = IGAP;
+        if ((getExpansionStyle() & TITLE_BAR) !is 0) {
+            Point tsize = null;
+            Point tcsize = null;
+            if (toggle !is null)
+                tsize = toggle.getSize();
+            int twidth = bounds.width - marginWidth - marginWidth;
+            if (tsize !is null)
+                twidth -= tsize.x + IGAP;
+            if (getTextClient() !is null)
+                tcsize = getTextClient().getSize();
+            if (tcsize !is null)
+                twidth -= tcsize.x + IGAP;
+            Point size = textLabel.getSize();
+            if (tsize !is null)
+                theight += Math.max(theight, tsize.y);
+            gradientheight = theight;
+            if (tcsize !is null) {
+                theight = Math.max(theight, tcsize.y);
+            }
+            theight = Math.max(theight, size.y);
+            gradientheight = Math.max(gradientheight, size.y);
+            theight += tvmargin + tvmargin;
+            gradientheight += tvmargin + tvmargin;
+        } else {
+            theight = 5;
+        }
+        if ((getExpansionStyle() & TITLE_BAR) !is 0) {
+            if (getBackgroundImage() is null)
+                updateHeaderImage(bg, bounds, gradientheight, theight);
+            gc.setBackground(getBackground());
+            gc.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height);
+            drawBackground(gc, bounds.x, bounds.y, bounds.width, theight);
+            if (marginWidth > 0) {
+                // fix up margins
+                gc.setBackground(getBackground());
+                gc.fillRectangle(0, 0, marginWidth, theight);
+                gc.fillRectangle(bounds.x + bounds.width - marginWidth, 0,
+                        marginWidth, theight);
+            }
+        } else if (isExpanded()) {
+            gc.setForeground(bg);
+            gc.setBackground(getBackground());
+            gc.fillGradientRectangle(marginWidth, marginHeight, bounds.width
+                    - marginWidth - marginWidth, theight, true);
+        }
+        gc.setBackground(getBackground());
+        FormUtil.setAntialias(gc, SWT.ON);
+        // repair the upper left corner
+        gc.fillPolygon([ marginWidth, marginHeight, marginWidth,
+                marginHeight + 2, marginWidth + 2, marginHeight ]);
+        // repair the upper right corner
+        gc.fillPolygon([ bounds.width - marginWidth - 3,
+                marginHeight, bounds.width - marginWidth, marginHeight,
+                bounds.width - marginWidth, marginHeight + 3 ]);
+        gc.setForeground(border);
+        if (isExpanded() || (getExpansionStyle() & TITLE_BAR) !is 0) {
+            // top left curve
+            gc.drawLine(marginWidth, marginHeight + 2, marginWidth + 2,
+                    marginHeight);
+            // top edge
+            gc.drawLine(marginWidth + 2, marginHeight, bounds.width
+                    - marginWidth - 3, marginHeight);
+            // top right curve
+            gc.drawLine(bounds.width - marginWidth - 3, marginHeight,
+                    bounds.width - marginWidth - 1, marginHeight + 2);
+        } else {
+            // collapsed short title bar
+            // top edge
+            gc.drawLine(marginWidth, marginHeight, bounds.width - 1,
+                    marginHeight);
+        }
+        if ((getExpansionStyle() & TITLE_BAR) !is 0 || isExpanded()) {
+            // left vertical edge gradient
+            gc.fillGradientRectangle(marginWidth, marginHeight + 2, 1,
+                    gradientheight - 2, true);
+            // right vertical edge gradient
+            gc.fillGradientRectangle(bounds.width - marginWidth - 1,
+                    marginHeight + 2, 1, gradientheight - 2, true);
+        }
+        if ((getExpansionStyle() & TITLE_BAR) !is 0) {
+            // New in 3.3 - edge treatmant
+            gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WHITE));
+            gc.drawPolyline([ marginWidth + 1,
+                    marginHeight + gradientheight - 1, marginWidth + 1,
+                    marginHeight + 2, marginWidth + 2, marginHeight + 2,
+                    marginWidth + 2, marginHeight + 1,
+                    bounds.width - marginWidth - 3, marginHeight + 1,
+                    bounds.width - marginWidth - 3, marginHeight + 2,
+                    bounds.width - marginWidth - 2, marginHeight + 2,
+                    bounds.width - marginWidth - 2,
+                    marginHeight + gradientheight - 1 ]);
+        }
+        if (buffer !is null) {
+            gc.dispose();
+            e.gc.drawImage(buffer, 0, 0);
+            buffer.dispose();
+        }
+    }
+
+    private void updateHeaderImage(Color bg, Rectangle bounds, int theight,
+            int realtheight) {
+        Image image = FormImages.getInstance().getGradient(getDisplay(), getBackground(), bg, realtheight, theight, marginHeight);
+        super.setBackgroundImage(image);
+    }
+
+    /**
+     * Background image is used for the title gradient - does nothing.
+     */
+    public final void setBackgroundImage(Image image) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/SharedScrolledComposite.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,283 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.widgets.SharedScrolledComposite;
+
+import org.eclipse.ui.forms.widgets.SizeCache;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.ui.internal.forms.widgets.FormUtil;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * This class is used to provide common scrolling services to a number of
+ * controls in the toolkit. Classes that extend it are not required to implement
+ * any method.
+ *
+ * @since 3.0
+ */
+public abstract class SharedScrolledComposite : ScrolledComposite {
+    private static const int H_SCROLL_INCREMENT = 5;
+
+    private static const int V_SCROLL_INCREMENT = 64;
+
+    private bool ignoreLayouts = true;
+
+    private bool ignoreResizes = false;
+
+    private bool expandHorizontal = false;
+
+    private bool expandVertical = false;
+
+    private SizeCache contentCache;
+
+    private bool reflowPending = false;
+
+    private bool delayedReflow = true;
+
+    /**
+     * Creates the new instance.
+     *
+     * @param parent
+     *            the parent composite
+     * @param style
+     *            the style to use
+     */
+    public this(Composite parent, int style) {
+        contentCache = new SizeCache();
+        super(parent, style);
+        addListener(SWT.Resize, new class Listener {
+            public void handleEvent(Event e) {
+                if (!ignoreResizes) {
+                    scheduleReflow(false);
+                }
+            }
+        });
+        initializeScrollBars();
+    }
+
+    /**
+     * Sets the foreground of the control and its content.
+     *
+     * @param fg
+     *            the new foreground color
+     */
+    public void setForeground(Color fg) {
+        super.setForeground(fg);
+        if (getContent() !is null)
+            getContent().setForeground(fg);
+    }
+
+    /**
+     * Sets the background of the control and its content.
+     *
+     * @param bg
+     *            the new background color
+     */
+    public void setBackground(Color bg) {
+        super.setBackground(bg);
+        if (getContent() !is null)
+            getContent().setBackground(bg);
+    }
+
+    /**
+     * Sets the font of the form. This font will be used to render the title
+     * text. It will not affect the body.
+     */
+    public void setFont(Font font) {
+        super.setFont(font);
+        if (getContent() !is null)
+            getContent().setFont(font);
+    }
+
+    /**
+     * Overrides 'super' to pass the proper colors and font
+     */
+    public void setContent(Control content) {
+        super.setContent(content);
+        if (content !is null) {
+            content.setForeground(getForeground());
+            content.setBackground(getBackground());
+            content.setFont(getFont());
+        }
+    }
+
+    /**
+     * If content is set, transfers focus to the content.
+     */
+    public bool setFocus() {
+        bool result;
+        FormUtil.setFocusScrollingEnabled(this, false);
+        if (getContent() !is null)
+            result = getContent().setFocus();
+        else
+            result = super.setFocus();
+        FormUtil.setFocusScrollingEnabled(this, true);
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.swt.widgets.Composite#layout(bool)
+     */
+    public void layout(bool changed) {
+        if (ignoreLayouts) {
+            return;
+        }
+
+        ignoreLayouts = true;
+        ignoreResizes = true;
+        super.layout(changed);
+        ignoreResizes = false;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.swt.custom.ScrolledComposite#setExpandHorizontal(bool)
+     */
+    public void setExpandHorizontal(bool expand) {
+        expandHorizontal = expand;
+        super.setExpandHorizontal(expand);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.swt.custom.ScrolledComposite#setExpandVertical(bool)
+     */
+    public void setExpandVertical(bool expand) {
+        expandVertical = expand;
+        super.setExpandVertical(expand);
+    }
+
+    /**
+     * Recomputes the body layout and the scroll bars. The method should be used
+     * when changes somewhere in the form body invalidate the current layout
+     * and/or scroll bars.
+     *
+     * @param flushCache
+     *            if <code>true</code>, drop the cached data
+     */
+    public void reflow(bool flushCache) {
+        Composite c = cast(Composite) getContent();
+        Rectangle clientArea = getClientArea();
+        if (c is null)
+            return;
+
+        contentCache.setControl(c);
+        if (flushCache) {
+            contentCache.flush();
+        }
+        try {
+            setRedraw(false);
+            Point newSize = contentCache.computeSize(FormUtil.getWidthHint(
+                    clientArea.width, c), FormUtil.getHeightHint(clientArea.height,
+                    c));
+
+            // Point currentSize = c.getSize();
+            if (!(expandHorizontal && expandVertical)) {
+                c.setSize(newSize);
+            }
+
+            setMinSize(newSize);
+            FormUtil.updatePageIncrement(this);
+
+            // reduce vertical scroll increment if necessary
+            ScrollBar vbar = getVerticalBar();
+            if (vbar !is null) {
+                if (getClientArea().height - 5 < V_SCROLL_INCREMENT)
+                    getVerticalBar().setIncrement(getClientArea().height - 5);
+                else
+                    getVerticalBar().setIncrement(V_SCROLL_INCREMENT);
+            }
+
+            ignoreLayouts = false;
+            layout(flushCache);
+            ignoreLayouts = true;
+
+            contentCache.layoutIfNecessary();
+        } finally {
+            setRedraw(true);
+        }
+    }
+
+    private void updateSizeWhilePending() {
+        Control c = getContent();
+        Rectangle area = getClientArea();
+        setMinSize(area.width, c.getSize().y);
+    }
+
+    private void handleScheduleReflow(bool flushCache) {
+        if (!isDisposed())
+            reflow(flushCache);
+        reflowPending = false;
+    }
+    private void scheduleReflow(bool flushCache) {
+        if (delayedReflow) {
+            if (reflowPending) {
+                updateSizeWhilePending();
+                return;
+            }
+            getDisplay().asyncExec( dgRunnable( &handleScheduleReflow, flushCache));
+            reflowPending = true;
+        } else
+            reflow(flushCache);
+    }
+
+    private void initializeScrollBars() {
+        ScrollBar hbar = getHorizontalBar();
+        if (hbar !is null) {
+            hbar.setIncrement(H_SCROLL_INCREMENT);
+        }
+        ScrollBar vbar = getVerticalBar();
+        if (vbar !is null) {
+            vbar.setIncrement(V_SCROLL_INCREMENT);
+        }
+        FormUtil.updatePageIncrement(this);
+    }
+
+    /**
+     * Tests if the control uses delayed reflow.
+     * @return <code>true</code> if reflow requests will
+     * be delayed, <code>false</code> otherwise.
+     */
+    public bool isDelayedReflow() {
+        return delayedReflow;
+    }
+
+    /**
+     * Sets the delayed reflow feature. When used,
+     * it will schedule a reflow on resize requests
+     * and reject subsequent reflows until the
+     * scheduled one is performed. This improves
+     * performance by
+     * @param delayedReflow
+     *            The delayedReflow to set.
+     */
+    public void setDelayedReflow(bool delayedReflow) {
+        this.delayedReflow = delayedReflow;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/SizeCache.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,539 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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 org.eclipse.ui.forms.widgets.SizeCache;
+
+import org.eclipse.ui.forms.widgets.ILayoutExtension;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.ProgressBar;
+import org.eclipse.swt.widgets.Sash;
+import org.eclipse.swt.widgets.Scale;
+import org.eclipse.swt.widgets.Scrollable;
+import org.eclipse.swt.widgets.Slider;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.jface.util.Geometry;
+import org.eclipse.ui.internal.forms.widgets.FormUtil;
+
+import java.lang.all;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Caches the preferred size of an SWT control
+ *
+ * @since 3.0
+ */
+public class SizeCache {
+    private Control control;
+
+    private Point preferredSize;
+
+    private int cachedWidthQuery;
+    private int cachedWidthResult;
+
+    private int cachedHeightQuery;
+    private int cachedHeightResult;
+
+    private int minimumWidth;
+    private int heightAtMinimumWidth = -1;
+    private int maximumWidth;
+
+    /**
+     * True iff we should recursively flush all children on the next layout
+     */
+    private bool flushChildren;
+
+    /**
+     * True iff changing the height hint does not affect the preferred width and changing
+     * the width hint does not change the preferred height
+     */
+    private bool independentDimensions = false;
+
+    /**
+     * True iff the preferred height for any hint larger than the preferred width will not
+     * change the preferred height.
+     */
+    private bool preferredWidthOrLargerIsMinimumHeight = false;
+
+    // HACK: these values estimate how much to subtract from the width and height
+    // hints that get passed into computeSize, in order to produce a result
+    // that is exactly the desired size. To be removed once bug 46112 is fixed (note:
+    // bug 46112 is currently flagged as a duplicate, but there is still no workaround).
+    private int widthAdjustment = 0;
+
+    private int heightAdjustment = 0;
+
+    private int minimumHeight;
+
+    private int widthAtMinimumHeight = -1;
+
+    // If the layout is dirty, this is the size of the control at the time its
+    // layout was dirtied. null if the layout is not dirty.
+    private Point dirtySize = null;
+
+
+    // END OF HACK
+
+    public this() {
+        this(null);
+    }
+
+    /**
+     * Creates a cache for size computations on the given control
+     *
+     * @param control the control for which sizes will be calculated,
+     * or null to always return (0,0)
+     */
+    public this(Control control) {
+        setControl(control);
+    }
+
+    /**
+     * Sets the control whose size is being cached. Does nothing (will not
+     * even flush the cache) if this is the same control as last time.
+     *
+     * @param newControl the control whose size is being cached, or null to always return (0,0)
+     */
+    public void setControl(Control newControl) {
+        if (newControl !is control) {
+            control = newControl;
+            if (control is null) {
+                independentDimensions = true;
+                preferredWidthOrLargerIsMinimumHeight = false;
+                widthAdjustment = 0;
+                heightAdjustment = 0;
+            } else {
+                independentDimensions = independentLengthAndWidth(control);
+                preferredWidthOrLargerIsMinimumHeight = isPreferredWidthMaximum(control);
+                computeHintOffset(control);
+                flush();
+            }
+        }
+    }
+
+    /**
+     * Returns the control whose size is being cached
+     *
+     * @return the control whose size is being cached, or null if this cache always returns (0,0)
+     */
+    public Control getControl() {
+        return control;
+    }
+
+    /**
+     * Flush the cache (should be called if the control's contents may have changed since the
+     * last query)
+     */
+    public void flush() {
+        flush(true);
+    }
+
+    public void flush(bool recursive) {
+        preferredSize = null;
+        cachedWidthQuery = -1;
+        cachedWidthResult = -1;
+        cachedHeightQuery = -1;
+        cachedHeightResult = -1;
+        minimumWidth = -1;
+        maximumWidth = -1;
+        minimumHeight = -1;
+        heightAtMinimumWidth = -1;
+        widthAtMinimumHeight = -1;
+
+        if (recursive || dirtySize !is null) {
+            if (control is null || control.isDisposed()) {
+                dirtySize = new Point(0,0);
+                control = null;
+            } else {
+                dirtySize = control.getSize();
+            }
+        }
+
+        this.flushChildren = this.flushChildren || recursive;
+    }
+
+    private Point getPreferredSize() {
+        if (preferredSize is null) {
+            preferredSize = controlComputeSize(SWT.DEFAULT, SWT.DEFAULT);
+        }
+
+        return preferredSize;
+    }
+
+    /**
+     * Computes the preferred size of the control.
+     *
+     * @param widthHint the known width of the control (pixels) or SWT.DEFAULT if unknown
+     * @param heightHint the known height of the control (pixels) or SWT.DEFAULT if unknown
+     * @return the preferred size of the control
+     */
+    public Point computeSize(int widthHint, int heightHint) {
+        if (control is null || control.isDisposed()) {
+            return new Point(0, 0);
+        }
+
+        // If we're asking for a result smaller than the minimum width
+        int minWidth = computeMinimumWidth();
+
+        if (widthHint !is SWT.DEFAULT && widthHint + widthAdjustment < minWidth) {
+            if (heightHint is SWT.DEFAULT) {
+                return new Point(minWidth, computeHeightAtMinimumWidth());
+            }
+
+            widthHint = minWidth - widthAdjustment;
+        }
+
+        // If we're asking for a result smaller than the minimum height
+        int minHeight = computeMinimumHeight();
+
+        if (heightHint !is SWT.DEFAULT && heightHint + heightAdjustment < minHeight) {
+            if (widthHint is SWT.DEFAULT) {
+                return new Point(computeWidthAtMinimumHeight(), minHeight);
+            }
+
+            heightHint = minHeight - heightAdjustment;
+        }
+
+        // If both dimensions were supplied in the input, compute the trivial result
+        if (widthHint !is SWT.DEFAULT && heightHint !is SWT.DEFAULT) {
+            return new Point(widthHint + widthAdjustment, heightHint + heightAdjustment);
+        }
+
+        // No hints given -- find the preferred size
+        if (widthHint is SWT.DEFAULT && heightHint is SWT.DEFAULT) {
+            return Geometry.copy(getPreferredSize());
+        }
+
+        // If the length and width are independent, compute the preferred size
+        // and adjust whatever dimension was supplied in the input
+        if (independentDimensions) {
+            Point result = Geometry.copy(getPreferredSize());
+
+            if (widthHint !is SWT.DEFAULT) {
+                result.x = widthHint + widthAdjustment;
+            }
+
+            if (heightHint !is SWT.DEFAULT) {
+                result.y = heightHint + heightAdjustment;
+            }
+
+            return result;
+        }
+
+        // Computing a height
+        if (heightHint is SWT.DEFAULT) {
+            // If we know the control's preferred size
+            if (preferredSize !is null) {
+                // If the given width is the preferred width, then return the preferred size
+                if (widthHint + widthAdjustment is preferredSize.x) {
+                    return Geometry.copy(preferredSize);
+                }
+            }
+
+            // If we have a cached height measurement
+            if (cachedHeightQuery !is -1) {
+                // If this was measured with the same width hint
+                if (cachedHeightQuery is widthHint) {
+                    return new Point(widthHint + widthAdjustment, cachedHeightResult);
+                }
+            }
+
+            // If this is a control where any hint larger than the
+            // preferred width results in the minimum height, determine if
+            // we can compute the result based on the preferred height
+            if (preferredWidthOrLargerIsMinimumHeight) {
+                // Computed the preferred size (if we don't already know it)
+                getPreferredSize();
+
+                // If the width hint is larger than the preferred width, then
+                // we can compute the result from the preferred width
+                if (widthHint + widthAdjustment >= preferredSize.x) {
+                    return new Point(widthHint + widthAdjustment, preferredSize.y);
+                }
+            }
+
+            // Else we can't find an existing size in the cache, so recompute
+            // it from scratch.
+            Point newHeight = controlComputeSize(widthHint - widthAdjustment, SWT.DEFAULT);
+
+            cachedHeightQuery = heightHint;
+            cachedHeightResult = newHeight.y;
+
+            return newHeight;
+        }
+
+        // Computing a width
+        if (widthHint is SWT.DEFAULT) {
+            // If we know the control's preferred size
+            if (preferredSize !is null) {
+                // If the given height is the preferred height, then return the preferred size
+                if (heightHint + heightAdjustment is preferredSize.y) {
+                    return Geometry.copy(preferredSize);
+                }
+            }
+
+            // If we have a cached width measurement with the same height hint
+            if (cachedWidthQuery is heightHint) {
+                return new Point(cachedWidthResult, heightHint + heightAdjustment);
+            }
+
+            Point widthResult = controlComputeSize(SWT.DEFAULT, heightHint - heightAdjustment);
+
+            cachedWidthQuery = heightHint;
+            cachedWidthResult = widthResult.x;
+
+            return widthResult;
+        }
+
+        return controlComputeSize(widthHint, heightHint);
+    }
+
+    /**
+     * Compute the control's size, and ensure that non-default hints are returned verbatim
+     * (this tries to compensate for SWT's hints, which aren't really the outer width of the
+     * control).
+     *
+     * @param widthHint the horizontal hint
+     * @param heightHint the vertical hint
+     * @return the control's size
+     */
+    public Point computeAdjustedSize(int widthHint, int heightHint) {
+        int adjustedWidthHint = widthHint is SWT.DEFAULT ? SWT.DEFAULT : Math
+                .max(0, widthHint - widthAdjustment);
+        int adjustedHeightHint = heightHint is SWT.DEFAULT ? SWT.DEFAULT : Math
+                .max(0, heightHint - heightAdjustment);
+
+        Point result = computeSize(adjustedWidthHint, adjustedHeightHint);
+
+        // If the amounts we subtracted off the widthHint and heightHint didn't do the trick, then
+        // manually adjust the result to ensure that a non-default hint will return that result verbatim.
+
+        return result;
+    }
+
+    /**
+     * Returns true if the preferred length of the given control is
+     * independent of the width and visa-versa. If this returns true,
+     * then changing the widthHint argument to control.computeSize will
+     * never change the resulting height and changing the heightHint
+     * will never change the resulting width. Returns false if unknown.
+     * <p>
+     * This information can be used to improve caching. Incorrectly returning
+     * a value of false may decrease performance, but incorrectly returning
+     * a value of true will generate incorrect layouts... so always return
+     * false if unsure.
+     * </p>
+     *
+     * @param control
+     * @return
+     */
+    static bool independentLengthAndWidth(Control control) {
+        if (control is null || control.isDisposed()) {
+            return true;
+        }
+
+        if (null !is cast(Button)control || null !is cast(ProgressBar)control
+                || null !is cast(Sash)control || null !is cast(Scale)control
+                || null !is cast(Slider)control || null !is cast(List)control
+                || null !is cast(Combo)control || null !is cast(Tree)control ) {
+            return true;
+        }
+
+        if (null !is cast(Label)control || null !is cast(Text)control ) {
+            return (control.getStyle() & SWT.WRAP) is 0;
+        }
+
+        // Unless we're certain that the control has this property, we should
+        // return false.
+
+        return false;
+    }
+
+    /**
+     * Try to figure out how much we need to subtract from the hints that we
+     * pass into the given control's computeSize(...) method. This tries to
+     * compensate for bug 46112. To be removed once SWT provides an "official"
+     * way to compute one dimension of a control's size given the other known
+     * dimension.
+     *
+     * @param control
+     */
+    private void computeHintOffset(Control control) {
+        if (null !is cast(Scrollable)control ) {
+            // For scrollables, subtract off the trim size
+            Scrollable scrollable = cast(Scrollable) control;
+            Rectangle trim = scrollable.computeTrim(0, 0, 0, 0);
+
+            widthAdjustment = trim.width;
+            heightAdjustment = trim.height;
+        } else {
+            // For non-composites, subtract off 2 * the border size
+            widthAdjustment = control.getBorderWidth() * 2;
+            heightAdjustment = widthAdjustment;
+        }
+    }
+
+    private Point controlComputeSize(int widthHint, int heightHint) {
+        Point result = control.computeSize(widthHint, heightHint, flushChildren);
+        flushChildren = false;
+
+        return result;
+    }
+
+    /**
+     * Returns true only if the control will return a constant height for any
+     * width hint larger than the preferred width. Returns false if there is
+     * any situation in which the control does not have this property.
+     *
+     * <p>
+     * Note: this method is only important for wrapping controls, and it can
+     * safely return false for anything else. AFAIK, all SWT controls have this
+     * property, but to be safe they will only be added to the list once the
+     * property has been confirmed.
+     * </p>
+     *
+     * @param control
+     * @return
+     */
+    private static bool isPreferredWidthMaximum(Control control) {
+        return (null !is cast(ToolBar)control
+        //|| control instanceof CoolBar
+        || null !is cast(Label)control );
+    }
+
+    public int computeMinimumWidth() {
+        if (minimumWidth is -1) {
+            if (null !is cast(Composite)control ) {
+                Layout layout = (cast(Composite)control).getLayout();
+                if (null !is cast(ILayoutExtension)layout ) {
+                    minimumWidth = (cast(ILayoutExtension)layout).computeMinimumWidth(cast(Composite)control, flushChildren);
+                    flushChildren = false;
+                }
+            }
+        }
+
+        if (minimumWidth is -1) {
+            Point minWidth = controlComputeSize(FormUtil.getWidthHint(5, control), SWT.DEFAULT);
+            minimumWidth = minWidth.x;
+            heightAtMinimumWidth = minWidth.y;
+        }
+
+        return minimumWidth;
+    }
+
+    public int computeMaximumWidth() {
+        if (maximumWidth is -1) {
+            if (null !is cast(Composite)control ) {
+                Layout layout = (cast(Composite)control).getLayout();
+                if (null !is cast(ILayoutExtension)layout ) {
+                    maximumWidth = (cast(ILayoutExtension)layout).computeMaximumWidth(cast(Composite)control, flushChildren);
+                    flushChildren = false;
+                }
+            }
+        }
+
+        if (maximumWidth is -1) {
+            maximumWidth = getPreferredSize().x;
+        }
+
+        return maximumWidth;
+    }
+
+    private int computeHeightAtMinimumWidth() {
+        int minimumWidth = computeMinimumWidth();
+
+        if (heightAtMinimumWidth is -1) {
+            heightAtMinimumWidth = controlComputeSize(minimumWidth - widthAdjustment, SWT.DEFAULT).y;
+        }
+
+        return heightAtMinimumWidth;
+    }
+
+    private int computeWidthAtMinimumHeight() {
+        int minimumHeight = computeMinimumHeight();
+
+        if (widthAtMinimumHeight is -1) {
+            widthAtMinimumHeight = controlComputeSize(SWT.DEFAULT, minimumHeight - heightAdjustment).x;
+        }
+
+        return widthAtMinimumHeight;
+    }
+
+    private int computeMinimumHeight() {
+        if (minimumHeight is -1) {
+            Point sizeAtMinHeight = controlComputeSize(SWT.DEFAULT, 0);
+
+            minimumHeight = sizeAtMinHeight.y;
+            widthAtMinimumHeight = sizeAtMinHeight.x;
+        }
+
+        return minimumHeight;
+    }
+
+    public Point computeMinimumSize() {
+        return new Point(computeMinimumWidth(), computeMinimumHeight());
+    }
+
+    public void setSize(Point newSize) {
+        if (control !is null) {
+            control.setSize(newSize);
+        }
+
+        layoutIfNecessary();
+    }
+
+    public void setSize(int width, int height) {
+        if (control !is null) {
+            control.setSize(width, height);
+        }
+
+        layoutIfNecessary();
+    }
+
+    public void setBounds(int x, int y, int width, int height) {
+        if (control !is null) {
+            control.setBounds(x, y, width, height);
+        }
+
+        layoutIfNecessary();
+    }
+
+    public void setBounds(Rectangle bounds) {
+        if (control !is null) {
+            control.setBounds(bounds);
+        }
+
+        layoutIfNecessary();
+    }
+
+    public void layoutIfNecessary() {
+        if (dirtySize !is null && control !is null && null !is cast(Composite)control ) {
+            if (control.getSize().opEquals(dirtySize)) {
+                (cast(Composite)control).layout(flushChildren);
+                flushChildren = false;
+            }
+        }
+        dirtySize = null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/TableWrapData.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,202 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.widgets.TableWrapData;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Layout data used in conjunction with <code>HTMLTableLayout</code>.
+ * Children in a composite that uses this layout should call <samp>setLayoutData
+ * </samp> and pass an instance of this class to control physical placement in
+ * the parent.
+ *
+ * @see TableWrapLayout
+ * @since 3.0
+ */
+public final class TableWrapData {
+    /**
+     * The control will be left-justified.
+     */
+    public static const int LEFT = 1 << 1;
+
+    /**
+     * The control will be centered horizontally.
+     */
+    public static const int CENTER = 1 << 2;
+
+    /**
+     * The control will be right-justified.
+     */
+    public static const int RIGHT = 1 << 3;
+
+    /**
+     * The control will be aligned with the top of the cell.
+     */
+    public static const int TOP = 1 << 4;
+
+    /**
+     * The control will be centered vertically.
+     */
+    public static const int MIDDLE = 1 << 5;
+
+    /**
+     * The control will be aligned with the bottom of the cell.
+     */
+    public static const int BOTTOM = 1 << 6;
+
+    /**
+     * The control will have the same width as the column it occupies.
+     */
+    public static const int FILL = 1 << 7;
+
+    /**
+     * In addition to filling width or height, the control will take part in
+     * allocation of any excess space. Note that this constant can only be
+     * passed to the constructor (cannot be directly assigned to
+     * <code>align</code> variable).
+     */
+    public static const int FILL_GRAB = 1 << 8;
+
+    /**
+     * Number of columns to span (default is 1).
+     */
+    public int colspan = 1;
+
+    /**
+     * Number of rows to span (default is 1).
+     */
+    public int rowspan = 1;
+
+    /**
+     * Horizontal alignment (LEFT, CENTER, RIGHT or FILL; default is LEFT).
+     */
+    public int align_ = LEFT;
+
+    /**
+     * Vertical alignment (TOP, MIDDLE, BOTTOM or FILL; default is TOP).
+     */
+    public int valign = TOP;
+
+    /**
+     * Horizontal indent (default is 0).
+     */
+    public int indent = 0;
+
+    /**
+     * Maximum width of the control (default is SWT.DEFAULT).
+     */
+    public int maxWidth = SWT.DEFAULT;
+
+    /**
+     * Maximum height of the control (default is SWT.DEFAULT).
+     */
+    public int maxHeight = SWT.DEFAULT;
+
+    /**
+     * Height hint of the control (default is SWT.DEFAULT).
+     */
+    public int heightHint = SWT.DEFAULT;
+
+    /**
+     * If <code>true</code>, take part in excess horizontal space
+     * distribution. (default is <code>false</code>).
+     */
+    public bool grabHorizontal;
+
+    /**
+     * If <code>true</code>, will grab any excess vertical space (default is
+     * <code>false</code>). Note that since TableWrapLayout works top-down
+     * and does not grows to fill the parent, this only applies to local excess
+     * space created by fixed-height children that span multiple rows.
+     */
+    public bool grabVertical;
+
+    int childIndex;
+
+    bool isItemData = true;
+
+    int compWidth;
+
+    Point compSize;
+
+    /**
+     * The default constructor.
+     */
+    public this() {
+    }
+
+    /**
+     * The convenience constructor - allows passing the horizontal alignment
+     * style.
+     *
+     * @param align
+     *            horizontal alignment (LEFT, CENTER, RIGHT, FILL or FILL_GRAB).
+     */
+    public this(int align_) {
+        this(align_, TOP, 1, 1);
+    }
+
+    /**
+     * The convenience constructor - allows passing the alignment styles.
+     *
+     * @param align
+     *            horizontal alignment (LEFT, CENTER, RIGHT, FILL or FILL_GRAB).
+     * @param valign
+     *            vertical alignment (TOP, MIDDLE, BOTTOM, FILL or FILL_GRAB).
+     */
+    public this(int align_, int valign) {
+        this(align_, valign, 1, 1);
+    }
+
+    /**
+     * The convenience constructor - allows passing the alignment styles, column
+     * and row spans.
+     *
+     * @param align
+     *            horizontal alignment (LEFT, CENTER, RIGHT, FILL or FILL_GRAB).
+     * @param valign
+     *            vertical alignment (TOP, MIDDLE, BOTTOM, FILL or FILL_GRAB)
+     * @param rowspan
+     *            row span (1 or more)
+     * @param colspan
+     *            column span (1 or more)
+     */
+    public this(int align_, int valign, int rowspan, int colspan) {
+        if (align_ !is LEFT && align_ !is CENTER && align_ !is RIGHT && align_ !is FILL
+                && align_ !is FILL_GRAB)
+            SWT.error(SWT.ERROR_INVALID_ARGUMENT, null, "align"); //$NON-NLS-1$
+        if (valign !is TOP && valign !is MIDDLE && valign !is BOTTOM
+                && valign !is FILL && valign !is FILL_GRAB)
+            SWT.error(SWT.ERROR_INVALID_ARGUMENT, null, "valign"); //$NON-NLS-1$
+        if (rowspan < 1)
+            SWT.error(SWT.ERROR_INVALID_ARGUMENT, null, "rowspan"); //$NON-NLS-1$
+        if (colspan < 1)
+            SWT.error(SWT.ERROR_INVALID_ARGUMENT, null, "colspan"); //$NON-NLS-1$
+        if (align_ is FILL_GRAB) {
+            this.align_ = FILL;
+            grabHorizontal = true;
+        } else
+            this.align_ = align_;
+        if (valign is FILL_GRAB) {
+            this.valign = FILL;
+            grabVertical = true;
+        } else
+            this.valign = valign;
+        this.rowspan = rowspan;
+        this.colspan = colspan;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/TableWrapLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,888 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.ILayoutExtension;
+import org.eclipse.ui.forms.widgets.LayoutCache;
+import org.eclipse.ui.forms.widgets.SizeCache;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Layout;
+
+import java.lang.all;
+import java.util.Enumeration;
+import java.util.Set;
+
+/**
+ * This implementation of the layout algorithm attempts to position controls in
+ * the composite using a two-pass autolayout HTML table altorithm recommeded by
+ * HTML 4.01 W3C specification (see
+ * http://www.w3.org/TR/html4/appendix/notes.html#h-B.5.2.2). The main
+ * differences with GridLayout is that it has two passes and that width and
+ * height are not calculated in the same pass.
+ * <p>
+ * The advantage of the algorithm over GridLayout is that it is capable of
+ * flowing text controls capable of line wrap. These controls do not have
+ * natural 'preferred size'. Instead, they are capable of providing the required
+ * height if the width is set. Consequently, this algorithm first calculates the
+ * widths that will be assigned to columns, and then passes those widths to the
+ * controls to calculate the height. When a composite with this layout is a
+ * child of the scrolling composite, they should interact in such a way that
+ * reduction in the scrolling composite width results in the reflow and increase
+ * of the overall height.
+ * <p>
+ * If none of the columns contain expandable and wrappable controls, the
+ * end-result will be similar to the one provided by GridLayout. The difference
+ * will show up for layouts that contain controls whose minimum and maximum
+ * widths are not the same.
+ *
+ * @see TableWrapData
+ * @since 3.0
+ */
+public final class TableWrapLayout : Layout, ILayoutExtension {
+
+    public alias Layout.computeSize computeSize;
+    /**
+     * Number of columns to use when positioning children (default is 1).
+     */
+    public int numColumns = 1;
+
+    /**
+     * Left margin variable (default is 5).
+     */
+    public int leftMargin = 5;
+
+    /**
+     * Right margin variable (default is 5).
+     */
+    public int rightMargin = 5;
+
+    /**
+     * Top margin variable (default is 5).
+     */
+    public int topMargin = 5;
+
+    /**
+     * Botom margin variable (default is 5).
+     */
+    public int bottomMargin = 5;
+
+    /**
+     * Horizontal spacing (default is 5).
+     */
+    public int horizontalSpacing = 5;
+
+    /**
+     * Vertical spacing (default is 5).
+     */
+    public int verticalSpacing = 5;
+
+    /**
+     * If set to <code>true</code>, all the columns will have the same width.
+     * Otherwise, column widths will be computed based on controls in them and
+     * their layout data (default is <code>false</code>).
+     */
+    public bool makeColumnsEqualWidth = false;
+
+    private bool initialLayout = true;
+
+    private Vector grid = null;
+
+    private Hashtable rowspans;
+
+    private int[] minColumnWidths, maxColumnWidths;
+
+    private int widestColumnWidth;
+
+    private int[] growingColumns;
+
+    private int[] growingRows;
+
+    private LayoutCache cache;
+
+    private class RowSpan {
+        Control child;
+
+        int row;
+
+        int column;
+
+        int height;
+
+        int totalHeight;
+
+        public this(Control child, int column, int row) {
+            this.child = child;
+            this.column = column;
+            this.row = row;
+        }
+
+        /*
+         * Updates this row span's height with the given one if it is within
+         * this span.
+         */
+        public void update(int currentRow, int rowHeight) {
+            TableWrapData td = cast(TableWrapData) child.getLayoutData();
+            // is currentRow within this span?
+            if (currentRow >= row && currentRow < row + td.rowspan) {
+                totalHeight += rowHeight;
+                if (currentRow > row)
+                    totalHeight += verticalSpacing;
+            }
+        }
+
+        public int getRequiredHeightIncrease() {
+            if (totalHeight < height)
+                return height - totalHeight;
+            return 0;
+        }
+    }
+
+    this(){
+        cache = new LayoutCache();
+    }
+
+    /**
+     * Implements ILayoutExtension. Should not be called directly.
+     *
+     * @see ILayoutExtension
+     */
+    public int computeMinimumWidth(Composite parent, bool changed) {
+
+        Control[] children = parent.getChildren();
+        if (changed) {
+            cache.flush();
+        }
+
+        cache.setControls(children);
+
+        changed = true;
+        initializeIfNeeded(parent, changed);
+        if (initialLayout) {
+            changed = true;
+            initialLayout = false;
+        }
+        if (grid is null || changed) {
+            changed = true;
+            grid = new Vector();
+            createGrid(parent);
+        }
+        if (minColumnWidths is null)
+            minColumnWidths = new int[numColumns];
+        for (int i = 0; i < numColumns; i++) {
+            minColumnWidths[i] = 0;
+        }
+        return internalGetMinimumWidth(parent, changed);
+    }
+
+    /**
+     * Implements ILayoutExtension. Should not be called directly.
+     *
+     * @see ILayoutExtension
+     */
+    public int computeMaximumWidth(Composite parent, bool changed) {
+        Control[] children = parent.getChildren();
+        if (changed) {
+            cache.flush();
+        }
+
+        cache.setControls(children);
+
+        changed = true;
+        initializeIfNeeded(parent, changed);
+        if (initialLayout) {
+            changed = true;
+            initialLayout = false;
+        }
+        if (grid is null || changed) {
+            changed = true;
+            grid = new Vector();
+            createGrid(parent);
+        }
+        if (maxColumnWidths is null)
+            maxColumnWidths = new int[numColumns];
+        for (int i = 0; i < numColumns; i++) {
+            maxColumnWidths[i] = 0;
+        }
+        return internalGetMaximumWidth(parent, changed);
+    }
+
+    /**
+     * @see Layout#layout(Composite, bool)
+     */
+    protected void layout(Composite parent, bool changed) {
+
+        Rectangle clientArea = parent.getClientArea();
+        Control[] children = parent.getChildren();
+        if (changed) {
+            cache.flush();
+        }
+
+        if (children.length is 0)
+            return;
+
+        cache.setControls(children);
+
+        int parentWidth = clientArea.width;
+        changed = true;
+        initializeIfNeeded(parent, changed);
+        if (initialLayout) {
+            changed = true;
+            initialLayout = false;
+        }
+        if (grid is null || changed) {
+            changed = true;
+            grid = new Vector();
+            createGrid(parent);
+        }
+        resetColumnWidths();
+        int minWidth = internalGetMinimumWidth(parent, changed);
+        int maxWidth = internalGetMaximumWidth(parent, changed);
+        int tableWidth = parentWidth;
+        int[] columnWidths;
+        if (parentWidth <= minWidth) {
+            tableWidth = minWidth;
+            if (makeColumnsEqualWidth) {
+                columnWidths = new int[numColumns];
+                for (int i = 0; i < numColumns; i++) {
+                    columnWidths[i] = widestColumnWidth;
+                }
+            } else
+                columnWidths = minColumnWidths;
+        } else if (parentWidth > maxWidth) {
+            if (growingColumns.length is 0) {
+                tableWidth = maxWidth;
+                columnWidths = maxColumnWidths;
+            } else {
+                columnWidths = new int[numColumns];
+                int colSpace = tableWidth - leftMargin - rightMargin;
+                colSpace -= (numColumns - 1) * horizontalSpacing;
+                int extra = parentWidth - maxWidth;
+                int colExtra = extra / growingColumns.length;
+                for (int i = 0; i < numColumns; i++) {
+                    columnWidths[i] = maxColumnWidths[i];
+                    if (isGrowingColumn(i)) {
+                        columnWidths[i] += colExtra;
+                    }
+                }
+            }
+        } else {
+            columnWidths = new int[numColumns];
+            if (makeColumnsEqualWidth) {
+                int colSpace = tableWidth - leftMargin - rightMargin;
+                colSpace -= (numColumns - 1) * horizontalSpacing;
+                int col = colSpace / numColumns;
+                for (int i = 0; i < numColumns; i++) {
+                    columnWidths[i] = col;
+                }
+            } else {
+                columnWidths = assignExtraSpace(tableWidth, maxWidth, minWidth);
+            }
+        }
+        int y = topMargin+clientArea.y;
+        int[] rowHeights = computeRowHeights(children, columnWidths, changed);
+        for (int i = 0; i < grid.size(); i++) {
+            int rowHeight = rowHeights[i];
+            int x = leftMargin+clientArea.x;
+            TableWrapData[] row = arrayFromObject!(TableWrapData)( grid.elementAt(i));
+            for (int j = 0; j < numColumns; j++) {
+                TableWrapData td = row[j];
+                if (td.isItemData) {
+                    Control child = children[td.childIndex];
+                    placeControl(child, td, x, y, rowHeights, i);
+                }
+                x += columnWidths[j];
+                if (j < numColumns - 1)
+                    x += horizontalSpacing;
+            }
+            y += rowHeight + verticalSpacing;
+        }
+    }
+
+    int[] computeRowHeights(Control[] children, int[] columnWidths,
+            bool changed) {
+        int[] rowHeights = new int[grid.size()];
+        for (int i = 0; i < grid.size(); i++) {
+            TableWrapData[] row = arrayFromObject!(TableWrapData)( grid.elementAt(i));
+            rowHeights[i] = 0;
+            for (int j = 0; j < numColumns; j++) {
+                TableWrapData td = row[j];
+                if (td.isItemData is false) {
+                    continue;
+                }
+                Control child = children[td.childIndex];
+                int span = td.colspan;
+                int cwidth = 0;
+                for (int k = j; k < j + span; k++) {
+                    cwidth += columnWidths[k];
+                    if (k < j + span - 1)
+                        cwidth += horizontalSpacing;
+                }
+                Point size = computeSize(td.childIndex, cwidth, td.indent, td.maxWidth, td.maxHeight);
+                td.compWidth = cwidth;
+                if (td.heightHint !is SWT.DEFAULT) {
+                    size = new Point(size.x, td.heightHint);
+                }
+                td.compSize = size;
+                RowSpan rowspan = cast(RowSpan) rowspans.get(child);
+                if (rowspan is null) {
+                    rowHeights[i] = Math.max(rowHeights[i], size.y);
+                } else
+                    rowspan.height = size.y;
+            }
+            updateRowSpans(i, rowHeights[i]);
+        }
+        for (Enumeration enm = rowspans.elements(); enm.hasMoreElements();) {
+            RowSpan rowspan = cast(RowSpan) enm.nextElement();
+            int increase = rowspan.getRequiredHeightIncrease();
+            if (increase is 0)
+                continue;
+            TableWrapData td = cast(TableWrapData) rowspan.child.getLayoutData();
+            int ngrowing = 0;
+            int[] affectedRows = new int[grid.size()];
+            for (int i = 0; i < growingRows.length; i++) {
+                int growingRow = growingRows[i];
+                if (growingRow >= rowspan.row
+                        && growingRow < rowspan.row + td.rowspan) {
+                    affectedRows[ngrowing++] = growingRow;
+                }
+            }
+            if (ngrowing is 0) {
+                ngrowing = 1;
+                affectedRows[0] = rowspan.row + td.rowspan - 1;
+            }
+            increase += increase % ngrowing;
+            int perRowIncrease = increase / ngrowing;
+            for (int i = 0; i < ngrowing; i++) {
+                int growingRow = affectedRows[i];
+                rowHeights[growingRow] += perRowIncrease;
+            }
+        }
+        return rowHeights;
+    }
+
+    bool isGrowingColumn(int col) {
+        if (growingColumns is null)
+            return false;
+        for (int i = 0; i < growingColumns.length; i++) {
+            if (col is growingColumns[i])
+                return true;
+        }
+        return false;
+    }
+
+    int[] assignExtraSpace(int tableWidth, int maxWidth, int minWidth) {
+        int fixedPart = leftMargin + rightMargin + (numColumns - 1)
+                * horizontalSpacing;
+        int D = maxWidth - minWidth;
+        int W = tableWidth - fixedPart - minWidth;
+        int widths[] = new int[numColumns];
+        int rem = 0;
+        for (int i = 0; i < numColumns; i++) {
+            int cmin = minColumnWidths[i];
+            int cmax = maxColumnWidths[i];
+            int d = cmax - cmin;
+            int extra = D !is 0 ? (d * W) / D : 0;
+            if (i < numColumns - 1) {
+                widths[i] = cmin + extra;
+                rem += widths[i];
+            } else {
+                widths[i] = tableWidth - fixedPart - rem;
+            }
+        }
+        return widths;
+    }
+
+    Point computeSize(int childIndex, int width, int indent, int maxWidth, int maxHeight) {
+        int widthArg = width - indent;
+        SizeCache controlCache = cache.getCache(childIndex);
+        if (!isWrap(controlCache.getControl()))
+            widthArg = SWT.DEFAULT;
+        Point size = controlCache.computeSize(widthArg, SWT.DEFAULT);
+        if (maxWidth !is SWT.DEFAULT)
+            size.x = Math.min(size.x, maxWidth);
+        if (maxHeight !is SWT.DEFAULT)
+            size.y = Math.min(size.y, maxHeight);
+        size.x += indent;
+        return size;
+    }
+
+    void placeControl(Control control, TableWrapData td, int x, int y,
+            int[] rowHeights, int row) {
+        int xloc = x + td.indent;
+        int yloc = y;
+        int height = td.compSize.y;
+        int colWidth = td.compWidth - td.indent;
+        int width = td.compSize.x-td.indent;
+        width = Math.min(width, colWidth);
+        int slotHeight = rowHeights[row];
+        RowSpan rowspan = cast(RowSpan) rowspans.get(control);
+        if (rowspan !is null) {
+            slotHeight = 0;
+            for (int i = row; i < row + td.rowspan; i++) {
+                if (i > row)
+                    slotHeight += verticalSpacing;
+                slotHeight += rowHeights[i];
+            }
+        }
+        // align horizontally
+        if (td.align_ is TableWrapData.CENTER) {
+            xloc = x + colWidth / 2 - width / 2;
+        } else if (td.align_ is TableWrapData.RIGHT) {
+            xloc = x + colWidth - width;
+        } else if (td.align_ is TableWrapData.FILL) {
+            width = colWidth;
+        }
+        // align vertically
+        if (td.valign is TableWrapData.MIDDLE) {
+            yloc = y + slotHeight / 2 - height / 2;
+        } else if (td.valign is TableWrapData.BOTTOM) {
+            yloc = y + slotHeight - height;
+        } else if (td.valign is TableWrapData.FILL) {
+            height = slotHeight;
+        }
+        control.setBounds(xloc, yloc, width, height);
+    }
+
+    void createGrid(Composite composite) {
+        int row, column, rowFill, columnFill;
+        Control[] children;
+        TableWrapData spacerSpec;
+        Vector growingCols = new Vector();
+        Vector growingRows = new Vector();
+        rowspans = new Hashtable();
+        //
+        children = composite.getChildren();
+        if (children.length is 0)
+            return;
+        //
+        grid.addElement( new ArrayWrapperObject(createEmptyRow()));
+        row = 0;
+        column = 0;
+        // Loop through the children and place their associated layout specs in
+        // the
+        // grid. Placement occurs left to right, top to bottom (i.e., by row).
+        for (int i = 0; i < children.length; i++) {
+            // Find the first available spot in the grid.
+            Control child = children[i];
+            TableWrapData spec = cast(TableWrapData) child.getLayoutData();
+            while (arrayFromObject!(TableWrapData)( grid.elementAt(row))[column] !is null) {
+                column = column + 1;
+                if (column >= numColumns) {
+                    row = row + 1;
+                    column = 0;
+                    if (row >= grid.size()) {
+                        grid.addElement(new ArrayWrapperObject(createEmptyRow()));
+                    }
+                }
+            }
+            // See if the place will support the widget's horizontal span. If
+            // not, go to the
+            // next row.
+            if (column + spec.colspan - 1 >= numColumns) {
+                grid.addElement(new ArrayWrapperObject(createEmptyRow()));
+                row = row + 1;
+                column = 0;
+            }
+            // The vertical span for the item will be at least 1. If it is > 1,
+            // add other rows to the grid.
+            if (spec.rowspan > 1) {
+                rowspans.put(child, new RowSpan(child, column, row));
+            }
+            for (int j = 2; j <= spec.rowspan; j++) {
+                if (row + j > grid.size()) {
+                    grid.addElement(new ArrayWrapperObject(createEmptyRow()));
+                }
+            }
+            // Store the layout spec. Also cache the childIndex. NOTE: That we
+            // assume the children of a
+            // composite are maintained in the order in which they are created
+            // and added to the composite.
+            (cast(ArrayWrapperObject) grid.elementAt(row)).array[column] = spec;
+            spec.childIndex = i;
+            if (spec.grabHorizontal) {
+                updateGrowingColumns(growingCols, spec, column);
+            }
+            if (spec.grabVertical) {
+                updateGrowingRows(growingRows, spec, row);
+            }
+            // Put spacers in the grid to account for the item's vertical and
+            // horizontal
+            // span.
+            rowFill = spec.rowspan - 1;
+            columnFill = spec.colspan - 1;
+            for (int r = 1; r <= rowFill; r++) {
+                for (int c = 0; c < spec.colspan; c++) {
+                    spacerSpec = new TableWrapData();
+                    spacerSpec.isItemData = false;
+                    (cast(ArrayWrapperObject) grid.elementAt(row + r)).array[column + c] = spacerSpec;
+                }
+            }
+            for (int c = 1; c <= columnFill; c++) {
+                for (int r = 0; r < spec.rowspan; r++) {
+                    spacerSpec = new TableWrapData();
+                    spacerSpec.isItemData = false;
+                    (cast(ArrayWrapperObject) grid.elementAt(row + r)).array[column + c] = spacerSpec;
+                }
+            }
+            column = column + spec.colspan - 1;
+        }
+        // Fill out empty grid cells with spacers.
+        for (int k = column + 1; k < numColumns; k++) {
+            spacerSpec = new TableWrapData();
+            spacerSpec.isItemData = false;
+            (cast(ArrayWrapperObject) grid.elementAt(row)).array[k] = spacerSpec;
+        }
+        for (int k = row + 1; k < grid.size(); k++) {
+            spacerSpec = new TableWrapData();
+            spacerSpec.isItemData = false;
+            (cast(ArrayWrapperObject) grid.elementAt(k)).array[column] = spacerSpec;
+        }
+        growingColumns = new int[growingCols.size()];
+        for (int i = 0; i < growingCols.size(); i++) {
+            growingColumns[i] = (cast(Integer) growingCols.get(i)).intValue();
+        }
+        this.growingRows = new int[growingRows.size()];
+        for (int i = 0; i < growingRows.size(); i++) {
+            this.growingRows[i] = (cast(Integer) growingRows.get(i)).intValue();
+        }
+    }
+
+    private void updateGrowingColumns(Vector growingColumns,
+            TableWrapData spec, int column) {
+        int affectedColumn = column + spec.colspan - 1;
+        for (int i = 0; i < growingColumns.size(); i++) {
+            Integer col = cast(Integer) growingColumns.get(i);
+            if (col.intValue() is affectedColumn)
+                return;
+        }
+        growingColumns.add(new Integer(affectedColumn));
+    }
+
+    private void updateGrowingRows(Vector growingRows, TableWrapData spec,
+            int row) {
+        int affectedRow = row + spec.rowspan - 1;
+        for (int i = 0; i < growingRows.size(); i++) {
+            Integer irow = cast(Integer) growingRows.get(i);
+            if (irow.intValue() is affectedRow)
+                return;
+        }
+        growingRows.add(new Integer(affectedRow));
+    }
+
+    private TableWrapData[] createEmptyRow() {
+        TableWrapData[] row = new TableWrapData[numColumns];
+        for (int i = 0; i < numColumns; i++)
+            row[i] = null;
+        return row;
+    }
+
+    /**
+     * @see Layout#computeSize(Composite, int, int, bool)
+     */
+    /+protected+/ override Point computeSize(Composite parent, int wHint, int hHint,
+            bool changed) {
+        Control[] children = parent.getChildren();
+        if (changed) {
+            cache.flush();
+        }
+        if (children.length is 0) {
+            return new Point(0, 0);
+        }
+        cache.setControls(children);
+
+        int parentWidth = wHint;
+        changed = true;
+        initializeIfNeeded(parent, changed);
+        if (initialLayout) {
+            changed = true;
+            initialLayout = false;
+        }
+        if (grid is null || changed) {
+            changed = true;
+            grid = new Vector();
+            createGrid(parent);
+        }
+        resetColumnWidths();
+        int minWidth = internalGetMinimumWidth(parent, changed);
+        int maxWidth = internalGetMaximumWidth(parent, changed);
+
+        if (wHint is SWT.DEFAULT)
+            parentWidth = maxWidth;
+
+        int tableWidth = parentWidth;
+        int[] columnWidths;
+        if (parentWidth <= minWidth) {
+            tableWidth = minWidth;
+            if (makeColumnsEqualWidth) {
+                columnWidths = new int[numColumns];
+                for (int i = 0; i < numColumns; i++) {
+                    columnWidths[i] = widestColumnWidth;
+                }
+            } else
+                columnWidths = minColumnWidths;
+        } else if (parentWidth >= maxWidth) {
+            if (makeColumnsEqualWidth) {
+                columnWidths = new int[numColumns];
+                int colSpace = parentWidth - leftMargin - rightMargin;
+                colSpace -= (numColumns - 1) * horizontalSpacing;
+                int col = colSpace / numColumns;
+                for (int i = 0; i < numColumns; i++) {
+                    columnWidths[i] = col;
+                }
+            } else {
+                tableWidth = maxWidth;
+                columnWidths = maxColumnWidths;
+            }
+        } else {
+            columnWidths = new int[numColumns];
+            if (makeColumnsEqualWidth) {
+                int colSpace = tableWidth - leftMargin - rightMargin;
+                colSpace -= (numColumns - 1) * horizontalSpacing;
+                int col = colSpace / numColumns;
+                for (int i = 0; i < numColumns; i++) {
+                    columnWidths[i] = col;
+                }
+            } else {
+                columnWidths = assignExtraSpace(tableWidth, maxWidth, minWidth);
+            }
+        }
+        int totalHeight = 0;
+        int innerHeight = 0;
+        // compute widths
+        for (int i = 0; i < grid.size(); i++) {
+            TableWrapData[] row = arrayFromObject!(TableWrapData)( grid.elementAt(i));
+            // assign widths, calculate heights
+            int rowHeight = 0;
+            for (int j = 0; j < numColumns; j++) {
+                TableWrapData td = row[j];
+                if (td.isItemData is false) {
+                    continue;
+                }
+                Control child = children[td.childIndex];
+                int span = td.colspan;
+                int cwidth = 0;
+                for (int k = j; k < j + span; k++) {
+                    if (k > j)
+                        cwidth += horizontalSpacing;
+                    cwidth += columnWidths[k];
+                }
+                int cy = td.heightHint;
+                if (cy is SWT.DEFAULT) {
+                    Point size = computeSize(td.childIndex, cwidth, td.indent, td.maxWidth, td.maxHeight);
+                    cy = size.y;
+                }
+                RowSpan rowspan = cast(RowSpan) rowspans.get(child);
+                if (rowspan !is null) {
+                    // don't take the height of this child into acount
+                    // because it spans multiple rows
+                    rowspan.height = cy;
+                } else {
+                    rowHeight = Math.max(rowHeight, cy);
+                }
+            }
+            updateRowSpans(i, rowHeight);
+            if (i > 0)
+                innerHeight += verticalSpacing;
+            innerHeight += rowHeight;
+        }
+        if (!rowspans.isEmpty())
+            innerHeight = compensateForRowSpans(innerHeight);
+        totalHeight = topMargin + innerHeight + bottomMargin;
+        return new Point(tableWidth, totalHeight);
+    }
+
+    private void updateRowSpans(int row, int rowHeight) {
+        if (rowspans is null || rowspans.size() is 0)
+            return;
+        for (Enumeration enm = rowspans.elements(); enm.hasMoreElements();) {
+            RowSpan rowspan = cast(RowSpan) enm.nextElement();
+            rowspan.update(row, rowHeight);
+        }
+    }
+
+    private int compensateForRowSpans(int totalHeight) {
+         for (Enumeration enm = rowspans.elements(); enm.hasMoreElements();) {
+            RowSpan rowspan = cast(RowSpan) enm.nextElement();
+            totalHeight += rowspan.getRequiredHeightIncrease();
+        }
+        return totalHeight;
+    }
+
+    int internalGetMinimumWidth(Composite parent, bool changed) {
+        if (changed)
+            //calculateMinimumColumnWidths(parent, true);
+            calculateColumnWidths(parent, minColumnWidths, false, true);
+        int minimumWidth = 0;
+        widestColumnWidth = 0;
+        if (makeColumnsEqualWidth) {
+            for (int i = 0; i < numColumns; i++) {
+                widestColumnWidth = Math.max(widestColumnWidth,
+                        minColumnWidths[i]);
+            }
+        }
+        for (int i = 0; i < numColumns; i++) {
+            if (i > 0)
+                minimumWidth += horizontalSpacing;
+            if (makeColumnsEqualWidth)
+                minimumWidth += widestColumnWidth;
+            else
+                minimumWidth += minColumnWidths[i];
+        }
+        // add margins
+        minimumWidth += leftMargin + rightMargin;
+        return minimumWidth;
+    }
+
+    int internalGetMaximumWidth(Composite parent, bool changed) {
+        if (changed)
+            //calculateMaximumColumnWidths(parent, true);
+            calculateColumnWidths(parent, maxColumnWidths, true, true);
+        int maximumWidth = 0;
+        for (int i = 0; i < numColumns; i++) {
+            if (i > 0)
+                maximumWidth += horizontalSpacing;
+            maximumWidth += maxColumnWidths[i];
+        }
+        // add margins
+        maximumWidth += leftMargin + rightMargin;
+        return maximumWidth;
+    }
+
+    void resetColumnWidths() {
+        if (minColumnWidths is null)
+            minColumnWidths = new int[numColumns];
+        if (maxColumnWidths is null)
+            maxColumnWidths = new int[numColumns];
+        for (int i = 0; i < numColumns; i++) {
+            minColumnWidths[i] = 0;
+        }
+        for (int i = 0; i < numColumns; i++) {
+            maxColumnWidths[i] = 0;
+        }
+    }
+
+    void calculateColumnWidths(Composite parent, int [] columnWidths, bool max, bool changed) {
+        bool secondPassNeeded=false;
+        for (int i = 0; i < grid.size(); i++) {
+            TableWrapData[] row = arrayFromObject!(TableWrapData)( grid.elementAt(i));
+            for (int j = 0; j < numColumns; j++) {
+                TableWrapData td = row[j];
+                if (td.isItemData is false)
+                    continue;
+
+                if (td.colspan>1) {
+                    // we will not do controls with multiple column span
+                    // here - increment and continue
+                    secondPassNeeded=true;
+                    j+=td.colspan-1;
+                    continue;
+                }
+
+                SizeCache childCache = cache.getCache(td.childIndex);
+                // !!
+                int width = max?childCache.computeMaximumWidth():childCache.computeMinimumWidth();
+                if (td.maxWidth !is SWT.DEFAULT)
+                    width = Math.min(width, td.maxWidth);
+
+                width += td.indent;
+                columnWidths[j] = Math.max(columnWidths[j], width);
+            }
+        }
+        if (!secondPassNeeded) return;
+
+        // Second pass for controls with multi-column horizontal span
+        for (int i = 0; i < grid.size(); i++) {
+            TableWrapData[] row = arrayFromObject!(TableWrapData)( grid.elementAt(i));
+            for (int j = 0; j < numColumns; j++) {
+                TableWrapData td = row[j];
+                if (td.isItemData is false || td.colspan is 1)
+                    continue;
+
+                SizeCache childCache = cache.getCache(td.childIndex);
+                int width = max?childCache.computeMaximumWidth():childCache.computeMinimumWidth();
+                if (td.maxWidth !is SWT.DEFAULT)
+                    width = Math.min(width, td.maxWidth);
+
+                width += td.indent;
+                // check if the current width is enough to
+                // support the control; if not, add the delta to
+                // the last column or to all the growing columns, if present
+                int current = 0;
+                for (int k = j; k < j + td.colspan; k++) {
+                    if (k > j)
+                        current += horizontalSpacing;
+                    current += columnWidths[k];
+                }
+                if (width <= current) {
+                    // we are ok - nothing to do here
+                } else {
+                    int ndiv = 0;
+                    if (growingColumns !is null) {
+                        for (int k = j; k < j + td.colspan; k++) {
+                            if (isGrowingColumn(k)) {
+                                ndiv++;
+                            }
+                        }
+                    }
+                    if (ndiv is 0) {
+                        // add the delta to the last column
+                        columnWidths[j + td.colspan - 1] += width
+                                - current;
+                    } else {
+                        // distribute the delta to the growing
+                        // columns
+                        int percolumn = (width - current) / ndiv;
+                        if ((width - current) % ndiv > 0)
+                            percolumn++;
+                        for (int k = j; k < j + td.colspan; k++) {
+                            if (isGrowingColumn(k))
+                                columnWidths[k] += percolumn;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    bool isWrap(Control control) {
+        if (null !is cast(Composite)control
+                && null !is cast(ILayoutExtension)((cast(Composite) control).getLayout()) )
+            return true;
+        return (control.getStyle() & SWT.WRAP) !is 0;
+    }
+
+    private void initializeIfNeeded(Composite parent, bool changed) {
+        if (changed)
+            initialLayout = true;
+        if (initialLayout) {
+            initializeLayoutData(parent);
+            initialLayout = false;
+        }
+    }
+
+    void initializeLayoutData(Composite composite) {
+        Control[] children = composite.getChildren();
+        for (int i = 0; i < children.length; i++) {
+            Control child = children[i];
+            if (child.getLayoutData() is null) {
+                child.setLayoutData(new TableWrapData());
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/ToggleHyperlink.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,269 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.widgets.ToggleHyperlink;
+
+import org.eclipse.ui.forms.widgets.AbstractHyperlink;
+import org.eclipse.ui.forms.widgets.ExpandableComposite;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.accessibility.ACC;
+import org.eclipse.swt.accessibility.AccessibleAdapter;
+import org.eclipse.swt.accessibility.AccessibleControlAdapter;
+import org.eclipse.swt.accessibility.AccessibleControlEvent;
+import org.eclipse.swt.accessibility.AccessibleEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.forms.events.HyperlinkAdapter;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.internal.forms.Messages;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A custom selectable control that can be used to control areas that can be
+ * expanded or collapsed.
+ * <p>
+ * This is an abstract class. Subclasses are responsible for rendering the
+ * control using decoration and hover decoration color. Control should be
+ * rendered based on the current expansion state.
+ *
+ * @since 3.0
+ */
+public abstract class ToggleHyperlink : AbstractHyperlink {
+
+    alias AbstractHyperlink.computeSize computeSize;
+
+    protected int innerWidth;
+    protected int innerHeight;
+    protected bool hover;
+    package bool hover_package(){
+        return hover;
+    }
+    package bool hover_package( bool v){
+        return (hover = v);
+    }
+    private bool expanded;
+    private Color decorationColor;
+    private Color hoverColor;
+    /**
+     * Creates a control in a provided composite.
+     *
+     * @param parent
+     *            the parent
+     * @param style
+     *            the style
+     */
+    public this(Composite parent, int style) {
+        super(parent, style);
+        Listener listener = new class Listener {
+            public void handleEvent(Event e) {
+                switch (e.type) {
+                    case SWT.MouseEnter:
+                        hover=true;
+                        redraw();
+                        break;
+                    case SWT.MouseExit:
+                        hover = false;
+                        redraw();
+                        break;
+                    case SWT.KeyDown:
+                        onKeyDown(e);
+                        break;
+                    default:
+                }
+            }
+        };
+        addListener(SWT.MouseEnter, listener);
+        addListener(SWT.MouseExit, listener);
+        addListener(SWT.KeyDown, listener);
+        addHyperlinkListener(new class HyperlinkAdapter {
+            public void linkActivated(HyperlinkEvent e) {
+                setExpanded(!isExpanded());
+            }
+        });
+        initAccessible();
+    }
+    /**
+     * Sets the color of the decoration.
+     *
+     * @param decorationColor
+     */
+    public void setDecorationColor(Color decorationColor) {
+        this.decorationColor = decorationColor;
+    }
+    /**
+     * Returns the color of the decoration.
+     *
+     * @return decoration color
+     */
+    public Color getDecorationColor() {
+        return decorationColor;
+    }
+    /**
+     * Sets the hover color of decoration. Hover color will be used when mouse
+     * enters the decoration area.
+     *
+     * @param hoverColor
+     *            the hover color to use
+     */
+    public void setHoverDecorationColor(Color hoverColor) {
+        this.hoverColor = hoverColor;
+    }
+    /**
+     * Returns the hover color of the decoration.
+     *
+     * @return the hover color of the decoration.
+     * @since 3.1
+     */
+    public Color getHoverDecorationColor() {
+        return hoverColor;
+    }
+
+    /**
+     * Returns the hover color of the decoration.
+     *
+     * @return the hover color of the decoration.
+     * @deprecated use <code>getHoverDecorationColor</code>
+     * @see #getHoverDecorationColor()
+     */
+    public Color geHoverDecorationColor() {
+        return hoverColor;
+    }
+    /**
+     * Computes the size of the control.
+     *
+     * @param wHint
+     *            width hint
+     * @param hHint
+     *            height hint
+     * @param changed
+     *            if true, flush any saved layout state
+     */
+    public Point computeSize(int wHint, int hHint, bool changed) {
+        int width, height;
+        /*
+        if (wHint !is SWT.DEFAULT)
+            width = wHint;
+        else */
+            width = innerWidth + 2 * marginWidth;
+        /*
+        if (hHint !is SWT.DEFAULT)
+            height = hHint;
+        else */
+            height = innerHeight + 2 * marginHeight;
+        return new Point(width, height);
+    }
+    /**
+     * Returns the expansion state of the toggle control. When toggle is in the
+     * normal (downward) state, the value is <samp>true </samp>. Collapsed
+     * control will return <samp>false </samp>.
+     *
+     * @return <samp>false </samp> if collapsed, <samp>true </samp> otherwise.
+     */
+    public bool isExpanded() {
+        return expanded;
+    }
+    /**
+     * Sets the expansion state of the twistie control
+     *
+     * @param expanded the expansion state
+     */
+    public void setExpanded(bool expanded) {
+        this.expanded = expanded;
+        getAccessible().selectionChanged();
+        redraw();
+    }
+    private void initAccessible() {
+        getAccessible().addAccessibleListener(new class AccessibleAdapter {
+            public void getHelp(AccessibleEvent e) {
+                e.result = getToolTipText();
+            }
+            public void getName(AccessibleEvent e) {
+                String name=Messages.ToggleHyperlink_accessibleName;
+                if (null !is cast(ExpandableComposite)getParent() ) {
+                    name ~= Messages.ToggleHyperlink_accessibleColumn ~ (cast(ExpandableComposite)getParent()).getText();
+                    int index = name.indexOf('&');
+                    if (index !is -1) {
+                        name = name.substring(0, index) ~ name.substring(index + 1);
+                    }
+                }
+                e.result = name;
+            }
+            public void getDescription(AccessibleEvent e) {
+                getName(e);
+            }
+        });
+        getAccessible().addAccessibleControlListener(
+                new class AccessibleControlAdapter {
+                    public void getChildAtPoint(AccessibleControlEvent e) {
+                        Point testPoint = toControl(new Point(e.x, e.y));
+                        if (getBounds().contains(testPoint)) {
+                            e.childID = ACC.CHILDID_SELF;
+                        }
+                    }
+                    public void getLocation(AccessibleControlEvent e) {
+                        Rectangle location = getBounds();
+                        Point pt = toDisplay(new Point(location.x, location.y));
+                        e.x = pt.x;
+                        e.y = pt.y;
+                        e.width = location.width;
+                        e.height = location.height;
+                    }
+                    public void getSelection (AccessibleControlEvent e) {
+                        if (this.outer.getSelection())
+                            e.childID = ACC.CHILDID_SELF;
+                    }
+
+                    public void getFocus (AccessibleControlEvent e) {
+                        if (this.outer.getSelection())
+                            e.childID = ACC.CHILDID_SELF;
+                    }
+                    public void getChildCount(AccessibleControlEvent e) {
+                        e.detail = 0;
+                    }
+                    public void getRole(AccessibleControlEvent e) {
+                        e.detail = ACC.ROLE_TREE;
+                    }
+                    public void getState(AccessibleControlEvent e) {
+                        int state = ACC.STATE_FOCUSABLE;
+                        if (this.outer.getSelection())
+                            state |= ACC.STATE_FOCUSED;
+                        state |= this.outer.isExpanded()
+                                ? ACC.STATE_EXPANDED
+                                : ACC.STATE_COLLAPSED;
+                        e.detail = state;
+                    }
+                });
+    }
+    private void onKeyDown(Event e) {
+        if (e.keyCode is SWT.ARROW_RIGHT) {
+            // expand if collapsed
+            if (!isExpanded()) {
+                handleActivate(e);
+            }
+            e.doit=false;
+        }
+        else if (e.keyCode is SWT.ARROW_LEFT) {
+            // collapse if expanded
+            if (isExpanded()) {
+                handleActivate(e);
+            }
+            e.doit=false;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/TreeNode.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.widgets.TreeNode;
+
+import org.eclipse.ui.forms.widgets.ToggleHyperlink;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A custom selectable control that can be used to control areas that can be
+ * expanded or collapsed. The control control can be toggled between selected
+ * and deselected state with a mouse or by pressing 'Enter' while the control
+ * has focus.
+ * <p>
+ * The control is rendered as box with a '+' or '-' sign, depending on the
+ * expansion state. Focus indication is rendered around the box when the
+ * control has keyboard focus.
+ *
+ * @see Twistie
+ * @since 3.0
+ */
+public class TreeNode : ToggleHyperlink {
+    /**
+     * Creates a control in a provided composite.
+     *
+     * @param parent
+     *            the parent
+     * @param style
+     *            the style
+     */
+    public this(Composite parent, int style) {
+        super(parent, style);
+        innerWidth = 10;
+        innerHeight = 10;
+    }
+    protected void paint(PaintEvent e) {
+        paintHyperlink(e.gc);
+    }
+    protected void paintHyperlink(GC gc) {
+        Rectangle box = getBoxBounds(gc);
+        gc.setForeground(getDisplay().getSystemColor(
+                SWT.COLOR_WIDGET_NORMAL_SHADOW));
+        gc.drawRectangle(box);
+        gc.setForeground(getForeground());
+        gc.drawLine(box.x + 2, box.y + 4, box.x + 6, box.y + 4);
+        if (!isExpanded()) {
+            gc.drawLine(box.x + 4, box.y + 2, box.x + 4, box.y + 6);
+        }
+        if (paintFocus && getSelection()) {
+            gc.setForeground(getForeground());
+            gc.drawFocus(box.x - 1, box.y - 1, box.width + 3, box.height + 3);
+        }
+    }
+    private Rectangle getBoxBounds(GC gc) {
+        int x = 1;
+        int y = 0;
+        gc.setFont(getFont());
+        //int height = gc.getFontMetrics().getHeight();
+        //y = height / 2 - 4;
+        //y = Math.max(y, 0);
+        y = 2;
+        return new Rectangle(x, y, 8, 8);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/Twistie.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.forms.widgets.Twistie;
+
+import org.eclipse.ui.forms.widgets.ToggleHyperlink;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A custom selectable control that can be used to control areas that can be
+ * expanded or collapsed. The control control can be toggled between selected
+ * and deselected state with a mouse or by pressing 'Enter' while the control
+ * has focus.
+ * <p>
+ * The control is rendered as a triangle that points to the right in the
+ * collapsed and down in the expanded state. Triangle color can be changed.
+ *
+ * @see TreeNode
+ * @since 3.0
+ */
+public class Twistie : ToggleHyperlink {
+    private static const int[] onPoints = [ 0, 2, 8, 2, 4, 6 ];
+
+    private static const int[] offPoints = [ 2, -1, 2, 8, 6, 4 ];
+
+    /**
+     * Creates a control in a provided composite.
+     *
+     * @param parent
+     *            the parent
+     * @param style
+     *            the style
+     */
+    public this(Composite parent, int style) {
+        super(parent, style);
+        innerWidth = 9;
+        innerHeight = 9;
+    }
+
+    /*
+     * @see SelectableControl#paint(GC)
+     */
+    protected void paintHyperlink(GC gc) {
+        Color bg;
+        if (!isEnabled())
+            bg = getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
+        else if (hover && getHoverDecorationColor() !is null)
+            bg = getHoverDecorationColor();
+        else if (getDecorationColor() !is null)
+            bg = getDecorationColor();
+        else
+            bg = getForeground();
+        gc.setBackground(bg);
+        int[] data;
+        Point size = getSize();
+        int x = (size.x - 9) / 2;
+        int y = (size.y - 9) / 2;
+        if (isExpanded())
+            data = translate(onPoints, x, y);
+        else
+            data = translate(offPoints, x, y);
+        gc.fillPolygon(data);
+        gc.setBackground(getBackground());
+    }
+
+    private int[] translate(int[] data, int x, int y) {
+        int[] target = new int[data.length];
+        for (int i = 0; i < data.length; i += 2) {
+            target[i] = data[i] + x;
+        }
+        for (int i = 1; i < data.length; i += 2) {
+            target[i] = data[i] + y;
+        }
+        return target;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.swt.widgets.Control#setEnabled(bool)
+     */
+    public void setEnabled(bool enabled) {
+        super.setEnabled(enabled);
+        redraw();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/IMessageToolTipManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.IMessageToolTipManager;
+
+
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.forms.IMessageManager;
+import org.eclipse.ui.forms.widgets.Form;
+
+import java.lang.all;
+
+/**
+ * The classes that implement this interface are responsible for managing custom
+ * tool tips for message-related controls in the form header. By default, a
+ * simple manager is installed by the form that uses the built-in widget tool
+ * tips for this purpose. Clients can replace this behaviour with richer tool
+ * tips that show images, links and other content.
+ * <p>
+ * The message-related controls in the header are:
+ * <ul>
+ * <li>Image label - used to replace the form title image with the message type
+ * image</li>
+ * <li>Message label - renders the message as a static text.</li>
+ * <li>Message hyperlink - renders the message as a hyperlink.</li>
+ * </ul>
+ * The message manager will be asked to create the tool tip for any and all of
+ * the controls listed above in its factory method. After that, it will be asked
+ * to update whenever the message information changes in the form. For this
+ * reason, the manager is expected to retain references to the tool tips and
+ * update them with new content when asked.
+ *
+ * @see IMessageManager
+ * @see Form
+ *      <p>
+ *      <strong>EXPERIMENTAL</strong>. This class or interface has been added
+ *      as part of a work in progress. There is no guarantee that this API will
+ *      work or that it will remain the same. Please do not use this API without
+ *      consulting with the Platform UA team.
+ *      </p>
+ * @since 3.3
+ */
+public interface IMessageToolTipManager {
+    /**
+     * Creates the custom tool tip for the provided control.
+     *
+     * @param control
+     *            the control for which to create a custom tool tip
+     * @param imageControl
+     *            <code>true</code> if the control is used to render the title
+     *            image, <code>false</code> otherwise.
+     */
+    void createToolTip(Control control, bool imageControl);
+
+    /**
+     * Updates all the managed tool tips. The manager should get the current
+     * message, message type and optional children messages from the form to
+     * update the custom tool tips.
+     */
+    void update();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/MessageManager.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,672 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.MessageManager;
+
+import org.eclipse.ui.internal.forms.Messages;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.jface.fieldassist.FieldDecoration;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.ui.forms.IMessage;
+import org.eclipse.ui.forms.IMessageManager;
+import org.eclipse.ui.forms.IMessagePrefixProvider;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+
+import java.lang.all;
+import java.util.Enumeration;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+import tango.util.Convert;
+import tango.text.Text;
+import tango.io.model.IFile;
+
+/**
+ * @see IMessageManager
+ */
+
+public class MessageManager : IMessageManager {
+
+    private static DefaultPrefixProvider DEFAULT_PREFIX_PROVIDER_;
+    private static DefaultPrefixProvider DEFAULT_PREFIX_PROVIDER(){
+        if( DEFAULT_PREFIX_PROVIDER_ is null ){
+            synchronized(MessageManager.classinfo){
+                if( DEFAULT_PREFIX_PROVIDER_ is null ){
+                    DEFAULT_PREFIX_PROVIDER_ = new DefaultPrefixProvider();
+                }
+            }
+        }
+        return DEFAULT_PREFIX_PROVIDER_;
+    }
+    private ArrayList messages;
+    private Hashtable decorators;
+    private bool autoUpdate = true;
+    private ScrolledForm scrolledForm;
+    private IMessagePrefixProvider prefixProvider;
+    private int decorationPosition = SWT.LEFT | SWT.BOTTOM;
+
+    private static FieldDecoration standardError_;
+    private static FieldDecoration standardError(){
+        if( standardError_ is null ){
+            synchronized(MessageManager.classinfo){
+                if( standardError_ is null ){
+                    standardError_ = FieldDecorationRegistry
+                        .getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_ERROR);
+                }
+            }
+        }
+        return standardError_;
+    }
+
+    private static FieldDecoration standardWarning_;
+    private static FieldDecoration standardWarning(){
+        if( standardWarning_ is null ){
+            synchronized(MessageManager.classinfo){
+                if( standardWarning_ is null ){
+                    standardWarning_ = FieldDecorationRegistry
+                        .getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_WARNING);
+                }
+            }
+        }
+        return standardWarning_;
+    }
+
+    private static FieldDecoration standardInformation_;
+    private static FieldDecoration standardInformation(){
+        if( standardInformation_ is null ){
+            synchronized(MessageManager.classinfo){
+                if( standardInformation_ is null ){
+                    standardInformation_ = FieldDecorationRegistry
+                        .getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_INFORMATION);
+                }
+            }
+        }
+        return standardInformation_;
+    }
+
+    private static String[] SINGLE_MESSAGE_SUMMARY_KEYS_;
+    private static String[] SINGLE_MESSAGE_SUMMARY_KEYS(){
+        if( SINGLE_MESSAGE_SUMMARY_KEYS_ is null ){
+            synchronized(MessageManager.classinfo){
+                if( SINGLE_MESSAGE_SUMMARY_KEYS_ is null ){
+                    SINGLE_MESSAGE_SUMMARY_KEYS_ = [
+                        Messages.MessageManager_sMessageSummary,
+                        Messages.MessageManager_sMessageSummary,
+                        Messages.MessageManager_sWarningSummary,
+                        Messages.MessageManager_sErrorSummary ];
+                }
+            }
+        }
+        return SINGLE_MESSAGE_SUMMARY_KEYS_;
+    }
+
+    private static String[] MULTIPLE_MESSAGE_SUMMARY_KEYS_;
+    private static String[] MULTIPLE_MESSAGE_SUMMARY_KEYS(){
+        if( MULTIPLE_MESSAGE_SUMMARY_KEYS_ is null ){
+            synchronized(MessageManager.classinfo){
+                if( MULTIPLE_MESSAGE_SUMMARY_KEYS_ is null ){
+                    MULTIPLE_MESSAGE_SUMMARY_KEYS_ = [
+                        Messages.MessageManager_pMessageSummary,
+                        Messages.MessageManager_pMessageSummary,
+                        Messages.MessageManager_pWarningSummary,
+                        Messages.MessageManager_pErrorSummary ];
+                }
+            }
+        }
+        return MULTIPLE_MESSAGE_SUMMARY_KEYS_;
+    }
+
+    static class Message : IMessage {
+        Control control;
+        Object data;
+        Object key;
+        String message;
+        int type;
+        String prefix;
+
+        this(Object key, String message, int type, Object data) {
+            this.key = key;
+            this.message = message;
+            this.type = type;
+            this.data = data;
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.jface.dialogs.IMessage#getKey()
+         */
+        public Object getKey() {
+            return key;
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.jface.dialogs.IMessageProvider#getMessage()
+         */
+        public String getMessage() {
+            return message;
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.jface.dialogs.IMessageProvider#getMessageType()
+         */
+        public int getMessageType() {
+            return type;
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.ui.forms.messages.IMessage#getControl()
+         */
+        public Control getControl() {
+            return control;
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.ui.forms.messages.IMessage#getData()
+         */
+        public Object getData() {
+            return data;
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.ui.forms.messages.IMessage#getPrefix()
+         */
+        public String getPrefix() {
+            return prefix;
+        }
+    }
+
+    static class DefaultPrefixProvider : IMessagePrefixProvider {
+
+        public String getPrefix(Control c) {
+            Composite parent = c.getParent();
+            Control[] siblings = parent.getChildren();
+            for (int i = 0; i < siblings.length; i++) {
+                if (siblings[i] is c) {
+                    // this is us - go backward until you hit
+                    // a label-like widget
+                    for (int j = i - 1; j >= 0; j--) {
+                        Control label = siblings[j];
+                        String ltext = null;
+                        if ( auto l = cast(Label)label ) {
+                            ltext = l.getText();
+                        } else if ( auto hl = cast(Hyperlink)label ) {
+                            ltext = hl.getText();
+                        } else if ( auto cl = cast(CLabel)label ) {
+                            ltext = cl.getText();
+                        }
+                        if (ltext !is null) {
+                            if (!ltext.endsWith(":")) //$NON-NLS-1$
+                                return ltext ~ ": "; //$NON-NLS-1$
+                            return ltext ~ " "; //$NON-NLS-1$
+                        }
+                    }
+                    break;
+                }
+            }
+            return null;
+        }
+    }
+
+    class ControlDecorator {
+        private ControlDecoration decoration;
+        private ArrayList controlMessages;
+        private String prefix;
+
+        this(Control control) {
+            controlMessages = new ArrayList();
+            this.decoration = new ControlDecoration(control, decorationPosition, scrolledForm.getBody());
+        }
+
+        public bool isDisposed() {
+            return decoration.getControl() is null;
+        }
+
+        void updatePrefix() {
+            prefix = null;
+        }
+
+        void updatePosition() {
+            Control control = decoration.getControl();
+            decoration.dispose();
+            this.decoration = new ControlDecoration(control, decorationPosition, scrolledForm.getBody());
+            update();
+        }
+
+        String getPrefix() {
+            if (prefix is null)
+                createPrefix();
+            return prefix;
+        }
+
+        private void createPrefix() {
+            if (prefixProvider is null) {
+                prefix = ""; //$NON-NLS-1$
+                return;
+            }
+            prefix = prefixProvider.getPrefix(decoration.getControl());
+            if (prefix is null)
+                // make a prefix anyway
+                prefix = ""; //$NON-NLS-1$
+        }
+
+        void addAll(ArrayList target) {
+            target.addAll(controlMessages);
+        }
+
+        void addMessage(Object key, String text, Object data, int type) {
+            Message message = this.outer.addMessage(getPrefix(), key,
+                    text, data, type, controlMessages);
+            message.control = decoration.getControl();
+            if (isAutoUpdate())
+                update();
+        }
+
+        bool removeMessage(Object key) {
+            Message message = findMessage(key, controlMessages);
+            if (message !is null) {
+                controlMessages.remove(message);
+                if (isAutoUpdate())
+                    update();
+            }
+            return message !is null;
+        }
+
+        bool removeMessages() {
+            if (controlMessages.isEmpty())
+                return false;
+            controlMessages.clear();
+            if (isAutoUpdate())
+                update();
+            return true;
+        }
+
+        public void update() {
+            if (controlMessages.isEmpty()) {
+                decoration.setDescriptionText(null);
+                decoration.hide();
+            } else {
+                ArrayList peers = createPeers(controlMessages);
+                int type = (cast(IMessage) peers.get(0)).getMessageType();
+                String description = createDetails(createPeers(peers), true);
+                if (type is IMessageProvider.ERROR)
+                    decoration.setImage(standardError.getImage());
+                else if (type is IMessageProvider.WARNING)
+                    decoration.setImage(standardWarning.getImage());
+                else if (type is IMessageProvider.INFORMATION)
+                    decoration.setImage(standardInformation.getImage());
+                decoration.setDescriptionText(description);
+                decoration.show();
+            }
+        }
+    }
+
+    /**
+     * Creates a new instance of the message manager that will work with the
+     * provided form.
+     *
+     * @param scrolledForm
+     *            the form to control
+     */
+    public this(ScrolledForm scrolledForm) {
+        prefixProvider = DEFAULT_PREFIX_PROVIDER;
+        messages = new ArrayList();
+        decorators = new Hashtable();
+        this.scrolledForm = scrolledForm;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageManager#addMessage(java.lang.Object,
+     *      java.lang.String, int)
+     */
+    public void addMessage(Object key, String messageText, Object data, int type) {
+        addMessage(null, key, messageText, data, type, messages);
+        if (isAutoUpdate())
+            updateForm();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageManager#addMessage(java.lang.Object,
+     *      java.lang.String, int, org.eclipse.swt.widgets.Control)
+     */
+    public void addMessage(Object key, String messageText, Object data,
+            int type, Control control) {
+        ControlDecorator dec = cast(ControlDecorator) decorators.get(control);
+
+        if (dec is null) {
+            dec = new ControlDecorator(control);
+            decorators.put(control, dec);
+        }
+        dec.addMessage(key, messageText, data, type);
+        if (isAutoUpdate())
+            updateForm();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageManager#removeMessage(java.lang.Object)
+     */
+    public void removeMessage(Object key) {
+        Message message = findMessage(key, messages);
+        if (message !is null) {
+            messages.remove(message);
+            if (isAutoUpdate())
+                updateForm();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageManager#removeMessages()
+     */
+    public void removeMessages() {
+        if (!messages.isEmpty()) {
+            messages.clear();
+            if (isAutoUpdate())
+                updateForm();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageManager#removeMessage(java.lang.Object,
+     *      org.eclipse.swt.widgets.Control)
+     */
+    public void removeMessage(Object key, Control control) {
+        ControlDecorator dec = cast(ControlDecorator) decorators.get(control);
+        if (dec is null)
+            return;
+        if (dec.removeMessage(key))
+            if (isAutoUpdate())
+                updateForm();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageManager#removeMessages(org.eclipse.swt.widgets.Control)
+     */
+    public void removeMessages(Control control) {
+        ControlDecorator dec = cast(ControlDecorator) decorators.get(control);
+        if (dec !is null) {
+            if (dec.removeMessages()) {
+                if (isAutoUpdate())
+                    updateForm();
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageManager#removeAllMessages()
+     */
+    public void removeAllMessages() {
+        bool needsUpdate = false;
+        for (Enumeration enm = decorators.elements(); enm.hasMoreElements();) {
+            ControlDecorator control = cast(ControlDecorator) enm.nextElement();
+            if (control.removeMessages())
+                needsUpdate = true;
+        }
+        if (!messages.isEmpty()) {
+            messages.clear();
+            needsUpdate = true;
+        }
+        if (needsUpdate && isAutoUpdate())
+            updateForm();
+    }
+
+    /*
+     * Adds the message if it does not already exist in the provided list.
+     */
+
+    private Message addMessage(String prefix, Object key, String messageText,
+            Object data, int type, ArrayList list) {
+        Message message = findMessage(key, list);
+        if (message is null) {
+            message = new Message(key, messageText, type, data);
+            message.prefix = prefix;
+            list.add(message);
+        } else {
+            message.message = messageText;
+            message.type = type;
+            message.data = data;
+        }
+        return message;
+    }
+
+    /*
+     * Finds the message with the provided key in the provided list.
+     */
+
+    private Message findMessage(Object key, ArrayList list) {
+        for (int i = 0; i < list.size(); i++) {
+            Message message = cast(Message) list.get(i);
+            if (message.getKey().opEquals(key))
+                return message;
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageManager#update()
+     */
+    public void update() {
+        // Update decorations
+        for (Iterator iter = decorators.values().iterator(); iter.hasNext();) {
+            ControlDecorator dec = cast(ControlDecorator) iter.next();
+            dec.update();
+        }
+        // Update the form
+        updateForm();
+    }
+
+    /*
+     * Updates the container by rolling the messages up from the controls.
+     */
+
+    private void updateForm() {
+        ArrayList mergedList = new ArrayList();
+        mergedList.addAll(messages);
+        for (Enumeration enm = decorators.elements(); enm.hasMoreElements();) {
+            ControlDecorator dec = cast(ControlDecorator) enm.nextElement();
+            dec.addAll(mergedList);
+        }
+        update(mergedList);
+    }
+
+    private void update(ArrayList mergedList) {
+        pruneControlDecorators();
+        if (scrolledForm.getForm().getHead().getBounds().height is 0 || mergedList.isEmpty() || mergedList is null) {
+            scrolledForm.setMessage(null, IMessageProvider.NONE);
+            return;
+        }
+        ArrayList peers = createPeers(mergedList);
+        int maxType = (cast(IMessage) peers.get(0)).getMessageType();
+        String messageText;
+        IMessage[] array = arraycast!(IMessage)( peers
+                .toArray());
+        if (peers.size() is 1 && (cast(Message) peers.get(0)).prefix is null) {
+            // a single message
+            IMessage message = cast(IMessage) peers.get(0);
+            messageText = message.getMessage();
+            scrolledForm.setMessage(messageText, maxType, array);
+        } else {
+            // show a summary message for the message
+            // and list of errors for the details
+            if (peers.size() > 1)
+                messageText = Messages.bind(
+                        MULTIPLE_MESSAGE_SUMMARY_KEYS[maxType],
+                        [ to!(String)(peers.size()) ]); //$NON-NLS-1$
+            else
+                messageText = SINGLE_MESSAGE_SUMMARY_KEYS[maxType];
+            scrolledForm.setMessage(messageText, maxType, array);
+        }
+    }
+
+    private static String getFullMessage(IMessage message) {
+        if (message.getPrefix() is null)
+            return message.getMessage();
+        return message.getPrefix() ~ message.getMessage();
+    }
+
+    private ArrayList createPeers(ArrayList messages) {
+        ArrayList peers = new ArrayList();
+        int maxType = 0;
+        for (int i = 0; i < messages.size(); i++) {
+            Message message = cast(Message) messages.get(i);
+            if (message.type > maxType) {
+                peers.clear();
+                maxType = message.type;
+            }
+            if (message.type is maxType)
+                peers.add(message);
+        }
+        return peers;
+    }
+
+    private String createDetails(ArrayList messages, bool excludePrefix) {
+        auto txt = new tango.text.Text.Text!(char);
+
+        for (int i = 0; i < messages.size(); i++) {
+            if (i > 0)
+                txt.append( FileConst.NewlineString );
+            IMessage m = cast(IMessage) messages.get(i);
+            txt.append(excludePrefix ? m.getMessage() : getFullMessage(m));
+        }
+        return txt.toString();
+    }
+
+    public static String createDetails(IMessage[] messages) {
+        if (messages is null || messages.length is 0)
+            return null;
+        auto txt = new tango.text.Text.Text!(char);
+
+        for (int i = 0; i < messages.length; i++) {
+            if (i > 0)
+                txt.append( FileConst.NewlineString );
+            txt.append(getFullMessage(messages[i]));
+        }
+        return txt.toString();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageManager#createSummary(org.eclipse.ui.forms.IMessage[])
+     */
+    public String createSummary(IMessage[] messages) {
+        return createDetails(messages);
+    }
+
+    private void pruneControlDecorators() {
+        for (Iterator iter = decorators.values().iterator(); iter.hasNext();) {
+            ControlDecorator dec = cast(ControlDecorator) iter.next();
+            if (dec.isDisposed())
+                iter.remove();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageManager#getMessagePrefixProvider()
+     */
+    public IMessagePrefixProvider getMessagePrefixProvider() {
+        return prefixProvider;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageManager#setMessagePrefixProvider(org.eclipse.ui.forms.IMessagePrefixProvider)
+     */
+    public void setMessagePrefixProvider(IMessagePrefixProvider provider) {
+        this.prefixProvider = provider;
+        for (Iterator iter = decorators.values().iterator(); iter.hasNext();) {
+            ControlDecorator dec = cast(ControlDecorator) iter.next();
+            dec.updatePrefix();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageManager#getDecorationPosition()
+     */
+    public int getDecorationPosition() {
+        return decorationPosition;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageManager#setDecorationPosition(int)
+     */
+    public void setDecorationPosition(int position) {
+        this.decorationPosition = position;
+        for (Iterator iter = decorators.values().iterator(); iter.hasNext();) {
+            ControlDecorator dec = cast(ControlDecorator) iter.next();
+            dec.updatePosition();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageManager#isAutoUpdate()
+     */
+    public bool isAutoUpdate() {
+        return autoUpdate;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.forms.IMessageManager#setAutoUpdate(bool)
+     */
+    public void setAutoUpdate(bool autoUpdate) {
+        bool needsUpdate = !this.autoUpdate && autoUpdate;
+        this.autoUpdate = autoUpdate;
+        if (needsUpdate)
+            update();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/Messages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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 org.eclipse.ui.internal.forms.Messages;
+
+import java.lang.all;
+
+public class Messages /*extends NLS*/ {
+    private static const String BUNDLE_NAME = "org.eclipse.ui.internal.forms.Messages"; //$NON-NLS-1$
+
+//     private const() {
+//     }
+
+//     static {
+        // initialize resource bundle
+//         NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+//     }
+
+    public static String FormDialog_defaultTitle;
+    public static String FormText_copy;
+    public static String Form_tooltip_minimize;
+    public static String Form_tooltip_restore;
+    /*
+     * Message manager
+     */
+    public static String MessageManager_sMessageSummary;
+    public static String MessageManager_sWarningSummary;
+    public static String MessageManager_sErrorSummary;
+    public static String MessageManager_pMessageSummary;
+    public static String MessageManager_pWarningSummary;
+    public static String MessageManager_pErrorSummary;
+    public static String ToggleHyperlink_accessibleColumn;
+    public static String ToggleHyperlink_accessibleName;
+    public static String bind(String string, String[] strings) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/AggregateHyperlinkSegment.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.AggregateHyperlinkSegment;
+
+import org.eclipse.ui.internal.forms.widgets.ParagraphSegment;
+import org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment;
+import org.eclipse.ui.internal.forms.widgets.TextHyperlinkSegment;
+import org.eclipse.ui.internal.forms.widgets.ImageHyperlinkSegment;
+import org.eclipse.ui.internal.forms.widgets.Locator;
+import org.eclipse.ui.internal.forms.widgets.SelectionData;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * This segment contains a collection of images and links that all belong to one
+ * logical hyperlink.
+ */
+public class AggregateHyperlinkSegment : ParagraphSegment,
+        IHyperlinkSegment {
+    private String href;
+
+    private Vector segments;
+
+    public this() {
+        segments = new Vector();
+    }
+
+    public void add(TextHyperlinkSegment segment) {
+        segments.add(segment);
+    }
+
+    public void add(ImageHyperlinkSegment segment) {
+        segments.add(segment);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.internal.forms.widgets.ParagraphSegment#advanceLocator(org.eclipse.swt.graphics.GC,
+     *      int, org.eclipse.ui.internal.forms.widgets.Locator,
+     *      java.util.Hashtable, bool)
+     */
+    public bool advanceLocator(GC gc, int wHint, Locator loc,
+            Hashtable objectTable, bool computeHeightOnly) {
+        bool newLine = false;
+        for (int i = 0; i < segments.size(); i++) {
+            ParagraphSegment segment = cast(ParagraphSegment) segments.get(i);
+            if (segment.advanceLocator(gc, wHint, loc, objectTable,
+                    computeHeightOnly))
+                newLine = true;
+        }
+        return newLine;
+    }
+
+    /**
+     * @return Returns the href.
+     */
+    public String getHref() {
+        return href;
+    }
+
+    /**
+     * @param href
+     *            The href to set.
+     */
+    public void setHref(String href) {
+        this.href = href;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment#repaint(org.eclipse.swt.graphics.GC,
+     *      bool)
+     */
+    public void paint(GC gc, bool hover, Hashtable resourceTable,
+            bool selected, SelectionData selData, Rectangle repaintRegion) {
+        for (int i = 0; i < segments.size(); i++) {
+            ParagraphSegment segment = cast(ParagraphSegment) segments.get(i);
+            segment.paint(gc, hover, resourceTable, selected, selData,
+                    repaintRegion);
+        }
+    }
+
+    public String getText() {
+        StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < segments.size(); i++) {
+            IHyperlinkSegment segment = cast(IHyperlinkSegment) segments.get(i);
+            buf.append(segment.getText());
+        }
+        return buf.toString();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment#paintFocus(org.eclipse.swt.graphics.GC,
+     *      org.eclipse.swt.graphics.Color, org.eclipse.swt.graphics.Color,
+     *      bool)
+     */
+    public void paintFocus(GC gc, Color bg, Color fg, bool selected,
+            Rectangle repaintRegion) {
+        for (int i = 0; i < segments.size(); i++) {
+            IHyperlinkSegment segment = cast(IHyperlinkSegment) segments.get(i);
+            segment.paintFocus(gc, bg, fg, selected, repaintRegion);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment#getBounds()
+     */
+    public Rectangle getBounds() {
+        Rectangle bounds = new Rectangle(Integer.MAX_VALUE, Integer.MAX_VALUE,
+                0, 0);
+        // TODO this is wrong
+        for (int i = 0; i < segments.size(); i++) {
+            IHyperlinkSegment segment = cast(IHyperlinkSegment) segments.get(i);
+            Rectangle sbounds = segment.getBounds();
+            bounds.x = Math.min(bounds.x, sbounds.x);
+            bounds.y = Math.min(bounds.y, sbounds.y);
+            bounds.width = Math.max(bounds.width, sbounds.width);
+            bounds.height = Math.max(bounds.height, sbounds.height);
+        }
+        return bounds;
+    }
+
+    public bool contains(int x, int y) {
+        for (int i = 0; i < segments.size(); i++) {
+            IHyperlinkSegment segment = cast(IHyperlinkSegment) segments.get(i);
+            if (segment.contains(x, y))
+                return true;
+        }
+        return false;
+    }
+
+    public bool intersects(Rectangle rect) {
+        for (int i = 0; i < segments.size(); i++) {
+            IHyperlinkSegment segment = cast(IHyperlinkSegment) segments.get(i);
+            if (segment.intersects(rect))
+                return true;
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.internal.forms.widgets.ParagraphSegment#layout(org.eclipse.swt.graphics.GC,
+     *      int, org.eclipse.ui.internal.forms.widgets.Locator,
+     *      java.util.Hashtable, bool,
+     *      org.eclipse.ui.internal.forms.widgets.SelectionData)
+     */
+    public void layout(GC gc, int width, Locator locator,
+            Hashtable resourceTable, bool selected) {
+        for (int i = 0; i < segments.size(); i++) {
+            ParagraphSegment segment = cast(ParagraphSegment) segments.get(i);
+            segment.layout(gc, width, locator, resourceTable, selected);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.internal.forms.widgets.ParagraphSegment#computeSelection(org.eclipse.swt.graphics.GC,
+     *      java.util.Hashtable, bool,
+     *      org.eclipse.ui.internal.forms.widgets.SelectionData)
+     */
+    public void computeSelection(GC gc, Hashtable resourceTable,
+            SelectionData selData) {
+        for (int i = 0; i < segments.size(); i++) {
+            ParagraphSegment segment = cast(ParagraphSegment) segments.get(i);
+            segment.computeSelection(gc, resourceTable, selData);
+        }
+    }
+
+    public void clearCache(String fontId) {
+        for (int i = 0; i < segments.size(); i++) {
+            ParagraphSegment segment = cast(ParagraphSegment) segments.get(i);
+            segment.clearCache(fontId);
+        }
+    }
+
+    public String getTooltipText() {
+        if (segments.size() > 0)
+            return (cast(ParagraphSegment) segments.get(0)).getTooltipText();
+        return super.getTooltipText();
+    }
+
+    public bool isFocusSelectable(Hashtable resourceTable) {
+        return true;
+    }
+
+    public bool setFocus(Hashtable resourceTable, bool direction) {
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/BreakSegment.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.ui.internal.forms.widgets.BreakSegment;
+
+import org.eclipse.ui.internal.forms.widgets.ParagraphSegment;
+import org.eclipse.ui.internal.forms.widgets.Locator;
+import org.eclipse.ui.internal.forms.widgets.SelectionData;
+
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * This segment serves as break within a paragraph. It has no data -
+ * just starts a new line and resets the locator.
+ */
+
+public class BreakSegment : ParagraphSegment {
+    /* (non-Javadoc)
+     * @see org.eclipse.ui.forms.internal.widgets.ParagraphSegment#advanceLocator(org.eclipse.swt.graphics.GC, int, org.eclipse.ui.forms.internal.widgets.Locator, java.util.Hashtable)
+     */
+    public bool advanceLocator(GC gc, int wHint, Locator locator,
+            Hashtable objectTable, bool computeHeightOnly) {
+        if (locator.rowHeight is 0) {
+            FontMetrics fm = gc.getFontMetrics();
+            locator.rowHeight = fm.getHeight();
+        }
+        if (computeHeightOnly) locator.collectHeights();
+        locator.x = locator.indent;
+        locator.y += locator.rowHeight;
+        locator.rowHeight = 0;
+        locator.leading = 0;
+        return true;
+    }
+
+    public void paint(GC gc, bool hover, Hashtable resourceTable, bool selected, SelectionData selData, Rectangle repaintRegion) {
+        //nothing to paint
+    }
+    public bool contains(int x, int y) {
+        return false;
+    }
+    public bool intersects(Rectangle rect) {
+        return false;
+    }
+    /* (non-Javadoc)
+     * @see org.eclipse.ui.internal.forms.widgets.ParagraphSegment#layout(org.eclipse.swt.graphics.GC, int, org.eclipse.ui.internal.forms.widgets.Locator, java.util.Hashtable, bool, org.eclipse.ui.internal.forms.widgets.SelectionData)
+     */
+    public void layout(GC gc, int width, Locator locator, Hashtable ResourceTable,
+            bool selected) {
+        locator.resetCaret();
+        if (locator.rowHeight is 0) {
+            FontMetrics fm = gc.getFontMetrics();
+            locator.rowHeight = fm.getHeight();
+        }
+        locator.y += locator.rowHeight;
+        locator.rowHeight = 0;
+        locator.rowCounter++;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.ui.internal.forms.widgets.ParagraphSegment#computeSelection(org.eclipse.swt.graphics.GC, java.util.Hashtable, bool, org.eclipse.ui.internal.forms.widgets.SelectionData)
+     */
+    public void computeSelection(GC gc, Hashtable resourceTable, SelectionData selData) {
+        selData.markNewLine();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/BulletParagraph.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.BulletParagraph;
+
+import org.eclipse.ui.internal.forms.widgets.Paragraph;
+import org.eclipse.ui.internal.forms.widgets.Locator;
+import org.eclipse.ui.internal.forms.widgets.SelectionData;
+import org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+
+import java.lang.all;
+import java.util.Set;
+
+public class BulletParagraph : Paragraph {
+    public static const int CIRCLE = 1;
+
+    public static const int TEXT = 2;
+
+    public static const int IMAGE = 3;
+
+    private int style = CIRCLE;
+
+    private String text;
+
+    private int CIRCLE_DIAM = 5;
+
+    private int SPACING = 10;
+
+    private int indent = -1;
+
+    private int bindent = -1;
+
+    private Rectangle bbounds;
+
+    /**
+     * Constructor for BulletParagraph.
+     *
+     * @param addVerticalSpace
+     */
+    public this(bool addVerticalSpace) {
+        super(addVerticalSpace);
+    }
+
+    public int getIndent() {
+        int ivalue = indent;
+        if (ivalue !is -1)
+            return ivalue;
+        switch (style) {
+        case CIRCLE:
+            ivalue = CIRCLE_DIAM + SPACING;
+            break;
+        default:
+            ivalue = 20;
+            break;
+        }
+        return getBulletIndent() + ivalue;
+    }
+
+    public int getBulletIndent() {
+        if (bindent !is -1)
+            return bindent;
+        return 0;
+    }
+
+    /*
+     * @see IBulletParagraph#getBulletStyle()
+     */
+    public int getBulletStyle() {
+        return style;
+    }
+
+    public void setBulletStyle(int style) {
+        this.style = style;
+    }
+
+    public void setBulletText(String text) {
+        this.text = text;
+    }
+
+    public void setIndent(int indent) {
+        this.indent = indent;
+    }
+
+    public void setBulletIndent(int bindent) {
+        this.bindent = bindent;
+    }
+
+    /*
+     * @see IBulletParagraph#getBulletText()
+     */
+    public String getBulletText() {
+        return text;
+    }
+
+    public void layout(GC gc, int width, Locator loc, int lineHeight,
+            Hashtable resourceTable, IHyperlinkSegment selectedLink) {
+        computeRowHeights(gc, width, loc, lineHeight, resourceTable);
+        layoutBullet(gc, loc, lineHeight, resourceTable);
+        super.layout(gc, width, loc, lineHeight, resourceTable, selectedLink);
+    }
+
+    public void paint(GC gc, Rectangle repaintRegion,
+            Hashtable resourceTable, IHyperlinkSegment selectedLink,
+            SelectionData selData) {
+        paintBullet(gc, repaintRegion, resourceTable);
+        super.paint(gc, repaintRegion, resourceTable, selectedLink, selData);
+    }
+
+    private void layoutBullet(GC gc, Locator loc, int lineHeight,
+            Hashtable resourceTable) {
+        int x = loc.x - getIndent() + getBulletIndent();
+        int rowHeight = (cast(ArrayWrapperInt) loc.heights.get(0)).array[0];
+        if (style is CIRCLE) {
+            int y = loc.y + rowHeight / 2 - CIRCLE_DIAM / 2;
+            bbounds = new Rectangle(x, y, CIRCLE_DIAM, CIRCLE_DIAM);
+        } else if (style is TEXT && text !is null) {
+            //int height = gc.getFontMetrics().getHeight();
+            Point textSize = gc.textExtent(text);
+            bbounds = new Rectangle(x, loc.y, textSize.x, textSize.y);
+        } else if (style is IMAGE && text !is null) {
+            Image image = cast(Image) resourceTable.get(text);
+            if (image !is null) {
+                Rectangle ibounds = image.getBounds();
+                int y = loc.y + rowHeight / 2 - ibounds.height / 2;
+                bbounds = new Rectangle(x, y, ibounds.width, ibounds.height);
+            }
+        }
+    }
+
+    public void paintBullet(GC gc, Rectangle repaintRegion,
+            Hashtable resourceTable) {
+        if (bbounds is null)
+            return;
+        int x = bbounds.x;
+        int y = bbounds.y;
+        if (repaintRegion !is null) {
+            x -= repaintRegion.x;
+            y -= repaintRegion.y;
+        }
+        if (style is CIRCLE) {
+            Color bg = gc.getBackground();
+            Color fg = gc.getForeground();
+            gc.setBackground(fg);
+            gc.fillRectangle(x, y + 1, 5, 3);
+            gc.fillRectangle(x + 1, y, 3, 5);
+            gc.setBackground(bg);
+        } else if (style is TEXT && text !is null) {
+            gc.drawText(text, x, y);
+        } else if (style is IMAGE && text !is null) {
+            Image image = cast(Image) resourceTable.get(text);
+            if (image !is null)
+                gc.drawImage(image, x, y);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/BusyIndicator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,339 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *     Stefan Mucke - fix for Bug 156456
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.ui.internal.forms.widgets.BusyIndicator;
+
+import org.eclipse.ui.internal.forms.widgets.FormUtil;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+// import org.eclipse.core.runtime.FileLocator;
+// import org.eclipse.core.runtime.Path;
+// import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import java.lang.all;
+import java.util.Set;
+
+import tango.util.Convert;
+import java.lang.JThread;
+
+public final class BusyIndicator : Canvas {
+
+    alias Canvas.computeSize computeSize;
+
+    class BusyThread : JThread {
+        Rectangle bounds;
+        Display display;
+        GC offScreenImageGC;
+        Image offScreenImage;
+        Image timage;
+        bool stop;
+
+        private this(Rectangle bounds, Display display, GC offScreenImageGC, Image offScreenImage) {
+            this.bounds = bounds;
+            this.display = display;
+            this.offScreenImageGC = offScreenImageGC;
+            this.offScreenImage = offScreenImage;
+        }
+
+        public override void run() {
+            try {
+                /*
+                 * Create an off-screen image to draw on, and fill it with
+                 * the shell background.
+                 */
+                FormUtil.setAntialias(offScreenImageGC, SWT.ON);
+                display.syncExec(dgRunnable( {
+                        if (!isDisposed())
+                            drawBackground(offScreenImageGC, 0, 0,
+                                    bounds.width,
+                                    bounds.height);
+                }));
+                if (isDisposed())
+                    return;
+
+                /*
+                 * Create the first image and draw it on the off-screen
+                 * image.
+                 */
+                int imageDataIndex = 0;
+                ImageData imageData;
+                synchronized (this.outer) {
+                    timage = getImage(imageDataIndex);
+                    imageData = timage.getImageData();
+                    offScreenImageGC.drawImage(timage, 0, 0,
+                            imageData.width, imageData.height, imageData.x,
+                            imageData.y, imageData.width, imageData.height);
+                }
+
+                /*
+                 * Now loop through the images, creating and drawing
+                 * each one on the off-screen image before drawing it on
+                 * the shell.
+                 */
+                while (!stop && !isDisposed() && timage !is null) {
+
+                    /*
+                     * Fill with the background color before
+                     * drawing.
+                     */
+                    display.syncExec(dgRunnable( (ImageData fimageData){
+                        if (!isDisposed()) {
+                            drawBackground(offScreenImageGC, fimageData.x,
+                                    fimageData.y, fimageData.width,
+                                    fimageData.height);
+                        }
+                    }, imageData ));
+
+                    synchronized (this.outer) {
+                        imageDataIndex = (imageDataIndex + 1) % IMAGE_COUNT;
+                        timage = getImage(imageDataIndex);
+                        imageData = timage.getImageData();
+                        offScreenImageGC.drawImage(timage, 0, 0,
+                                imageData.width, imageData.height,
+                                imageData.x, imageData.y, imageData.width,
+                                imageData.height);
+                    }
+
+                    /* Draw the off-screen image to the shell. */
+                    animationImage = offScreenImage;
+                    display.syncExec(dgRunnable( {
+                        if (!isDisposed())
+                            redraw();
+                    }));
+                    /*
+                     * Sleep for the specified delay time
+                     */
+                    try {
+                        JThread.sleep(MILLISECONDS_OF_DELAY);
+                    } catch (InterruptedException e) {
+                        ExceptionPrintStackTrace(e);
+                    }
+
+
+                }
+            } catch (Exception e) {
+            } finally {
+                display.syncExec(dgRunnable( {
+                    if (offScreenImage !is null
+                            && !offScreenImage.isDisposed())
+                        offScreenImage.dispose();
+                    if (offScreenImageGC !is null
+                            && !offScreenImageGC.isDisposed())
+                        offScreenImageGC.dispose();
+                }));
+                clearImages();
+            }
+            if (busyThread is null)
+                display.syncExec(dgRunnable( {
+                    animationImage = null;
+                    if (!isDisposed())
+                        redraw();
+                }));
+        }
+
+        public void setStop(bool stop) {
+            this.stop = stop;
+        }
+    }
+
+    private static const int MARGIN = 0;
+    private static const int IMAGE_COUNT = 8;
+    private static const int MILLISECONDS_OF_DELAY = 180;
+    private Image[] imageCache;
+    protected Image image;
+
+    protected Image animationImage;
+
+    protected BusyThread busyThread;
+
+    /**
+     * BusyWidget constructor comment.
+     *
+     * @param parent
+     *            org.eclipse.swt.widgets.Composite
+     * @param style
+     *            int
+     */
+    public this(Composite parent, int style) {
+        super(parent, style);
+
+        addPaintListener(new class PaintListener {
+            public void paintControl(PaintEvent event) {
+                onPaint(event);
+            }
+        });
+    }
+
+    public Point computeSize(int wHint, int hHint, bool changed) {
+        Point size = new Point(0, 0);
+        if (image !is null) {
+            Rectangle ibounds = image.getBounds();
+            size.x = ibounds.width;
+            size.y = ibounds.height;
+        }
+        if (isBusy()) {
+            Rectangle bounds = getImage(0).getBounds();
+            size.x = Math.max(size.x, bounds.width);
+            size.y = Math.max(size.y, bounds.height);
+        }
+        size.x += MARGIN + MARGIN;
+        size.y += MARGIN + MARGIN;
+        return size;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.swt.widgets.Control#forceFocus()
+     */
+    public bool forceFocus() {
+        return false;
+    }
+
+    /**
+     * Creates a thread to animate the image.
+     */
+    protected synchronized void createBusyThread() {
+        if (busyThread !is null)
+            return;
+
+        Rectangle bounds = getImage(0).getBounds();
+        Display display = getDisplay();
+        Image offScreenImage = new Image(display, bounds.width, bounds.height);
+        GC offScreenImageGC = new GC(offScreenImage);
+        busyThread = new BusyThread(bounds, display, offScreenImageGC, offScreenImage);
+        busyThread.setPriority(JThread.NORM_PRIORITY + 2);
+        busyThread.setDaemon(true);
+        busyThread.start();
+    }
+
+    public void dispose() {
+        if (busyThread !is null) {
+            busyThread.setStop(true);
+            busyThread = null;
+        }
+        super.dispose();
+    }
+
+    /**
+     * Return the image or <code>null</code>.
+     */
+    public Image getImage() {
+        return image;
+    }
+
+    /**
+     * Returns true if it is currently busy.
+     *
+     * @return bool
+     */
+    public bool isBusy() {
+        return (busyThread !is null);
+    }
+
+    /*
+     * Process the paint event
+     */
+    protected void onPaint(PaintEvent event) {
+        if (animationImage !is null && animationImage.isDisposed()) {
+            animationImage = null;
+        }
+        Rectangle rect = getClientArea();
+        if (rect.width is 0 || rect.height is 0)
+            return;
+
+        GC gc = event.gc;
+        Image activeImage = animationImage !is null ? animationImage : image;
+        if (activeImage !is null) {
+            Rectangle ibounds = activeImage.getBounds();
+            gc.drawImage(activeImage, rect.width / 2 - ibounds.width / 2,
+                    rect.height / 2 - ibounds.height / 2);
+        }
+    }
+
+    /**
+     * Sets the indicators busy count up (true) or down (false) one.
+     *
+     * @param busy
+     *            bool
+     */
+    public synchronized void setBusy(bool busy) {
+        if (busy) {
+            if (busyThread is null)
+                createBusyThread();
+        } else {
+            if (busyThread !is null) {
+                busyThread.setStop(true);
+                busyThread = null;
+            }
+        }
+    }
+
+    /**
+     * Set the image. The value <code>null</code> clears it.
+     */
+    public void setImage(Image image) {
+        if (image !is this.image && !isDisposed()) {
+            this.image = image;
+            redraw();
+        }
+    }
+
+
+    private ImageDescriptor createImageDescriptor(String relativePath) {
+//         Bundle bundle = Platform.getBundle("org.eclipse.ui.forms"); //$NON-NLS-1$
+//         URL url = FileLocator.find(bundle, new Path(relativePath),null);
+//         if (url is null) return null;
+//         try {
+//             url = FileLocator.resolve(url);
+//             return ImageDescriptor.createFromURL(url);
+//         } catch (IOException e) {
+//             return null;
+//         }
+        return null;
+    }
+
+    private synchronized Image getImage(int index) {
+        if (imageCache is null) {
+            imageCache = new Image[IMAGE_COUNT];
+        }
+        if (imageCache[index] is null){
+            ImageDescriptor descriptor = createImageDescriptor("$nl$/icons/progress/ani/" ~ to!(String)(index + 1) ~ ".png"); //$NON-NLS-1$ //$NON-NLS-2$
+            imageCache[index] = descriptor.createImage();
+        }
+        return imageCache[index];
+    }
+
+    private synchronized void clearImages() {
+        if (busyThread !is null)
+            return;
+        if (imageCache !is null) {
+            for (int index = 0; index < IMAGE_COUNT; index++) {
+                if (imageCache[index] !is null && !imageCache[index].isDisposed()) {
+                    imageCache[index].dispose();
+                    imageCache[index] = null;
+                }
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/ControlSegment.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 org.eclipse.ui.internal.forms.widgets.ControlSegment;
+
+import org.eclipse.ui.internal.forms.widgets.ObjectSegment;
+import org.eclipse.ui.internal.forms.widgets.IFocusSelectable;
+import org.eclipse.ui.internal.forms.widgets.Locator;
+import org.eclipse.ui.internal.forms.widgets.FormUtil;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+import java.lang.all;
+import java.util.Set;
+
+public class ControlSegment : ObjectSegment, IFocusSelectable {
+
+
+    private bool fill;
+    private int width = SWT.DEFAULT;
+    private int height = SWT.DEFAULT;
+
+    // reimpl for interface
+    Rectangle getBounds(){
+        return super.getBounds();
+    }
+
+    public this() {
+    }
+
+    public void setFill(bool fill) {
+        this.fill = fill;
+    }
+
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    public void setHeight(int height) {
+        this.height = height;
+    }
+
+    public Control getControl(Hashtable resourceTable) {
+        Object obj = resourceTable.get(getObjectId());
+        if ( auto c = cast(Control)obj ) {
+            if (!c.isDisposed())
+                return c;
+        }
+        return null;
+    }
+
+    protected Point getObjectSize(Hashtable resourceTable, int wHint) {
+        Control control = getControl(resourceTable);
+        if (control is null)
+            return new Point(0,0);
+        int realWhint = FormUtil.getWidthHint(wHint, control);
+        Point size = control.computeSize(realWhint, SWT.DEFAULT);
+        if (realWhint !is SWT.DEFAULT && fill)
+            size.x = Math.max(size.x, realWhint);
+        if (width !is SWT.DEFAULT)
+            size.x = width;
+        if (height !is SWT.DEFAULT)
+            size.y = height;
+        return size;
+    }
+
+    public void layout(GC gc, int width, Locator loc, Hashtable resourceTable,
+            bool selected) {
+        super.layout(gc, width, loc, resourceTable, selected);
+        Control control = getControl(resourceTable);
+        if (control !is null)
+            control.setBounds(getBounds());
+    }
+
+    public bool setFocus(Hashtable resourceTable, bool next) {
+        Control c = getControl(resourceTable);
+        if (c !is null) {
+            return setFocus(c, next);
+        }
+        return false;
+    }
+
+    private bool setFocus(Control c, bool direction) {
+        if (auto comp = cast(Composite)c ) {
+            Control [] tabList = comp.getTabList();
+            if (direction) {
+                for (int i=0; i<tabList.length; i++) {
+                    if (setFocus(tabList[i], direction))
+                        return true;
+                }
+            }
+            else {
+                for (int i=tabList.length-1; i>=0; i--) {
+                    if (setFocus(tabList[i], direction))
+                        return true;
+                }
+            }
+            if (!(null !is cast(Canvas)c ))
+                return false;
+        }
+        return c.setFocus();
+    }
+
+    public bool isFocusSelectable(Hashtable resourceTable) {
+        Control c = getControl(resourceTable);
+        if (c !is null)
+            return true;
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormFonts.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.FormFonts;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.widgets.Display;
+
+import java.lang.all;
+import java.util.HashMap;
+import java.util.Set;
+
+public class FormFonts {
+
+    private static FormFonts instance;
+
+    public static FormFonts getInstance() {
+        if (instance is null)
+            instance = new FormFonts();
+        return instance;
+    }
+
+    private HashMap fonts;
+    private HashMap ids;
+
+    private this() {
+    }
+
+    private class FontIdentifier {
+        private Display fDisplay;
+        private Font fFont;
+
+        this (Display display, Font font) {
+            fDisplay = display;
+            fFont = font;
+        }
+
+        public bool equals(Object obj) {
+            if (auto id = cast(FontIdentifier)obj ) {
+                return id.fDisplay.opEquals(fDisplay) && id.fFont.opEquals(fFont);
+            }
+            return false;
+        }
+
+        public override hash_t toHash() {
+            return fDisplay.toHash() * 7 + fFont.toHash();
+        }
+    }
+
+    private class FontReference {
+        private Font fFont;
+        private int fCount;
+
+        public this(Font font) {
+            fFont = font;
+            fCount = 1;
+        }
+
+        public Font getFont() {
+            return fFont;
+        }
+        // returns a bool indicating if all clients of this font are finished
+        // a true result indicates the underlying image should be disposed
+        public bool decCount() {
+            return --fCount is 0;
+        }
+        public void incCount() {
+            fCount++;
+        }
+    }
+
+    public Font getBoldFont(Display display, Font font) {
+        checkHashMaps();
+        FontIdentifier fid = new FontIdentifier(display, font);
+        FontReference result = cast(FontReference) fonts.get(fid);
+        if (result !is null && !result.getFont().isDisposed()) {
+            result.incCount();
+            return result.getFont();
+        }
+        Font boldFont = createBoldFont(display, font);
+        fonts.put(fid, new FontReference(boldFont));
+        ids.put(boldFont, fid);
+        return boldFont;
+    }
+
+    public bool markFinished(Font boldFont) {
+        checkHashMaps();
+        FontIdentifier id = cast(FontIdentifier)ids.get(boldFont);
+        if (id !is null) {
+            FontReference ref_ = cast(FontReference) fonts.get(id);
+            if (ref_ !is null) {
+                if (ref_.decCount()) {
+                    fonts.remove(id);
+                    ids.remove(ref_.getFont());
+                    ref_.getFont().dispose();
+                    validateHashMaps();
+                }
+                return true;
+            }
+        }
+        // if the image was not found, dispose of it for the caller
+        boldFont.dispose();
+        return false;
+    }
+
+    private Font createBoldFont(Display display, Font regularFont) {
+        FontData[] fontDatas = regularFont.getFontData();
+        for (int i = 0; i < fontDatas.length; i++) {
+            fontDatas[i].setStyle(fontDatas[i].getStyle() | SWT.BOLD);
+        }
+        return new Font(display, fontDatas);
+    }
+
+    private void checkHashMaps() {
+        if (fonts is null)
+            fonts = new HashMap();
+        if (ids is null)
+            ids = new HashMap();
+    }
+
+    private void validateHashMaps() {
+        if (fonts.size() is 0)
+            fonts = null;
+        if (ids.size() is 0)
+            ids = null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormHeading.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1031 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.FormHeading;
+
+import org.eclipse.ui.internal.forms.widgets.TitleRegion;
+import org.eclipse.ui.internal.forms.widgets.FormImages;
+import org.eclipse.ui.internal.forms.widgets.FormsResources;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.dnd.DragSourceListener;
+import org.eclipse.swt.dnd.DropTargetListener;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.ui.forms.IFormColors;
+import org.eclipse.ui.forms.IMessage;
+import org.eclipse.ui.forms.events.IHyperlinkListener;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.ILayoutExtension;
+import org.eclipse.ui.forms.widgets.SizeCache;
+import org.eclipse.ui.internal.forms.IMessageToolTipManager;
+import org.eclipse.ui.internal.forms.MessageManager;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Form header moved out of the form class.
+ */
+public class FormHeading : Canvas {
+    private static const int TITLE_HMARGIN = 1;
+    private static const int SPACING = 5;
+    private static const int VSPACING = 5;
+    private static const int HMARGIN = 6;
+    private static const int VMARGIN = 1;
+    private static const int CLIENT_MARGIN = 1;
+
+    private static const int SEPARATOR = 1 << 1;
+    private static const int BOTTOM_TOOLBAR = 1 << 2;
+    private static const int BACKGROUND_IMAGE_TILED = 1 << 3;
+    private static const int SEPARATOR_HEIGHT = 2;
+    private static const int MESSAGE_AREA_LIMIT = 50;
+    static IMessage[] NULL_MESSAGE_ARRAY;
+
+    public static const String COLOR_BASE_BG = "baseBg"; //$NON-NLS-1$
+
+    private Image backgroundImage;
+
+    private Image gradientImage;
+
+    Hashtable colors;
+
+    private int flags;
+
+    private GradientInfo gradientInfo;
+
+    private ToolBarManager toolBarManager;
+
+    private SizeCache toolbarCache;
+
+    private SizeCache clientCache;
+
+    private SizeCache messageCache;
+
+    private TitleRegion titleRegion;
+
+    private MessageRegion messageRegion;
+
+    private IMessageToolTipManager messageToolTipManager;
+
+    private Control headClient;
+
+    private class DefaultMessageToolTipManager :
+            IMessageToolTipManager {
+        public void createToolTip(Control control, bool imageLabel) {
+        }
+
+        public void update() {
+            String details = getMessageType() is 0 ? null : MessageManager
+                    .createDetails(getChildrenMessages());
+            if (messageRegion !is null)
+                messageRegion.updateToolTip(details);
+            if (getMessageType() > 0
+                    && (details is null || details.length is 0))
+                details = getMessage();
+            titleRegion.updateToolTip(details);
+        }
+    }
+
+    private class GradientInfo {
+        Color[] gradientColors;
+
+        int[] percents;
+
+        bool vertical;
+    }
+
+    private class FormHeadingLayout : Layout, ILayoutExtension {
+        public int computeMinimumWidth(Composite composite, bool flushCache) {
+            return computeSize(composite, 5, SWT.DEFAULT, flushCache).x;
+        }
+
+        public int computeMaximumWidth(Composite composite, bool flushCache) {
+            return computeSize(composite, SWT.DEFAULT, SWT.DEFAULT, flushCache).x;
+        }
+
+        public Point computeSize(Composite composite, int wHint, int hHint,
+                bool flushCache) {
+            return layout(composite, false, 0, 0, wHint, hHint, flushCache);
+        }
+
+        protected void layout(Composite composite, bool flushCache) {
+            Rectangle rect = composite.getClientArea();
+            layout(composite, true, rect.x, rect.y, rect.width, rect.height,
+                    flushCache);
+        }
+
+        private Point layout(Composite composite, bool move, int x, int y,
+                int width, int height, bool flushCache) {
+            Point tsize = null;
+            Point msize = null;
+            Point tbsize = null;
+            Point clsize = null;
+
+            if (flushCache) {
+                clientCache.flush();
+                messageCache.flush();
+                toolbarCache.flush();
+            }
+            if (hasToolBar()) {
+                ToolBar tb = toolBarManager.getControl();
+                toolbarCache.setControl(tb);
+                tbsize = toolbarCache.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+            }
+            if (headClient !is null) {
+                clientCache.setControl(headClient);
+                int cwhint = width;
+                if (cwhint !is SWT.DEFAULT) {
+                    cwhint -= HMARGIN * 2;
+                    if (tbsize !is null && getToolBarAlignment() is SWT.BOTTOM)
+                        cwhint -= tbsize.x + SPACING;
+                }
+                clsize = clientCache.computeSize(cwhint, SWT.DEFAULT);
+            }
+            int totalFlexWidth = width;
+            int flexWidth = totalFlexWidth;
+            if (totalFlexWidth !is SWT.DEFAULT) {
+                totalFlexWidth -= TITLE_HMARGIN * 2;
+                // complete right margin
+                if (hasToolBar() && getToolBarAlignment() is SWT.TOP
+                        || hasMessageRegion())
+                    totalFlexWidth -= SPACING;
+                // subtract tool bar
+                if (hasToolBar() && getToolBarAlignment() is SWT.TOP)
+                    totalFlexWidth -= tbsize.x + SPACING;
+                flexWidth = totalFlexWidth;
+                if (hasMessageRegion()) {
+                    // remove message region spacing and divide by 2
+                    flexWidth -= SPACING;
+                    // flexWidth /= 2;
+                }
+            }
+            /*
+             * // compute text and message sizes tsize =
+             * titleRegion.computeSize(flexWidth, SWT.DEFAULT); if (flexWidth !is
+             * SWT.DEFAULT && tsize.x < flexWidth) flexWidth += flexWidth -
+             * tsize.x;
+             *
+             * if (hasMessageRegion()) {
+             * messageCache.setControl(messageRegion.getMessageControl()); msize =
+             * messageCache.computeSize(flexWidth, SWT.DEFAULT); int maxWidth =
+             * messageCache.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; if
+             * (maxWidth < msize.x) { msize.x = maxWidth; // recompute title
+             * with the reclaimed width int tflexWidth = totalFlexWidth -
+             * SPACING - msize.x; tsize = titleRegion.computeSize(tflexWidth,
+             * SWT.DEFAULT); } }
+             */
+            if (!hasMessageRegion()) {
+                tsize = titleRegion.computeSize(flexWidth, SWT.DEFAULT);
+            } else {
+                // Total flexible area in the first row is flexWidth.
+                // Try natural widths of title and
+                Point tsizeNatural = titleRegion.computeSize(SWT.DEFAULT,
+                        SWT.DEFAULT);
+                messageCache.setControl(messageRegion.getMessageControl());
+                Point msizeNatural = messageCache.computeSize(SWT.DEFAULT,
+                        SWT.DEFAULT);
+                // try to fit all
+                tsize = tsizeNatural;
+                msize = msizeNatural;
+                if (flexWidth !is SWT.DEFAULT) {
+                    int needed = tsizeNatural.x + msizeNatural.x;
+                    if (needed > flexWidth) {
+                        // too big - try to limit the message
+                        int mwidth = flexWidth - tsizeNatural.x;
+                        if (mwidth >= MESSAGE_AREA_LIMIT) {
+                            msize.x = mwidth;
+                        } else {
+                            // message is squeezed to the limit
+                            int flex = flexWidth - MESSAGE_AREA_LIMIT;
+                            tsize = titleRegion.computeSize(flex, SWT.DEFAULT);
+                            msize.x = MESSAGE_AREA_LIMIT;
+                        }
+                    }
+                }
+            }
+
+            Point size = new Point(width, height);
+            if (!move) {
+                // compute sizes
+                int width1 = 2 * TITLE_HMARGIN;
+                width1 += tsize.x;
+                if (msize !is null)
+                    width1 += SPACING + msize.x;
+                if (tbsize !is null && getToolBarAlignment() is SWT.TOP)
+                    width1 += SPACING + tbsize.x;
+                if (msize !is null
+                        || (tbsize !is null && getToolBarAlignment() is SWT.TOP))
+                    width1 += SPACING;
+                size.x = width1;
+                if (clsize !is null) {
+                    int width2 = clsize.x;
+                    if (tbsize !is null && getToolBarAlignment() is SWT.BOTTOM)
+                        width2 += SPACING + tbsize.x;
+                    width2 += 2 * HMARGIN;
+                    size.x = Math.max(width1, width2);
+                }
+                // height, first row
+                size.y = tsize.y;
+                if (msize !is null)
+                    size.y = Math.max(msize.y, size.y);
+                if (tbsize !is null && getToolBarAlignment() is SWT.TOP)
+                    size.y = Math.max(tbsize.y, size.y);
+                if (size.y > 0)
+                    size.y += VMARGIN * 2;
+                // add second row
+                int height2 = 0;
+                if (tbsize !is null && getToolBarAlignment() is SWT.BOTTOM)
+                    height2 = tbsize.y;
+                if (clsize !is null)
+                    height2 = Math.max(height2, clsize.y);
+                if (height2 > 0)
+                    size.y += VSPACING + height2 + CLIENT_MARGIN;
+                // add separator
+                if (size.y > 0 && isSeparatorVisible())
+                    size.y += SEPARATOR_HEIGHT;
+            } else {
+                // position controls
+                int xloc = x;
+                int yloc = y + VMARGIN;
+                int row1Height = tsize.y;
+                if (hasMessageRegion())
+                    row1Height = Math.max(row1Height, msize.y);
+                if (hasToolBar() && getToolBarAlignment() is SWT.TOP)
+                    row1Height = Math.max(row1Height, tbsize.y);
+                titleRegion.setBounds(xloc,
+                // yloc + row1Height / 2 - tsize.y / 2,
+                        yloc, tsize.x, tsize.y);
+                xloc += tsize.x;
+
+                if (hasMessageRegion()) {
+                    xloc += SPACING;
+                    int messageOffset = 0;
+                    if (tsize.y > 0) {
+                        // space between title area and title text
+                        int titleLeadingSpace = (tsize.y - titleRegion.getFontHeight()) / 2;
+                        // space between message control and message text
+                        int messageLeadingSpace = (msize.y - messageRegion.getFontHeight()) / 2;
+                        // how much to offset the message so baselines align
+                        messageOffset = (titleLeadingSpace + titleRegion.getFontBaselineHeight())
+                            - (messageLeadingSpace + messageRegion.getFontBaselineHeight());
+                    }
+
+                    messageRegion
+                            .getMessageControl()
+                            .setBounds(
+                                    xloc,
+                                    tsize.y > 0 ? (yloc + messageOffset)
+                                            : (yloc + row1Height / 2 - msize.y / 2),
+                                    msize.x, msize.y);
+                    xloc += msize.x;
+                }
+                if (toolBarManager !is null)
+                    toolBarManager.getControl().setVisible(
+                            !toolBarManager.isEmpty());
+                if (tbsize !is null && getToolBarAlignment() is SWT.TOP) {
+                    ToolBar tbar = toolBarManager.getControl();
+                    tbar.setBounds(x + width - 1 - tbsize.x - HMARGIN, yloc
+                            + row1Height - 1 - tbsize.y, tbsize.x, tbsize.y);
+                }
+                // second row
+                xloc = HMARGIN;
+                yloc += row1Height + VSPACING;
+                int tw = 0;
+
+                if (tbsize !is null && getToolBarAlignment() is SWT.BOTTOM) {
+                    ToolBar tbar = toolBarManager.getControl();
+                    tbar.setBounds(x + width - 1 - tbsize.x - HMARGIN, yloc,
+                            tbsize.x, tbsize.y);
+                    tw = tbsize.x + SPACING;
+                }
+                if (headClient !is null) {
+                    int carea = width - HMARGIN * 2 - tw;
+                    headClient.setBounds(xloc, yloc, carea, clsize.y);
+                }
+            }
+            return size;
+        }
+    }
+    this(){
+        toolbarCache = new SizeCache();
+        clientCache = new SizeCache();
+        messageCache = new SizeCache();
+    }
+    /* (non-Javadoc)
+     * @see org.eclipse.swt.widgets.Control#forceFocus()
+     */
+    public bool forceFocus() {
+        return false;
+    }
+
+    private bool hasToolBar() {
+        return toolBarManager !is null && !toolBarManager.isEmpty();
+    }
+
+    private bool hasMessageRegion() {
+        return messageRegion !is null && !messageRegion.isEmpty();
+    }
+
+    private class MessageRegion {
+        private String message;
+        private int messageType;
+        private CLabel messageLabel;
+        private IMessage[] messages;
+        private Hyperlink messageHyperlink;
+        private ListenerList listeners;
+        private Color fg;
+        private int fontHeight = -1;
+        private int fontBaselineHeight = -1;
+
+        public this() {
+        }
+
+        public bool isDisposed() {
+            Control c = getMessageControl();
+            return c !is null && c.isDisposed();
+        }
+
+        public bool isEmpty() {
+            Control c = getMessageControl();
+            if (c is null)
+                return true;
+            return !c.getVisible();
+        }
+
+        public int getFontHeight() {
+            if (fontHeight is -1) {
+                Control c = getMessageControl();
+                if (c is null)
+                    return 0;
+                GC gc = new GC(c.getDisplay());
+                gc.setFont(c.getFont());
+                fontHeight = gc.getFontMetrics().getHeight();
+                gc.dispose();
+            }
+            return fontHeight;
+        }
+
+        public int getFontBaselineHeight() {
+            if (fontBaselineHeight is -1) {
+                Control c = getMessageControl();
+                if (c is null)
+                    return 0;
+                GC gc = new GC(c.getDisplay());
+                gc.setFont(c.getFont());
+                FontMetrics fm = gc.getFontMetrics();
+                fontBaselineHeight = fm.getHeight() - fm.getDescent();
+                gc.dispose();
+            }
+            return fontBaselineHeight;
+        }
+
+        public void showMessage(String newMessage, int newType,
+                IMessage[] messages) {
+            Control oldControl = getMessageControl();
+            int oldType = messageType;
+            this.message = newMessage;
+            this.messageType = newType;
+            this.messages = messages;
+            if (newMessage is null) {
+                // clearing of the message
+                if (oldControl !is null && oldControl.getVisible())
+                    oldControl.setVisible(false);
+                return;
+            }
+            ensureControlExists();
+            if (needHyperlink()) {
+                messageHyperlink.setText(newMessage);
+                messageHyperlink.setHref(new ArrayWrapperObject(arraycast!(Object)(messages)));
+            } else {
+                messageLabel.setText(newMessage);
+            }
+            if (oldType !is newType)
+                updateForeground();
+        }
+
+        public void updateToolTip(String toolTip) {
+            Control control = getMessageControl();
+            if (control !is null)
+                control.setToolTipText(toolTip);
+        }
+
+        public String getMessage() {
+            return message;
+        }
+
+        public int getMessageType() {
+            return messageType;
+        }
+
+        public IMessage[] getChildrenMessages() {
+            return messages;
+        }
+
+        public String getDetailedMessage() {
+            Control c = getMessageControl();
+            if (c !is null)
+                return c.getToolTipText();
+            return null;
+        }
+
+        public Control getMessageControl() {
+            if (needHyperlink() && messageHyperlink !is null)
+                return messageHyperlink;
+            return messageLabel;
+        }
+
+        public Image getMessageImage() {
+            switch (messageType) {
+            case IMessageProvider.INFORMATION:
+                return JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_INFO);
+            case IMessageProvider.WARNING:
+                return JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_WARNING);
+            case IMessageProvider.ERROR:
+                return JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_ERROR);
+            default:
+                return null;
+            }
+        }
+
+        public void addMessageHyperlinkListener(IHyperlinkListener listener) {
+            if (listeners is null)
+                listeners = new ListenerList();
+            listeners.add(cast(Object)listener);
+            ensureControlExists();
+            if (messageHyperlink !is null)
+                messageHyperlink.addHyperlinkListener(listener);
+            if (listeners.size() is 1)
+                updateForeground();
+        }
+
+        private void removeMessageHyperlinkListener(IHyperlinkListener listener) {
+            if (listeners !is null) {
+                listeners.remove(cast(Object)listener);
+                if (messageHyperlink !is null)
+                    messageHyperlink.removeHyperlinkListener(listener);
+                if (listeners.isEmpty())
+                    listeners = null;
+                ensureControlExists();
+                if (listeners is null && !isDisposed())
+                    updateForeground();
+            }
+        }
+
+        private void ensureControlExists() {
+            if (needHyperlink()) {
+                if (messageLabel !is null)
+                    messageLabel.setVisible(false);
+                if (messageHyperlink is null) {
+                    messageHyperlink = new Hyperlink(this.outer, SWT.NULL);
+                    messageHyperlink.setUnderlined(true);
+                    messageHyperlink.setText(message);
+                    messageHyperlink.setHref(new ArrayWrapperObject(arraycast!(Object)(messages)));
+                    Object[] llist = listeners.getListeners();
+                    for (int i = 0; i < llist.length; i++)
+                        messageHyperlink
+                                .addHyperlinkListener(cast(IHyperlinkListener) llist[i]);
+                    if (messageToolTipManager !is null)
+                        messageToolTipManager.createToolTip(messageHyperlink, false);
+                } else if (!messageHyperlink.getVisible()) {
+                    messageHyperlink.setText(message);
+                    messageHyperlink.setHref(new ArrayWrapperObject(arraycast!(Object)(messages)));
+                    messageHyperlink.setVisible(true);
+                }
+            } else {
+                // need a label
+                if (messageHyperlink !is null)
+                    messageHyperlink.setVisible(false);
+                if (messageLabel is null) {
+                    messageLabel = new CLabel(this.outer, SWT.NULL);
+                    messageLabel.setText(message);
+                    if (messageToolTipManager !is null)
+                        messageToolTipManager.createToolTip(messageLabel, false);
+                } else if (!messageLabel.getVisible()) {
+                    messageLabel.setText(message);
+                    messageLabel.setVisible(true);
+                }
+            }
+            layout(true);
+        }
+
+        private bool needHyperlink() {
+            return messageType > 0 && listeners !is null;
+        }
+
+        public void setBackground(Color bg) {
+            if (messageHyperlink !is null)
+                messageHyperlink.setBackground(bg);
+            if (messageLabel !is null)
+                messageLabel.setBackground(bg);
+        }
+
+        public void setForeground(Color fg) {
+            this.fg = fg;
+        }
+
+        private void updateForeground() {
+            Color theFg;
+
+            switch (messageType) {
+            case IMessageProvider.ERROR:
+                theFg = getDisplay().getSystemColor(SWT.COLOR_RED);
+                break;
+            case IMessageProvider.WARNING:
+                theFg = getDisplay().getSystemColor(SWT.COLOR_DARK_YELLOW);
+                break;
+            default:
+                theFg = fg;
+            }
+            getMessageControl().setForeground(theFg);
+        }
+    }
+
+    /**
+     * Creates the form content control as a child of the provided parent.
+     *
+     * @param parent
+     *            the parent widget
+     */
+    public this(Composite parent, int style) {
+        colors = new Hashtable();
+        toolbarCache = new SizeCache();
+        clientCache = new SizeCache();
+        messageCache = new SizeCache();
+        messageToolTipManager = new DefaultMessageToolTipManager();
+
+        super(parent, style);
+        setBackgroundMode(SWT.INHERIT_DEFAULT);
+        addListener(SWT.Paint, new class Listener {
+            public void handleEvent(Event e) {
+                onPaint(e.gc);
+            }
+        });
+        addListener(SWT.Dispose, new class Listener {
+            public void handleEvent(Event e) {
+                if (gradientImage !is null) {
+                    FormImages.getInstance().markFinished(gradientImage);
+                    gradientImage = null;
+                }
+            }
+        });
+        addListener(SWT.Resize, new class Listener {
+            public void handleEvent(Event e) {
+                if (gradientInfo !is null
+                        || (backgroundImage !is null && !isBackgroundImageTiled()))
+                    updateGradientImage();
+            }
+        });
+        addMouseMoveListener(new class MouseMoveListener {
+            public void mouseMove(MouseEvent e) {
+                updateTitleRegionHoverState(e);
+            }
+        });
+        addMouseTrackListener(new class MouseTrackListener {
+            public void mouseEnter(MouseEvent e) {
+                updateTitleRegionHoverState(e);
+            }
+
+            public void mouseExit(MouseEvent e) {
+                titleRegion.setHoverState(TitleRegion.STATE_NORMAL);
+            }
+
+            public void mouseHover(MouseEvent e) {
+            }
+        });
+        super.setLayout(new FormHeadingLayout());
+        titleRegion = new TitleRegion(this);
+    }
+
+    /**
+     * Fully delegates the size computation to the internal layout manager.
+     */
+    public final Point computeSize(int wHint, int hHint, bool changed) {
+        return (cast(FormHeadingLayout) getLayout()).computeSize(this, wHint,
+                hHint, changed);
+    }
+
+    /**
+     * Prevents from changing the custom control layout.
+     */
+    public final void setLayout(Layout layout) {
+    }
+
+    /**
+     * Returns the title text that will be rendered at the top of the form.
+     *
+     * @return the title text
+     */
+    public String getText() {
+        return titleRegion.getText();
+    }
+
+    /**
+     * Returns the title image that will be rendered to the left of the title.
+     *
+     * @return the title image
+     * @since 3.2
+     */
+    public Image getImage() {
+        return titleRegion.getImage();
+    }
+
+    /**
+     * Sets the background color of the header.
+     */
+    public void setBackground(Color bg) {
+        super.setBackground(bg);
+        internalSetBackground(bg);
+    }
+
+    private void internalSetBackground(Color bg) {
+        titleRegion.setBackground(bg);
+        if (messageRegion !is null)
+            messageRegion.setBackground(bg);
+        if (toolBarManager !is null)
+            toolBarManager.getControl().setBackground(bg);
+        putColor(COLOR_BASE_BG, bg);
+    }
+
+    /**
+     * Sets the foreground color of the header.
+     */
+    public void setForeground(Color fg) {
+        super.setForeground(fg);
+        titleRegion.setForeground(fg);
+        if (messageRegion !is null)
+            messageRegion.setForeground(fg);
+    }
+
+    /**
+     * Sets the text to be rendered at the top of the form above the body as a
+     * title.
+     *
+     * @param text
+     *            the title text
+     */
+    public void setText(String text) {
+        titleRegion.setText(text);
+    }
+
+    public void setFont(Font font) {
+        super.setFont(font);
+        titleRegion.setFont(font);
+    }
+
+    /**
+     * Sets the image to be rendered to the left of the title.
+     *
+     * @param image
+     *            the title image or <code>null</code> to show no image.
+     * @since 3.2
+     */
+    public void setImage(Image image) {
+        titleRegion.setImage(image);
+        if (messageRegion !is null)
+            titleRegion.updateImage(messageRegion.getMessageImage(), true);
+        else
+            titleRegion.updateImage(null, true);
+    }
+
+    public void setTextBackground(Color[] gradientColors, int[] percents,
+            bool vertical) {
+        if (gradientColors !is null) {
+            gradientInfo = new GradientInfo();
+            gradientInfo.gradientColors = gradientColors;
+            gradientInfo.percents = percents;
+            gradientInfo.vertical = vertical;
+            setBackground(null);
+            updateGradientImage();
+        } else {
+            // reset
+            gradientInfo = null;
+            if (gradientImage !is null) {
+                FormImages.getInstance().markFinished(gradientImage);
+                gradientImage = null;
+                setBackgroundImage(null);
+            }
+        }
+    }
+
+    public void setHeadingBackgroundImage(Image image) {
+        this.backgroundImage = image;
+        if (image !is null)
+            setBackground(null);
+        if (isBackgroundImageTiled()) {
+            setBackgroundImage(image);
+        } else
+            updateGradientImage();
+    }
+
+    public Image getHeadingBackgroundImage() {
+        return backgroundImage;
+    }
+
+    public void setBackgroundImageTiled(bool tiled) {
+        if (tiled)
+            flags |= BACKGROUND_IMAGE_TILED;
+        else
+            flags &= ~BACKGROUND_IMAGE_TILED;
+        setHeadingBackgroundImage(this.backgroundImage);
+    }
+
+    public bool isBackgroundImageTiled() {
+        return (flags & BACKGROUND_IMAGE_TILED) !is 0;
+    }
+
+    public void setBackgroundImage(Image image) {
+        super.setBackgroundImage(image);
+        if (image !is null) {
+            internalSetBackground(null);
+        }
+    }
+
+    /**
+     * Returns the tool bar manager that is used to manage tool items in the
+     * form's title area.
+     *
+     * @return form tool bar manager
+     */
+    public IToolBarManager getToolBarManager() {
+        if (toolBarManager is null) {
+            toolBarManager = new ToolBarManager(SWT.FLAT);
+            ToolBar toolbar = toolBarManager.createControl(this);
+            toolbar.setBackground(getBackground());
+            toolbar.setForeground(getForeground());
+            toolbar.setCursor(FormsResources.getHandCursor());
+            addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent e) {
+                    if (toolBarManager !is null) {
+                        toolBarManager.dispose();
+                        toolBarManager = null;
+                    }
+                }
+            });
+        }
+        return toolBarManager;
+    }
+
+    /**
+     * Returns the menu manager that is used to manage tool items in the form's
+     * title area.
+     *
+     * @return form drop-down menu manager
+     * @since 3.3
+     */
+    public IMenuManager getMenuManager() {
+        return titleRegion.getMenuManager();
+    }
+
+    /**
+     * Updates the local tool bar manager if used. Does nothing if local tool
+     * bar manager has not been created yet.
+     */
+    public void updateToolBar() {
+        if (toolBarManager !is null)
+            toolBarManager.update(false);
+    }
+
+    private void onPaint(GC gc) {
+        if (!isSeparatorVisible() && getBackgroundImage() is null)
+            return;
+        Rectangle carea = getClientArea();
+        Image buffer = new Image(getDisplay(), carea.width, carea.height);
+        buffer.setBackground(getBackground());
+        GC igc = new GC(buffer);
+        igc.setBackground(getBackground());
+        igc.fillRectangle(0, 0, carea.width, carea.height);
+        if (getBackgroundImage() !is null) {
+            if (gradientInfo !is null)
+                drawBackground(igc, carea.x, carea.y, carea.width, carea.height);
+            else {
+                Image bgImage = getBackgroundImage();
+                Rectangle ibounds = bgImage.getBounds();
+                drawBackground(igc, carea.x, carea.y, ibounds.width,
+                        ibounds.height);
+            }
+        }
+
+        if (isSeparatorVisible()) {
+            // bg separator
+            if (hasColor(IFormColors.H_BOTTOM_KEYLINE1))
+                igc.setForeground(getColor(IFormColors.H_BOTTOM_KEYLINE1));
+            else
+                igc.setForeground(getBackground());
+            igc.drawLine(carea.x, carea.height - 2, carea.x + carea.width - 1,
+                    carea.height - 2);
+            if (hasColor(IFormColors.H_BOTTOM_KEYLINE2))
+                igc.setForeground(getColor(IFormColors.H_BOTTOM_KEYLINE2));
+            else
+                igc.setForeground(getForeground());
+            igc.drawLine(carea.x, carea.height - 1, carea.x + carea.width - 1,
+                    carea.height - 1);
+        }
+        igc.dispose();
+        gc.drawImage(buffer, carea.x, carea.y);
+        buffer.dispose();
+    }
+
+    private void updateTitleRegionHoverState(MouseEvent e) {
+        Rectangle titleRect = titleRegion.getBounds();
+        titleRect.width += titleRect.x + 15;
+        titleRect.height += titleRect.y + 15;
+        titleRect.x = 0;
+        titleRect.y = 0;
+        if (titleRect.contains(e.x, e.y))
+            titleRegion.setHoverState(TitleRegion.STATE_HOVER_LIGHT);
+        else
+            titleRegion.setHoverState(TitleRegion.STATE_NORMAL);
+    }
+
+    private void updateGradientImage() {
+        Rectangle rect = getBounds();
+        if (gradientImage !is null) {
+            FormImages.getInstance().markFinished(gradientImage);
+            gradientImage = null;
+        }
+        if (gradientInfo !is null) {
+            gradientImage = FormImages.getInstance().getGradient(getDisplay(), gradientInfo.gradientColors, gradientInfo.percents,
+                    gradientInfo.vertical ? rect.height : rect.width, gradientInfo.vertical, getColor(COLOR_BASE_BG));
+        } else if (backgroundImage !is null && !isBackgroundImageTiled()) {
+            gradientImage = new Image(getDisplay(), Math.max(rect.width, 1),
+                    Math.max(rect.height, 1));
+            gradientImage.setBackground(getBackground());
+            GC gc = new GC(gradientImage);
+            gc.drawImage(backgroundImage, 0, 0);
+            gc.dispose();
+        }
+        setBackgroundImage(gradientImage);
+    }
+
+    public bool isSeparatorVisible() {
+        return (flags & SEPARATOR) !is 0;
+    }
+
+    public void setSeparatorVisible(bool addSeparator) {
+        if (addSeparator)
+            flags |= SEPARATOR;
+        else
+            flags &= ~SEPARATOR;
+    }
+
+    public void setToolBarAlignment(int alignment) {
+        if (alignment is SWT.BOTTOM)
+            flags |= BOTTOM_TOOLBAR;
+        else
+            flags &= ~BOTTOM_TOOLBAR;
+    }
+
+    public int getToolBarAlignment() {
+        return (flags & BOTTOM_TOOLBAR) !is 0 ? SWT.BOTTOM : SWT.TOP;
+    }
+
+    public void addMessageHyperlinkListener(IHyperlinkListener listener) {
+        ensureMessageRegionExists();
+        messageRegion.addMessageHyperlinkListener(listener);
+    }
+
+    public void removeMessageHyperlinkListener(IHyperlinkListener listener) {
+        if (messageRegion !is null)
+            messageRegion.removeMessageHyperlinkListener(listener);
+    }
+
+    public String getMessage() {
+        return messageRegion !is null ? messageRegion.getMessage() : null;
+    }
+
+    public int getMessageType() {
+        return messageRegion !is null ? messageRegion.getMessageType() : 0;
+    }
+
+    public IMessage[] getChildrenMessages() {
+        return messageRegion !is null ? messageRegion.getChildrenMessages()
+                : NULL_MESSAGE_ARRAY;
+    }
+
+    private void ensureMessageRegionExists() {
+        // ensure message region exists
+        if (messageRegion is null)
+            messageRegion = new MessageRegion();
+    }
+
+    public void showMessage(String newMessage, int type, IMessage[] messages) {
+        if (messageRegion is null) {
+            // check the trivial case
+            if (newMessage is null)
+                return;
+        } else if (messageRegion.isDisposed())
+            return;
+        ensureMessageRegionExists();
+        messageRegion.showMessage(newMessage, type, messages);
+        titleRegion.updateImage(messageRegion.getMessageImage(), false);
+        if (messageToolTipManager !is null)
+            messageToolTipManager.update();
+        layout();
+        redraw();
+    }
+
+    /**
+     * Tests if the form is in the 'busy' state.
+     *
+     * @return <code>true</code> if busy, <code>false</code> otherwise.
+     */
+
+    public bool isBusy() {
+        return titleRegion.isBusy();
+    }
+
+    /**
+     * Sets the form's busy state. Busy form will display 'busy' animation in
+     * the area of the title image.
+     *
+     * @param busy
+     *            the form's busy state
+     */
+
+    public void setBusy(bool busy) {
+        if (titleRegion.setBusy(busy))
+            layout();
+    }
+
+    public Control getHeadClient() {
+        return headClient;
+    }
+
+    public void setHeadClient(Control headClient) {
+        if (headClient !is null)
+            Assert.isTrue(headClient.getParent() is this);
+        this.headClient = headClient;
+        layout();
+    }
+
+    public void putColor(String key, Color color) {
+        if (color is null)
+            colors.remove(key);
+        else
+            colors.put(key, color);
+    }
+
+    public Color getColor(String key) {
+        return cast(Color) colors.get(key);
+    }
+
+    public bool hasColor(String key) {
+        return colors.containsKey(key);
+    }
+
+    public void addDragSupport(int operations, Transfer[] transferTypes,
+            DragSourceListener listener) {
+        titleRegion.addDragSupport(operations, transferTypes, listener);
+    }
+
+    public void addDropSupport(int operations, Transfer[] transferTypes,
+            DropTargetListener listener) {
+        titleRegion.addDropSupport(operations, transferTypes, listener);
+    }
+
+    public IMessageToolTipManager getMessageToolTipManager() {
+        return messageToolTipManager;
+    }
+
+    public void setMessageToolTipManager(
+            IMessageToolTipManager messageToolTipManager) {
+        this.messageToolTipManager = messageToolTipManager;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormImages.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,309 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 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 org.eclipse.ui.internal.forms.widgets.FormImages;
+
+//import org.eclipse.ui.internal.forms.widgets.FormImages;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+
+import java.lang.all;
+import java.util.HashMap;
+import java.util.Set;
+
+public class FormImages {
+    private static FormImages instance;
+
+    public static FormImages getInstance() {
+        if (instance is null)
+            instance = new FormImages();
+        return instance;
+    }
+
+    private HashMap images;
+    private HashMap ids;
+
+    private this() {
+    }
+
+    private abstract class ImageIdentifier {
+        Display fDisplay;
+        RGB[] fRGBs;
+        int fLength;
+
+        this(Display display, Color[] colors, int length) {
+            fDisplay = display;
+            fRGBs = new RGB[colors.length];
+            for (int i = 0; i < colors.length; i++) {
+                Color color = colors[i];
+                fRGBs[i] = color is null ? null : color.getRGB();
+            }
+            fLength = length;
+        }
+
+        public bool equals(Object obj) {
+            if (null !is cast(ImageIdentifier)obj ) {
+                ImageIdentifier id = cast(ImageIdentifier)obj;
+                if (id.fRGBs.length is fRGBs.length) {
+                    bool result = id.fDisplay.opEquals(fDisplay) && id.fLength is fLength;
+                    for (int i = 0; i < fRGBs.length && result; i++) {
+                        result = result && id.fRGBs[i].opEquals(fRGBs[i]);
+                    }
+                    return result;
+                }
+            }
+            return false;
+        }
+
+        public override hash_t toHash() {
+            int hash = fDisplay.toHash();
+            for (int i = 0; i < fRGBs.length; i++)
+                hash = hash * 7 + fRGBs[i].toHash();
+            hash = hash * 7 + fLength;
+            return hash;
+        }
+    }
+
+    private class SimpleImageIdentifier : ImageIdentifier{
+        private int fTheight;
+        private int fMarginHeight;
+
+        this (Display display, Color color1, Color color2,
+                int realtheight, int theight, int marginHeight) {
+            super(display, [color1, color2], realtheight);
+            fTheight = theight;
+            fMarginHeight = marginHeight;
+        }
+
+        public bool equals(Object obj) {
+            if (null !is cast(SimpleImageIdentifier)obj ) {
+                SimpleImageIdentifier id = cast(SimpleImageIdentifier) obj;
+                if (super.equals(obj)  &&
+                        id.fTheight is fTheight && id.fMarginHeight is fMarginHeight)
+                    return true;
+            }
+            return false;
+        }
+
+        public override hash_t toHash() {
+            int hash = super.toHash();
+            hash = hash * 7 + (new Integer(fTheight)).toHash();
+            hash = hash * 7 + (new Integer(fMarginHeight)).toHash();
+            return hash;
+        }
+    }
+
+    private class ComplexImageIdentifier : ImageIdentifier {
+        RGB fBgRGB;
+        bool fVertical;
+        int[] fPercents;
+
+        public this(Display display, Color[] colors, int length,
+                int[] percents, bool vertical, Color bg) {
+            super(display, colors, length);
+            fBgRGB = bg is null ? null : bg.getRGB();
+            fVertical = vertical;
+            fPercents = percents;
+        }
+
+        public bool equals(Object obj) {
+            if (null !is cast(ComplexImageIdentifier)obj ) {
+                ComplexImageIdentifier id = cast(ComplexImageIdentifier) obj;
+                if (super.equals(obj)  &&
+                        id.fVertical is fVertical && ArrayEquals(id.fPercents, fPercents)) {
+                    if ((id.fBgRGB is null && fBgRGB is null) ||
+                            (id.fBgRGB !is null && id.fBgRGB.opEquals(fBgRGB)))
+                        return true;
+                    // if the only thing that isn't the same is the background color
+                    // still return true if it does not matter (percents add up to 100)
+                    int sum = 0;
+                    for (int i = 0; i < fPercents.length; i++)
+                        sum += fPercents[i];
+                    if (sum >= 100)
+                        return true;
+                }
+            }
+            return false;
+        }
+
+        public override hash_t toHash() {
+            int hash = super.toHash();
+            hash = hash * 7 + (new Boolean(fVertical)).toHash();
+            for (int i = 0; i < fPercents.length; i++)
+                hash = hash * 7 + (new Integer(fPercents[i])).toHash();
+            return hash;
+        }
+    }
+
+    private class ImageReference {
+        private Image fImage;
+        private int fCount;
+
+        public this(Image image) {
+            fImage = image;
+            fCount = 1;
+        }
+
+        public Image getImage() {
+            return fImage;
+        }
+        // returns a bool indicating if all clients of this image are finished
+        // a true result indicates the underlying image should be disposed
+        public bool decCount() {
+            return --fCount is 0;
+        }
+        public void incCount() {
+            fCount++;
+        }
+    }
+
+    public Image getGradient(Display display, Color color1, Color color2,
+            int realtheight, int theight, int marginHeight) {
+        checkHashMaps();
+        ImageIdentifier id = new SimpleImageIdentifier(display, color1, color2, realtheight, theight, marginHeight);
+        ImageReference result = cast(ImageReference) images.get(id);
+        if (result !is null && !result.getImage().isDisposed()) {
+            result.incCount();
+            return result.getImage();
+        }
+        Image image = createGradient(display, color1, color2, realtheight, theight, marginHeight);
+        images.put(id, new ImageReference(image));
+        ids.put(image, id);
+        return image;
+    }
+
+    public Image getGradient(Display display, Color[] colors, int[] percents,
+            int length, bool vertical, Color bg) {
+        checkHashMaps();
+        ImageIdentifier id = new ComplexImageIdentifier(display, colors, length, percents, vertical, bg);
+        ImageReference result = cast(ImageReference) images.get(id);
+        if (result !is null && !result.getImage().isDisposed()) {
+            result.incCount();
+            return result.getImage();
+        }
+        Image image = createGradient(display, colors, percents, length, vertical, bg);
+        images.put(id, new ImageReference(image));
+        ids.put(image, id);
+        return image;
+    }
+
+    public bool markFinished(Image image) {
+        checkHashMaps();
+        ImageIdentifier id = cast(ImageIdentifier)ids.get(image);
+        if (id !is null) {
+            ImageReference ref_ = cast(ImageReference) images.get(id);
+            if (ref_ !is null) {
+                if (ref_.decCount()) {
+                    images.remove(id);
+                    ids.remove(ref_.getImage());
+                    ref_.getImage().dispose();
+                    validateHashMaps();
+                }
+                return true;
+            }
+        }
+        // if the image was not found, dispose of it for the caller
+        image.dispose();
+        return false;
+    }
+
+    private void checkHashMaps() {
+        if (images is null)
+            images = new HashMap();
+        if (ids is null)
+            ids = new HashMap();
+    }
+
+    private void validateHashMaps() {
+        if (images.size() is 0)
+            images = null;
+        if (ids.size() is 0)
+            ids = null;
+    }
+
+    private Image createGradient(Display display, Color color1, Color color2,
+            int realtheight, int theight, int marginHeight) {
+        Image image = new Image(display, 1, realtheight);
+        image.setBackground(color1);
+        GC gc = new GC(image);
+        gc.setBackground(color1);
+        gc.fillRectangle(0, 0, 1, realtheight);
+        gc.setForeground(color2);
+        gc.setBackground(color1);
+        gc.fillGradientRectangle(0, marginHeight + 2, 1, theight - 2, true);
+        gc.dispose();
+        return image;
+    }
+
+    private Image createGradient(Display display, Color[] colors, int[] percents,
+            int length, bool vertical, Color bg) {
+        int width = vertical ? 1 : length;
+        int height = vertical ? length : 1;
+        Image gradient = new Image(display, Math.max(width, 1), Math
+                .max(height, 1));
+        GC gc = new GC(gradient);
+        drawTextGradient(gc, width, height, colors, percents, vertical, bg);
+        gc.dispose();
+        return gradient;
+    }
+
+    private void drawTextGradient(GC gc, int width, int height, Color[] colors,
+            int[] percents, bool vertical, Color bg) {
+        final Color oldBackground = gc.getBackground();
+        if (colors.length is 1) {
+            if (colors[0] !is null)
+                gc.setBackground(colors[0]);
+            gc.fillRectangle(0, 0, width, height);
+        } else {
+            final Color oldForeground = gc.getForeground();
+            Color lastColor = colors[0];
+            if (lastColor is null)
+                lastColor = oldBackground;
+            int pos = 0;
+            for (int i = 0; i < percents.length; ++i) {
+                gc.setForeground(lastColor);
+                lastColor = colors[i + 1];
+                if (lastColor is null)
+                    lastColor = oldBackground;
+                gc.setBackground(lastColor);
+                if (vertical) {
+                    int gradientHeight = percents[i] * height / 100;
+
+                    gc.fillGradientRectangle(0, pos, width, gradientHeight,
+                            true);
+                    pos += gradientHeight;
+                } else {
+                    int gradientWidth = percents[i] * height / 100;
+
+                    gc.fillGradientRectangle(pos, 0, gradientWidth, height,
+                            false);
+                    pos += gradientWidth;
+                }
+            }
+            if (vertical && pos < height) {
+                if (bg !is null)
+                    gc.setBackground(bg);
+                gc.fillRectangle(0, pos, width, height - pos);
+            }
+            if (!vertical && pos < width) {
+                if (bg !is null)
+                    gc.setBackground(bg);
+                gc.fillRectangle(pos, 0, width - pos, height);
+            }
+            gc.setForeground(oldForeground);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormTextModel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,769 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.FormTextModel;
+
+import org.eclipse.ui.internal.forms.widgets.Paragraph;
+import org.eclipse.ui.internal.forms.widgets.IFocusSelectable;
+import org.eclipse.ui.internal.forms.widgets.ParagraphSegment;
+import org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment;
+import org.eclipse.ui.internal.forms.widgets.ControlSegment;
+import org.eclipse.ui.internal.forms.widgets.ImageSegment;
+import org.eclipse.ui.internal.forms.widgets.ObjectSegment;
+import org.eclipse.ui.internal.forms.widgets.SWTUtil;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.ui.forms.HyperlinkSettings;
+
+import java.lang.all;
+import java.util.Set;
+import java.io.InputStream;
+
+static import tango.text.xml.Document;
+static import tango.io.device.Array;
+
+public class FormTextModel {
+//     private static const DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
+//             .newInstance();
+
+    alias tango.text.xml.Document.Document!(char) Document;
+    alias Document.NodeImpl Node;
+
+    private bool whitespaceNormalized = true;
+
+    private Vector paragraphs;
+
+    private IFocusSelectable[] selectableSegments;
+
+    private int selectedSegmentIndex = -1;
+
+    private int savedSelectedLinkIndex = -1;
+
+    private HyperlinkSettings hyperlinkSettings;
+
+    public static const String BOLD_FONT_ID = "f.____bold"; //$NON-NLS-1$
+
+    //private static final int TEXT_ONLY_LINK = 1;
+
+    //private static final int IMG_ONLY_LINK = 2;
+
+    //private static final int TEXT_AND_IMAGES_LINK = 3;
+
+    public this() {
+        reset();
+    }
+
+    /*
+     * @see ITextModel#getParagraphs()
+     */
+    public Paragraph[] getParagraphs() {
+        if (paragraphs is null)
+            return new Paragraph[0];
+        return arraycast!(Paragraph)(paragraphs
+                .toArray());
+    }
+
+    public String getAccessibleText() {
+        if (paragraphs is null)
+            return ""; //$NON-NLS-1$
+        StringBuffer sbuf = new StringBuffer();
+        for (int i = 0; i < paragraphs.size(); i++) {
+            Paragraph paragraph = cast(Paragraph) paragraphs.get(i);
+            String text = paragraph.getAccessibleText();
+            sbuf.append(text);
+        }
+        return sbuf.toString();
+    }
+
+    /*
+     * @see ITextModel#parse(String)
+     */
+    public void parseTaggedText(String taggedText, bool expandURLs) {
+        if (taggedText is null) {
+            reset();
+            return;
+        }
+        _parseTaggedText(taggedText, expandURLs);
+    }
+
+    public void parseInputStream(InputStream is_, bool expandURLs) {
+        auto buf = new tango.io.device.Array.Array( 1024 );
+        {
+            int l;
+            byte[1024] a = void;
+            while((l = is_.read(a)) > 0 ){
+                buf.append( a[ 0 .. l ] );
+            }
+        }
+        _parseTaggedText( cast(char[]) buf.slice(), expandURLs );
+//         documentBuilderFactory.setNamespaceAware(true);
+//         documentBuilderFactory.setIgnoringComments(true);
+//
+//         reset();
+//         try {
+//             DocumentBuilder parser = documentBuilderFactory
+//                     .newDocumentBuilder();
+//             InputSource source = new InputSource(is_);
+//             Document doc = parser.parse(source);
+//             processDocument(doc, expandURLs);
+//         } catch (ParserConfigurationException e) {
+//             SWT.error(SWT.ERROR_INVALID_ARGUMENT, e);
+//         } catch (SAXException e) {
+//             SWT.error(SWT.ERROR_INVALID_ARGUMENT, e);
+//         } catch (IOException e) {
+//             SWT.error(SWT.ERROR_IO, e);
+//         }
+    }
+    private void _parseTaggedText( char[] text, bool expandURLs ){
+        reset();
+//         try {
+//             auto doc = tango.text.xml.Document.Document!(char)();
+//             doc.parse(text);
+//             processDocument(doc, expandURLs);
+//         } catch (XmlException e) {
+//             SWT.error(SWT.ERROR_INVALID_ARGUMENT, e);
+//         } catch (IOException e) {
+//             SWT.error(SWT.ERROR_IO, e);
+//         }
+    }
+
+//     private void processDocument(tango.text.xml.Document.Document!(char) doc, bool expandURLs) {
+//         auto root = doc.query.root.dup;
+//         auto children = root.childs.dup;
+//         processSubnodes(paragraphs, children, expandURLs);
+//     }
+
+/+    private void processSubnodes(Vector plist, NodeList children, bool expandURLs) {
+//o         for (int i = 0; i < children.getLength(); i++) {
+//o             Node child = children.item(i);
+        foreach( child; children ){
+//o             if (child.getNodeType() is Node.TEXT_NODE) {
+            if (child.type is XmlNodeType.Data) {
+                // Make an implicit paragraph
+                String text = getSingleNodeText(child);
+                if ( !isIgnorableWhiteSpace(text, true)) {
+                    Paragraph p = new Paragraph(true);
+//                     p.parseRegularText(text, expandURLs, true,
+//                             getHyperlinkSettings(), null);
+//                     plist.add(p);
+//                 }
+//             } else if (child.getNodeType() is Node.ELEMENT_NODE) {
+//                 String tag = child.getNodeName().toLowerCase();
+//                 if (tag.equals("p")) { //$NON-NLS-1$
+//                     Paragraph p = processParagraph(child, expandURLs);
+//                     if (p !is null)
+//                         plist.add(p);
+//                 } else if (tag.equals("li")) { //$NON-NLS-1$
+//                     Paragraph p = processListItem(child, expandURLs);
+//                     if (p !is null)
+//                         plist.add(p);
+                }
+            }
+        }
+    }+/
+/++
+
+    private Paragraph processParagraph(Node paragraph, bool expandURLs) {
+        NodeList children = paragraph.getChildNodes();
+        NamedNodeMap atts = paragraph.getAttributes();
+        Node addSpaceAtt = atts.getNamedItem("addVerticalSpace"); //$NON-NLS-1$
+        bool addSpace = true;
+
+        if (addSpaceAtt is null)
+            addSpaceAtt = atts.getNamedItem("vspace"); //$NON-NLS-1$
+
+        if (addSpaceAtt !is null) {
+            String value = addSpaceAtt.getNodeValue();
+            addSpace = value.equalsIgnoreCase("true"); //$NON-NLS-1$
+        }
+        Paragraph p = new Paragraph(addSpace);
+
+        processSegments(p, children, expandURLs);
+        return p;
+    }
+
+    private Paragraph processListItem(Node listItem, bool expandURLs) {
+        NodeList children = listItem.getChildNodes();
+        NamedNodeMap atts = listItem.getAttributes();
+        Node addSpaceAtt = atts.getNamedItem("addVerticalSpace");//$NON-NLS-1$
+        Node styleAtt = atts.getNamedItem("style");//$NON-NLS-1$
+        Node valueAtt = atts.getNamedItem("value");//$NON-NLS-1$
+        Node indentAtt = atts.getNamedItem("indent");//$NON-NLS-1$
+        Node bindentAtt = atts.getNamedItem("bindent");//$NON-NLS-1$
+        int style = BulletParagraph.CIRCLE;
+        int indent = -1;
+        int bindent = -1;
+        String text = null;
+        bool addSpace = true;
+
+        if (addSpaceAtt !is null) {
+            String value = addSpaceAtt.getNodeValue();
+            addSpace = value.equalsIgnoreCase("true"); //$NON-NLS-1$
+        }
+        if (styleAtt !is null) {
+            String value = styleAtt.getNodeValue();
+            if (value.equalsIgnoreCase("text")) { //$NON-NLS-1$
+                style = BulletParagraph.TEXT;
+            } else if (value.equalsIgnoreCase("image")) { //$NON-NLS-1$
+                style = BulletParagraph.IMAGE;
+            } else if (value.equalsIgnoreCase("bullet")) { //$NON-NLS-1$
+                style = BulletParagraph.CIRCLE;
+            }
+        }
+        if (valueAtt !is null) {
+            text = valueAtt.getNodeValue();
+            if (style is BulletParagraph.IMAGE)
+                text = "i." + text; //$NON-NLS-1$
+        }
+        if (indentAtt !is null) {
+            String value = indentAtt.getNodeValue();
+            try {
+                indent = Integer.parseInt(value);
+            } catch (NumberFormatException e) {
+            }
+        }
+        if (bindentAtt !is null) {
+            String value = bindentAtt.getNodeValue();
+            try {
+                bindent = Integer.parseInt(value);
+            } catch (NumberFormatException e) {
+            }
+        }
+
+        BulletParagraph p = new BulletParagraph(addSpace);
+        p.setIndent(indent);
+        p.setBulletIndent(bindent);
+        p.setBulletStyle(style);
+        p.setBulletText(text);
+
+        processSegments(p, children, expandURLs);
+        return p;
+    }
+
+    private void processSegments(Paragraph p, NodeList children,
+            bool expandURLs) {
+        for (int i = 0; i < children.getLength(); i++) {
+            Node child = children.item(i);
+            ParagraphSegment segment = null;
+
+            if (child.getNodeType() is Node.TEXT_NODE) {
+                String value = getSingleNodeText(child);
+
+                if (value !is null && !isIgnorableWhiteSpace(value, false)) {
+                    p.parseRegularText(value, expandURLs, true,
+                            getHyperlinkSettings(), null);
+                }
+            } else if (child.getNodeType() is Node.ELEMENT_NODE) {
+                String name = child.getNodeName();
+                if (name.equalsIgnoreCase("img")) { //$NON-NLS-1$
+                    segment = processImageSegment(child);
+                } else if (name.equalsIgnoreCase("a")) { //$NON-NLS-1$
+                    segment = processHyperlinkSegment(child,
+                            getHyperlinkSettings());
+                } else if (name.equalsIgnoreCase("span")) { //$NON-NLS-1$
+                    processTextSegment(p, expandURLs, child);
+                } else if (name.equalsIgnoreCase("b")) { //$NON-NLS-1$
+                    String text = getNodeText(child);
+                    String fontId = BOLD_FONT_ID;
+                    p.parseRegularText(text, expandURLs, true,
+                            getHyperlinkSettings(), fontId);
+                } else if (name.equalsIgnoreCase("br")) { //$NON-NLS-1$
+                    segment = new BreakSegment();
+                } else if (name.equalsIgnoreCase("control")) { //$NON-NLS-1$
+                    segment = processControlSegment(child);
+                }
+            }
+            if (segment !is null) {
+                p.addSegment(segment);
+            }
+        }
+    }
+++/
+    private bool isIgnorableWhiteSpace(String text, bool ignoreSpaces) {
+        for (int i = 0; i < text.length; i++) {
+            char c = text.charAt(i);
+            if (ignoreSpaces && c is ' ')
+                continue;
+            if (c is '\n' || c is '\r' || c is '\f')
+                continue;
+            return false;
+        }
+        return true;
+    }
+/++
+    private ImageSegment processImageSegment(Node image) {
+        ImageSegment segment = new ImageSegment();
+        processObjectSegment(segment, image, "i."); //$NON-NLS-1$
+        return segment;
+    }
+
+    private ControlSegment processControlSegment(Node control) {
+        ControlSegment segment = new ControlSegment();
+        processObjectSegment(segment, control, "o."); //$NON-NLS-1$
+        Node fill = control.getAttributes().getNamedItem("fill"); //$NON-NLS-1$
+        if (fill !is null) {
+            String value = fill.getNodeValue();
+            bool doFill = value.equalsIgnoreCase("true"); //$NON-NLS-1$
+            segment.setFill(doFill);
+        }
+        try {
+            Node width = control.getAttributes().getNamedItem("width"); //$NON-NLS-1$
+            if (width !is null) {
+                String value = width.getNodeValue();
+                int doWidth = Integer.parseInt(value);
+                segment.setWidth(doWidth);
+            }
+            Node height = control.getAttributes().getNamedItem("height"); //$NON-NLS-1$
+            if (height !is null) {
+                String value = height.getNodeValue();
+                int doHeight = Integer.parseInt(value);
+                segment.setHeight(doHeight);
+            }
+        }
+        catch (NumberFormatException e) {
+            // ignore invalid width or height
+        }
+        return segment;
+    }
+
+    private void processObjectSegment(ObjectSegment segment, Node object, String prefix) {
+        NamedNodeMap atts = object.getAttributes();
+        Node id = atts.getNamedItem("href"); //$NON-NLS-1$
+        Node align_ = atts.getNamedItem("align"); //$NON-NLS-1$
+        if (id !is null) {
+            String value = id.getNodeValue();
+            segment.setObjectId(prefix + value);
+        }
+        if (align_ !is null) {
+            String value = align_.getNodeValue().toLowerCase();
+            if (value.equals("top")) //$NON-NLS-1$
+                segment.setVerticalAlignment(ImageSegment.TOP);
+            else if (value.equals("middle")) //$NON-NLS-1$
+                segment.setVerticalAlignment(ImageSegment.MIDDLE);
+            else if (value.equals("bottom")) //$NON-NLS-1$
+                segment.setVerticalAlignment(ImageSegment.BOTTOM);
+        }
+    }
+++/
+    private void appendText(String value, StringBuffer buf, int[] spaceCounter) {
+        if (!whitespaceNormalized)
+            buf.append(value);
+        else {
+            for (int j = 0; j < value.length; j++) {
+                char c = value.charAt(j);
+                if (c is ' ' || c is '\t') {
+                    // space
+                    if (++spaceCounter[0] is 1) {
+                        buf.append(c);
+                    }
+                } else if (c is '\n' || c is '\r' || c is '\f') {
+                    // new line
+                    if (++spaceCounter[0] is 1) {
+                        buf.append(' ');
+                    }
+                } else {
+                    // other characters
+                    spaceCounter[0] = 0;
+                    buf.append(c);
+                }
+            }
+        }
+    }
+
+    private String getNormalizedText(String text) {
+        int[] spaceCounter = new int[1];
+        StringBuffer buf = new StringBuffer();
+
+        if (text is null)
+            return null;
+        appendText(text, buf, spaceCounter);
+        return buf.toString();
+    }
+
+    private String getSingleNodeText(tango.text.xml.Document.Document!(char).NodeImpl node) {
+        return getNormalizedText(node.value());
+    }
+/++
+
+    private String getNodeText(Node node) {
+        NodeList children = node.getChildNodes();
+        StringBuffer buf = new StringBuffer();
+        int[] spaceCounter = new int[1];
+
+        for (int i = 0; i < children.getLength(); i++) {
+            Node child = children.item(i);
+            if (child.getNodeType() is Node.TEXT_NODE) {
+                String value = child.getNodeValue();
+                appendText(value, buf, spaceCounter);
+            }
+        }
+        return buf.toString().trim();
+    }
+
+    private ParagraphSegment processHyperlinkSegment(Node link,
+            HyperlinkSettings settings) {
+        NamedNodeMap atts = link.getAttributes();
+        String href = null;
+        bool wrapAllowed = true;
+        String boldFontId = null;
+
+        Node hrefAtt = atts.getNamedItem("href"); //$NON-NLS-1$
+        if (hrefAtt !is null) {
+            href = hrefAtt.getNodeValue();
+        }
+        Node boldAtt = atts.getNamedItem("bold"); //$NON-NLS-1$
+        if (boldAtt !is null) {
+            boldFontId = BOLD_FONT_ID;
+        }
+        Node nowrap = atts.getNamedItem("nowrap"); //$NON-NLS-1$
+        if (nowrap !is null) {
+            String value = nowrap.getNodeValue();
+            if (value !is null && value.equalsIgnoreCase("true")) //$NON-NLS-1$
+                wrapAllowed = false;
+        }
+        Object status = checkChildren(link);
+        if ( auto child = cast(Node)status ) {
+            ImageHyperlinkSegment segment = new ImageHyperlinkSegment();
+            segment.setHref(href);
+            segment.setWordWrapAllowed(wrapAllowed);
+            Node alt = child.getAttributes().getNamedItem("alt"); //$NON-NLS-1$
+            if (alt !is null)
+                segment.setTooltipText(alt.getNodeValue());
+            Node text = child.getAttributes().getNamedItem("text"); //$NON-NLS-1$
+            if (text !is null)
+                segment.setText(text.getNodeValue());
+            processObjectSegment(segment, child, "i."); //$NON-NLS-1$
+            return segment;
+        }  else if ( auto textObj = cast(ArrayWrapperString)status ) {
+            String text = textObj.array;
+            TextHyperlinkSegment segment = new TextHyperlinkSegment(text,
+                    settings, null);
+            segment.setHref(href);
+            segment.setFontId(boldFontId);
+            Node alt = atts.getNamedItem("alt"); //$NON-NLS-1$
+            if (alt !is null)
+                segment.setTooltipText(alt.getNodeValue());
+            segment.setWordWrapAllowed(wrapAllowed);
+            return segment;
+        } else {
+            AggregateHyperlinkSegment parent = new AggregateHyperlinkSegment();
+            parent.setHref(href);
+            NodeList children = link.getChildNodes();
+            for (int i = 0; i < children.getLength(); i++) {
+                Node child = children.item(i);
+                if (child.getNodeType() is Node.TEXT_NODE) {
+                    String value = child.getNodeValue();
+                    TextHyperlinkSegment ts = new TextHyperlinkSegment(
+                            getNormalizedText(value), settings, null);
+                    Node alt = atts.getNamedItem("alt"); //$NON-NLS-1$
+                    if (alt !is null)
+                        ts.setTooltipText(alt.getNodeValue());
+                    ts.setWordWrapAllowed(wrapAllowed);
+                    parent.add(ts);
+                } else if (child.getNodeType() is Node.ELEMENT_NODE) {
+                    String name = child.getNodeName();
+                    if (name.equalsIgnoreCase("img")) { //$NON-NLS-1$
+                        ImageHyperlinkSegment is_ = new ImageHyperlinkSegment();
+                        processObjectSegment(is_, child, "i."); //$NON-NLS-1$
+                        Node alt = child.getAttributes().getNamedItem("alt"); //$NON-NLS-1$
+                        if (alt !is null)
+                            is_.setTooltipText(alt.getNodeValue());
+                        parent.add(is_);
+                        is_.setWordWrapAllowed(wrapAllowed);
+                    }
+                }
+            }
+            return parent;
+        }
+    }
+
+    private Object checkChildren(Node node) {
+        bool text = false;
+        Node imgNode = null;
+        //int status = 0;
+
+        NodeList children = node.getChildNodes();
+        for (int i = 0; i < children.getLength(); i++) {
+            Node child = children.item(i);
+            if (child.getNodeType() is Node.TEXT_NODE)
+                text = true;
+            else if (child.getNodeType() is Node.ELEMENT_NODE
+                    && child.getNodeName().equalsIgnoreCase("img")) { //$NON-NLS-1$
+                imgNode = child;
+            }
+        }
+        if (text && imgNode is null)
+            return getNodeText(node);
+        else if (!text && imgNode !is null)
+            return imgNode;
+        else return null;
+    }
+
+    private void processTextSegment(Paragraph p, bool expandURLs,
+            Node textNode) {
+        String text = getNodeText(textNode);
+
+        NamedNodeMap atts = textNode.getAttributes();
+        Node font = atts.getNamedItem("font"); //$NON-NLS-1$
+        Node color = atts.getNamedItem("color"); //$NON-NLS-1$
+        bool wrapAllowed=true;
+        Node nowrap = atts.getNamedItem("nowrap"); //$NON-NLS-1$
+        if (nowrap !is null) {
+            String value = nowrap.getNodeValue();
+            if (value !is null && value.equalsIgnoreCase("true")) //$NON-NLS-1$
+                wrapAllowed = false;
+        }
+        String fontId = null;
+        String colorId = null;
+        if (font !is null) {
+            fontId = "f." + font.getNodeValue(); //$NON-NLS-1$
+        }
+        if (color !is null) {
+            colorId = "c." + color.getNodeValue(); //$NON-NLS-1$
+        }
+        p.parseRegularText(text, expandURLs, wrapAllowed, getHyperlinkSettings(), fontId,
+                colorId);
+    }
+++/
+    public void parseRegularText(String regularText, bool convertURLs) {
+        reset();
+
+        if (regularText is null)
+            return;
+
+        regularText = getNormalizedText(regularText);
+
+        Paragraph p = new Paragraph(true);
+        paragraphs.add(p);
+        int pstart = 0;
+
+        for (int i = 0; i < regularText.length; i++) {
+            char c = regularText.charAt(i);
+            if (p is null) {
+                p = new Paragraph(true);
+                paragraphs.add(p);
+            }
+            if (c is '\n') {
+                String text = regularText.substring(pstart, i);
+                pstart = i + 1;
+                p.parseRegularText(text, convertURLs, true, getHyperlinkSettings(),
+                        null);
+                p = null;
+            }
+        }
+        if (p !is null) {
+            // no new line
+            String text = regularText.substring(pstart);
+            p.parseRegularText(text, convertURLs, true, getHyperlinkSettings(), null);
+        }
+    }
+
+    public HyperlinkSettings getHyperlinkSettings() {
+        // #132723 cannot have null settings
+        if (hyperlinkSettings is null)
+            hyperlinkSettings = new HyperlinkSettings(SWTUtil.getStandardDisplay());
+        return hyperlinkSettings;
+    }
+
+    public void setHyperlinkSettings(HyperlinkSettings settings) {
+        this.hyperlinkSettings = settings;
+    }
+
+    private void reset() {
+        if (paragraphs is null)
+            paragraphs = new Vector;
+        paragraphs.clear();
+        selectedSegmentIndex = -1;
+        savedSelectedLinkIndex = -1;
+        selectableSegments = null;
+    }
+
+    IFocusSelectable[] getFocusSelectableSegments() {
+        if (selectableSegments !is null || paragraphs is null)
+            return selectableSegments;
+        Vector result = new Vector();
+        for (int i = 0; i < paragraphs.size(); i++) {
+            Paragraph p = cast(Paragraph) paragraphs.get(i);
+            ParagraphSegment[] segments = p.getSegments();
+            for (int j = 0; j < segments.length; j++) {
+                if (null !is cast(IFocusSelectable)segments[j] )
+                    result.add(segments[j]);
+            }
+        }
+        selectableSegments = arraycast!(IFocusSelectable)(result.toArray());
+        return selectableSegments;
+    }
+
+    public IHyperlinkSegment getHyperlink(int index) {
+        IFocusSelectable[] selectables = getFocusSelectableSegments();
+        if (selectables.length>index) {
+            IFocusSelectable link = selectables[index];
+            if (auto l = cast(IHyperlinkSegment)link )
+                return l;
+        }
+        return null;
+    }
+
+    public IHyperlinkSegment findHyperlinkAt(int x, int y) {
+        IFocusSelectable[] selectables = getFocusSelectableSegments();
+        for (int i = 0; i < selectables.length; i++) {
+            IFocusSelectable segment = selectables[i];
+            if ( auto link = cast(IHyperlinkSegment)segment ) {
+                if (link.contains(x, y))
+                    return link;
+            }
+        }
+        return null;
+    }
+
+    public int getHyperlinkCount() {
+        return getFocusSelectableSegments().length;
+    }
+
+    public int indexOf(IHyperlinkSegment link) {
+        IFocusSelectable[] selectables = getFocusSelectableSegments();
+        for (int i = 0; i < selectables.length; i++) {
+            IFocusSelectable segment = selectables[i];
+            if (auto l = cast(IHyperlinkSegment)segment ) {
+                if (link is l)
+                    return i;
+            }
+        }
+        return -1;
+    }
+
+    public ParagraphSegment findSegmentAt(int x, int y) {
+        for (int i = 0; i < paragraphs.size(); i++) {
+            Paragraph p = cast(Paragraph) paragraphs.get(i);
+            ParagraphSegment segment = p.findSegmentAt(x, y);
+            if (segment !is null)
+                return segment;
+        }
+        return null;
+    }
+
+    public void clearCache(String fontId) {
+        for (int i = 0; i < paragraphs.size(); i++) {
+            Paragraph p = cast(Paragraph) paragraphs.get(i);
+            p.clearCache(fontId);
+        }
+    }
+
+    public IFocusSelectable getSelectedSegment() {
+        if (selectableSegments is null || selectedSegmentIndex is -1)
+            return null;
+        return selectableSegments[selectedSegmentIndex];
+    }
+
+    public int getSelectedSegmentIndex() {
+        return selectedSegmentIndex;
+    }
+
+    public bool linkExists(IHyperlinkSegment link) {
+        if (selectableSegments is null)
+            return false;
+        for (int i=0; i<selectableSegments.length; i++) {
+            if (selectableSegments[i] is link)
+                return true;
+        }
+        return false;
+    }
+
+    public bool traverseFocusSelectableObjects(bool next) {
+        IFocusSelectable[] selectables = getFocusSelectableSegments();
+        if (selectables is null)
+            return false;
+        int size = selectables.length;
+        if (next) {
+            selectedSegmentIndex++;
+        } else
+            selectedSegmentIndex--;
+
+        if (selectedSegmentIndex < 0 || selectedSegmentIndex > size - 1) {
+            selectedSegmentIndex = -1;
+        }
+        return selectedSegmentIndex !is -1;
+    }
+
+    public IFocusSelectable getNextFocusSegment(bool next) {
+        IFocusSelectable[] selectables = getFocusSelectableSegments();
+        if (selectables is null)
+            return null;
+        int nextIndex = next?selectedSegmentIndex+1:selectedSegmentIndex-1;
+
+        if (nextIndex < 0 || nextIndex > selectables.length - 1) {
+            return null;
+        }
+        return selectables[nextIndex];
+    }
+
+    public bool restoreSavedLink() {
+        if (savedSelectedLinkIndex !is -1) {
+            selectedSegmentIndex = savedSelectedLinkIndex;
+            return true;
+        }
+        return false;
+    }
+
+    public void selectLink(IHyperlinkSegment link) {
+        if (link is null) {
+            savedSelectedLinkIndex = selectedSegmentIndex;
+            selectedSegmentIndex = -1;
+        }
+        else {
+            select(link);
+
+        }
+    }
+
+    public void select(IFocusSelectable selectable) {
+        IFocusSelectable[] selectables = getFocusSelectableSegments();
+        selectedSegmentIndex = -1;
+        if (selectables is null)
+            return;
+        for (int i = 0; i < selectables.length; i++) {
+            if ((cast(Object)selectables[i]).opEquals(cast(Object)selectable)) {
+                selectedSegmentIndex = i;
+                break;
+            }
+        }
+    }
+
+    public bool hasFocusSegments() {
+        IFocusSelectable[] segments = getFocusSelectableSegments();
+        if (segments.length > 0)
+            return true;
+        return false;
+    }
+
+    public void dispose() {
+        paragraphs = null;
+        selectedSegmentIndex = -1;
+        savedSelectedLinkIndex = -1;
+        selectableSegments = null;
+    }
+
+    /**
+     * @return Returns the whitespaceNormalized.
+     */
+    public bool isWhitespaceNormalized() {
+        return whitespaceNormalized;
+    }
+
+    /**
+     * @param whitespaceNormalized
+     *            The whitespaceNormalized to set.
+     */
+    public void setWhitespaceNormalized(bool whitespaceNormalized) {
+        this.whitespaceNormalized = whitespaceNormalized;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormUtil.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,532 @@
+/*******************************************************************************
+ * 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
+ *     Chriss Gross (schtoo@schtoo.com) - fix for 61670
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.ui.internal.forms.widgets.FormUtil;
+
+
+// import com.ibm.icu.text.BreakIterator;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.forms.widgets.ColumnLayout;
+import org.eclipse.ui.forms.widgets.Form;
+import org.eclipse.ui.forms.widgets.FormText;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ILayoutExtension;
+
+import java.lang.all;
+import java.util.Set;
+
+import  org.eclipse.dwtxhelper.mangoicu.UBreakIterator;
+
+public class FormUtil {
+
+    public static const String PLUGIN_ID = "org.eclipse.ui.forms"; //$NON-NLS-1$
+
+    static const int H_SCROLL_INCREMENT = 5;
+
+    static const int V_SCROLL_INCREMENT = 64;
+
+    public static const String DEBUG = PLUGIN_ID ~ "/debug"; //$NON-NLS-1$
+
+    public static const String DEBUG_TEXT = DEBUG ~ "/text"; //$NON-NLS-1$
+    public static const String DEBUG_TEXTSIZE = DEBUG ~ "/textsize"; //$NON-NLS-1$
+
+    public static const String DEBUG_FOCUS = DEBUG ~ "/focus"; //$NON-NLS-1$
+
+    public static const String FOCUS_SCROLLING = "focusScrolling"; //$NON-NLS-1$
+
+    public static const String IGNORE_BODY = "__ignore_body__"; //$NON-NLS-1$
+
+    public static Text createText(Composite parent, String label,
+            FormToolkit factory) {
+        return createText(parent, label, factory, 1);
+    }
+
+    public static Text createText(Composite parent, String label,
+            FormToolkit factory, int span) {
+        factory.createLabel(parent, label);
+        Text text = factory.createText(parent, ""); //$NON-NLS-1$
+        int hfill = span is 1 ? GridData.FILL_HORIZONTAL
+                : GridData.HORIZONTAL_ALIGN_FILL;
+        GridData gd = new GridData(hfill | GridData.VERTICAL_ALIGN_CENTER);
+        gd.horizontalSpan = span;
+        text.setLayoutData(gd);
+        return text;
+    }
+
+    public static Text createText(Composite parent, String label,
+            FormToolkit factory, int span, int style) {
+        Label l = factory.createLabel(parent, label);
+        if ((style & SWT.MULTI) !is 0) {
+            GridData gd = new GridData(GridData.VERTICAL_ALIGN_BEGINNING);
+            l.setLayoutData(gd);
+        }
+        Text text = factory.createText(parent, "", style); //$NON-NLS-1$
+        int hfill = span is 1 ? GridData.FILL_HORIZONTAL
+                : GridData.HORIZONTAL_ALIGN_FILL;
+        GridData gd = new GridData(hfill | GridData.VERTICAL_ALIGN_CENTER);
+        gd.horizontalSpan = span;
+        text.setLayoutData(gd);
+        return text;
+    }
+
+    public static Text createText(Composite parent, FormToolkit factory,
+            int span) {
+        Text text = factory.createText(parent, ""); //$NON-NLS-1$
+        int hfill = span is 1 ? GridData.FILL_HORIZONTAL
+                : GridData.HORIZONTAL_ALIGN_FILL;
+        GridData gd = new GridData(hfill | GridData.VERTICAL_ALIGN_CENTER);
+        gd.horizontalSpan = span;
+        text.setLayoutData(gd);
+        return text;
+    }
+
+    public static int computeMinimumWidth(GC gc, String text) {
+        auto wb =  UBreakIterator.openWordIterator( ULocale.Default, text );
+        scope(exit) wb.close();
+        int last = 0;
+        int width = 0;
+
+        for (int loc = wb.first(); loc !is UBreakIterator.Done; loc = wb.next()) {
+            String word = text.substring(last, loc);
+            Point extent = gc.textExtent(word);
+            width = Math.max(width, extent.x);
+            last = loc;
+        }
+        String lastWord = text.substring(last);
+        Point extent = gc.textExtent(lastWord);
+        width = Math.max(width, extent.x);
+        return width;
+    }
+
+    public static Point computeWrapSize(GC gc, String text, int wHint) {
+        auto wb =  UBreakIterator.openWordIterator( ULocale.Default, text );
+        scope(exit) wb.close();
+        FontMetrics fm = gc.getFontMetrics();
+        int lineHeight = fm.getHeight();
+
+        int saved = 0;
+        int last = 0;
+        int height = lineHeight;
+        int maxWidth = 0;
+        for (int loc = wb.first(); loc !is UBreakIterator.Done; loc = wb.next()) {
+            String word = text.substring(saved, loc);
+            Point extent = gc.textExtent(word);
+            if (extent.x > wHint) {
+                // overflow
+                saved = last;
+                height += extent.y;
+                // switch to current word so maxWidth will accommodate very long single words
+                word = text.substring(last, loc);
+                extent = gc.textExtent(word);
+            }
+            maxWidth = Math.max(maxWidth, extent.x);
+            last = loc;
+        }
+        /*
+         * Correct the height attribute in case it was calculated wrong due to wHint being less than maxWidth.
+         * The recursive call proved to be the only thing that worked in all cases. Some attempts can be made
+         * to estimate the height, but the algorithm needs to be run again to be sure.
+         */
+        if (maxWidth > wHint)
+            return computeWrapSize(gc, text, maxWidth);
+        return new Point(maxWidth, height);
+    }
+
+    public static void paintWrapText(GC gc, String text, Rectangle bounds) {
+        paintWrapText(gc, text, bounds, false);
+    }
+
+    public static void paintWrapText(GC gc, String text, Rectangle bounds,
+            bool underline) {
+        auto wb =  UBreakIterator.openWordIterator( ULocale.Default, text );
+        scope(exit) wb.close();
+        FontMetrics fm = gc.getFontMetrics();
+        int lineHeight = fm.getHeight();
+        int descent = fm.getDescent();
+
+        int saved = 0;
+        int last = 0;
+        int y = bounds.y;
+        int width = bounds.width;
+
+        for (int loc = wb.first(); loc !is UBreakIterator.Done; loc = wb.next()) {
+            String line = text.substring(saved, loc);
+            Point extent = gc.textExtent(line);
+
+            if (extent.x > width) {
+                // overflow
+                String prevLine = text.substring(saved, last);
+                gc.drawText(prevLine, bounds.x, y, true);
+                if (underline) {
+                    Point prevExtent = gc.textExtent(prevLine);
+                    int lineY = y + lineHeight - descent + 1;
+                    gc
+                            .drawLine(bounds.x, lineY, bounds.x + prevExtent.x,
+                                    lineY);
+                }
+
+                saved = last;
+                y += lineHeight;
+            }
+            last = loc;
+        }
+        // paint the last line
+        String lastLine = text.substring(saved, last);
+        gc.drawText(lastLine, bounds.x, y, true);
+        if (underline) {
+            int lineY = y + lineHeight - descent + 1;
+            Point lastExtent = gc.textExtent(lastLine);
+            gc.drawLine(bounds.x, lineY, bounds.x + lastExtent.x, lineY);
+        }
+    }
+
+    public static ScrolledComposite getScrolledComposite(Control c) {
+        Composite parent = c.getParent();
+
+        while (parent !is null) {
+            if ( auto sc = cast(ScrolledComposite)parent ) {
+                return sc;
+            }
+            parent = parent.getParent();
+        }
+        return null;
+    }
+
+    public static void ensureVisible(Control c) {
+        ScrolledComposite scomp = getScrolledComposite(c);
+        if (scomp !is null) {
+            Object data = scomp.getData(FOCUS_SCROLLING);
+            if (data is null || !data.opEquals(Boolean.FALSE))
+                FormUtil.ensureVisible(scomp, c);
+        }
+    }
+
+    public static void ensureVisible(ScrolledComposite scomp, Control control) {
+        // if the control is a FormText we do not need to scroll since it will
+        // ensure visibility of its segments as necessary
+        if ( auto ft = cast(FormText)control )
+            return;
+        Point controlSize = control.getSize();
+        Point controlOrigin = getControlLocation(scomp, control);
+        ensureVisible(scomp, controlOrigin, controlSize);
+    }
+
+    public static void ensureVisible(ScrolledComposite scomp,
+            Point controlOrigin, Point controlSize) {
+        Rectangle area = scomp.getClientArea();
+        Point scompOrigin = scomp.getOrigin();
+
+        int x = scompOrigin.x;
+        int y = scompOrigin.y;
+
+        // horizontal right, but only if the control is smaller
+        // than the client area
+        if (controlSize.x < area.width
+                && (controlOrigin.x + controlSize.x > scompOrigin.x
+                        + area.width)) {
+            x = controlOrigin.x + controlSize.x - area.width;
+        }
+        // horizontal left - make sure the left edge of
+        // the control is showing
+        if (controlOrigin.x < x) {
+            if (controlSize.x < area.width)
+                x = controlOrigin.x + controlSize.x - area.width;
+            else
+                x = controlOrigin.x;
+        }
+        // vertical bottom
+        if (controlSize.y < area.height
+                && (controlOrigin.y + controlSize.y > scompOrigin.y
+                        + area.height)) {
+            y = controlOrigin.y + controlSize.y - area.height;
+        }
+        // vertical top - make sure the top of
+        // the control is showing
+        if (controlOrigin.y < y) {
+            if (controlSize.y < area.height)
+                y = controlOrigin.y + controlSize.y - area.height;
+            else
+                y = controlOrigin.y;
+        }
+
+        if (scompOrigin.x !is x || scompOrigin.y !is y) {
+            // scroll to reveal
+            scomp.setOrigin(x, y);
+        }
+    }
+
+    public static void ensureVisible(ScrolledComposite scomp, Control control,
+            MouseEvent e) {
+        Point controlOrigin = getControlLocation(scomp, control);
+        int rX = controlOrigin.x + e.x;
+        int rY = controlOrigin.y + e.y;
+        Rectangle area = scomp.getClientArea();
+        Point scompOrigin = scomp.getOrigin();
+
+        int x = scompOrigin.x;
+        int y = scompOrigin.y;
+        // System.out.println("Ensure: area="+area+", origin="+scompOrigin+",
+        // cloc="+controlOrigin+", csize="+controlSize+", x="+x+", y="+y);
+
+        // horizontal right
+        if (rX > scompOrigin.x + area.width) {
+            x = rX - area.width;
+        }
+        // horizontal left
+        else if (rX < x) {
+            x = rX;
+        }
+        // vertical bottom
+        if (rY > scompOrigin.y + area.height) {
+            y = rY - area.height;
+        }
+        // vertical top
+        else if (rY < y) {
+            y = rY;
+        }
+
+        if (scompOrigin.x !is x || scompOrigin.y !is y) {
+            // scroll to reveal
+            scomp.setOrigin(x, y);
+        }
+    }
+
+    public static Point getControlLocation(ScrolledComposite scomp,
+            Control control) {
+        int x = 0;
+        int y = 0;
+        Control content = scomp.getContent();
+        Control currentControl = control;
+        for (;;) {
+            if (currentControl is content)
+                break;
+            Point location = currentControl.getLocation();
+            // if (location.x > 0)
+            // x += location.x;
+            // if (location.y > 0)
+            // y += location.y;
+            x += location.x;
+            y += location.y;
+            currentControl = currentControl.getParent();
+        }
+        return new Point(x, y);
+    }
+
+    static void scrollVertical(ScrolledComposite scomp, bool up) {
+        scroll(scomp, 0, up ? -V_SCROLL_INCREMENT : V_SCROLL_INCREMENT);
+    }
+
+    static void scrollHorizontal(ScrolledComposite scomp, bool left) {
+        scroll(scomp, left ? -H_SCROLL_INCREMENT : H_SCROLL_INCREMENT, 0);
+    }
+
+    static void scrollPage(ScrolledComposite scomp, bool up) {
+        Rectangle clientArea = scomp.getClientArea();
+        int increment = up ? -clientArea.height : clientArea.height;
+        scroll(scomp, 0, increment);
+    }
+
+    static void scroll(ScrolledComposite scomp, int xoffset, int yoffset) {
+        Point origin = scomp.getOrigin();
+        Point contentSize = scomp.getContent().getSize();
+        int xorigin = origin.x + xoffset;
+        int yorigin = origin.y + yoffset;
+        xorigin = Math.max(xorigin, 0);
+        xorigin = Math.min(xorigin, contentSize.x - 1);
+        yorigin = Math.max(yorigin, 0);
+        yorigin = Math.min(yorigin, contentSize.y - 1);
+        scomp.setOrigin(xorigin, yorigin);
+    }
+
+    public static void updatePageIncrement(ScrolledComposite scomp) {
+        ScrollBar vbar = scomp.getVerticalBar();
+        if (vbar !is null) {
+            Rectangle clientArea = scomp.getClientArea();
+            int increment = clientArea.height - 5;
+            vbar.setPageIncrement(increment);
+        }
+        ScrollBar hbar = scomp.getHorizontalBar();
+        if (hbar !is null) {
+            Rectangle clientArea = scomp.getClientArea();
+            int increment = clientArea.width - 5;
+            hbar.setPageIncrement(increment);
+        }
+    }
+
+    public static void processKey(int keyCode, Control c) {
+        ScrolledComposite scomp = FormUtil.getScrolledComposite(c);
+        if (scomp !is null) {
+            if (null !is cast(Combo)c )
+                return;
+            switch (keyCode) {
+            case SWT.ARROW_DOWN:
+                if (scomp.getData("novarrows") is null) //$NON-NLS-1$
+                    FormUtil.scrollVertical(scomp, false);
+                break;
+            case SWT.ARROW_UP:
+                if (scomp.getData("novarrows") is null) //$NON-NLS-1$
+                    FormUtil.scrollVertical(scomp, true);
+                break;
+            case SWT.ARROW_LEFT:
+                FormUtil.scrollHorizontal(scomp, true);
+                break;
+            case SWT.ARROW_RIGHT:
+                FormUtil.scrollHorizontal(scomp, false);
+                break;
+            case SWT.PAGE_UP:
+                FormUtil.scrollPage(scomp, true);
+                break;
+            case SWT.PAGE_DOWN:
+                FormUtil.scrollPage(scomp, false);
+                break;
+            default:
+            }
+        }
+    }
+
+    public static bool isWrapControl(Control c) {
+        if ((c.getStyle() & SWT.WRAP) !is 0)
+            return true;
+        if (auto comp = cast(Composite)c ) {
+            return ( null !is cast(ILayoutExtension)( comp.getLayout() ));
+        }
+        return false;
+    }
+
+    public static int getWidthHint(int wHint, Control c) {
+        bool wrap = isWrapControl(c);
+        return wrap ? wHint : SWT.DEFAULT;
+    }
+
+    public static int getHeightHint(int hHint, Control c) {
+        if ( auto comp = cast(Composite)c ) {
+            Layout layout = comp.getLayout();
+            if (null !is cast(ColumnLayout)layout )
+                return hHint;
+        }
+        return SWT.DEFAULT;
+    }
+
+    public static int computeMinimumWidth(Control c, bool changed) {
+        if ( auto comp = cast(Composite)c ) {
+            Layout layout = comp.getLayout();
+            if ( auto le = cast(ILayoutExtension)layout )
+                return le.computeMinimumWidth(
+                        comp, changed);
+        }
+        return c.computeSize(FormUtil.getWidthHint(5, c), SWT.DEFAULT, changed).x;
+    }
+
+    public static int computeMaximumWidth(Control c, bool changed) {
+        if ( auto comp = cast(Composite)c ) {
+            Layout layout = comp.getLayout();
+            if ( auto le = cast(ILayoutExtension)layout )
+                return le.computeMaximumWidth(
+                        comp, changed);
+        }
+        return c.computeSize(SWT.DEFAULT, SWT.DEFAULT, changed).x;
+    }
+
+    public static Form getForm(Control c) {
+        Composite parent = c.getParent();
+        while (parent !is null) {
+            if ( auto frm = cast(Form)parent ) {
+                return frm;
+            }
+            parent = parent.getParent();
+        }
+        return null;
+    }
+
+    public static Image createAlphaMashImage(Device device, Image srcImage) {
+        Rectangle bounds = srcImage.getBounds();
+        int alpha = 0;
+        int calpha = 0;
+        ImageData data = srcImage.getImageData();
+        // Create a new image with alpha values alternating
+        // between fully transparent (0) and fully opaque (255).
+        // This image will show the background through the
+        // transparent pixels.
+        for (int i = 0; i < bounds.height; i++) {
+            // scan line
+            alpha = calpha;
+            for (int j = 0; j < bounds.width; j++) {
+                // column
+                data.setAlpha(j, i, alpha);
+                alpha = alpha is 255 ? 0 : 255;
+            }
+            calpha = calpha is 255 ? 0 : 255;
+        }
+        return new Image(device, data);
+    }
+
+    public static bool mnemonicMatch(String text, dchar key) {
+        char mnemonic = findMnemonic(text);
+        if (mnemonic is '\0')
+            return false;
+        return CharacterToUpper(key) is CharacterToUpper(mnemonic);
+    }
+
+    private static char findMnemonic(String string) {
+        int index = 0;
+        int length = string.length;
+        do {
+            while (index < length && string.charAt(index) !is '&')
+                index++;
+            if (++index >= length)
+                return '\0';
+            if (string.charAt(index) !is '&')
+                return string.charAt(index);
+            index++;
+        } while (index < length);
+        return '\0';
+    }
+
+    public static void setFocusScrollingEnabled(Control c, bool enabled) {
+        ScrolledComposite scomp = null;
+
+        if ( auto sc = cast(ScrolledComposite)c )
+            scomp = sc;
+        else
+            scomp = getScrolledComposite(c);
+        if (scomp !is null)
+            scomp.setData(FormUtil.FOCUS_SCROLLING, enabled ? null : Boolean.FALSE);
+    }
+
+    public static void setAntialias(GC gc, int style) {
+        if (!gc.getAdvanced()) {
+            gc.setAdvanced(true);
+            if (!gc.getAdvanced())
+                return;
+        }
+        gc.setAntialias(style);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/FormsResources.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.FormsResources;
+
+import java.lang.all;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Utility methods to access shared form-specific resources.
+ * <p>
+ * All methods declared on this class are static. This
+ * class cannot be instantiated.
+ * </p>
+ * <p>
+ * </p>
+ */
+public class FormsResources {
+    private static Cursor busyCursor;
+    private static Cursor handCursor;
+    private static Cursor textCursor;
+
+    public static Cursor getBusyCursor() {
+        if (busyCursor is null)
+            busyCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_WAIT);
+        return busyCursor;
+    }
+    public static Cursor getHandCursor() {
+        if (handCursor is null)
+            handCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_HAND);
+        return handCursor;
+    }
+    public static Cursor getTextCursor() {
+        if (textCursor is null)
+            textCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_IBEAM);
+        return textCursor;
+    }
+
+    public static int getProgressDelay(int index) {
+        /*
+        if (progressDelays is null)
+            return 0;
+        return progressDelays[index];
+        */
+        return 100;
+    }
+
+    public static void shutdown() {
+        if (busyCursor !is null)
+            busyCursor.dispose();
+        if (handCursor !is null)
+            handCursor.dispose();
+        if (textCursor !is null)
+            textCursor.dispose();
+        busyCursor=null;
+        handCursor=null;
+        textCursor=null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/IFocusSelectable.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.IFocusSelectable;
+
+
+import org.eclipse.swt.graphics.Rectangle;
+import java.lang.all;
+import java.util.Set;
+
+public interface IFocusSelectable {
+    bool isFocusSelectable(Hashtable resourceTable);
+    bool setFocus(Hashtable resourceTable, bool direction);
+    Rectangle getBounds();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/IHyperlinkSegment.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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
+ *     Chriss Gross (schtoo@schtoo.com) - fix for 61670
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment;
+
+import org.eclipse.ui.internal.forms.widgets.IFocusSelectable;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+
+import java.lang.all;
+
+public interface IHyperlinkSegment : IFocusSelectable {
+    String getHref();
+    String getText();
+    void paintFocus(GC gc, Color bg, Color fg, bool selected, Rectangle repaintRegion);
+    bool contains(int x, int y);
+    bool intersects(Rectangle rect);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/ImageHyperlinkSegment.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.ImageHyperlinkSegment;
+
+import org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment;
+import org.eclipse.ui.internal.forms.widgets.ImageSegment;
+
+//import java.util.Hashtable;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+
+import java.lang.all;
+import java.util.Set;
+
+public class ImageHyperlinkSegment : ImageSegment,
+        IHyperlinkSegment {
+    private String href;
+    private String text;
+
+    private String tooltipText;
+
+    // reimpl for interface
+    bool contains(int x, int y){
+        return super.contains(x,y);
+    }
+    bool intersects(Rectangle rect){
+        return super.intersects(rect);
+    }
+
+    public this() {
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment#setHref(java.lang.String)
+     */
+    public void setHref(String href) {
+        this.href = href;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment#getHref()
+     */
+    public String getHref() {
+        return href;
+    }
+
+    public void paintFocus(GC gc, Color bg, Color fg, bool selected,
+            Rectangle repaintRegion) {
+        Rectangle bounds = getBounds();
+        if (bounds is null)
+            return;
+        if (selected) {
+            gc.setBackground(bg);
+            gc.setForeground(fg);
+            gc.drawFocus(bounds.x, bounds.y, bounds.width, bounds.height);
+        } else {
+            gc.setForeground(bg);
+            gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1,
+                    bounds.height - 1);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment#isWordWrapAllowed()
+     */
+    public bool isWordWrapAllowed() {
+        return !isNowrap();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment#setWordWrapAllowed(bool)
+     */
+    public void setWordWrapAllowed(bool value) {
+        setNowrap(!value);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment#getText()
+     */
+    public String getText() {
+        return text !is null?text:""; //$NON-NLS-1$
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    /**
+     * @return Returns the tooltipText.
+     */
+    public String getTooltipText() {
+        return tooltipText;
+    }
+
+    /**
+     * @param tooltipText
+     *            The tooltipText to set.
+     */
+    public void setTooltipText(String tooltipText) {
+        this.tooltipText = tooltipText;
+    }
+
+    public bool isSelectable() {
+        return true;
+    }
+
+    public bool isFocusSelectable(Hashtable resourceTable) {
+        return true;
+    }
+
+    public bool setFocus(Hashtable resourceTable, bool direction) {
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/ImageSegment.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.ImageSegment;
+
+import org.eclipse.ui.internal.forms.widgets.ObjectSegment;
+import org.eclipse.ui.internal.forms.widgets.SelectionData;
+import org.eclipse.ui.internal.forms.widgets.FormUtil;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * @version 1.0
+ * @author
+ */
+public class ImageSegment : ObjectSegment {
+    public static const String SEL_IMAGE_PREFIX = "isel."; //$NON-NLS-1$
+
+    public Image getImage(Hashtable objectTable) {
+        return getImage(getObjectId(), objectTable);
+    }
+
+    private Image getImage(String key, Hashtable objectTable) {
+        if (key is null)
+            return null;
+        Object obj = objectTable.get(key);
+        if (obj is null)
+            return null;
+        if ( auto image = cast(Image)obj )
+            return image;
+        return null;
+    }
+
+    private Image getSelectedImage(Hashtable objectTable, SelectionData selData) {
+        String key = SEL_IMAGE_PREFIX ~ getObjectId();
+        Image image = getImage(key, objectTable);
+        if (image is null) {
+            image = FormUtil.createAlphaMashImage(selData.display, getImage(objectTable));
+            if (image !is null)
+                objectTable.put(key, image);
+        }
+        return image;
+    }
+/*
+    private String getSelectedImageId() {
+        if (getObjectId() is null)
+            return null;
+        return SEL_IMAGE_PREFIX + getObjectId();
+    }
+*/
+
+    public void paint(GC gc, bool hover, Hashtable resourceTable, bool selected, SelectionData selData, Rectangle repaintRegion) {
+        Image image = getImage(resourceTable);
+        int iwidth = 0;
+        int iheight = 0;
+        if (image !is null) {
+            Rectangle rect = image.getBounds();
+            iwidth = rect.width + (isSelectable()?2:0);
+            iheight = rect.height + (isSelectable()?2:0);
+        } else
+            return;
+        Rectangle bounds = getBounds();
+        int ix = bounds.x+(isSelectable()?1:0);
+        int iy = bounds.y+(isSelectable()?1:0);
+
+        if (selData !is null) {
+            int leftOffset = selData.getLeftOffset(bounds.height);
+            int rightOffset = selData.getRightOffset(bounds.height);
+            bool firstRow = selData.isFirstSelectionRow(bounds.y,
+                    bounds.height);
+            bool lastRow = selData.isLastSelectionRow(bounds.y,
+                    bounds.height);
+            bool selectedRow = selData
+                    .isSelectedRow(bounds.y, bounds.height);
+            if (selectedRow) {
+                if ((firstRow && leftOffset > ix) ||
+                    (lastRow && rightOffset < ix + iwidth/2)) {
+                    drawClipImage(gc, image, ix, iy, repaintRegion);
+                }
+                else {
+                    Color savedBg = gc.getBackground();
+                    gc.setBackground(selData.bg);
+                    int sx = ix;
+                    int sy = iy;
+                    if (repaintRegion !is null) {
+                        sx -= repaintRegion.x;
+                        sy -= repaintRegion.y;
+                    }
+                    gc.fillRectangle(sx, sy, iwidth, iheight);
+                    Image selImage = getSelectedImage(resourceTable, selData);
+                    gc.drawImage(selImage, sx, sy);
+                    gc.setBackground(savedBg);
+                }
+            }
+            else
+                drawClipImage(gc, image, ix, iy, repaintRegion);
+        } else
+            drawClipImage(gc, image, ix, iy, repaintRegion);
+        if (selected) {
+            int fx = bounds.x;
+            int fy = bounds.y;
+            if (repaintRegion !is null) {
+                fx -= repaintRegion.x;
+                fy -= repaintRegion.y;
+            }
+            Color fg = gc.getForeground();
+            gc.setForeground(gc.getBackground());
+            // Clean up to avoid canceling out XOR if it is already
+            // selected.
+            gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1,
+                    bounds.height - 1);
+            gc.setForeground(fg);
+            gc.drawFocus(fx, fy, bounds.width, bounds.height);
+        }
+    }
+    private void drawClipImage(GC gc, Image image, int ix, int iy, Rectangle repaintRegion) {
+        if (repaintRegion !is null) {
+            ix -= repaintRegion.x;
+            iy -= repaintRegion.y;
+        }
+        gc.drawImage(image, ix, iy);
+    }
+
+    protected Point getObjectSize(Hashtable resourceTable, int wHint) {
+        Image image = getImage(resourceTable);
+        if (image is null)
+            return new Point(0, 0);
+        Rectangle ibounds = image.getBounds();
+        return new Point(ibounds.width, ibounds.height);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/Locator.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.Locator;
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Set;
+
+public class Locator : Cloneable {
+    public int indent;
+    public int x, y;
+    public int width;
+    public int leading;
+    public int rowHeight;
+    public int marginWidth;
+    public int marginHeight;
+    public int rowCounter;
+    public ArrayList heights;
+
+    public Locator clone(){
+        auto res = new Locator();
+        res.indent = indent;
+        res.x = x;
+        res.y = y;
+        res.width = width;
+        res.leading = leading;
+        res.rowHeight = rowHeight;
+        res.marginWidth = marginWidth;
+        res.marginHeight = marginHeight;
+        res.rowCounter = rowCounter;
+        res.heights = heights;
+        return res;
+    }
+
+    public void newLine() {
+        resetCaret();
+        y += rowHeight;
+        rowHeight = 0;
+    }
+
+    public Locator create() {
+//         try {
+            return cast(Locator)clone();
+//         }
+//         catch (CloneNotSupportedException e) {
+//             return null;
+//         }
+    }
+    public void collectHeights() {
+        heights.add(new ArrayWrapperObject( [ new Integer(rowHeight), new Integer(leading) ] ) );
+        rowCounter++;
+    }
+    public int getBaseline(int segmentHeight) {
+        return getBaseline(segmentHeight, true);
+
+    }
+    public int getMiddle(int segmentHeight, bool text) {
+        if (heights !is null && heights.size() > rowCounter) {
+            Integer [] rdata = arrayFromObject!(Integer)(heights.get(rowCounter));
+            int rheight = rdata[0].value;
+            int rleading = rdata[1].value;
+            if (text)
+                return y + rheight/2 - segmentHeight/2 - rleading;
+            return y + rheight/2 - segmentHeight/2;
+        }
+        return y;
+    }
+    public int getBaseline(int segmentHeight, bool text) {
+        if (heights !is null && heights.size()>rowCounter) {
+            Integer [] rdata = arrayFromObject!(Integer)(heights.get(rowCounter));
+            int rheight = rdata[0].value;
+            int rleading = rdata[1].value;
+            if (text)
+                return y + rheight - segmentHeight - rleading;
+            return y + rheight - segmentHeight;
+        }
+        return y;
+    }
+
+    public void resetCaret() {
+        x = getStartX();
+    }
+    public int getStartX() {
+        return marginWidth + indent;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/ObjectSegment.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.ObjectSegment;
+
+import org.eclipse.ui.internal.forms.widgets.ParagraphSegment;
+import org.eclipse.ui.internal.forms.widgets.Locator;
+import org.eclipse.ui.internal.forms.widgets.SelectionData;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+
+import java.lang.all;
+import java.util.Set;
+
+public abstract class ObjectSegment : ParagraphSegment {
+    public static const int TOP = 1;
+
+    public static const int MIDDLE = 2;
+
+    public static const int BOTTOM = 3;
+
+    private int alignment = BOTTOM;
+    private bool nowrap=false;
+    private Rectangle bounds;
+    private String objectId;
+
+    public int getVerticalAlignment() {
+        return alignment;
+    }
+
+    void setVerticalAlignment(int alignment) {
+        this.alignment = alignment;
+    }
+
+    public String getObjectId() {
+        return objectId;
+    }
+
+    void setObjectId(String objectId) {
+        this.objectId = objectId;
+    }
+
+    protected abstract Point getObjectSize(Hashtable resourceTable, int wHint);
+
+    public bool advanceLocator(GC gc, int wHint, Locator loc,
+            Hashtable objectTable, bool computeHeightOnly) {
+        Point objectSize = getObjectSize(objectTable, wHint);
+        int iwidth = 0;
+        int iheight = 0;
+        bool newLine = false;
+
+        if (objectSize !is null) {
+            iwidth = objectSize.x + (isSelectable()?2:0);
+            iheight = objectSize.y + (isSelectable()?2:0);
+        }
+        if (wHint !is SWT.DEFAULT && !nowrap && loc.x + iwidth > wHint) {
+            // new line
+            if (computeHeightOnly)
+                loc.collectHeights();
+            loc.x = loc.indent;
+            loc.x += iwidth;
+            loc.y += loc.rowHeight;
+            loc.width = loc.indent + iwidth;
+            loc.rowHeight = iheight;
+            loc.leading = 0;
+            newLine = true;
+        } else {
+            loc.x += iwidth;
+            loc.width += iwidth;
+            loc.rowHeight = Math.max(loc.rowHeight, iheight);
+        }
+        return newLine;
+    }
+
+    public bool contains(int x, int y) {
+        if (bounds is null)
+            return false;
+        return bounds.contains(x, y);
+    }
+    public bool intersects(Rectangle rect) {
+        if (bounds is null)
+            return false;
+        return bounds.intersects(rect);
+    }
+
+    public Rectangle getBounds() {
+        return bounds;
+    }
+
+    public bool isSelectable() {
+        return false;
+    }
+    /**
+     * @return Returns the nowrap.
+     */
+    public bool isNowrap() {
+        return nowrap;
+    }
+    /**
+     * @param nowrap The nowrap to set.
+     */
+    public void setNowrap(bool nowrap) {
+        this.nowrap = nowrap;
+    }
+    public void paint(GC gc, bool hover, Hashtable resourceTable, bool selected, SelectionData selData, Rectangle repaintRegion) {
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.ui.internal.forms.widgets.ParagraphSegment#layout(org.eclipse.swt.graphics.GC, int, org.eclipse.ui.internal.forms.widgets.Locator, java.util.Hashtable, bool, org.eclipse.ui.internal.forms.widgets.SelectionData)
+     */
+    public void layout(GC gc, int width, Locator loc, Hashtable resourceTable,
+            bool selected) {
+        Point size = getObjectSize(resourceTable, width);
+
+        int objWidth = 0;
+        int objHeight = 0;
+        if (size !is null) {
+            objWidth = size.x + (isSelectable()?2:0);
+            objHeight = size.y + (isSelectable()?2:0);
+        } else
+            return;
+        loc.width = objWidth;
+
+        if (!nowrap && loc.x + objWidth > width) {
+            // new row
+            loc.newLine();
+            loc.rowCounter++;
+        }
+        int ix = loc.x;
+        int iy = loc.y;
+
+        if (alignment is MIDDLE)
+            iy = loc.getMiddle(objHeight, false);
+        else if (alignment is BOTTOM)
+            iy = loc.getBaseline(objHeight, false);
+        loc.x += objWidth;
+        loc.rowHeight = Math.max(loc.rowHeight, objHeight);
+        bounds = new Rectangle(ix, iy, objWidth, objHeight);
+    }
+    /* (non-Javadoc)
+     * @see org.eclipse.ui.internal.forms.widgets.ParagraphSegment#computeSelection(org.eclipse.swt.graphics.GC, java.util.Hashtable, bool, org.eclipse.ui.internal.forms.widgets.SelectionData)
+     */
+    public void computeSelection(GC gc, Hashtable resourceTable, SelectionData selData) {
+        // TODO we should add this to the selection
+        // if we want to support rich text
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/Paragraph.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,234 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.Paragraph;
+
+import org.eclipse.ui.internal.forms.widgets.ParagraphSegment;
+import org.eclipse.ui.internal.forms.widgets.Locator;
+import org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment;
+import org.eclipse.ui.internal.forms.widgets.SelectionData;
+import org.eclipse.ui.internal.forms.widgets.TextSegment;
+import org.eclipse.ui.internal.forms.widgets.TextHyperlinkSegment;
+
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.ui.forms.HyperlinkSettings;
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Set;
+import tango.io.model.IFile;
+
+import tango.text.Text;
+
+/**
+ * @version 1.0
+ * @author
+ */
+public class Paragraph {
+    public static const String HTTP = "http://"; //$NON-NLS-1$
+
+    private Vector segments;
+
+    private bool addVerticalSpace = true;
+
+    public this(bool addVerticalSpace) {
+        this.addVerticalSpace = addVerticalSpace;
+    }
+
+    public int getIndent() {
+        return 0;
+    }
+
+    public bool getAddVerticalSpace() {
+        return addVerticalSpace;
+    }
+
+    /*
+     * @see IParagraph#getSegments()
+     */
+    public ParagraphSegment[] getSegments() {
+        if (segments is null)
+            return null;
+        return arraycast!(ParagraphSegment)(segments
+                .toArray());
+    }
+
+    public void addSegment(ParagraphSegment segment) {
+        if (segments is null)
+            segments = new Vector;
+        segments.add(segment);
+    }
+
+    public void parseRegularText(String text, bool expandURLs, bool wrapAllowed,
+            HyperlinkSettings settings, String fontId) {
+        parseRegularText(text, expandURLs, wrapAllowed, settings, fontId, null);
+    }
+
+    public void parseRegularText(String text, bool expandURLs, bool wrapAllowed,
+            HyperlinkSettings settings, String fontId, String colorId) {
+        if (text.length is 0)
+            return;
+        if (expandURLs) {
+            int loc = text.indexOf(HTTP);
+
+            if (loc is -1)
+                addSegment(new TextSegment(text, fontId, colorId, wrapAllowed));
+            else {
+                int textLoc = 0;
+                while (loc !is -1) {
+                    addSegment(new TextSegment(text.substring(textLoc, loc),
+                            fontId, colorId, wrapAllowed));
+                    bool added = false;
+                    for (textLoc = loc; textLoc < text.length; textLoc++) {
+                        char c = text.charAt(textLoc);
+                        if (CharacterIsWhitespace(c)) {
+                            addHyperlinkSegment(text.substring(loc, textLoc),
+                                    settings, fontId);
+                            added = true;
+                            break;
+                        }
+                    }
+                    if (!added) {
+                        // there was no space - just end of text
+                        addHyperlinkSegment(text.substring(loc), settings,
+                                fontId);
+                        break;
+                    }
+                    loc = text.indexOf(HTTP, textLoc);
+                }
+                if (textLoc < text.length) {
+                    addSegment(new TextSegment(text.substring(textLoc), fontId,
+                            colorId, wrapAllowed));
+                }
+            }
+        } else {
+            addSegment(new TextSegment(text, fontId, colorId, wrapAllowed));
+        }
+    }
+
+    private void addHyperlinkSegment(String text, HyperlinkSettings settings,
+            String fontId) {
+        TextHyperlinkSegment hs = new TextHyperlinkSegment(text, settings,
+                fontId);
+        hs.setWordWrapAllowed(false);
+        hs.setHref(text);
+        addSegment(hs);
+    }
+
+    protected void computeRowHeights(GC gc, int width, Locator loc,
+            int lineHeight, Hashtable resourceTable) {
+        ParagraphSegment[] segments = getSegments();
+        // compute heights
+        Locator hloc = loc.create();
+        ArrayList heights = new ArrayList();
+        hloc.heights = heights;
+        hloc.rowCounter = 0;
+        int innerWidth = width - loc.marginWidth*2;
+        for (int j = 0; j < segments.length; j++) {
+            ParagraphSegment segment = segments[j];
+            segment.advanceLocator(gc, innerWidth, hloc, resourceTable, true);
+        }
+        hloc.collectHeights();
+        loc.heights = heights;
+        loc.rowCounter = 0;
+    }
+
+    public void layout(GC gc, int width, Locator loc, int lineHeight,
+            Hashtable resourceTable, IHyperlinkSegment selectedLink) {
+        ParagraphSegment[] segments = getSegments();
+        //int height;
+        if (segments.length > 0) {
+            /*
+            if (segments[0] instanceof TextSegment
+                    && ((TextSegment) segments[0]).isSelectable())
+                loc.x += 1;
+            */
+            // compute heights
+            if (loc.heights is null)
+                computeRowHeights(gc, width, loc, lineHeight, resourceTable);
+            for (int j = 0; j < segments.length; j++) {
+                ParagraphSegment segment = segments[j];
+                bool doSelect = false;
+                if (selectedLink !is null && segment.opEquals(cast(Object)selectedLink))
+                    doSelect = true;
+                segment.layout(gc, width, loc, resourceTable, doSelect);
+            }
+            loc.heights = null;
+            loc.y += loc.rowHeight;
+        } else {
+            loc.y += lineHeight;
+        }
+    }
+
+    public void paint(GC gc, Rectangle repaintRegion,
+            Hashtable resourceTable, IHyperlinkSegment selectedLink,
+            SelectionData selData) {
+        ParagraphSegment[] segments = getSegments();
+
+        for (int i = 0; i < segments.length; i++) {
+            ParagraphSegment segment = segments[i];
+            if (!segment.intersects(repaintRegion))
+                continue;
+            bool doSelect = false;
+            if (selectedLink !is null && segment.opEquals(cast(Object)selectedLink))
+                doSelect = true;
+            segment.paint(gc, false, resourceTable, doSelect, selData, repaintRegion);
+        }
+    }
+
+    public void computeSelection(GC gc, Hashtable resourceTable, IHyperlinkSegment selectedLink,
+            SelectionData selData) {
+        ParagraphSegment[] segments = getSegments();
+
+        for (int i = 0; i < segments.length; i++) {
+            ParagraphSegment segment = segments[i];
+            //bool doSelect = false;
+            //if (selectedLink !is null && segment.equals(selectedLink))
+                //doSelect = true;
+            segment.computeSelection(gc, resourceTable, selData);
+        }
+    }
+
+    public String getAccessibleText() {
+        ParagraphSegment[] segments = getSegments();
+        auto txt = new tango.text.Text.Text!(char);
+        for (int i = 0; i < segments.length; i++) {
+            ParagraphSegment segment = segments[i];
+            if ( auto ts = cast(TextSegment)segment ) {
+                String text = ts.getText();
+                txt.append(text);
+            }
+        }
+        txt.append( FileConst.NewlineString );
+        return txt.toString();
+    }
+
+    public ParagraphSegment findSegmentAt(int x, int y) {
+        if (segments !is null) {
+            for (int i = 0; i < segments.size(); i++) {
+                ParagraphSegment segment = cast(ParagraphSegment) segments.get(i);
+                if (segment.contains(x, y))
+                    return segment;
+            }
+        }
+        return null;
+    }
+    public void clearCache(String fontId) {
+        if (segments !is null) {
+            for (int i = 0; i < segments.size(); i++) {
+                ParagraphSegment segment = cast(ParagraphSegment) segments.get(i);
+                segment.clearCache(fontId);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/ParagraphSegment.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.ParagraphSegment;
+
+import org.eclipse.ui.internal.forms.widgets.Locator;
+import org.eclipse.ui.internal.forms.widgets.SelectionData;
+
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+
+import java.lang.all;
+
+/**
+ * @version     1.0
+ * @author
+ */
+public abstract class ParagraphSegment {
+    /**
+     * Moves the locator according to the content of this segment.
+     * @param gc
+     * @param wHint
+     * @param loc
+     * @param objectTable
+     * @param computeHeightOnly
+     * @return <code>true</code> if text wrapped to the new line, <code>false</code> otherwise.
+     */
+    public abstract bool advanceLocator(GC gc, int wHint, Locator loc, Hashtable objectTable, bool computeHeightOnly);
+    /**
+     * Computes bounding rectangles and row heights of this segments.
+     * @param gc
+     * @param width
+     * @param loc
+     * @param resourceTable
+     * @param selected
+     */
+    public abstract void layout(GC gc, int width, Locator loc, Hashtable resourceTable, bool selected);
+    /**
+     * Paints this segment.
+     * @param gc
+     * @param hover
+     * @param resourceTable
+     * @param selected
+     * @param selData
+     * @param region
+     */
+    public abstract void paint(GC gc, bool hover, Hashtable resourceTable, bool selected, SelectionData selData, Rectangle region);
+    /**
+     * Paints this segment.
+     * @param gc
+     * @param resourceTable
+     * @param selData
+     */
+    public abstract void computeSelection(GC gc, Hashtable resourceTable, SelectionData selData);
+    /**
+     * Tests if the coordinates are contained in one of the
+     * bounding rectangles of this segment.
+     * @param x
+     * @param y
+     * @return true if inside the bounding rectangle, false otherwise.
+     */
+    public abstract bool contains(int x, int y);
+    /**
+     * Tests if the source rectangle intersects with
+     * one of the bounding rectangles of this segment.
+     * @param rect
+     * @return true if the two rectangles intersect, false otherwise.
+     */
+    public abstract bool intersects(Rectangle rect);
+    /**
+     * Returns the tool tip of this segment or <code>null</code>
+     * if not defined.
+     * @return tooltip or <code>null</code>.
+     */
+    public String getTooltipText() {
+        return null;
+    }
+    /**
+     * Clears the text metrics cache for the provided font id.
+     * @param fontId the id of the font that the cache is kept for.
+     *
+     */
+    public void clearCache(String fontId) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/PixelConverter.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.PixelConverter;
+
+import java.lang.all;
+
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Control;
+
+public class PixelConverter {
+    /**
+     * Number of horizontal dialog units per character, value <code>4</code>.
+     */
+    private static const int HORIZONTAL_DIALOG_UNIT_PER_CHAR = 4;
+
+    private FontMetrics fFontMetrics;
+
+    public this(Control control) {
+        GC gc = new GC(control);
+        gc.setFont(control.getFont());
+        fFontMetrics = gc.getFontMetrics();
+        gc.dispose();
+    }
+
+    public int convertHorizontalDLUsToPixels(int dlus) {
+        // round to the nearest pixel
+        return (fFontMetrics.getAverageCharWidth() * dlus + HORIZONTAL_DIALOG_UNIT_PER_CHAR / 2)
+                / HORIZONTAL_DIALOG_UNIT_PER_CHAR;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/SWTUtil.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.SWTUtil;
+
+import java.lang.all;
+import org.eclipse.swt.dnd.DragSource;
+import org.eclipse.swt.dnd.DropTarget;
+import org.eclipse.swt.widgets.Caret;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * Utility class to simplify access to some SWT resources.
+ */
+public class SWTUtil {
+
+    /**
+     * Returns the standard display to be used. The method first checks, if
+     * the thread calling this method has an associated disaply. If so, this
+     * display is returned. Otherwise the method returns the default display.
+     */
+    public static Display getStandardDisplay() {
+        Display display;
+        display = Display.getCurrent();
+        if (display is null)
+            display = Display.getDefault();
+        return display;
+    }
+
+    /**
+     * Returns the shell for the given widget. If the widget doesn't represent
+     * a SWT object that manage a shell, <code>null</code> is returned.
+     *
+     * @return the shell for the given widget
+     */
+    public static Shell getShell(Widget widget) {
+        if (null !is cast(Control)widget )
+            return (cast(Control) widget).getShell();
+        if (null !is cast(Caret)widget )
+            return (cast(Caret) widget).getParent().getShell();
+        if (null !is cast(DragSource)widget )
+            return (cast(DragSource) widget).getControl().getShell();
+        if (null !is cast(DropTarget)widget )
+            return (cast(DropTarget) widget).getControl().getShell();
+        if (null !is cast(Menu)widget )
+            return (cast(Menu) widget).getParent().getShell();
+        if (null !is cast(ScrollBar)widget )
+            return (cast(ScrollBar) widget).getParent().getShell();
+
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/SelectionData.d	Sat Mar 14 18:23:29 2009 +0100
@@ -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 org.eclipse.ui.internal.forms.widgets.SelectionData;
+
+import org.eclipse.ui.internal.forms.widgets.Locator;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Display;
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Set;
+
+static import tango.text.Text;
+
+public class SelectionData {
+    public Display display;
+    public Color bg;
+    public Color fg;
+    private Point start;
+    private Point stop;
+    private ArrayList segments;
+    private bool newLineNeeded;
+
+    public this(MouseEvent e) {
+        display = e.display;
+        segments = new ArrayList();
+        start = new Point(e.x, e.y);
+        stop = new Point(e.x, e.y);
+        bg = e.display.getSystemColor(SWT.COLOR_LIST_SELECTION);
+        fg = e.display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
+    }
+
+    public void markNewLine() {
+        newLineNeeded=true;
+    }
+    public void addSegment(String text) {
+        if (newLineNeeded) {
+            segments.add(System.getProperty("line.separator")); //$NON-NLS-1$
+            newLineNeeded=false;
+        }
+        segments.add(text);
+    }
+
+    public void update(MouseEvent e) {
+        //Control c = (Control)e.widget;
+        stop.x = e.x;
+        stop.y = e.y;
+    }
+    public void reset() {
+        segments.clear();
+    }
+    public String getSelectionText() {
+        auto buf = new tango.text.Text.Text!(char);
+        for (int i=0; i<segments.size(); i++) {
+            buf.append(stringcast(segments.get(i)));
+        }
+        return buf.toString();
+    }
+    public bool canCopy() {
+        return segments.size()>0;
+    }
+
+    private int getTopOffset() {
+        return start.y<stop.y?start.y:stop.y;
+    }
+    private int getBottomOffset() {
+        return start.y>stop.y?start.y:stop.y;
+    }
+    public int getLeftOffset(Locator locator) {
+        return isInverted(locator)? stop.x:start.x;
+    }
+    public int getLeftOffset(int rowHeight) {
+        return isInverted(rowHeight) ? stop.x:start.x;
+    }
+    public int getRightOffset(Locator locator) {
+        return isInverted(locator)? start.x: stop.x;
+    }
+    public int getRightOffset(int rowHeight) {
+        return isInverted(rowHeight) ? start.x:stop.x;
+    }
+    private bool isInverted(Locator locator) {
+        int rowHeight = arrayFromObject!(Integer)(locator.heights.get(locator.rowCounter))[0].value;
+        return isInverted(rowHeight);
+    }
+    private bool isInverted(int rowHeight) {
+        int deltaY = start.y - stop.y;
+        if (Math.abs(deltaY) > rowHeight) {
+            // inter-row selection
+            return deltaY>0;
+        }
+        // intra-row selection
+        return start.x > stop.x;
+    }
+    public bool isEnclosed() {
+        return !start.opEquals(stop);
+    }
+
+    public bool isSelectedRow(Locator locator) {
+        if (!isEnclosed())
+            return false;
+        int rowHeight =  arrayFromObject!(Integer)(locator.heights.get(locator.rowCounter))[0].value;
+        return isSelectedRow(locator.y, rowHeight);
+    }
+    public bool isSelectedRow(int y, int rowHeight) {
+        if (!isEnclosed())
+            return false;
+        return (y + rowHeight >= getTopOffset() &&
+                y <= getBottomOffset());
+    }
+    public bool isFirstSelectionRow(Locator locator) {
+        if (!isEnclosed())
+            return false;
+        int rowHeight =  arrayFromObject!(Integer)(locator.heights.get(locator.rowCounter))[0].value;
+        return (locator.y + rowHeight >= getTopOffset() &&
+                locator.y <= getTopOffset());
+    }
+    public bool isFirstSelectionRow(int y, int rowHeight) {
+        if (!isEnclosed())
+            return false;
+        return (y + rowHeight >= getTopOffset() &&
+                y <= getTopOffset());
+    }
+    public bool isLastSelectionRow(Locator locator) {
+        if (!isEnclosed())
+            return false;
+        int rowHeight = arrayFromObject!(Integer)(locator.heights.get(locator.rowCounter))[0].value;
+        return (locator.y + rowHeight >=getBottomOffset() &&
+                locator.y <= getBottomOffset());
+    }
+    public bool isLastSelectionRow(int y, int rowHeight) {
+        if (!isEnclosed())
+            return false;
+        return (y + rowHeight >=getBottomOffset() &&
+                y <= getBottomOffset());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/TextHyperlinkSegment.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.TextHyperlinkSegment;
+
+import org.eclipse.ui.internal.forms.widgets.TextSegment;
+import org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment;
+import org.eclipse.ui.internal.forms.widgets.SelectionData;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.ui.forms.HyperlinkSettings;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * @version 1.0
+ * @author
+ */
+public class TextHyperlinkSegment : TextSegment,
+        IHyperlinkSegment {
+    private String href;
+
+    private String tooltipText;
+
+    //private static final String LINK_FG = "c.___link_fg";
+
+    private HyperlinkSettings settings;
+
+    // reimpl for interface
+    String getText(){
+        return super.getText();
+    }
+    void paintFocus(GC gc, Color bg, Color fg, bool selected, Rectangle repaintRegion){
+        super.paintFocus(gc,bg,fg,selected,repaintRegion);
+    }
+    bool contains(int x, int y){
+        return super.contains(x,y);
+    }
+    bool intersects(Rectangle rect){
+        return super.intersects(rect);
+    }
+
+    public this(String text, HyperlinkSettings settings,
+            String fontId) {
+        super(text, fontId);
+        this.settings = settings;
+        underline = settings.getHyperlinkUnderlineMode() is HyperlinkSettings.UNDERLINE_ALWAYS;
+    }
+
+    /*
+     * @see IObjectReference#getObjectId()
+     */
+    public String getHref() {
+        return href;
+    }
+
+    public void setHref(String href) {
+        this.href = href;
+    }
+
+    /*
+     * public void paint(GC gc, int width, Locator locator, Hashtable
+     * resourceTable, bool selected, SelectionData selData) {
+     * resourceTable.put(LINK_FG, settings.getForeground());
+     * setColorId(LINK_FG); super.paint(gc, width, locator, resourceTable,
+     * selected, selData); }
+     */
+
+    public void paint(GC gc, bool hover, Hashtable resourceTable,
+            bool selected, SelectionData selData, Rectangle repaintRegion) {
+        bool rolloverMode = settings.getHyperlinkUnderlineMode() is HyperlinkSettings.UNDERLINE_HOVER;
+        Color savedFg = gc.getForeground();
+        Color newFg = hover ? settings.getActiveForeground() : settings
+                .getForeground();
+        if (newFg !is null)
+            gc.setForeground(newFg);
+        super.paint(gc, hover, resourceTable, selected, rolloverMode, selData,
+                repaintRegion);
+        gc.setForeground(savedFg);
+    }
+
+    protected void drawString(GC gc, String s, int clipX, int clipY) {
+        gc.drawString(s, clipX, clipY, false);
+    }
+
+    public String getTooltipText() {
+        return tooltipText;
+    }
+
+    public void setTooltipText(String tooltip) {
+        this.tooltipText = tooltip;
+    }
+
+    public bool isSelectable() {
+        return true;
+    }
+
+    public bool isFocusSelectable(Hashtable resourceTable) {
+        return true;
+    }
+
+    public bool setFocus(Hashtable resourceTable, bool direction) {
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/TextSegment.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,741 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.TextSegment;
+
+import org.eclipse.ui.internal.forms.widgets.ParagraphSegment;
+import org.eclipse.ui.internal.forms.widgets.Locator;
+import org.eclipse.ui.internal.forms.widgets.SelectionData;
+import org.eclipse.ui.internal.forms.widgets.FormTextModel;
+
+// import com.ibm.icu.text.BreakIterator;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+
+import java.lang.all;
+import java.util.ArrayList;
+import java.util.Set;
+import org.eclipse.dwtxhelper.mangoicu.UBreakIterator;
+
+/**
+ * @version 1.0
+ * @author
+ */
+public class TextSegment : ParagraphSegment {
+
+    private String colorId;
+
+    private String fontId;
+
+    private String text;
+
+    protected bool underline;
+
+    private bool wrapAllowed = true;
+
+    protected Vector areaRectangles;
+
+    private TextFragment[] textFragments;
+
+    class AreaRectangle {
+        Rectangle rect;
+
+        int from, to;
+
+        public this(Rectangle rect, int from, int to) {
+            this.rect = rect;
+            this.from = from;
+            this.to = to;
+        }
+
+        public bool contains(int x, int y) {
+            return rect.contains(x, y);
+        }
+
+        public bool intersects(Rectangle region) {
+            return rect.intersects(region);
+        }
+
+        public String getText() {
+            if (from is 0 && to is -1)
+                return this.outer.getText();
+            if (from > 0 && to is -1)
+                return this.outer.getText().substring(from);
+            return this.outer.getText().substring(from, to);
+        }
+    }
+
+    static class SelectionRange {
+        public int start;
+
+        public int stop;
+
+        public this() {
+            reset();
+        }
+
+        public void reset() {
+            start = -1;
+            stop = -1;
+        }
+    }
+
+    static class TextFragment {
+        short index;
+
+        short length;
+
+        public this(short index, short length) {
+            this.index = index;
+            this.length = length;
+        }
+    }
+
+    public this(String text, String fontId) {
+        this(text, fontId, null, true);
+    }
+
+    public this(String text, String fontId, String colorId) {
+        this(text, fontId, colorId, true);
+    }
+
+    public this(String text, String fontId, String colorId, bool wrapAllowed) {
+        areaRectangles = new Vector();
+        this.text = cleanup(text);
+        this.fontId = fontId;
+        this.colorId = colorId;
+        this.wrapAllowed = wrapAllowed;
+    }
+
+    private String cleanup(String text) {
+        StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < text.length; i++) {
+            char c = text.charAt(i);
+            if (c is '\n' || c is '\r' || c is '\f') {
+                if (i > 0)
+                    buf.append(' ');
+            } else
+                buf.append(c);
+        }
+        return buf.toString();
+    }
+
+    public void setWordWrapAllowed(bool value) {
+        wrapAllowed = value;
+    }
+
+    public bool isWordWrapAllowed() {
+        return wrapAllowed;
+    }
+
+    public bool isSelectable() {
+        return false;
+    }
+
+    public String getColorId() {
+        return colorId;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    void setText(String text) {
+        this.text = cleanup(text);
+        textFragments = null;
+    }
+
+    void setColorId(String colorId) {
+        this.colorId = colorId;
+    }
+
+    void setFontId(String fontId) {
+        this.fontId = fontId;
+        textFragments = null;
+    }
+
+    public bool contains(int x, int y) {
+        for (int i = 0; i < areaRectangles.size(); i++) {
+            AreaRectangle ar = cast(AreaRectangle) areaRectangles.get(i);
+            if (ar.contains(x, y))
+                return true;
+            if (i<areaRectangles.size()-1) {
+                // test the gap
+                Rectangle top = ar.rect;
+                Rectangle bot = (cast(AreaRectangle)areaRectangles.get(i+1)).rect;
+                if (y >= top.y+top.height && y < bot.y) {
+                    // in the gap
+                    int left = Math.max(top.x, bot.x);
+                    int right = Math.min(top.x+top.width, bot.x+bot.width);
+                    if (x>=left && x<=right) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    public bool intersects(Rectangle rect) {
+        for (int i = 0; i < areaRectangles.size(); i++) {
+            AreaRectangle ar = cast(AreaRectangle) areaRectangles.get(i);
+            if (ar.intersects(rect))
+                return true;
+            if (i<areaRectangles.size()-1) {
+                // test the gap
+                Rectangle top = ar.rect;
+                Rectangle bot = (cast(AreaRectangle)areaRectangles.get(i+1)).rect;
+                if (top.y+top.height < bot.y) {
+                    int y = top.y+top.height;
+                    int height = bot.y-y;
+                    int left = Math.max(top.x, bot.x);
+                    int right = Math.min(top.x+top.width, bot.x+bot.width);
+                    Rectangle gap = new Rectangle(left, y, right-left, height);
+                    if (gap.intersects(rect))
+                        return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public Rectangle getBounds() {
+        int x = 0, y = 0;
+        int width = 0, height = 0;
+
+        for (int i = 0; i < areaRectangles.size(); i++) {
+            AreaRectangle ar = cast(AreaRectangle) areaRectangles.get(i);
+            if (i is 0) {
+                x = ar.rect.x;
+                y = ar.rect.y;
+            } else
+                x = Math.min(ar.rect.x, x);
+            width = Math.max(ar.rect.width, width);
+            height += ar.rect.height;
+        }
+        return new Rectangle(x, y, width, height);
+    }
+
+    public bool advanceLocator(GC gc, int wHint, Locator locator,
+            Hashtable objectTable, bool computeHeightOnly) {
+        Font oldFont = null;
+        if (fontId !is null) {
+            oldFont = gc.getFont();
+            Font newFont = cast(Font) objectTable.get(fontId);
+            if (newFont !is null)
+                gc.setFont(newFont);
+        }
+        FontMetrics fm = gc.getFontMetrics();
+        int lineHeight = fm.getHeight();
+        bool newLine = false;
+
+        if (wHint is SWT.DEFAULT || !wrapAllowed) {
+            Point extent = gc.textExtent(text);
+            int totalExtent = locator.x+extent.x;
+            if (isSelectable())
+                totalExtent+=1;
+
+            if (wHint !is SWT.DEFAULT && totalExtent > wHint) {
+                // new line
+                locator.x = locator.indent;
+                locator.y += locator.rowHeight;
+                if (computeHeightOnly)
+                    locator.collectHeights();
+                locator.rowHeight = 0;
+                locator.leading = 0;
+                newLine = true;
+            }
+            int width = extent.x;
+            if (isSelectable())
+                width += 1;
+            locator.x += width;
+            locator.width = locator.indent + width;
+            locator.rowHeight = Math.max(locator.rowHeight, extent.y);
+            locator.leading = Math.max(locator.leading, fm.getLeading());
+            return newLine;
+        }
+
+        computeTextFragments(gc);
+
+        int width = 0;
+        Point lineExtent = new Point(0, 0);
+
+        for (int i = 0; i < textFragments.length; i++) {
+            TextFragment textFragment = textFragments[i];
+            int currentExtent = locator.x + lineExtent.x;
+
+            if (isSelectable())
+                currentExtent += 1;
+
+            // i !is 0 || locator.x > locator.getStartX() + (isSelectable() ? 1 : 0) means:
+            // only wrap on the first fragment if we are not at the start of a line
+            if ((i !is 0 || locator.x > locator.getStartX() + (isSelectable() ? 1 : 0)) && currentExtent + textFragment.length > wHint) {
+                // overflow
+                int lineWidth = currentExtent;
+                locator.rowHeight = Math.max(locator.rowHeight, lineExtent.y);
+                locator.leading = Math.max(locator.leading, fm.getLeading());
+                if (computeHeightOnly)
+                    locator.collectHeights();
+                locator.x = locator.indent;
+                locator.y += locator.rowHeight;
+                locator.rowHeight = 0;
+                locator.leading = 0;
+                lineExtent.x = 0;
+                lineExtent.y = 0;
+                width = Math.max(width, lineWidth);
+                newLine = true;
+            }
+            lineExtent.x += textFragment.length;
+            lineExtent.y = Math.max(lineHeight, lineExtent.y);
+        }
+        int lineWidth = lineExtent.x;
+        if (isSelectable())
+            lineWidth += 1;
+        locator.x += lineWidth;
+        locator.width = width;
+        locator.rowHeight = Math.max(locator.rowHeight, lineExtent.y);
+        locator.leading = Math.max(locator.leading, fm.getLeading());
+        if (oldFont !is null) {
+            gc.setFont(oldFont);
+        }
+        return newLine;
+    }
+
+    /**
+     * @param gc
+     * @param width
+     * @param locator
+     * @param selected
+     * @param selData
+     * @param color
+     * @param fm
+     * @param lineHeight
+     * @param descent
+     */
+    private void layoutWithoutWrapping(GC gc, int width, Locator locator,
+            bool selected, FontMetrics fm, int lineHeight, int descent) {
+        Point extent = gc.textExtent(text);
+        int ewidth = extent.x;
+        if (isSelectable())
+            ewidth += 1;
+        if (locator.x + ewidth > width-locator.marginWidth) {
+            // new line
+            locator.resetCaret();
+            locator.y += locator.rowHeight;
+            locator.rowHeight = 0;
+            locator.rowCounter++;
+        }
+        int ly = locator.getBaseline(fm.getHeight() - fm.getLeading());
+        //int lineY = ly + lineHeight - descent + 1;
+        Rectangle br = new Rectangle(locator.x, ly, ewidth,
+                lineHeight - descent + 3);
+        areaRectangles.add(new AreaRectangle(br, 0, -1));
+        locator.x += ewidth;
+        locator.width = ewidth;
+        locator.rowHeight = Math.max(locator.rowHeight, extent.y);
+    }
+
+    protected int convertOffsetToStringIndex(GC gc, String s, int x,
+            int swidth, int selOffset) {
+        int index = s.length;
+        while (index > 0 && x + swidth > selOffset) {
+            index--;
+            String ss = s.substring(0, index);
+            swidth = gc.textExtent(ss).x;
+        }
+        return index;
+    }
+
+    public void paintFocus(GC gc, Color bg, Color fg, bool selected,
+            Rectangle repaintRegion) {
+        if (areaRectangles is null)
+            return;
+        for (int i = 0; i < areaRectangles.size(); i++) {
+            AreaRectangle areaRectangle = cast(AreaRectangle) areaRectangles.get(i);
+            Rectangle br = areaRectangle.rect;
+            int bx = br.x;
+            int by = br.y;
+            if (repaintRegion !is null) {
+                bx -= repaintRegion.x;
+                by -= repaintRegion.y;
+            }
+            if (selected) {
+                gc.setBackground(bg);
+                gc.setForeground(fg);
+                gc.drawFocus(bx, by, br.width, br.height);
+            } else {
+                gc.setForeground(bg);
+                gc.drawRectangle(bx, by, br.width - 1, br.height - 1);
+            }
+        }
+    }
+
+    public void paint(GC gc, bool hover, Hashtable resourceTable,
+            bool selected, SelectionData selData, Rectangle repaintRegion) {
+        this.paint(gc, hover, resourceTable, selected, false, selData,
+                repaintRegion);
+    }
+
+    protected void paint(GC gc, bool hover, Hashtable resourceTable,
+            bool selected, bool rollover, SelectionData selData,
+            Rectangle repaintRegion) {
+        Font oldFont = null;
+        Color oldColor = null;
+        Color oldBg = null;
+
+        // apply segment-specific font, color and background
+        if (fontId !is null) {
+            oldFont = gc.getFont();
+            Font newFont = cast(Font) resourceTable.get(fontId);
+            if (newFont !is null)
+                gc.setFont(newFont);
+        }
+        if (!hover && colorId !is null) {
+            oldColor = gc.getForeground();
+            Color newColor = cast(Color) resourceTable.get(colorId);
+            if (newColor !is null)
+                gc.setForeground(newColor);
+        }
+        oldBg = gc.getBackground();
+
+        FontMetrics fm = gc.getFontMetrics();
+        int lineHeight = fm.getHeight();
+        int descent = fm.getDescent();
+
+        // paint area rectangles of the segment
+        for (int i = 0; i < areaRectangles.size(); i++) {
+            AreaRectangle areaRectangle = cast(AreaRectangle) areaRectangles.get(i);
+            Rectangle rect = areaRectangle.rect;
+            String text = areaRectangle.getText();
+            Point extent = gc.textExtent(text);
+            int textX = rect.x + (isSelectable()?1:0);
+            int lineY = rect.y + lineHeight - descent + 1;
+            paintString(gc, text, extent.x, textX, rect.y, lineY, selData,
+                    rect, hover, rollover, repaintRegion);
+            if (selected) {
+                int fx = rect.x;
+                int fy = rect.y;
+                if (repaintRegion !is null) {
+                    fx -= repaintRegion.x;
+                    fy -= repaintRegion.y;
+                }
+                //To avoid partially cancelling the focus by painting over
+                //X-ORed pixels, first cancel it yourself
+                Color fg = gc.getForeground();
+                gc.setForeground(oldBg);
+                gc.drawRectangle(fx, fy, rect.width - 1, rect.height - 1);
+                gc.setForeground(fg);
+                gc.drawFocus(fx, fy, rect.width, rect.height);
+            }
+        }
+        // restore GC resources
+        if (oldFont !is null) {
+            gc.setFont(oldFont);
+        }
+        if (oldColor !is null) {
+            gc.setForeground(oldColor);
+        }
+        if (oldBg !is null) {
+            gc.setBackground(oldBg);
+        }
+    }
+
+    public void computeSelection(GC gc, Hashtable resourceTable, SelectionData selData) {
+        Font oldFont = null;
+
+        if (fontId !is null) {
+            oldFont = gc.getFont();
+            Font newFont = cast(Font) resourceTable.get(fontId);
+            if (newFont !is null)
+                gc.setFont(newFont);
+        }
+
+        for (int i = 0; i < areaRectangles.size(); i++) {
+            AreaRectangle areaRectangle = cast(AreaRectangle) areaRectangles.get(i);
+            Rectangle rect = areaRectangle.rect;
+            String text = areaRectangle.getText();
+            Point extent = gc.textExtent(text);
+            computeSelection(gc, text, extent.x, selData,
+                    rect);
+        }
+        // restore GC resources
+        if (oldFont !is null) {
+            gc.setFont(oldFont);
+        }
+    }
+
+    private void paintString(GC gc, String s, int swidth, int x, int y,
+            int lineY, SelectionData selData, Rectangle bounds, bool hover,
+            bool rolloverMode, Rectangle repaintRegion) {
+        // repaints one area rectangle
+        if (selData !is null && selData.isEnclosed()) {
+            Color savedBg = gc.getBackground();
+            Color savedFg = gc.getForeground();
+            int leftOffset = selData.getLeftOffset(bounds.height);
+            int rightOffset = selData.getRightOffset(bounds.height);
+            bool firstRow = selData.isFirstSelectionRow(bounds.y,
+                    bounds.height);
+            bool lastRow = selData.isLastSelectionRow(bounds.y,
+                    bounds.height);
+            bool selectedRow = selData
+                    .isSelectedRow(bounds.y, bounds.height);
+
+            int sstart = -1;
+            int sstop = -1;
+
+            if ((firstRow && x + swidth < leftOffset)
+                    || (lastRow && x > rightOffset)) {
+                paintStringSegment(gc, s, gc.textExtent(s).x, x, y, lineY,
+                        hover, rolloverMode, repaintRegion);
+                return;
+            }
+
+            if (firstRow && bounds.x + swidth > leftOffset) {
+                sstart = convertOffsetToStringIndex(gc, s, bounds.x, swidth,
+                        leftOffset);
+            }
+            if (lastRow && bounds.x + swidth > rightOffset) {
+                sstop = convertOffsetToStringIndex(gc, s, bounds.x, swidth,
+                        rightOffset);
+            }
+
+            if (firstRow && sstart !is -1) {
+                String left = s.substring(0, sstart);
+                int width = gc.textExtent(left).x;
+                paintStringSegment(gc, left, width, x, y, lineY, hover,
+                        rolloverMode, repaintRegion);
+                x += width;
+            }
+            if (selectedRow) {
+                int lindex = sstart !is -1 ? sstart : 0;
+                int rindex = sstop !is -1 ? sstop : s.length;
+                String mid = s.substring(lindex, rindex);
+                Point extent = gc.textExtent(mid);
+                gc.setForeground(selData.fg);
+                gc.setBackground(selData.bg);
+                gc.fillRectangle(x, y, extent.x, extent.y);
+                paintStringSegment(gc, mid, extent.x, x, y, lineY, hover,
+                        rolloverMode, repaintRegion);
+                x += extent.x;
+                gc.setForeground(savedFg);
+                gc.setBackground(savedBg);
+            } else {
+                paintStringSegment(gc, s, gc.textExtent(s).x, x, y, lineY,
+                        hover, rolloverMode, repaintRegion);
+            }
+            if (lastRow && sstop !is -1) {
+                String right = s.substring(sstop);
+                paintStringSegment(gc, right, gc.textExtent(right).x, x, y,
+                        lineY, hover, rolloverMode, repaintRegion);
+            }
+        } else {
+            paintStringSegment(gc, s, gc.textExtent(s).x, x, y, lineY, hover,
+                    rolloverMode, repaintRegion);
+        }
+    }
+
+    private void computeSelection(GC gc, String s, int swidth, SelectionData selData, Rectangle bounds) {
+        int leftOffset = selData.getLeftOffset(bounds.height);
+        int rightOffset = selData.getRightOffset(bounds.height);
+        bool firstRow = selData.isFirstSelectionRow(bounds.y, bounds.height);
+        bool lastRow = selData.isLastSelectionRow(bounds.y, bounds.height);
+        bool selectedRow = selData.isSelectedRow(bounds.y, bounds.height);
+
+        int sstart = -1;
+        int sstop = -1;
+
+        if (firstRow && bounds.x + swidth > leftOffset) {
+            sstart = convertOffsetToStringIndex(gc, s, bounds.x, swidth,
+                    leftOffset);
+        }
+        if (lastRow && bounds.x + swidth > rightOffset) {
+            sstop = convertOffsetToStringIndex(gc, s, bounds.x, swidth,
+                    rightOffset);
+        }
+
+        if (selectedRow) {
+            int lindex = sstart !is -1 ? sstart : 0;
+            int rindex = sstop !is -1 ? sstop : s.length;
+            String mid = s.substring(lindex, rindex);
+            selData.addSegment(mid);
+        }
+    }
+
+    /**
+     * @param gc
+     * @param s
+     * @param x
+     * @param y
+     * @param lineY
+     * @param hover
+     * @param rolloverMode
+     */
+    private void paintStringSegment(GC gc, String s, int swidth, int x, int y,
+            int lineY, bool hover, bool rolloverMode,
+            Rectangle repaintRegion) {
+        bool reverse = false;
+        int clipX = x;
+        int clipY = y;
+        int clipLineY = lineY;
+        if (repaintRegion !is null) {
+            clipX -= repaintRegion.x;
+            clipY -= repaintRegion.y;
+            clipLineY -= repaintRegion.y;
+        }
+        if (underline || hover || rolloverMode) {
+            if (rolloverMode && !hover)
+                reverse = true;
+        }
+        if (reverse) {
+            drawUnderline(gc, swidth, clipX, clipLineY, hover, rolloverMode);
+            drawString(gc, s, clipX, clipY);
+        } else {
+            drawString(gc, s, clipX, clipY);
+            drawUnderline(gc, swidth, clipX, clipLineY, hover, rolloverMode);
+        }
+    }
+
+    protected void drawString(GC gc, String s, int clipX, int clipY) {
+        gc.drawString(s, clipX, clipY, true);
+    }
+
+    private void drawUnderline(GC gc, int swidth, int x, int y, bool hover,
+            bool rolloverMode) {
+        if (underline || hover || rolloverMode) {
+            Color saved = null;
+            if (rolloverMode && !hover) {
+                saved = gc.getForeground();
+                gc.setForeground(gc.getBackground());
+            }
+            gc.drawLine(x, y, x + swidth-1, y);
+            if (saved !is null)
+                gc.setForeground(saved);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.ui.internal.forms.widgets.ParagraphSegment#layout(org.eclipse.swt.graphics.GC,
+     *      int, org.eclipse.ui.internal.forms.widgets.Locator,
+     *      java.util.Hashtable, bool,
+     *      org.eclipse.ui.internal.forms.widgets.SelectionData)
+     */
+    public void layout(GC gc, int width, Locator locator,
+            Hashtable resourceTable, bool selected) {
+        Font oldFont = null;
+
+        areaRectangles.clear();
+
+        if (fontId !is null) {
+            oldFont = gc.getFont();
+            Font newFont = cast(Font) resourceTable.get(fontId);
+            if (newFont !is null)
+                gc.setFont(newFont);
+        }
+        FontMetrics fm = gc.getFontMetrics();
+        int lineHeight = fm.getHeight();
+        int descent = fm.getDescent();
+
+        if (!wrapAllowed) {
+            layoutWithoutWrapping(gc, width, locator, selected, fm, lineHeight,
+                    descent);
+        } else {
+            int lineStart = 0;
+            int lastLoc = 0;
+            Point lineExtent = new Point(0, 0);
+            computeTextFragments(gc);
+            int rightEdge = width-locator.marginWidth;
+            for (int i = 0; i < textFragments.length; i++) {
+                TextFragment fragment = textFragments[i];
+                int breakLoc = fragment.index;
+                if (breakLoc is 0)
+                    continue;
+                // (i !is 0 || locator.x > locator.getStartX() + (isSelectable() ? 1 : 0)) means:
+                // only wrap on the first fragment if we are not at the start of a line
+                if ((i !is 0 || locator.x > locator.getStartX() + (isSelectable() ? 1 : 0)) && locator.x + lineExtent.x + fragment.length > rightEdge) {
+                    // overflow
+                    int lineWidth = locator.x + lineExtent.x;
+                    if (isSelectable())
+                        lineWidth += 1;
+                    int ly = locator.getBaseline(lineHeight - fm.getLeading());
+                    Rectangle br = new Rectangle(isSelectable()?
+                            locator.x - 1:locator.x, ly,
+                            isSelectable()?lineExtent.x + 1:lineExtent.x, lineHeight - descent + 3);
+                    areaRectangles
+                            .add(new AreaRectangle(br, lineStart, lastLoc));
+
+                    locator.rowHeight = Math.max(locator.rowHeight,
+                            lineExtent.y);
+                    locator.resetCaret();
+                    if (isSelectable())
+                        locator.x += 1;
+                    locator.y += locator.rowHeight;
+                    locator.rowCounter++;
+                    locator.rowHeight = 0;
+                    lineStart = lastLoc;
+                    lineExtent.x = 0;
+                    lineExtent.y = 0;
+                }
+                lastLoc = breakLoc;
+                lineExtent.x += fragment.length;
+                lineExtent.y = Math.max(lineHeight, lineExtent.y);
+            }
+            //String lastLine = text.substring(lineStart, lastLoc);
+            int ly = locator.getBaseline(lineHeight - fm.getLeading());
+            int lastWidth = lineExtent.x;
+            if (isSelectable())
+                lastWidth += 1;
+            Rectangle br = new Rectangle(isSelectable()?locator.x - 1:locator.x, ly,
+                    isSelectable()?lineExtent.x + 1:lineExtent.x,
+                    lineHeight - descent + 3);
+            //int lineY = ly + lineHeight - descent + 1;
+            areaRectangles.add(new AreaRectangle(br, lineStart, lastLoc));
+            locator.x += lastWidth;
+            locator.rowHeight = Math.max(locator.rowHeight, lineExtent.y);
+        }
+        if (oldFont !is null) {
+            gc.setFont(oldFont);
+        }
+    }
+
+    private void computeTextFragments(GC gc) {
+        if (textFragments !is null)
+            return;
+        ArrayList list = new ArrayList();
+        auto wb =  UBreakIterator.openLineIterator( ULocale.Default, getText() );
+        scope(exit) wb.close();
+        int cursor = 0;
+        for (int loc = wb.first(); loc !is UBreakIterator.Done; loc = wb.next()) {
+            if (loc is 0)
+                continue;
+            String word = text.substring(cursor, loc);
+            Point extent = gc.textExtent(word);
+            list.add(new TextFragment(cast(short) loc, cast(short) extent.x));
+            cursor = loc;
+        }
+        textFragments = arraycast!(TextFragment)(list.toArray());
+    }
+
+    public void clearCache(String fontId) {
+        if (fontId is null && (this.fontId is null||this.fontId.equals(FormTextModel.BOLD_FONT_ID)))
+            textFragments = null;
+        else if (fontId !is null && this.fontId !is null && fontId.equals(this.fontId))
+            textFragments = null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/TitleRegion.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,550 @@
+/*******************************************************************************
+ * 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 org.eclipse.ui.internal.forms.widgets.TitleRegion;
+
+import org.eclipse.ui.internal.forms.widgets.BusyIndicator;
+import org.eclipse.ui.internal.forms.widgets.FormHeading;
+import org.eclipse.ui.internal.forms.widgets.FormUtil;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DragSource;
+import org.eclipse.swt.dnd.DragSourceEffect;
+import org.eclipse.swt.dnd.DragSourceEvent;
+import org.eclipse.swt.dnd.DragSourceListener;
+import org.eclipse.swt.dnd.DropTarget;
+import org.eclipse.swt.dnd.DropTargetListener;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.ui.forms.IFormColors;
+import org.eclipse.ui.forms.widgets.ILayoutExtension;
+import org.eclipse.ui.forms.widgets.SizeCache;
+import org.eclipse.ui.forms.widgets.Twistie;
+import org.eclipse.ui.internal.forms.IMessageToolTipManager;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * Form heading title.
+ */
+public class TitleRegion : Canvas {
+
+    alias Canvas.computeSize computeSize;
+
+    public static const int STATE_NORMAL = 0;
+    public static const int STATE_HOVER_LIGHT = 1;
+    public static const int STATE_HOVER_FULL = 2;
+    private int hoverState;
+    private static const int HMARGIN = 1;
+    private static const int VMARGIN = 5;
+    private static const int SPACING = 5;
+    private static const int ARC_WIDTH = 20;
+    private static const int ARC_HEIGHT = 20;
+    private Image image;
+    private BusyIndicator busyLabel;
+    private Label titleLabel;
+    private SizeCache titleCache;
+    private int fontHeight = -1;
+    private int fontBaselineHeight = -1;
+    private MenuHyperlink menuHyperlink;
+    private MenuManager menuManager;
+    private bool dragSupport;
+    private int dragOperations;
+    private Transfer[] dragTransferTypes;
+    private DragSourceListener dragListener;
+    private DragSource dragSource;
+    private Image dragImage;
+
+    private class HoverListener : MouseTrackListener,
+            MouseMoveListener {
+
+        public void mouseEnter(MouseEvent e) {
+            setHoverState(STATE_HOVER_FULL);
+        }
+
+        public void mouseExit(MouseEvent e) {
+            setHoverState(STATE_NORMAL);
+        }
+
+        public void mouseHover(MouseEvent e) {
+        }
+
+        public void mouseMove(MouseEvent e) {
+            if (e.button > 0)
+                setHoverState(STATE_NORMAL);
+            else
+                setHoverState(STATE_HOVER_FULL);
+        }
+    }
+
+    private class MenuHyperlink : Twistie {
+        private bool firstTime = true;
+
+        public this(Composite parent, int style) {
+            super(parent, style);
+            setExpanded(true);
+        }
+
+        public void setExpanded(bool expanded) {
+            if (firstTime) {
+                super.setExpanded(expanded);
+                firstTime = false;
+            } else {
+                Menu menu = menuManager.createContextMenu(menuHyperlink);
+                menu.setVisible(true);
+            }
+        }
+    }
+
+    private class TitleRegionLayout : Layout, ILayoutExtension {
+
+        protected Point computeSize(Composite composite, int wHint, int hHint,
+                bool flushCache) {
+            return layout(composite, false, 0, 0, wHint, hHint, flushCache);
+        }
+
+        protected void layout(Composite composite, bool flushCache) {
+            Rectangle carea = composite.getClientArea();
+            layout(composite, true, carea.x, carea.y, carea.width,
+                    carea.height, flushCache);
+        }
+
+        private Point layout(Composite composite, bool move, int x, int y,
+                int width, int height, bool flushCache) {
+            int iwidth = width is SWT.DEFAULT ? SWT.DEFAULT : width - HMARGIN
+                    * 2;
+            Point bsize = null;
+            Point tsize = null;
+            Point msize = null;
+
+            if (busyLabel !is null) {
+                bsize = busyLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+            }
+            if (menuManager !is null) {
+                menuHyperlink.setVisible(!menuManager.isEmpty()
+                        && titleLabel.getVisible());
+                if (menuHyperlink.getVisible())
+                    msize = menuHyperlink.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+            }
+            if (flushCache)
+                titleCache.flush();
+            titleCache.setControl(titleLabel);
+            int twidth = iwidth is SWT.DEFAULT ? iwidth : iwidth - SPACING * 2;
+            if (bsize !is null && twidth !is SWT.DEFAULT)
+                twidth -= bsize.x + SPACING;
+            if (msize !is null && twidth !is SWT.DEFAULT)
+                twidth -= msize.x + SPACING;
+            if (titleLabel.getVisible()) {
+                tsize = titleCache.computeSize(twidth, SWT.DEFAULT);
+                if (twidth !is SWT.DEFAULT) {
+                    // correct for the case when width hint is larger
+                    // than the maximum width - this is when the text
+                    // can be rendered on one line with width to spare
+                    int maxWidth = titleCache.computeSize(SWT.DEFAULT,
+                            SWT.DEFAULT).x;
+                    tsize.x = Math.min(tsize.x, maxWidth);
+                    // System.out.println("twidth="+twidth+",
+                    // tsize.x="+tsize.x); //$NON-NLS-1$//$NON-NLS-2$
+                }
+            } else
+                tsize = new Point(0, 0);
+            Point size = new Point(width, height);
+            if (!move) {
+                // compute size
+                size.x = tsize.x > 0 ? HMARGIN * 2 + SPACING * 2 + tsize.x : 0;
+                size.y = tsize.y;
+                if (bsize !is null) {
+                    size.x += bsize.x + SPACING;
+                    size.y = Math.max(size.y, bsize.y);
+                }
+                if (msize !is null) {
+                    size.x += msize.x + SPACING;
+                    size.y = Math.max(size.y, msize.y);
+                }
+                if (size.y > 0)
+                    size.y += VMARGIN * 2;
+                // System.out.println("Compute size: width="+width+",
+                // size.x="+size.x); //$NON-NLS-1$ //$NON-NLS-2$
+            } else {
+                // position controls
+                int xloc = x + HMARGIN + SPACING;
+                int yloc = y + VMARGIN;
+                if (bsize !is null) {
+                    busyLabel.setBounds(xloc,
+                            // yloc + height / 2 - bsize.y / 2,
+                            yloc + (getFontHeight() >= bsize.y ? getFontHeight() : bsize.y) - 1 - bsize.y,
+                            bsize.x, bsize.y);
+                    xloc += bsize.x + SPACING;
+                }
+                if (titleLabel.getVisible()) {
+                    int tw = width - HMARGIN * 2 - SPACING * 2;
+                    if (bsize !is null)
+                        tw -= bsize.x + SPACING;
+                    if (msize !is null)
+                        tw -= msize.x + SPACING;
+                    titleLabel.setBounds(xloc,
+                    // yloc + height / 2 - tsize.y / 2,
+                            yloc, tw, tsize.y);
+                    // System.out.println("tw="+tw); //$NON-NLS-1$
+                    xloc += tw + SPACING;
+                }
+                if (msize !is null) {
+                    menuHyperlink.setBounds(xloc, yloc
+                            + getFontHeight() / 2 - msize.y / 2,
+                            msize.x, msize.y);
+                }
+            }
+            return size;
+        }
+
+        public int computeMaximumWidth(Composite parent, bool changed) {
+            return computeSize(parent, SWT.DEFAULT, SWT.DEFAULT, changed).x;
+        }
+
+        public int computeMinimumWidth(Composite parent, bool changed) {
+            return computeSize(parent, 0, SWT.DEFAULT, changed).x;
+        }
+    }
+
+    public this(Composite parent) {
+        super(parent, SWT.NULL);
+        titleLabel = new Label(this, SWT.WRAP);
+        titleLabel.setVisible(false);
+        titleCache = new SizeCache();
+        super.setLayout(new TitleRegionLayout());
+        hookHoverListeners();
+        addListener(SWT.Dispose, new class Listener {
+            public void handleEvent(Event e) {
+                if (dragImage !is null) {
+                    dragImage.dispose();
+                    dragImage = null;
+                }
+            }
+        });
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.swt.widgets.Control#forceFocus()
+     */
+    public bool forceFocus() {
+        return false;
+    }
+
+    private Color getColor(String key) {
+        return cast(Color) (cast(FormHeading) getParent()).colors.get(key);
+    }
+
+    private void hookHoverListeners() {
+        HoverListener listener = new HoverListener();
+        addMouseTrackListener(listener);
+        addMouseMoveListener(listener);
+        titleLabel.addMouseTrackListener(listener);
+        titleLabel.addMouseMoveListener(listener);
+        addPaintListener(new class PaintListener {
+            public void paintControl(PaintEvent e) {
+                onPaint(e);
+            }
+        });
+    }
+
+    private void onPaint(PaintEvent e) {
+        if (hoverState is STATE_NORMAL)
+            return;
+        GC gc = e.gc;
+        Rectangle carea = getClientArea();
+        gc.setBackground(getHoverBackground());
+        int savedAntialias = gc.getAntialias();
+        FormUtil.setAntialias(gc, SWT.ON);
+        gc.fillRoundRectangle(carea.x + HMARGIN, carea.y + 2, carea.width
+                - HMARGIN * 2, carea.height - 4, ARC_WIDTH, ARC_HEIGHT);
+        FormUtil.setAntialias(gc, savedAntialias);
+    }
+
+    private Color getHoverBackground() {
+        if (hoverState is STATE_NORMAL)
+            return null;
+        Color color = getColor(hoverState is STATE_HOVER_FULL ? IFormColors.H_HOVER_FULL
+                : IFormColors.H_HOVER_LIGHT);
+        if (color is null)
+            color = getDisplay()
+                    .getSystemColor(
+                            hoverState is STATE_HOVER_FULL ? SWT.COLOR_WIDGET_BACKGROUND
+                                    : SWT.COLOR_WIDGET_LIGHT_SHADOW);
+        return color;
+    }
+
+    public void setHoverState(int state) {
+        if (dragSource is null || this.hoverState is state)
+            return;
+        this.hoverState = state;
+        Color color = getHoverBackground();
+        titleLabel.setBackground(color !is null ? color
+                : getColor(FormHeading.COLOR_BASE_BG));
+        if (busyLabel !is null)
+            busyLabel.setBackground(color !is null ? color
+                    : getColor(FormHeading.COLOR_BASE_BG));
+        if (menuHyperlink !is null)
+            menuHyperlink.setBackground(color !is null ? color
+                    : getColor(FormHeading.COLOR_BASE_BG));
+        redraw();
+    }
+
+    /**
+     * Fully delegates the size computation to the internal layout manager.
+     */
+    public final Point computeSize(int wHint, int hHint, bool changed) {
+        return (cast(TitleRegionLayout) getLayout()).computeSize(this, wHint,
+                hHint, changed);
+    }
+
+    public final void setLayout(Layout layout) {
+        // do nothing
+    }
+
+    public Image getImage() {
+        return image;
+    }
+
+    public void setImage(Image image) {
+        this.image = image;
+    }
+
+    public void updateImage(Image newImage, bool doLayout) {
+        Image theImage = newImage !is null ? newImage : this.image;
+
+        if (theImage !is null) {
+            ensureBusyLabelExists();
+        } else if (busyLabel !is null) {
+            if (!busyLabel.isBusy()) {
+                busyLabel.dispose();
+                busyLabel = null;
+            }
+        }
+        if (busyLabel !is null) {
+            busyLabel.setImage(theImage);
+        }
+        if (doLayout)
+            layout();
+    }
+
+    public void updateToolTip(String toolTip) {
+        if (busyLabel !is null)
+            busyLabel.setToolTipText(toolTip);
+    }
+
+    public void setBackground(Color bg) {
+        super.setBackground(bg);
+        titleLabel.setBackground(bg);
+        if (busyLabel !is null)
+            busyLabel.setBackground(bg);
+        if (menuHyperlink !is null)
+            menuHyperlink.setBackground(bg);
+    }
+
+    public void setForeground(Color fg) {
+        super.setForeground(fg);
+        titleLabel.setForeground(fg);
+        if (menuHyperlink !is null)
+            menuHyperlink.setForeground(fg);
+    }
+
+    public void setText(String text) {
+        if (text !is null)
+            titleLabel.setText(text);
+        titleLabel.setVisible(text !is null);
+        layout();
+        redraw();
+    }
+
+    public String getText() {
+        return titleLabel.getText();
+    }
+
+    public void setFont(Font font) {
+        super.setFont(font);
+        titleLabel.setFont(font);
+        fontHeight = -1;
+        fontBaselineHeight = -1;
+        layout();
+    }
+
+    private void ensureBusyLabelExists() {
+        if (busyLabel is null) {
+            busyLabel = new BusyIndicator(this, SWT.NULL);
+            busyLabel.setBackground(getColor(FormHeading.COLOR_BASE_BG));
+            HoverListener listener = new HoverListener();
+            busyLabel.addMouseTrackListener(listener);
+            busyLabel.addMouseMoveListener(listener);
+            if (menuManager !is null)
+                busyLabel.setMenu(menuManager.createContextMenu(this));
+            if (dragSupport)
+                addDragSupport(busyLabel, dragOperations, dragTransferTypes, dragListener);
+            IMessageToolTipManager mng = (cast(FormHeading) getParent())
+                    .getMessageToolTipManager();
+            if (mng !is null)
+                mng.createToolTip(busyLabel, true);
+        }
+    }
+
+    private void createMenuHyperlink() {
+        menuHyperlink = new MenuHyperlink(this, SWT.NULL);
+        menuHyperlink.setBackground(getColor(FormHeading.COLOR_BASE_BG));
+        menuHyperlink.setDecorationColor(getForeground());
+        menuHyperlink.setHoverDecorationColor(getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND));
+        HoverListener listener = new HoverListener();
+        menuHyperlink.addMouseTrackListener(listener);
+        menuHyperlink.addMouseMoveListener(listener);
+        if (dragSupport)
+            addDragSupport(menuHyperlink, dragOperations, dragTransferTypes, dragListener);
+    }
+
+    /**
+     * Sets the form's busy state. Busy form will display 'busy' animation in
+     * the area of the title image.
+     *
+     * @param busy
+     *            the form's busy state
+     */
+
+    public bool setBusy(bool busy) {
+        if (busy)
+            ensureBusyLabelExists();
+        else if (busyLabel is null)
+            return false;
+        if (busy is busyLabel.isBusy())
+            return false;
+        busyLabel.setBusy(busy);
+        if (busyLabel.getImage() is null) {
+            layout();
+            return true;
+        }
+        return false;
+    }
+
+    public bool isBusy() {
+        return busyLabel !is null && busyLabel.isBusy();
+    }
+
+    /*
+     * Returns the complete height of the font.
+     */
+    public int getFontHeight() {
+        if (fontHeight is -1) {
+            Font font = getFont();
+            GC gc = new GC(getDisplay());
+            gc.setFont(font);
+            FontMetrics fm = gc.getFontMetrics();
+            fontHeight = fm.getHeight();
+            gc.dispose();
+        }
+        return fontHeight;
+    }
+
+    /*
+     * Returns the height of the font starting at the baseline,
+     * i.e. without the descent.
+     */
+    public int getFontBaselineHeight() {
+        if (fontBaselineHeight is -1) {
+            Font font = getFont();
+            GC gc = new GC(getDisplay());
+            gc.setFont(font);
+            FontMetrics fm = gc.getFontMetrics();
+            fontBaselineHeight = fm.getHeight() - fm.getDescent();
+            gc.dispose();
+        }
+        return fontBaselineHeight;
+    }
+
+    public IMenuManager getMenuManager() {
+        if (menuManager is null) {
+            menuManager = new MenuManager();
+            Menu menu = menuManager.createContextMenu(this);
+            setMenu(menu);
+            titleLabel.setMenu(menu);
+            if (busyLabel !is null)
+                busyLabel.setMenu(menu);
+            createMenuHyperlink();
+        }
+        return menuManager;
+    }
+
+    public void addDragSupport(int operations, Transfer[] transferTypes,
+            DragSourceListener listener) {
+        dragSupport = true;
+        dragOperations = operations;
+        dragTransferTypes = transferTypes;
+        dragListener = listener;
+        dragSource = addDragSupport(titleLabel, operations, transferTypes,
+                listener);
+        addDragSupport(this, operations, transferTypes, listener);
+        if (busyLabel !is null)
+            addDragSupport(busyLabel, operations, transferTypes, listener);
+        if (menuHyperlink !is null)
+            addDragSupport(menuHyperlink, operations, transferTypes, listener);
+    }
+
+    private DragSource addDragSupport(Control control, int operations,
+            Transfer[] transferTypes, DragSourceListener listener) {
+        DragSource source = new DragSource(control, operations);
+        source.setTransfer(transferTypes);
+        source.addDragListener(listener);
+        source.setDragSourceEffect(new class(control) DragSourceEffect {
+            this(Control c){
+                super(c);
+            }
+            public void dragStart(DragSourceEvent event) {
+                event.image = createDragEffectImage();
+            }
+        });
+        return source;
+    }
+
+    private Image createDragEffectImage() {
+        /*
+         * if (dragImage !is null) { dragImage.dispose(); } GC gc = new GC(this);
+         * Point size = getSize(); dragImage = new Image(getDisplay(), size.x,
+         * size.y); gc.copyArea(dragImage, 0, 0); gc.dispose(); return
+         * dragImage;
+         */
+        return null;
+    }
+
+    public void addDropSupport(int operations, Transfer[] transferTypes,
+            DropTargetListener listener) {
+        final DropTarget target = new DropTarget(this, operations);
+        target.setTransfer(transferTypes);
+        target.addDropListener(listener);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/internal/forms/widgets/WrappedPageBook.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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 org.eclipse.ui.internal.forms.widgets.WrappedPageBook;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.ui.forms.widgets.ILayoutExtension;
+
+import java.lang.all;
+import java.util.Set;
+
+/**
+ * A pagebook is a composite control where only a single control is visible at
+ * a time. It is similar to a notebook, but without tabs.
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.
+ * </p>
+ */
+public class WrappedPageBook : Composite {
+    class PageBookLayout : Layout, ILayoutExtension {
+        protected Point computeSize(Composite composite, int wHint, int hHint,
+                bool flushCache) {
+            if (wHint !is SWT.DEFAULT && hHint !is SWT.DEFAULT)
+                return new Point(wHint, hHint);
+            Point result = null;
+            if (currentPage !is null) {
+                result = currentPage.computeSize(wHint, hHint, flushCache);
+            } else {
+                result = new Point(0, 0);
+            }
+            return result;
+        }
+        protected void layout(Composite composite, bool flushCache) {
+            if (currentPage !is null) {
+                currentPage.setBounds(composite.getClientArea());
+            }
+        }
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.ui.forms.widgets.ILayoutExtension#computeMaximumWidth(org.eclipse.swt.widgets.Composite,
+         *      bool)
+         */
+        public int computeMaximumWidth(Composite parent, bool changed) {
+            return computeSize(parent, SWT.DEFAULT, SWT.DEFAULT, changed).x;
+        }
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.ui.forms.widgets.ILayoutExtension#computeMinimumWidth(org.eclipse.swt.widgets.Composite,
+         *      bool)
+         */
+        public int computeMinimumWidth(Composite parent, bool changed) {
+            return computeSize(parent, 0, SWT.DEFAULT, changed).x;
+        }
+    }
+    /**
+     * The current control; <code>null</code> if none.
+     */
+    private Control currentPage = null;
+    /**
+     * Creates a new empty pagebook.
+     *
+     * @param parent
+     *            the parent composite
+     * @param style
+     *            the SWT style bits
+     */
+    public this(Composite parent, int style) {
+        super(parent, style);
+        setLayout(new PageBookLayout());
+    }
+    /**
+     * Shows the given page. This method has no effect if the given page is not
+     * contained in this pagebook.
+     *
+     * @param page
+     *            the page to show
+     */
+    public void showPage(Control page) {
+        if (page is currentPage)
+            return;
+        if (page.getParent() !is this)
+            return;
+        Control oldPage = currentPage;
+        currentPage = page;
+        // show new page
+        if (page !is null) {
+            if (!page.isDisposed()) {
+                //page.setVisible(true);
+                layout(true);
+                page.setVisible(true);
+            }
+        }
+        // hide old *after* new page has been made visible in order to avoid
+        // flashing
+        if (oldPage !is null && !oldPage.isDisposed())
+            oldPage.setVisible(false);
+    }
+    public Point computeSize(int wHint, int hHint, bool changed) {
+        return (cast(PageBookLayout) getLayout()).computeSize(this, wHint, hHint,
+                changed);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/packageimport.rb	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,61 @@
+require 'find'
+require 'fileutils'
+require 'set'
+
+def ProcessModule( path, modName, modList )
+    puts "ProcessModule #{path}"
+    identifiers = Set.new
+    File.open(path).each_line do |s|
+        if s =~ /^[ \t]*\*/ then
+            next
+        end
+        s.split( /[^0-9A-Za-z_]/ ).each do |tok|
+            if tok =~ /^[_A-Z][_A-Za-z0-9]*$/ then
+                identifiers << tok
+            end
+        end
+    end
+    w=File.new(path+".new","w+")
+    File.open(path).each_line do |s|
+        if s =~/\/\/ packageimport$/ then
+        else
+            w.puts s
+        end
+        if s =~ /^module +((([a-zA-Z0-9_]+)\.)*)([a-zA-Z0-9_]+);/ then
+            packname = $1
+            (modList-modName).intersection(identifiers).each do|id|
+                w.puts "import #{packname}#{id};"
+            end
+        end
+    end
+end
+
+def ProcessDirectory( path )
+    puts path
+    modList = Set.new
+    Dir.foreach(path) do |entry|
+        filename = File.join(path,entry)
+        if FileTest.file?(filename) && filename =~ /\.d$/ then
+            modList << entry[ 0 .. -3 ]
+        end
+    end
+    Dir.foreach(path) do |entry|
+        filename = File.join(path,entry)
+        if FileTest.file?(filename) && filename =~ /\.d$/ then
+            ProcessModule( filename, entry[ 0 .. -3 ], modList )
+        end
+    end
+end
+
+ARGV.each do|startpath|
+    puts "processing #{startpath}"
+    if !FileTest.directory?(startpath) then
+        raise "Argument is not a directory"
+    end
+    Find.find( startpath ) do |path|
+        if FileTest.directory?(path) then
+            ProcessDirectory(path)
+        end
+    end
+end
+
--- a/rakefile	Fri Mar 13 17:44:24 2009 +0100
+++ b/rakefile	Sat Mar 14 18:23:29 2009 +0100
@@ -12,7 +12,7 @@
 OBJDIR="obj"
 DIMPDIR="imp"
 RSPNAME="rsp"
-ALL_RESDIRS= [ "java/res", "org.eclipse.swt.win32.win32.x86/res" ]
+ALL_RESDIRS= [ "java/res", "org.eclipse.swt.win32.win32.x86/res", "res" ]
 
 class String
     def to_path
@@ -21,6 +21,7 @@
 end
 
 def buildTree( basedir, srcdir, resdir )
+    puts "Building #{basedir} #{srcdir}"
 
     objdir_abs = File.expand_path( OBJDIR )
     dimpdir_abs = File.expand_path( DIMPDIR )
@@ -31,7 +32,6 @@
     FileUtils.mkdir_p dimpdir_abs
     FileUtils.mkdir_p objdir_abs
 
-    puts "Create response file"
     rsp = File.new( rspfile_abs, "w+" )
     rsp.puts "-H"
     rsp.puts "-I#{srcdir_abs.to_path}"
@@ -50,7 +50,6 @@
     rsp.close
 
     Dir.chdir(basedir) do
-        puts "Compile"
         sh "#{DMD} @#{rspfile_abs.to_path}" do |ok, res|
 
             if !ok then
@@ -65,7 +64,6 @@
         end
     end
 
-    puts "Move D Headers"
     Find.find( srcdir_abs ) do |path|
         if FileTest.file?(path) && path =~ /\.di$/ then
             trgfile = File.join( dimpdir_abs, path[ srcdir_abs.length+1 .. -1 ])
@@ -74,7 +72,6 @@
         end
     end
 
-    puts "Move Objects"
     srcdirparts = split_all( srcdir_abs ).length
     Find.find( srcdir_abs ) do |path|
         if FileTest.file?(path) && path =~ /\.o(bj)?$/ then
@@ -104,6 +101,17 @@
 
 desc "Build jface"
 task :jface do
-    buildTree( "org.eclipse.jface", "src", "res" )
+    # buildTree( "org.eclipse.osgi", "src", "res" )
+    # buildTree( "org.eclipse.osgi", "supplement/src", "res" )
+    # buildTree( "org.eclipse.equinox.common", "src", "res" )
+    # buildTree( "org.eclipse.core.runtime", "src", "res" )
+    # buildTree( "org.eclipse.core.commands", "src", "res" )
+    # buildTree( "org.eclipse.core.jobs", "src", "res" )
+    # buildTree( "org.eclipse.jface", "src", "res" )
+    buildTree( "org.eclipse.text", "src", "res" )
+    buildTree( "org.eclipse.jface.text", "src", "res" )
+    buildTree( "org.eclipse.jface.text", "projection", "res" )
+    buildTree( "org.eclipse.ui.forms", "src", "res" )
+    buildTree( "org.eclipse.draw2d", "src", "res" )
 end
 
Binary file res/eclipse-red-128.png has changed
Binary file res/eclipse-red-16.png has changed
Binary file res/eclipse-red-24.png has changed
Binary file res/eclipse-red-256.png has changed
Binary file res/eclipse-red-32.png has changed
Binary file res/eclipse-red-48.png has changed
Binary file res/eclipse-red-64.png has changed
Binary file res/icons.full.help.gif has changed
Binary file res/icons.full.message_error.gif has changed
Binary file res/icons.full.message_info.gif has changed
Binary file res/icons.full.message_warning.gif has changed
Binary file res/icons.full.page.gif has changed
Binary file res/icons.full.pref_dialog_title.gif has changed
Binary file res/icons.full.title_banner.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.core.internal.jobs.messages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,20 @@
+###############################################################################
+# 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
+###############################################################################
+### Runtime jobs plugin messages
+
+### Job Manager and Locks
+jobs_blocked0=The user operation is waiting for background work to complete.
+jobs_blocked1=The user operation is waiting for \"{0}\" to complete.
+jobs_internalError=An internal error occurred during: \"{0}\".
+jobs_waitFamSub={0} work item(s) left.
+
+### metadata
+meta_pluginProblems = Problems occurred when invoking code from plug-in: \"{0}\".
Binary file res/org.eclipse.draw2d.checkboxenabledoff.gif has changed
Binary file res/org.eclipse.draw2d.checkboxenabledon.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.action.ExternalActionManager.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,12 @@
+###############################################################################
+# 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
+###############################################################################
+
+undefinedCommand.WarningMessage = The command ("{0}") is undefined
Binary file res/org.eclipse.jface.action.images.stop.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.bindings.keys.formatting.AbstractKeyFormatter.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,74 @@
+###############################################################################
+# 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
+###############################################################################
+
+# Modifier keys
+ALT=Alt
+COMMAND=Command
+CTRL=Ctrl
+SHIFT=Shift
+
+# Special keys
+ARROW_DOWN=Down
+ARROW_LEFT=Left
+ARROW_RIGHT=Right
+ARROW_UP=Up
+BREAK=Break
+CAPS_LOCK=CapsLock
+END=End
+F1=F1
+F10=F10
+F11=F11
+F12=F12
+F2=F2
+F3=F3
+F4=F4
+F5=F5
+F6=F6
+F7=F7
+F8=F8
+F9=F9
+HOME=Home
+INSERT=Insert
+NUM_LOCK=NumLock
+NUMPAD_0=Numpad_0
+NUMPAD_1=Numpad_1
+NUMPAD_2=Numpad_2
+NUMPAD_3=Numpad_3
+NUMPAD_4=Numpad_4
+NUMPAD_5=Numpad_5
+NUMPAD_6=Numpad_6
+NUMPAD_7=Numpad_7
+NUMPAD_8=Numpad_8
+NUMPAD_9=Numpad_9
+NUMPAD_ADD=Numpad_Add
+NUMPAD_DECIMAL=Numpad_Decimal
+NUMPAD_DIVIDE=Numpad_Divide
+NUMPAD_ENTER=Numpad_Enter
+NUMPAD_EQUAL=Numpad_Equal
+NUMPAD_MULTIPLY=Numpad_Multiply
+NUMPAD_SUBTRACT=Numpad_Subtract
+PAGE_DOWN=PageDown
+PAGE_UP=PageUp
+PAUSE=Pause
+PRINT_SCREEN=PrintScreen
+SCROLL_LOCK=ScrollLock
+
+# Character keys
+BS=Backspace
+CR=Enter
+DEL=Delete
+ESC=Esc
+FF=FormFeed
+LF=LineFeed
+NUL=Null
+SPACE=Space
+TAB=Tab
+VT=VerticalTab
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.bindings.keys.formatting.EmacsKeyFormatter.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,19 @@
+###############################################################################
+# 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
+###############################################################################
+
+# Modifier keys
+ALT=M
+COMMAND=D
+CTRL=C
+SHIFT=S
+
+# Delimiters
+KEY_DELIMITER=-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.bindings.keys.formatting.NativeKeyFormatter.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,14 @@
+###############################################################################
+# Copyright (c) 2000, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+
+KEY_DELIMITER=+
+CARBON_KEY_DELIMITER=
+WIN32_KEY_STROKE_DELIMITER=, 
Binary file res/org.eclipse.jface.dialogs.images.help.gif has changed
Binary file res/org.eclipse.jface.dialogs.images.message_error.gif has changed
Binary file res/org.eclipse.jface.dialogs.images.message_info.gif has changed
Binary file res/org.eclipse.jface.dialogs.images.message_warning.gif has changed
Binary file res/org.eclipse.jface.dialogs.images.popup_menu.gif has changed
Binary file res/org.eclipse.jface.dialogs.images.popup_menu_disabled.gif has changed
Binary file res/org.eclipse.jface.dialogs.images.title_banner.gif has changed
Binary file res/org.eclipse.jface.fieldassist.images.contassist_ovr.gif has changed
Binary file res/org.eclipse.jface.fieldassist.images.error_ovr.gif has changed
Binary file res/org.eclipse.jface.fieldassist.images.errorqf_ovr.gif has changed
Binary file res/org.eclipse.jface.fieldassist.images.info_ovr.gif has changed
Binary file res/org.eclipse.jface.fieldassist.images.required_field_cue.gif has changed
Binary file res/org.eclipse.jface.fieldassist.images.warn_ovr.gif has changed
Binary file res/org.eclipse.jface.images.dots_button.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.internal.text.html.HTMLMessages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 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
+###############################################################################
+
+
+HTMLTextPresenter.ellipse= ...
+
+# The following property value must end with a space
+HTML2TextReader.listItemPrefix=\t-\ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.internal.text.link.contentassist.ContentAssistMessages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,20 @@
+###############################################################################
+# 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
+###############################################################################
+
+
+InfoPopup.info_delay_timer_name=AdditionalInfo Delay
+
+ContentAssistant.assist_delay_timer_name=AutoAssist Delay
+
+HTMLTextPresenter.ellipse= ...
+
+# The following property value must end with a space
+HTML2TextReader.listItemPrefix=\t-\ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.messages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,209 @@
+###############################################################################
+# 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
+###############################################################################
+# NLS file for JFace
+#############################################################
+
+#############################################################
+# Global label definitions
+#############################################################
+ok=OK
+cancel=Cancel
+yes=&Yes
+yestoall=Yes To &All
+skip=S&kip
+stop=&Stop
+abort=&Abort
+retry=&Retry
+ignore=&Ignore
+proceed=&Proceed
+open=&Open
+close=&Close
+showDetails=&Details >>
+hideDetails=<< &Details
+backButton=< &Back
+nextButton=&Next >
+finish=&Finish
+help=&Help
+helpToolTip=Help
+no=&No
+notoall=No &To All
+finish=&Finish
+help=&Help
+defaults=Restore &Defaults
+apply=&Apply
+openBrowse=&Browse...
+
+openChange=&Change...
+help=&Help
+close=&Close
+copy = &Copy
+
+error=Error
+warning=Warning
+info=Info
+question=Question
+
+##############################################################
+# Wizard Closing Dialog
+##############################################################
+WizardClosingDialog.title=Wizard Closing
+WizardClosingDialog.message=Wizard can not be closed due to an active operation. You must cancel the operation before you can close the wizard.
+
+##############################################################
+# Wizard Dialog
+##############################################################
+WizardDialog.missingSetControl = {0} has no page control
+
+##############################################################
+# The Progress Monitor Dialog
+##############################################################
+ProgressMonitorDialog.title=Progress Information
+ProgressMonitorDialog.message=Operation in progress...
+
+##############################################################
+# Preference Dialog
+##############################################################
+PreferenceDialog.title=Preferences
+PreferenceDialog.saveErrorTitle=Problem Saving Preferences
+PreferenceDialog.saveErrorMessage=A problem was encountered saving the page {0}.\n{1}
+
+##############################################################
+# Change Errors
+##############################################################
+PreferenceStore.changeError=Error notifying a preference change listener. Check the log for details.
+FontRegistry.changeError=Error notifying a font registry change listener. Check the log for details.
+		
+##############################################################
+# Abort Page Flipping Dialog
+##############################################################
+AbortPageFlippingDialog.title=Could Not Accept Changes 
+AbortPageFlippingDialog.message=The currently displayed page contains invalid values.
+
+##############################################################
+# Field editors
+##############################################################
+StringFieldEditor.errorMessage=Field contains an invalid value
+IntegerFieldEditor.errorMessage=Value must be an Integer
+IntegerFieldEditor.errorMessageRange=Value must be an Integer between {0} and {1}
+FileFieldEditor.errorMessage=Value must be an existing file
+FileFieldEditor.errorMessage2=Value must be an absolute path
+DirectoryFieldEditor.errorMessage=Value must be an existing directory
+ListEditor.add = Ne&w...
+ListEditor.remove = &Remove
+ListEditor.up = &Up
+ListEditor.down = Dow&n
+
+##############################################################
+# Error messages for TextViewer
+##############################################################
+TextViewer.invalidRangeArg=Invalid range argument
+TextViewer.invalidVisibleRegionArg=Invalid visible region argument
+
+#############################################################
+# org.eclipse.jface.action 
+#############################################################
+Cancel_Current_Operation = Cancel Current Operation
+Set_SubTask = {0} {1}
+Toolbar_Tooltip_Accelerator = {0} ({1})
+
+#############################################################
+# org.eclipse.jface.dialog
+############################################################
+Problem_Occurred = Problem Occurred
+Reason = {0}\n\nReason:\n {1}
+MessageDialogWithToggle.defaultToggleMessage = &Remember my decision
+PopupDialog.resize = &Resize
+PopupDialog.move = &Move
+PopupDialog.persistBounds = R&emember Size and Location
+PopupDialog.menuTooltip = Menu
+
+############################################################
+
+Image_not_found = Image not found
+<empty_selection> = <empty selection>
+
+########################################################
+# Font properties labels for displaying a font
+#######################################################
+BoldItalicFont = bold italic
+BoldFont = bold
+ItalicFont = italic
+RegularFont = regular
+
+#############################################################
+# String representations of keys for Status line messages
+############################################################
+Ctrl = Ctrl
+Command = Command
+Alt = Alt
+Shift = Shift
+Backspace = Backspace
+Tab = Tab
+Return = Return
+Enter = Enter
+Escape = Escape
+Esc = Esc
+Delete = Delete
+Space = Space
+Arrow_Up = Arrow Up
+Arrow_Down = Arrow Down
+Arrow_Left = Arrow Left
+Arrow_Right = Arrow Right
+Page_Up = Page Up
+Page_Down = Page Down
+Home = Home
+End = End
+Insert = Insert
+F1 = F1
+F2 = F2
+F3 = F3
+F4 = F4
+F5 = F5
+F6 = F6
+F7 = F7
+F8 = F8
+F9 = F9
+F10 = F10
+F11 = F11
+F12 = F12
+
+##############################################
+# Safe Runnable
+##############################################
+SafeRunnable.errorMessage = An error has occurred. See error log for more details.
+ColorSelector.Name=Color Selector
+
+#############################################################
+# org.eclipse.jface.viewers.deferred 
+#############################################################
+Sorting = sorting
+
+
+#############################################################
+# org.eclipse.jface.util 
+#############################################################
+LocalSelectionTransfer.errorMessage=Received wrong transfer data. 
+
+#############################################################
+# org.eclipse.jface.fieldAssist
+#############################################################
+FieldDecorationRegistry.errorMessage=Error in field content
+FieldDecorationRegistry.contentAssistMessage=Content Assist Available 
+FieldDecorationRegistry.requiredFieldMessage=Required Field
+FieldDecorationRegistry.errorQuickFixMessage=Error in field content.  Quick Fix Available.
+
+###############################################################
+# org.eclipse.jface.util.SafeRunnableDialog
+#############################################################
+SafeRunnableDialog_reason=Reason
+SafeRunnableDialog_checkDetailsMessage=Check the details
+SafeRunnableDialog_MultipleErrorsTitle=Multiple Errors have Occurred
+SafeRunnableDialog_MultipleErrorsMessage=Multiple operations have reported errors. Select an error to view its details.
Binary file res/org.eclipse.jface.preference.images.pref_dialog_title.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.resource.jfacefonts.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,23 @@
+###############################################################################
+# Copyright (c) 2000, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+# Generic NLS font file for JFace
+# *.0 is Windows font
+# *.1 is Unix font
+#############################################################
+dwtx.jface.bannerfont.0=MS Sans Serif-bold-10
+dwtx.jface.bannerfont.1=b&h-lucidabright-regular-10
+
+dwtx.jface.headerfont.0=MS Sans Serif-bold-12
+dwtx.jface.headerfont.1=b&h-lucidabright-regular-12
+
+dwtx.jface.textfont.0=Courier New-regular-10
+dwtx.jface.textfont.1=Courier-regular-10
+dwtx.jface.textfont.2=b&h-lucidabright-regular-9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.resource.jfacefonts_aix.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2000, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+# Aix font file for JFace
+#############################################################
+dwtx.jface.bannerfont.0=adobe-courier-bold-14
+dwtx.jface.headerfont.0=misc-fixed-bold-14
+dwtx.jface.textfont.0=adobe-courier-regular-12
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.resource.jfacefonts_hp-ux.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2000, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+# HP UX font file for JFace
+#############################################################
+dwtx.jface.bannerfont.0=adobe-courier-bold-14
+dwtx.jface.headerfont.0=adobe-courier-regular-14
+dwtx.jface.textfont.0=adobe-courier-regular-14
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.resource.jfacefonts_linux.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2000, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+# Linux font file for JFace
+#############################################################
+dwtx.jface.bannerfont.0=adobe-courier-bold-14
+dwtx.jface.headerfont.0=misc-fixed-bold-14
+dwtx.jface.textfont.0=adobe-courier-regular-12
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.resource.jfacefonts_linux_gtk.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2000, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+# Linux GTK font file for JFace
+#############################################################
+dwtx.jface.bannerfont.0=Sans-bold-10
+dwtx.jface.headerfont.0=Sans-bold-12
+dwtx.jface.textfont.0=Monospace-regular-10
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.resource.jfacefonts_macosx.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2000, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+# Carbon font file for JFace
+#############################################################
+dwtx.jface.bannerfont.0=Lucida Grande-bold-12
+dwtx.jface.headerfont.0=Lucida Grande-bold-14
+
+dwtx.jface.textfont.0=Monaco-regular-11
+dwtx.jface.textfont.1=Courier-regular-12
+dwtx.jface.textfont.2=Courier New-regular-12
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.resource.jfacefonts_qnx.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,18 @@
+###############################################################################
+# 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
+###############################################################################
+# QNX font file for JFace
+#############################################################
+dwtx.jface.bannerfont.0=TextFont-bold-9
+dwtx.jface.headerfont.0=TextFont-bold-9
+dwtx.jface.textfont.0=TextFont-regular-9
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.resource.jfacefonts_sunos.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2000, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+# Solaris font file for JFace
+#############################################################
+dwtx.jface.bannerfont.0=adobe-courier-bold-14
+dwtx.jface.headerfont.0=misc-fixed-bold-14
+dwtx.jface.textfont.0=adobe-courier-regular-12
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.resource.jfacefonts_windows2000.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,19 @@
+###############################################################################
+# Copyright (c) 2000, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+# NLS font file for JFace when running under Windows 2000
+#############################################################
+dwtx.jface.bannerfont.0=Tahoma-bold-10
+
+dwtx.jface.headerfont.0=Tahoma-bold-12
+
+dwtx.jface.textfont.0=Courier New-regular-10
+dwtx.jface.textfont.1=Courier-regular-10
+dwtx.jface.textfont.2=Lucida Console-regular-9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.resource.jfacefonts_windows98.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,19 @@
+###############################################################################
+# Copyright (c) 2000, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+# NLS font file for JFace when running under Windows 98
+#############################################################
+dwtx.jface.bannerfont.0=MS Sans Serif-bold-10
+
+dwtx.jface.headerfont.0=MS Sans Serif-bold-12
+
+dwtx.jface.textfont.0=Courier New-regular-10
+dwtx.jface.textfont.1=Courier-regular-10
+dwtx.jface.textfont.2=Lucida Console-regular-9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.resource.jfacefonts_windowsnt.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,19 @@
+###############################################################################
+# Copyright (c) 2000, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+# NLS font file for JFace when running under Windows NT
+#############################################################
+dwtx.jface.bannerfont.0=MS Sans Serif-bold-10
+
+dwtx.jface.headerfont.0=MS Sans Serif-bold-12
+
+dwtx.jface.textfont.0=Courier New-regular-10
+dwtx.jface.textfont.1=Courier-regular-10
+dwtx.jface.textfont.2=Lucida Console-regular-9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.resource.jfacefonts_windowsxp.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,19 @@
+###############################################################################
+# Copyright (c) 2000, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+# NLS font file for JFace when running under Windows XP
+#############################################################
+dwtx.jface.bannerfont.0=Tahoma-bold-10
+
+dwtx.jface.headerfont.0=Tahoma-bold-12
+
+dwtx.jface.textfont.0=Courier New-regular-10
+dwtx.jface.textfont.1=Courier-regular-10
+dwtx.jface.textfont.2=Lucida Console-regular-9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.text.JFaceTextMessages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,45 @@
+###############################################################################
+# 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
+###############################################################################
+
+
+TextViewer.error.bad_location.WidgetCommand.setEvent= TextViewer.WidgetCommand.setEvent: BadLocationException
+TextViewer.error.bad_location.findAndSelect= TextViewer.findAndSelect: BadLocationException
+TextViewer.error.bad_location.getBottomIndex= TextViewer.getBottomIndex: BadLocationException
+TextViewer.error.bad_location.getBottomIndexEndOffset= TextViewer.getBottomIndexEndOffset: BadLocationException
+TextViewer.error.bad_location.getFirstCompleteLineOfRegion= TextViewer.getFirstCompleteLineOfRegion: BadLocationException
+TextViewer.error.bad_location.getTopIndex= TextViewer.getTopIndex: BadLocationException
+TextViewer.error.bad_location.getTopIndexStartOffset= TextViewer.getTopIndexStartOffset: BadLocationException
+TextViewer.error.bad_location.selectContentTypePlugin= TextViewer.selectContentTypePlugin: BadLocationException
+TextViewer.error.bad_location.setTopIndex_1= TextViewer.setTopIndex: BadLocationException
+TextViewer.error.bad_location.setTopIndex_2= TextViewer.setTopIndex: BadLocationException
+TextViewer.error.bad_location.shift_1= TextViewer.shift: BadLocationException
+TextViewer.error.bad_location.shift_2= TextViewer.shift: BadLocationException
+TextViewer.error.bad_location.verifyText= TextViewer.verifyText: BadLocationException
+TextViewer.error.invalid_range= Invalid range argument
+TextViewer.error.invalid_visible_region_1= Invalid visible region argument
+TextViewer.error.invalid_visible_region_2= Invalid visible region argument
+
+AbstractHoverInformationControlManager.hover.restarter= Hover Restart Delay
+
+# The first parameter is the annotation type label and the second is the number of annotations
+OverviewRulerHeader.toolTipTextEntry= {0}: {1}
+
+DefaultUndoManager.operationLabel= Typing
+DefaultUndoManager.error.undoFailed.title= Undo Failed
+DefaultUndoManager.error.redoFailed.title= Redo Failed
+
+DefaultAnnotationHover.multipleMarkers= Multiple markers at this line
+# The parameter is the text of the list item
+DefaultAnnotationHover.listItem= - {0}
+
+# The parameter is the line number
+DefaultAnnotationHover.lineNumber=Line: {0}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.text.RegExMessages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,535 @@
+###############################################################################
+# Copyright (c) 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
+#     Cagatay Calli <ccalli@gmail.com> - [find/replace] retain caps when replacing - https://bugs.eclipse.org/bugs/show_bug.cgi?id=28949
+#     Cagatay Calli <ccalli@gmail.com> - [find/replace] define & fix behavior of retain caps with other escapes and text before \C - https://bugs.eclipse.org/bugs/show_bug.cgi?id=217061
+###############################################################################
+
+## Content Assist for regular expressions ##
+# use \\\u0075 for a backslash-u
+displayString_bs_bs= \\\\ - Backslash
+additionalInfo_bs_bs= Backslash
+displayString_bs_0= \\0nnn - Octal character code
+additionalInfo_bs_0= Octal character code\n\nExamples:\n\\011 (tabulator)\n\\0112 (character J)
+displayString_bs_x= \\xhh - Hex character code
+additionalInfo_bs_x= Hexadecimal character code\n\nExamples:\n\\x09 (tabulator)\n\\x4A or \\x4a (character J)
+displayString_bs_u= \\\u0075hhhh - Hex code for Unicode character
+additionalInfo_bs_u= Hexadecimal code for Unicode character\n\nExamples:\n\\\u0075004A (character J)\n\\\u007503B2 (lowercase Greek letter beta: \u03B2)
+displayString_bs_t= \\t - Tab
+additionalInfo_bs_t= Tabulator (\\x09, decimal: 9)
+displayString_bs_R= \\R - Line delimiter (platform independent)
+additionalInfo_bs_R= Line delimiter (platform independent)\n\n\
+This pattern matches any form of line delimiter, i.e.\n\
+- Windows (\\r\\n)\n\
+- Unix (\\n)\n\
+- Mac OS 9 (\\r)\n\n\
+Note that this pattern does not work inside [].
+displayString_bs_n= \\n - Newline
+additionalInfo_bs_n= Newline (\\x0A, decimal: 10)\n\n\
+WARNING: \\n only finds newline characters. \
+This can lead to unexpected results when the actual document uses different line delimiters.\n\n\
+RECOMMENDATION: use \\R to find a line delimiter.
+displayString_bs_r= \\r - CR
+additionalInfo_bs_r= Carriage Return (\\x0D, decimal: 13)\n\n\
+WARNING: \\r only finds carriage return characters. \
+This can lead to unexpected results when the actual document uses different line delimiters.\n\n\
+RECOMMENDATION: use \\R to find a line delimiter.
+displayString_bs_f= \\f - FF
+additionalInfo_bs_f= Form Feed (\\x0C, decimal: 12)
+displayString_bs_a= \\a - Beep
+additionalInfo_bs_a= Beep, Bell, Alert (\\x07, decimal: 7)
+displayString_bs_e= \\e - Esc
+additionalInfo_bs_e= Escape (\\x1B, decimal: 27)
+displayString_bs_c= \\cC - Control character
+additionalInfo_bs_c= Control character for C\n\nExample:\n\\cC (Ctrl+C, \\x03, decimal: 3)
+
+displayString_dot= . - Any character
+additionalInfo_dot= The dot matches any character except line terminators.\n\n\
+To make the dot match line terminators as well, \n\
+start the expression with the embedded flag expression \n\
+"(?s)" (without quotes).
+displayString_bs_d= \\d - A digit
+additionalInfo_bs_d= A digit: [0-9]
+displayString_bs_D= \\D - Not a digit
+additionalInfo_bs_D= Not a digit: [^0-9]
+displayString_bs_s= \\s - A whitespace
+additionalInfo_bs_s= A whitespace: [ \\t\\n\\x0B\\f\\r]
+displayString_bs_S= \\S - Not a whitespace
+additionalInfo_bs_S= Not a whitespace: [^\\s]
+displayString_bs_w= \\w - An alphanumeric (word character)
+additionalInfo_bs_w= An alphanumeric (a word character): [a-zA-Z_0-9]
+displayString_bs_W= \\W - Not an alphanumeric
+additionalInfo_bs_W= Not an alphanumeric (not a word character): [^\\w]
+
+displayString_start= ^ - Line start
+additionalInfo_start= Line start (positional match)\n\nExample:\n\
+The expression "^Eclipse" matches the term "Eclipse"\n\
+only on the second line of text\n\
+"The Eclipse Project\n\
+Eclipse Platform".
+displayString_end= $ - Line end
+additionalInfo_end= Line end (positional match)\n\nExample:\n\
+The expression "Eclipse$" matches the term "Eclipse"\n\
+only on the second line of text\n\
+"- Install the Eclipse Platform\n\
+- Run Eclipse".
+displayString_bs_b= \\b- Word beginning or end
+additionalInfo_bs_b= Word beginning or end (positional match)\n\nExample:\n\
+The expression "s\\b" matches only the last "s" of "glasses" in text\n\
+"I lost my glasses."
+displayString_bs_B= \\B - Not a word beginning or end
+additionalInfo_bs_B= Not a word beginning or end (positional match)\n\nExample:\n\
+The expression "\\BS" matches only "S" of "printString" in text\n\
+"void print(String printString)".
+displayString_bs_A= \\A - Start of input
+additionalInfo_bs_A= Start of input (positional match)\n\nExample:\n\
+The expression "\\ABC" matches only "BC" of "BCD" in text\n\
+"BCD ABC\n\
+BCDEF".
+displayString_bs_G= \\G - Previous match's end
+additionalInfo_bs_G= Previous match's end (positional match)\n\nExample:\n\
+The expression "\\Ga" matches the first and then the second "a" in text\n\
+"aardvark" (when starting from the beginning).
+displayString_bs_Z= \\Z - End of input, does not consider last line terminator
+additionalInfo_bs_Z= End of input, does not consider last line terminator (positional match)\n\n\
+The expression matches at the end of the file, except for when the\n\
+file ends in a line terminator, in which case it matches before that\n\
+line terminator.\n\nExample:\n\
+The expression "ing\\Z" matches "ing" in text\n\
+"testing", as well as in text\n\
+"testing\n\
+", but doesn't match in text\n\
+"testing\n\
+\n\
+"
+displayString_bs_z= \\z - End of input
+additionalInfo_bs_z= End of input (positional match)\n\nExample:\n\
+The expression "ing\\z" matches "ing" in text\n\
+"testing", but doesn't match in text\n\
+"testing\n\
+"
+
+### repetition quantifiers ###
+displayString_quest= ? - Greedy match 0 or 1 times
+additionalInfo_quest= Greedy match 0 or 1 times.\n\n\
+First tries to match the preceding token.\n\
+Falls back to not matching if this choice made a full match impossible.\n\nExample:\n\
+The expression "fo?" matches "f", "fo", and "fo" in text\n\
+"f fo foo".
+displayString_star= * - Greedy match 0 or more times
+additionalInfo_star= Greedy match 0 or more times.\n\n\
+First tries to match the preceding token as many times as possible.\n\
+Falls back to matching it less often if this choice made a full match impossible.\n\nExamples:\n\
+- The expression "fo*" matches "f", "fo", and "foo" in text\n\
+"f fo foo".\n\
+- The expression "fo*o\\d" matches all three words in text\n\
+"fo1 foo2 fooo3".\n\
+- The expression "<.*>" matches the whole text\n\
+"<p><b>bold</b>".
+displayString_plus= + - Greedy match 1 or more times
+additionalInfo_plus= Greedy match 1 or more times\n\n\
+First tries to match the preceding token as many times as possible.\n\
+Falls back to matching it less often if this choice made a full match impossible.\n\nExamples:\n\
+- The expression "fo+" matches "fo" and "foo" in text\n\
+"f fo foo".\n\
+- The expression "fo+o\\d" matches "foo2" and "fooo3" in text\n\
+"fo1 foo2 fooo3".\n\
+- The expression "<.+>" matches the whole text\n\
+"<p><b>bold</b>", but does not match anywhere in "<>".
+displayString_exact= {n} - Greedy match exactly n times
+additionalInfo_exact= Greedy match exactly n times.\n\nExamples:\n\
+- The expression "\\\\0[0-3][0-7]{2}" matches all three-digit octal character tokens.\n\
+- The expression "\\b\\w{4}\\b" matches all four-letter-words\n\
+such as "Java", "cool", or "food" (but not "dog").
+displayString_least= {n,} - Greedy match >= n times
+additionalInfo_least= Greedy match >= n times.\n\n\
+First tries to match the preceding token as many times as possible.\n\
+Falls back to matching it less often (but at least n times),\n\
+if this choice made a full match impossible.\n\nExamples:\n\
+- The expression "fo{2,}" matches "foo" and "fooo" in text\n\
+"f fo foo fooo".\n\
+- The expression "fo{2,}o\\d" matches "fooo3" and "foooo4" in text\n\
+"fo1 foo2 fooo3 foooo4".\n\
+- The expression "10{3,}[^0]" matches all powers of ten that are larger than one thousand.\n\n\
+Note: The expressions "{0,}" and "*" are equivalent;\n\
+likewise, "{1,}" is equivalent to "+".
+displayString_count= {n,m} - Greedy match >= n times but <= m times
+additionalInfo_count= Greedy match >= n times but <= m times.\n\n\
+First tries to match the preceding token m times.\n\
+Falls back to matching it less often (but at least n times),\n\
+if this choice made a full match impossible.\n\nExamples:\n\
+- The expression "fo{1,2}" matches "fo", "foo", and "foo" in text\n\
+"f fo foo fooo".\n\
+- The expression "fo{1,2}o\\d" matches "foo2" and "fooo3" in text\n\
+"fo1 foo2 fooo3 foooo4".\n\
+- The expression "^.{70,80}$" matches all the lines that contain\n\
+between 70 and 80 characters (inclusive).
+
+displayString_questLazy= ?? - Lazy match 0 or 1 times
+additionalInfo_questLazy= Lazy match 0 or 1 times.\n\n\
+First tries to not match the preceding token.\n\
+Falls back to matching it if this choice made a full match impossible.\n\nExample:\n\
+The expression "fo??" matches "f", "f", and "f" in text\n\
+"f fo foo".
+displayString_starLazy= *? - Lazy match 0 or more times
+additionalInfo_starLazy= Lazy match 0 or more times.\n\n\
+First tries to not match the preceding token.\n\
+Falls back to matching it more often if this choice made a full match impossible.\n\nExamples:\n\
+- The expression "fo*?" matches "f", "f", and "f" in text\n\
+"f fo foo".\n\
+- The expression "fo*?o\\d" matches all three words in text\n\
+"fo1 foo2 fooo3".\n\
+- The expression "<.*?>" matches "<p>", "<b>", and "</b>" in text\n\
+"<p><b>bold</b>". Note: a more performant expression for finding\n\
+xml tags is "<[^>]*>", which avoids backtracking.
+displayString_plusLazy= +? - Lazy match 1 or more times
+additionalInfo_plusLazy= Lazy match 1 or more times\n\n\
+First tries to match the preceding token once.\n\
+Falls back to matching it more often if this choice made a full match impossible.\n\nExamples:\n\
+- The expression "fo+?" matches "fo" and "fo" in text\n\
+"f fo foo".\n\
+- The expression "fo+?o\\d" matches "foo2" and "fooo3" in text\n\
+"fo1 foo2 fooo3".\n\
+- The expression "<.+?>" matches "<p>", "<b>", and "</b>" in text\n\
+"<p><b>bold</b>". Note: a more performant expression for finding\n\
+xml tags is "<[^>]*>", which avoids backtracking.
+displayString_exactLazy= {n}? - Lazy match exactly n times
+additionalInfo_exactLazy= Lazy match exactly n times.\n\n\
+This expression is equivalent to the expression\n\
+{n} - Greedy match exactly n times.
+displayString_leastLazy= {n,}? - Lazy match >= n times
+additionalInfo_leastLazy= Lazy match >= n times.\n\n\
+First tries to match the preceding token n times. Falls back to\n\
+matching it more often, if this choice made a full match impossible.\n\nExamples:\n\
+- The expression "fo{2,}?" matches "foo" and "foo" in text\n\
+"f fo foo fooo".\n\
+- The expression "fo{2,}?o\\d" matches "fooo3" and "foooo4" in text\n\
+"fo1 foo2 fooo3 foooo4".\n\
+- The expression "10{3,}?[^0]" matches all powers of ten that are larger than one thousand.\n\n\
+Note: The expressions "{0,}?" and "*?" are equivalent;\n\
+likewise, "{1,}?" is equivalent to "+?".
+displayString_countLazy= {n,m}? - Lazy match >= n times but <= m times
+additionalInfo_countLazy= Lazy match >= n times but <= m times.\n\n\
+First tries to match the preceding token n times.\n\
+Falls back to matching it more often (but at most m times),\n\
+if this choice made a full match impossible.\n\nExamples:\n\
+- The expression "fo{1,2}?" matches "fo", "fo", and "fo" in text\n\
+"f fo foo fooo".\n\
+- The expression "fo{1,2}?o\\d" matches "foo2" and "fooo3" in text\n\
+"fo1 foo2 fooo3 foooo4".\n\
+
+displayString_questPoss= ?+ - Possessive match 0 or 1 times (no backtracking)
+additionalInfo_questPoss= Possessive match 0 or 1 times.\n\n\
+Matches the preceding token if possible. Never backtracks,\n\
+even if this choice renders a full match impossible.\n\nExample:\n\
+The expression "fo?+o\\d" matches the first, but not the second line in text\n\
+"foo1\n\
+fo1".
+displayString_starPoss= *+ Possessive match 0 or more times (no backtracking)
+additionalInfo_starPoss= Possessive match 0 or more times.\n\n\
+Tries to match the preceding token as many times as possible. Never backtracks,\n\
+even if this choice renders a full match impossible.\n\nExamples:\n\
+- The expression "fo*+" matches "f", "fo" and "foo" in text\n\
+"f fo foo".\n\
+- The expression "fo*+o\\d" matches nowhere in text\n\
+"fo1 foo2 fooo3".\n\
+- The expression "<.*+>" matches nowhere in text\n\
+"<p><b>bold</b>".
+displayString_plusPoss= ++ - Possessive match 1 or more times (no backtracking)
+additionalInfo_plusPoss= Possessive match 1 or more times.\n\n\
+Tries to match the preceding token as many times as possible. Never backtracks,\n\
+even if this choice renders a full match impossible.\n\nExamples:\n\
+- The expression "fo++" matches "fo" and "foo" in text\n\
+"f fo foo".\n\
+- The expression "fo++o\\d" matches nowhere in text\n\
+"fo1 foo2 fooo3".\n\
+- The expression "<.++>" matches nowhere in text\n\
+"<p><b>bold</b>".
+
+displayString_exactPoss= {n}+ - Possessive match exactly n times (no backtracking)
+additionalInfo_exactPoss= Possessive match exactly n times.\n\n\
+This expression is equivalent to the expression\n\
+{n} - Greedy match exactly n times.
+displayString_leastPoss= {n,}+ - Possessive match >= n times (no backtracking)
+additionalInfo_leastPoss= Possessive match >= n times.\n\n\
+Tries to match the preceding token as many times as possible, but at least n times.\n\
+Never backtracks, even if this choice renders a full match impossible.\n\nExamples:\n\
+- The expression "fo{2,}+" matches "foo" and "fooo" in text\n\
+"f fo foo fooo".\n\
+- The expression "fo{2,}?o\\d" matches nowhere in text\n\
+"fo1 foo2 fooo3 foooo4".\n\
+Note: The expressions "{0,}?" and "*?" are equivalent;\n\
+likewise, "{1,}?" is equivalent to "+?".
+
+displayString_countPoss= {n,m}+ - Possessive match >= n times but <= m times (no backtracking)
+additionalInfo_countPoss= Possessive match >= n times but <= m times.\n\n\
+Tries to match the preceding token as many times as possible, \n\
+at least n times and at most m times.\n\
+Never backtracks, even if this choice renders a full match impossible.\n\nExamples:\n\
+- The expression "fo{1,2}+" matches "fo", "foo", and "foo" in text\n\
+"f fo foo fooo".\n\
+- The expression "fo{1,2}+o\\d" matches only "fooo3" in text\n\
+"fo1 foo2 fooo3 foooo4".\n\
+- The expression "^.{70,80}+$" matches all the lines that contain\n\
+between 70 and 80 characters (inclusive).
+
+displayString_alt= U|V - Alternation: U or V
+additionalInfo_alt= Alternation.\n\n\
+First tries to match subexpression U. Falls back and tries to match V if U didn't match.\n\nExamples:\n\
+- The expression "A|B" applied to text "BA" first matches "B", then "A".\n\
+- The expression "AB|BC|CD" applied to text "ABC BC DAB" matches, in sequence:\n\
+"AB" in the first word, the second word "BC", "AB" at the very end.
+displayString_group= (Expr) - Mark Expr as capturing group
+additionalInfo_group= Mark Expr as capturing group.\n\n\
+Capturing groups are numbered by counting their opening parentheses from left to right.\n\
+In the expression "((A)(B(C)))", for example, there are four such groups:\n\
+1   ((A)(B(C)))\n\
+2   (A)\n\
+3   (B(C))\n\
+4   (C)\n\
+\n\
+Group zero always stands for the entire expression. During a match,\n\
+each subsequence of the input sequence that matches such a group is saved.\n\
+The captured subsequence i may be used later in the expression, via a back reference "\\i",\n\
+and may also be used in the replace string via "$i".\n\
+\n\
+Note: Groups beginning with (? are pure, non-capturing groups that\n\
+do not capture text and do not count towards the group total.
+
+displayString_bs_i= \\i - Match of the capturing group i
+additionalInfo_bs_i= Match of the capturing group i.\n\n\
+\\i matches the subsequence that has already been saved as capturing group i.\n\
+\\0 is not a valid group number in the regular expression.\n\nExample:\n\
+The expression "(\\d+)\\+\\1" matches "10+10" in text "9+10+10+11".\n\
+\n\
+Note: in the replace string, $i stands for the capturing group i. 
+
+displayString_bs= \\ - Quote next character
+additionalInfo_bs= Quote next character\n\nExample:\n\
+The expression "\\{\\n\\}" matches the text "{n}".
+
+displayString_bs_Q= \\Q - Start quoting
+additionalInfo_bs_Q= Start quoting\n\n\
+All characters between \\Q and the next \\E are taken literally and are not interpreted.\n\nExample:\n\
+The expression "\\Qnew int[] {42}\\E;" matches text "new int[] {42}".
+displayString_bs_E= \\E - End quoting
+additionalInfo_bs_E= End quoting\n\n\
+All characters between \\Q and the next \\E are taken literally and are not interpreted.\n\nExample:\n\
+The expression "\\Qnew int[] {42}\\E;" matches text "new int[] {42}".
+
+displayString_set= [ecl] - Character set
+additionalInfo_set= Character set\n\n\
+Matches a single character out of the set.\n\nExample:\n\
+The expression "[ecl]" matches "c" and "l" in text "cold".
+displayString_setExcl= [^ecl] - Excluded character set
+additionalInfo_setExcl= Excluded character set\n\n\
+Matches a single character that is not one of the excluded characters.\n\nExamples:\n\
+The expression "[^ecl]" matches "o" and "d" in text "cold".\n\
+The expression "[a-z&&[^ecl]]" matches any character from a to z, excluding e, c, and l.
+displayString_setRange= [c-l] - Character range
+additionalInfo_setRange= Character range\n\n\
+Matches a single character out of the range from 'c' to 'l'.\n\nExamples:\n\
+The expression "[c-l]" matches "c", "l", and "d" in text "cold".\n\
+The expression "[a-z&&[^ecl]]" matches any character from a to z, excluding e, c, and l.
+displayString_setInter= && - Intersection of character sets
+additionalInfo_setInter= Intersection of character sets\n\n\
+Matches a character that is in both of the given sets.\n\nExample:\n\
+The expression "[a-z&&[^ecl]]" matches any character from a to z, excluding e, c, and l.
+
+displayString_posix= \\p{Class} - POSIX or Unicode character class
+additionalInfo_posix= POSIX or Unicode character class\n\n\
+Matches a character from the given character class 'Class'.\n\
+Valid classes are:\n\
+\n\
+- POSIX character classes (US-ASCII only):\n\
+\    Lower, Upper, ASCII, Alpha, Digit, Alnum, Punct,\n\
+\    Graph, Print, Blank, Cntrl, XDigit, and Space.\n\
+\n\
+- Unicode blocks (with the prefix 'In'), e.g.:\n\
+\    InBasicLatin\n\
+\    InLatin-1Supplement\n\
+\    InGreek\n\
+\n\
+- Unicode categories, e.g.:\n\
+\    Lu: Uppercase Letter\n\
+\    Ll: Lowercase Letter\n\
+\    L:  Letter\n\
+\    N:  Number\n\
+\    Z:  Separator\n\
+\    LD: Letter or Digit\n\
+\    L1: Latin-1
+
+displayString_posixNot= \\P{Class} - Excluded POSIX or Unicode character class
+additionalInfo_posixNot= Excluded POSIX or Unicode character class\n\n\
+Negation of character set \\p{Class}. Example:\n\
+\\P{ASCII} is equivalent to [^\\p{ASCII}] and matches all non-ASCII characters.\n\n\
+Valid classes are:\n\
+\n\
+- POSIX character classes (US-ASCII only):\n\
+\    Lower, Upper, ASCII, Alpha, Digit, Alnum, Punct,\n\
+\    Graph, Print, Blank, Cntrl, XDigit, and Space.\n\
+\n\
+- Unicode blocks (with the prefix 'In'), e.g.:\n\
+\    InBasicLatin\n\
+\    InLatin-1Supplement\n\
+\    InGreek\n\
+\n\
+- Unicode categories, e.g.:\n\
+\    Lu: Uppercase Letter\n\
+\    Ll: Lowercase Letter\n\
+\    L:  Letter\n\
+\    N:  Number\n\
+\    Z:  Separator\n\
+\    LD: Letter or Digit\n\
+\    L1: Latin-1
+
+
+#Flags:
+displayString_flag= (?ismd-ismd) - Turn flags on or off
+additionalInfo_flag= Turn flags on and off for the rest of the matching process.\n\n\
+Flags before the dash are turned on; those after the dash are turned off.\n\
+The following flags are supported:\n\
+- i: case-insensitive matching\n\
+\n\
+- s: single-line, or dotall matching mode:\n\
+\        The expression . matches any character, including a line terminator.\n\
+\n\
+- m: multiline matching mode:\n\
+\        The expressions ^ and $ match just after or just before,\n\
+\        respectively, a line terminator or the end of the input sequence.\n\
+\        When multiline matching is turned off, these expressions only\n\
+\        match at the beginning and the end of the entire input sequence.\n\
+\        This flag is ON by default.\n\
+\n\
+- d: Unix lines matching mode:\n\
+\        Only the '\\n' line terminator\n\
+\        is recognized in the behavior of ., ^, and $
+# - u: unicode-aware case folding:\n\
+#        Case-insensitive matching, when enabled, is done in a manner consistent\n\
+#        with the Unicode Standard. By default, case-insensitive matching\n\
+#        assumes that only characters in the US-ASCII charset are being matched.
+# - c: canonical equivalence\n\
+#        Two characters will be considered to match if, and only if, their full\n\
+#        canonical decompositions match. The expression "a\\\u0075030A", for example,\n\
+#        will match the string "a\u030A" when this flag is specified.\n\
+#        By default, matching does not take canonical equivalence into account.
+# - x: comments mode\n\
+#        Whitespace is ignored, and embedded comments starting with\n\
+#        # are ignored until the end of a line.\n\
+
+displayString_flagExpr= (?ismd-ismd:Expr) - Turn flags on or off in Expr
+additionalInfo_flagExpr= Turn flags on and off in Expr.\n\n\
+Flags before the dash are turned on; those after the dash are turned off.\n\
+The following flags are supported:\n\
+- i: case-insensitive matching\n\
+\n\
+- s: single-line, or dotall matching mode:\n\
+\        The expression . matches any character, including a line terminator.\n\
+\n\
+- m: multiline matching mode:\n\
+\        The expressions ^ and $ match just after or just before,\n\
+\        respectively, a line terminator or the end of the input sequence.\n\
+\        When multiline matching is turned off, these expressions only\n\
+\        match at the beginning and the end of the entire input sequence.\n\
+\        This flag is ON by default.\n\
+\n\
+- d: Unix lines matching mode:\n\
+\        Only the '\\n' line terminator\n\
+\        is recognized in the behavior of ., ^, and $
+
+
+#Noncapturing groups:
+displayString_nonCap= (?:Expr) - Non-capturing group
+additionalInfo_nonCap= Non-capturing group of regular expression Expr.\n\n\
+The group is not saved in a back reference.\n\nExample:\n\
+The expression "(?:\\w+) (\\d+)" matches "bug 42" in text "It's bug 42.".\n\
+A back reference "$1" in the replace string will be replaced by "42".
+
+displayString_atomicCap= (?>Expr) - Non-capturing atomic group
+additionalInfo_atomicCap= Non-capturing atomic group of regular expression Expr.\n\n\
+Matches the regular expression Expr once, but does not backtrack into the expression\n\
+again if the first match did not prove to be successful later on.\n\
+The group is not saved in a back reference.
+
+#Lookaround:
+displayString_posLookahead= (?=Expr) - Zero-width positive lookahead
+additionalInfo_posLookahead= Expr, via zero-width positive lookahead.\n\n\
+Matches a position (zero-width: does not consume the matched characters),\n\
+where the next characters (-> lookahead)\n\
+do match (-> positive) the embedded expression Expr.\n\nExamples:\n\
+- The expression "var(?==)" matches only the first "var" in text "var=17; other=var;".\n\
+- The expression "\\b(?=\\w{7}\\b)\\w*clip\\w*\\b" matches any\n\
+seven-letter-word that contains "clip". It matches "Eclipse", but not "paperclip".
+
+displayString_negLookahead= (?!Expr) - Zero-width negative lookahead
+additionalInfo_negLookahead= Expr, via zero-width negative lookahead.\n\n\
+Matches a position (zero-width: does not consume the matched characters),\n\
+where the next characters (-> lookahead)\n\
+do not match (-> negative) the embedded expression Expr.\n\nExamples:\n\
+- The expression "var(?!=)" matches only the second "var" in text "var=17; other=var;".\n\
+- The expression "\\b(?!\\w{5,7}\\b)\\w*clip\\w*\\b" matches any\n\
+word that contains "clip" and consists of less than 5 or more than 7 characters.\n\
+It matches "clip" and "paperclip", but not "Eclipse".
+
+displayString_posLookbehind= (?<=Expr) - Zero-width positive lookbehind
+additionalInfo_posLookbehind= Expr, via zero-width positive lookbehind.\n\n\
+Matches a position (zero-width: does not consume the matched characters),\n\
+where the previous characters (-> lookbehind)\n\
+do match (-> positive) the embedded expression Expr.\n\nExample:\n\
+- The expression "\\w{5,}+(?<=as)\\b" matches "alias" and "bananas",\n\
+but does not match "peas", "apples", or "Alaska".
+
+displayString_negLookbehind= (?<!Expr) - Zero-width negative lookbehind
+additionalInfo_negLookbehind= Expr, via zero-width negative lookbehind.\n\n\
+Matches a position (zero-width: does not consume the matched characters),\n\
+where the previous characters (-> lookbehind)\n\
+do not match (-> negative) the embedded expression Expr.\n\nExample:\n\
+- The expression "\\w{5,}+(?<!as)\\b" matches "Eclipse" and "apples",\n\
+but does not match "peas" or "bananas".
+
+#Replace string:
+displayString_dollar= $i - Match of the capturing group i
+additionalInfo_dollar= Match of the capturing group i.\n\n\
+$i is the string that has been saved as capturing group i.\n\
+$0 is the subsequence matched by the entire expression.\n\
+\n\
+Note: in the find expression, \\i stands for the capturing group i. 
+displayString_replace_cap= \\i - Match of the capturing group i
+additionalInfo_replace_cap= Match of the capturing group i.\n\n\
+\\i is the string that has been saved as capturing group i.\n\
+\\0 is the subsequence matched by the entire expression.\n\
+\n\
+Note: \\i is equivalent to $i 
+displayString_replace_bs= \\ - Quote next character
+additionalInfo_replace_bs= Quote next character\n\nExamples:\n\
+"\\$" will be replaced by "$".\n\
+"\\q" will be replaced by "q".\n\
+"\\\\" will be replaced by "\\".
+displayString_replace_bs_n= \\n - Newline
+additionalInfo_replace_bs_n= Newline (\\x0A, decimal: 10)\n\n\
+Note that \\n always inserts the newline character,\n\
+even if the document uses different line delimiters.\n\n\
+To insert the document line delimiter, use \\R.
+displayString_replace_bs_r= \\r - CR
+additionalInfo_replace_bs_r= Carriage Return (\\x0D, decimal: 13)\n\n\
+Note that \\r always inserts the carriage return character,\n\
+even if the document uses different line delimiters.\n\n\
+To insert the document line delimiter, use \\R.
+displayString_replace_bs_R= \\R - Line delimiter
+additionalInfo_replace_bs_R= Line delimiter\n\n\
+Inserts the default line delimiter of the document.
+displayString_replace_bs_C=\\C - Retain case
+additionalInfo_replace_bs_C=\\C - Retain casing of match (all lower case, all upper case, capitalized)\n\
+when replacing expression after \\C.\n\nExamples:\n\
+Find: "foo" Replace: "my\\Cbar\\CFar"\n\
+"foo" will be replaced by "mybarfar".\n\
+"FOO" will be replaced by "myBARFAR".\n\
+"Foo" will be replaced by "myBarFar".\n\n\
+Note that the content of a group ($i, \\i) is currently inserted unmodified.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.text.TextMessages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 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
+###############################################################################
+
+FindReplaceDocumentAdapter.illegalControlEscape= Illegal control escape sequence {0}
+FindReplaceDocumentAdapter.illegalHexEscape= Illegal hexadecimal escape sequence {0}
+FindReplaceDocumentAdapter.illegalLinebreak=Illegal position for \\R
+FindReplaceDocumentAdapter.illegalUnicodeEscape= Illegal Unicode escape sequence {0}
+FindReplaceDocumentAdapter.incompatibleLineDelimiter= Incompatible line delimiter
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.text.contentassist.JFaceTextMessages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,17 @@
+###############################################################################
+# Copyright (c) 2000, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+
+
+InfoPopup.info_delay_timer_name=Additional info timer
+AdditionalInfoController.job_name=Computing additional info
+
+ContentAssistant.assist_delay_timer_name=AutoAssist Delay
+CompletionProposalPopup.no_proposals=no proposals
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.text.hyperlink.HyperlinkMessages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 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
+###############################################################################
+
+LinkListInformationControl.unknownLink= Unknown Hyperlink
+
+MultipleHyperlinkPresenter.clickLinkAfordance =Click link to open
+
+URLHyperlink.hyperlinkText= Open ''{0}'' in a browser
Binary file res/org.eclipse.jface.text.source.projection.collapsed.gif has changed
Binary file res/org.eclipse.jface.text.source.projection.expanded.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.text.templates.JFaceTextTemplateMessages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,14 @@
+###############################################################################
+# 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
+###############################################################################
+
+# template proposal
+TemplateProposal.displayString= {0} - {1}
+TemplateProposal.errorDialog.title=Template Evaluation Error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.text.templates.TextTemplateMessages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,25 @@
+###############################################################################
+# Copyright (c) 2000, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+
+# template translator
+TemplateTranslator.error.incomplete.variable=Template has incomplete variables. Type '$$' if to enter the dollar character.
+TemplateTranslator.error.invalid.identifier=Template has invalid variable identifiers.
+TemplateTranslator.error.incompatible.type=Template variable ''{0}'' has incompatible types
+
+# global variables
+GlobalVariables.variable.description.cursor=The cursor position after editing template variables
+GlobalVariables.variable.description.dollar=The dollar symbol
+GlobalVariables.variable.description.date=Current date
+GlobalVariables.variable.description.year=Current year
+GlobalVariables.variable.description.time=Current time
+GlobalVariables.variable.description.user=User name
+GlobalVariables.variable.description.selectedWord= The selected word
+GlobalVariables.variable.description.selectedLines= The selected lines
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.jface.text.templates.persistence.TemplatePersistenceMessages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,14 @@
+###############################################################################
+# 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
+###############################################################################
+
+TemplateReaderWriter.duplicate.id= Duplicate template id
+TemplateReaderWriter.error.missing_attribute= Missing required attribute
+TemplateReaderWriter.error.illegal_boolean_attribute= Illegal boolean attribute, must be "true" or "false".
Binary file res/org.eclipse.jface.wizard.images.page.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.text.undo.UndoMessages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,12 @@
+###############################################################################
+# Copyright (c) 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
+###############################################################################
+
+DocumentUndoManager.operationLabel= Typing
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/org.eclipse.ui.internal.forms.Messages.properties	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,24 @@
+###############################################################################
+# 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
+###############################################################################
+FormText_copy=&Copy
+Form_tooltip_minimize=Minimize
+FormDialog_defaultTitle=Form Dialog
+Form_tooltip_restore=Restore
+
+#Message manager
+MessageManager_sMessageSummary = 1 message detected
+MessageManager_sWarningSummary = 1 warning detected
+MessageManager_sErrorSummary = 1 error detected
+MessageManager_pMessageSummary = {0} messages detected 
+MessageManager_pWarningSummary = {0} warnings detected
+MessageManager_pErrorSummary = {0} errors detected
+ToggleHyperlink_accessibleName=Expandable
+ToggleHyperlink_accessibleColumn=: